Hi 游客

更多精彩,请登录!

比特池塘 区块链技术 正文

Solidity通用模式

温室小书生室d
387 0 0
从合约中提款' g# ?  z: |9 D+ S/ l: c

- h! q; N) n2 N8 |9 b在某个操作之后发送资金的推荐方式是使用取回(withdrawal)模式。尽管在某个操作之后,最直接地发送以太币方法是一个 send 调用,& F# c3 t5 r+ ~8 ?  n
但这并不推荐;因为这会引入一个潜在的安全风险。你可能需要参考 :ref:security_considerations 来获取更多信息。
* `' p! \- [$ |; J7 z& J/ [# O这里是一个在合约中使用取回模式的示例,它目标是通过向合约发送最多的钱来成为“最富有的人”,2 ?. {  G! y! Q- ?- f8 Q% _6 L- L
其灵感来自 King of the Ether _。4 J6 r9 [0 q% J9 q4 n# T
在下边的合约中,如果你的“最富有”位置被其他人取代,你可以收到取代你成为“最富有”的人发送到合约的资金。" T9 S* d: S9 n# a6 I6 o+ r/ X: D
::
% `- n- ~  l- p8 W% E* bpragma solidity ^0.4.11;% O. A9 z0 H& C! ]
contract WithdrawalContract {# c9 i) N9 O9 E2 S6 `( Y
    address public richest;9 w6 j' O1 B* z+ z' t% T  `" Z6 x
    uint public mostSent;2 F7 v7 n/ `, |% ^6 [0 u# q
    mapping (address => uint) pendingWithdrawals;
) [2 c, Q2 T1 E$ j/ l    function WithdrawalContract() public payable {9 z, y' X' B+ o
        richest = msg.sender;
) d+ Q* p6 q/ U8 |1 d5 {* F        mostSent = msg.value;
, D9 r1 q3 Y9 a9 a: d( e    }
% R" @# k4 U: i" ?( s3 c% |' P4 {8 n    function becomeRichest() public payable returns (bool) {
9 r( ~  i  n: w7 T- m( d        if (msg.value > mostSent) {
2 r3 m& t9 o1 V& U+ P3 E6 R            pendingWithdrawals[richest] += msg.value;& {2 q1 j* \1 L3 q8 ]
            richest = msg.sender;
$ g4 E, d/ C" C            mostSent = msg.value;
% ?: M6 i& ^( w; I$ ?            return true;; k4 f& M6 D4 m( j' J. A
        } else {) A* Z: B& n, W& a& z
            return false;; b% ?8 ?' ~- h3 b) A
        }
9 s" E3 O/ Q! x    }
" `* Y2 V6 B2 z$ n    function withdraw() public {0 A0 N- l9 v8 l; F
        uint amount = pendingWithdrawals[msg.sender];2 y0 a3 |2 \; a8 i
        // 记住,在发送资金之前将待发金额清零
8 v$ K' K' T8 x- e1 d% T        // 来防止重入(re-entrancy)攻击& I3 y- y- E: J' c/ Y
        pendingWithdrawals[msg.sender] = 0;
! q: k# I* v8 T9 |" P        msg.sender.transfer(amount);) i; _& y& T2 w" T" }+ O/ [# [
    }3 M9 r% p1 ]3 q/ [6 Q9 A+ Y, A; |5 G
}
( S) D( i: m8 |1 g7 ]# R9 B下面是一个相反的直接使用发送模式的例子:& f+ Z' g+ E1 e
::
! i/ B) ?/ l. ~pragma solidity ^0.4.11;. |1 ?0 h0 F8 S# Z% G
contract SendContract {" r/ O% W6 t/ }1 }- Y9 }# C  q
    address public richest;) R" L) u- C1 z1 Y' b6 h: @. r+ n; n
    uint public mostSent;+ x, A$ I1 S* F: I
    function SendContract() public payable {
2 Q* `- H# }3 Q: Y! K7 ]* ~; q        richest = msg.sender;
# g' [: _, a3 s        mostSent = msg.value;) k& {6 _8 U4 `! X
    }
; Z1 T  w& {- R0 {; [$ W' C    function becomeRichest() public payable returns (bool) {
1 A9 c* o5 ~$ R" o0 `        if (msg.value > mostSent) {+ Y4 q! {. T& N6 J! _- e
            // 这一行会导致问题(详见下文)
% n8 d1 q3 e" |            richest.transfer(msg.value);7 b. D/ @4 X7 J6 N
            richest = msg.sender;4 _* h8 r! V0 ]' _5 v; b- l
            mostSent = msg.value;
9 A1 E$ l2 W  W' l5 B            return true;# h7 Q5 `4 g4 r/ d( l
        } else {
8 E& _2 \# q5 K2 s* D            return false;$ d' o$ I7 a# p: s0 h+ |- u
        }$ N, P" x0 O1 M. Y/ k6 Z( t% l
    }
3 L- d1 f1 d/ ]) e) P  ^8 ^}
" }- E0 Z5 G: p0 R注意,在这个例子里,攻击者可以给这个合约设下陷阱,使其进入不可用状态,比如通过使一个 fallback 函数会失败的合约成为 richest
# H3 t3 g6 G$ U( x2 K" J(可以在 fallback 函数中调用 revert() 或者直接在 fallback 函数中使用超过 2300 gas 来使其执行失败)。这样,当这个合约调用 transfer 来给“下过毒”的合约9 y  V+ V$ A/ E! N# M* L5 g
发送资金时,调用会失败,从而导致 becomeRichest 函数失败,这个合约也就被永远卡住了。/ V" S8 |8 j5 m
如果在合约中像第一个例子那样使用“取回(withdraw)”模式,那么攻击者只能使他/她自己的“取回”失败,并不会导致整个合约无法运作。
* b* o" P6 c8 f) y, k9 J… index:: access;restricting
/ C" D: O, ^% T4 p, a$ z% R5 X7 W/ {6 A. g# p/ C. V% |5 N1 d
限制访问
. q. h7 E7 q, H' N. A; t8 u
/ v8 C  p; R' p2 V. z, h; Y- f, `限制访问是合约的一个通用模式。注意,你不可能限制任何人或机器读取你的交易内容或合约状态。
+ b" u* |4 \; Q# `4 G你可以通过加密使这种访问变得困难一些,但如果你想让你的合约读取这些数据,那么其他人也将可以做到。
0 |5 d3 V* @) z" r$ Z0 g你可以限制 其他合约 读取你的合约状态。
% J4 O6 ]7 ]; n! z: a: U3 E; {, g这(其他合约不能读取你的合约状态)是默认的,除非你将合约状态变量声明为 public。6 z' B; Z" a# X% O8 Y8 Y
此外,你可以对谁可以修改你的合约状态或调用你的合约函数加以限制,这是本节要介绍的内容。
6 d9 i4 W8 P! e, |2 ^( ?) `… index:: function;modifier
4 v% R3 S9 \- h: u) W通过使用“函数 |modifier|”,可以使这些限制变得非常明确。; {, N' T0 |4 }/ i1 k
::( l9 V1 h$ V. D* X
pragma solidity ^0.4.22;$ c- W& {! U0 v# Y! t! C, D
contract AccessRestriction {7 e; _" L, L+ e
    // 这些将在构造阶段被赋值7 ~) @4 j; V) ?/ W( V
    // 其中,`msg.sender` 是( R8 C2 m1 U5 S. d' ?& I/ `7 ^
    // 创建这个合约的账户。" D4 O7 z) _8 `2 [
    address public owner = msg.sender;
% Q2 \  o& w3 A/ [# \; a    uint public creationTime = now;  t/ u4 W! t/ K& D* m9 K
    // 修饰器可以用来更改
1 K2 e$ D6 ?+ N7 k3 C" a5 ^+ Y    // 一个函数的函数体。
, L9 c' |2 t$ l: {3 R5 J" ?    // 如果使用这个修饰器,
9 j, m: M. b8 O$ g" k3 R1 g/ t8 h    // 它会预置一个检查,仅允许
4 ^5 c5 }+ e: `3 e1 E( y% }    // 来自特定地址的, D7 h/ e3 T& z9 _
    // 函数调用。& e( g1 N( K! h8 u: g+ b+ a
    modifier onlyBy(address _account)1 R" D  T0 Z' {: d! L1 b
    {6 E3 {+ {* Z+ E( N6 C
        require(
1 `- n! j+ T2 z/ Z, L1 ^            msg.sender == _account,
' V, N; o7 p  [8 {. ]/ c$ v* h7 p            "Sender not authorized."8 _6 p, q- ?! @7 ?0 {4 i2 B2 T1 o
        );, m, t8 E+ d# i  N
        // 不要忘记写 `_;`!
: ~) i) o& q4 y0 a1 g        // 它会被实际使用这个修饰器的  P; |: @4 P5 W: m0 G; s3 O
        // 函数体所替代。" U5 d- I7 G2 q0 T1 \& I
        _;7 Z: d- V( b0 M% W
    }9 B3 M8 Z4 J: @7 ^' g
    // 使 `_newOwner` 成为这个合约的
4 D) A. _0 t& w/ K; U    // 新所有者。" o7 ]7 h: f) r: H7 @
    function changeOwner(address _newOwner)
6 f# P$ g3 l7 O% V+ o0 F9 r5 z        public; Z2 P/ \6 z: j. e% o
        onlyBy(owner)5 E) J& {6 V) p" R+ M7 u
    {
; R+ D2 \; k: Z9 ^, y9 \        owner = _newOwner;
2 ~- W) e2 M) E  g: y; H/ p9 q, X    }- j) l% P0 J9 g. Q  A' |/ @
    modifier onlyAfter(uint _time) {& x$ v1 _. K6 A) {1 f
        require(8 f4 s  z" v& x8 f
            now >= _time,! C% ]9 N# y0 ^) x1 n: _; a
            "Function called too early."
) z: Z- c* K9 x8 e% G3 d- |8 r        );
' k/ k" N* _/ G- `1 f5 b$ B' s        _;! k# p5 Z& C4 g
    }) x& U9 ?, u5 g& n% K2 _2 o  o
    // 抹掉所有者信息。
! f( E6 a: S3 V% J6 H    // 仅允许在合约创建成功 6 周以后8 v1 u) G2 B( T0 j) B
    // 的时间被调用。
" ^" `, S- N. x# f( L    function disown(); ~  A1 ?% j- k, _. u1 K6 f" W) q
        public  Q8 a9 K- g8 Y* D, e
        onlyBy(owner)
% i, s1 i. k/ `. e        onlyAfter(creationTime + 6 weeks)
6 ~5 }6 J; M# F; I& O    {
' M  j$ a( ^" Z2 b% [        delete owner;
& I2 r! f  O) E    }
  u: A2 C. ?; B( U+ N# M    // 这个修饰器要求对函数调用
8 E: \9 g; y6 `8 y/ T( D    // 绑定一定的费用。
: |$ ]& G/ K! x1 p' @! I/ d    // 如果调用方发送了过多的费用,. G! [" i9 c6 ]6 e2 N2 c( [8 F% z
    // 他/她会得到退款,但需要先执行函数体。! l  }  e9 G2 ?
    // 这在 0.4.0 版本以前的 Solidity 中很危险,
: N3 d% ~" d7 g0 O& o2 ]' }7 S    // 因为很可能会跳过 `_;` 之后的代码。
* N: q. i* o' e# }$ ~    modifier costs(uint _amount) {
# _$ F7 f" R+ p$ E; `        require(
3 |+ x; P3 R4 q& F% ?8 n5 Y6 s            msg.value >= _amount,9 x" c+ ~& D3 J
            "Not enough Ether provided."
. t% r, z8 ?: _$ M) F; z  E4 ]0 V        );' s" h+ X6 w* p6 v8 A4 H3 v
        _;# A' T' R* [4 W1 ]
        if (msg.value > _amount); e- w* \3 Z8 d3 E$ c/ M
            msg.sender.send(msg.value - _amount);" C4 y7 K) B6 P
    }5 i; E) k. M# \( o+ @
    function forceOwnerChange(address _newOwner)
3 d# n3 I% L4 c, ?# r        public$ g* _6 m0 T; j: X
        payable9 R* p, j- h2 E. `
        costs(200 ether)* V/ @( U* ~& w
    {
. I0 h2 \; l& J5 S1 i! L) I8 s% a# {        owner = _newOwner;- Y2 p% K! L9 I! Y
        // 这只是示例条件' X6 c& \, x8 r
        if (uint(owner) & 0 == 1)
8 l6 X* ?. ]5 G' T' l  U            // 这无法在 0.4.0 版本之前的
; k1 |* o4 e0 V* J8 P' w; u            // Solidity 上进行退还。
0 T& M8 {9 I' V9 q# n            return;
1 c) W6 T6 \' c7 S) _; {. S" x3 P        // 退还多付的费用
  {2 h8 n- _) \8 _: u. q    }
, L& r( k7 A5 i2 P}
7 [8 i+ H0 \2 l4 @一个更专用地限制函数调用的方法将在下一个例子中介绍。
% P) h8 M' P5 @. l7 k: Z, E… index:: state machine
, U) h1 s+ S1 X- ]. q
) u7 q' U2 X8 y/ b9 M% d/ n状态机
9 Y+ ^! E' ^% i: Y" C) R3 c% {5 v% p! U0 a- d8 G# @- s% ^
合约通常会像状态机那样运作,这意味着它们有特定的 阶段,使它们有不同的表现或者仅允许特定的不同函数被调用。
" p; X3 M% X4 `4 X/ b8 v一个函数调用通常会结束一个阶段,并将合约转换到下一个阶段(特别是如果一个合约是以 交互 来建模的时候)。
  D1 c& C- D, @% H通过达到特定的 时间 点来达到某些阶段也是很常见的。
2 C7 X: |, I, |$ x1 p一个典型的例子是盲拍(blind auction)合约,它起始于“接受盲目出价”,* Y# l2 A3 ?" e6 u3 p/ W: ]
然后转换到“公示出价”,最后结束于“确定拍卖结果”。
" v) O7 F( K) F% a! L… index:: function;modifier
5 k, s/ O+ E: o8 A函数 |modifier| 可以用在这种情况下来对状态进行建模,并确保合约被正常的使用。
- o; B' o' I- K3 z" g示例
# S7 l2 Z( U& u! ~在下边的示例中, |modifier| atStage 确保了函数仅在特定的阶段才可以被调用。* Q: e/ I9 |4 p3 U
根据时间来进行的自动阶段转换,是由 |modifier| timeTransitions 来处理的,/ o8 k. h& h( m
它应该用在所有函数上。
. m4 [0 s* R1 f… note::; u& O( a1 W3 ~1 @
|modifier| 的顺序非常重要! H! k. H  O7 L; K9 n+ N
如果 atStage 和 timedTransitions 要一起使用,; `# w4 h# S; c8 K# \! f
请确保在 timedTransitions 之后声明 atStage,- b. A4 p* T- C! Z% Q' ?
以便新的状态可以
: ~7 J+ G0 u, K0 Y/ Z. V首先被反映到账户中。/ s1 ?+ p, R" ^
最后, |modifier| transitionNext 能够用来在函数执行结束时自动转换到下一个阶段。
* Q) h* F  R1 X) B) }, S9 j" D3 n… note::
% @  \+ z! t/ L  i3 a5 N! i|modifier| 可以被忽略. s& z7 {+ J  e: d
以下特性仅在 0.4.0 版本之前的 Solidity 中有效:8 M& G: b5 ?: n  V1 S
由于 |modifier| 是通过简单的替换代码
# M2 D9 Q6 @1 n. x) W. a而不是使用函数调用来提供的,+ S" Y: _( [$ {4 I
如果函数本身使用了 return,那么 transitionNext |modifier|3 f# {% T1 k# u% x0 L& v
的代码是可以被忽略的。
6 [# q& i+ M) s2 e7 g0 F如果你希望这么做,2 ~9 ~/ G8 O& f8 Y
请确保你在这些函数中手工调用了 nextStage。% l6 h* E3 A$ x' g
从 0.4.0 版本开始,即使函数明确地 return 了,
. D6 }' e8 I* V" W|modifier| 的代码也会执行。
/ W  F9 w6 ~3 j* [& J::
; u, N9 i/ V9 i3 N! Ppragma solidity ^0.4.22;
# g$ G2 Q5 i4 H0 N  x2 Q8 Ccontract StateMachine {
! k& Y5 W* Q. j$ d    enum Stages {$ V5 U$ _! N" Q; Y! M0 ~1 Q/ c* \
        AcceptingBlindedBids,
' K% @8 B& T( z, w+ s! c        RevealBids,
' c; g* L) l+ r6 k        AnotherStage,! F/ X9 s( w; I8 W
        AreWeDoneYet,  E8 W& W; C' O3 e% k! c) g, g
        Finished- o2 ?: C' o& S( X$ @! l
    }
1 d% O% E& i) h1 ~8 }7 \1 n    // 这是当前阶段。
* O9 t4 a$ R) b- S    Stages public stage = Stages.AcceptingBlindedBids;7 ~  A5 ]3 I3 n' O- a6 f1 W9 k2 U
    uint public creationTime = now;& x% ^6 ]. u! D2 d5 O  C3 O* i& q0 p$ N
    modifier atStage(Stages _stage) {8 \: Z$ O2 b- |% @. G
        require(
! p2 E. I' {3 ?& e( e            stage == _stage,
& o9 V/ X" d+ ]% N9 r4 d            "Function cannot be called at this time."
0 n  Z% r6 J5 |8 C: o8 C- k        );
, a7 D6 ?" G% }  f        _;
1 I1 x& m9 B6 N  H    }
5 X5 j+ O1 C8 n0 K- M7 b" _# C1 r5 g) X1 G    function nextStage() internal {
$ g) F+ E( u/ }        stage = Stages(uint(stage) + 1);: z1 E" {- K5 v1 }
    }
8 u4 s, R! k( r9 m& T; k5 N    // 执行基于时间的阶段转换。, H% K! \$ g" B! ]* M
    // 请确保首先声明这个修饰器,
# g9 A: y* Z$ B: [3 h8 K    // 否则新阶段不会被带入账户。- r, E, H. ]4 C
    modifier timedTransitions() {7 b' G6 O" m0 X2 C! M
        if (stage == Stages.AcceptingBlindedBids &&
; q+ ^3 I* B+ \/ Q! F                    now >= creationTime + 10 days)8 A% H$ H7 k" F
            nextStage();6 G. m7 P9 y4 J( e! {
        if (stage == Stages.RevealBids &&
, ^3 R" U3 l- w( m- W3 }& [2 L/ p                now >= creationTime + 12 days)8 i. K. X' E  |5 {4 u& [5 \" g
            nextStage();
" ^9 j/ n& B2 ~6 q& {, S" E/ `        // 由交易触发的其他阶段转换
  M4 v% t  o! A( [5 V, L: _        _;
& C$ r7 C7 W* W    }
9 u. @$ I* e& B3 f! ?8 K    // 这里的修饰器顺序非常重要!
4 Q9 b9 C% g2 r! M% c' l    function bid()# F( ]6 p2 M( D
        public
- J$ ^9 M8 Q6 ~# m; L' y0 o/ b/ C( x        payable
. A7 v" B3 m8 v( T. w        timedTransitions
* S+ Q) k& z8 ~" C: a9 P# F1 S        atStage(Stages.AcceptingBlindedBids)
1 {; \6 s3 Z& R* p% j, u: B: u" G! l1 o    {8 X) X% p; t- \% l
        // 我们不会在这里实现实际功能(因为这仅是个代码示例,译者注)
6 W* a1 V$ e  L; o1 N    }
( s) A8 s- A# e& g! `% j4 F/ c% ^    function reveal()
1 I* @9 }5 `5 J+ T) E- H        public
6 W9 n4 X) ?& s# n% p        timedTransitions/ i& L1 @# s  J3 R/ ?/ X
        atStage(Stages.RevealBids)
% W% M) R  S6 G0 E    {
$ K2 K* L/ u7 d& x# Z+ ^% J    }' }6 M6 [$ x- _. I6 J- B
    // 这个修饰器在函数执行结束之后
1 J( t4 I. W% g% N    // 使合约进入下一个阶段。: s' C$ }" ^& J' k- I
    modifier transitionNext()* ?! [' l# `( r  b: T
    {$ q( i3 C1 J4 k6 s, O0 n: B: G2 b* n, x
        _;
8 _/ B/ Z+ J* @6 K        nextStage();% H5 ^# k% O) c; [3 M
    }
6 U# D, G6 A& N8 s$ v    function g()
# T7 j: E6 }" d2 F* ?3 F% x        public
4 }. O. S% ^" I% p        timedTransitions1 V3 C8 J$ E5 ^0 s- b& F( t
        atStage(Stages.AnotherStage)  [- f3 d$ Z$ b
        transitionNext
0 Y1 j) z. L! h& d7 [& C. u" W6 E    {
9 E" |5 [0 D0 f# u    }, O0 Z% l& p2 J. W3 y
    function h()" {0 v: {! |3 \! j# ?* A
        public7 F. A6 M! o: ^9 |7 M8 u( _
        timedTransitions$ P/ T, W# J+ u: m5 \5 X
        atStage(Stages.AreWeDoneYet)8 Z' n" _8 ^8 ~  z, y3 ~* A
        transitionNext
1 F2 a2 f; S1 A7 W' J    {# Q, o9 _, Q5 J
    }# Q) o4 a8 Y3 I: o- {
    function i()( a# t8 r! t& J3 Q
        public4 ~9 J- U6 L* \! d) k
        timedTransitions
- L+ d  M( }; ]" j5 Y% l6 P        atStage(Stages.Finished)
5 V! }. G7 V0 d    {
7 w/ V& [) S, ~    }
+ S9 k/ u& ~" E& \4 {3 h) N}
6 u% z  L! f0 h& k原文:https://raw.githubusercontent.com/etherchina/solidity-doc-cn/develop/common-patterns.rst
标签: Solidity
BitMere.com 比特池塘系信息发布平台,比特池塘仅提供信息存储空间服务。
声明:该文观点仅代表作者本人,本文不代表比特池塘立场,且不构成建议,请谨慎对待。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

成为第一个吐槽的人

温室小书生室d 初中生
  • 粉丝

    0

  • 关注

    0

  • 主题

    13