Hi 游客

更多精彩,请登录!

比特池塘 区块链技术 正文

Solidity通用模式

温室小书生室d
526 0 0
从合约中提款' O. ?1 a8 Y+ S  _) V- C8 v7 u/ N

$ E  Y, H; A* S4 d( O9 {; C在某个操作之后发送资金的推荐方式是使用取回(withdrawal)模式。尽管在某个操作之后,最直接地发送以太币方法是一个 send 调用,
: W, ?, z( n& }/ z9 r( ^但这并不推荐;因为这会引入一个潜在的安全风险。你可能需要参考 :ref:security_considerations 来获取更多信息。2 i  ]  k9 J7 u0 ^$ j4 R
这里是一个在合约中使用取回模式的示例,它目标是通过向合约发送最多的钱来成为“最富有的人”,
( i/ i4 P( O& o# R其灵感来自 King of the Ether _。- W; n) N& {' [* x% d/ h
在下边的合约中,如果你的“最富有”位置被其他人取代,你可以收到取代你成为“最富有”的人发送到合约的资金。0 K( e4 f: Q& a
::) @# V7 F: G  Q: \1 E8 X! [% X
pragma solidity ^0.4.11;
2 D' s  O% T  kcontract WithdrawalContract {
' u7 t7 j+ F$ C9 M8 D, X    address public richest;
1 T; @! B3 r# V! _9 z& [0 q    uint public mostSent;. r9 O& n, p4 J5 M& j, H
    mapping (address => uint) pendingWithdrawals;7 d+ j# S+ c9 Z8 b8 S5 K' t
    function WithdrawalContract() public payable {0 T/ T4 Q) h. c1 ~0 w
        richest = msg.sender;
# b4 |; T2 a  y        mostSent = msg.value;
0 s! a+ y% c# Y- Z    }; {9 N' Z( s9 H  H" U+ q  R
    function becomeRichest() public payable returns (bool) {2 ^2 Q  ?& d. p! `  t: s
        if (msg.value > mostSent) {2 F4 G& h1 w3 Y7 `4 h7 w
            pendingWithdrawals[richest] += msg.value;& i" c4 y' z2 c: }
            richest = msg.sender;
! ~! W. w, r) m. z& E3 c7 g) @            mostSent = msg.value;
2 r2 t7 \/ L3 v7 m# H            return true;
0 \# ]  w9 K2 C. v, p; J0 B        } else {
8 X  j$ W" Y+ V  e            return false;% S, o' `# H- j6 X0 {4 Q
        }
* S& Q' P4 g% q0 ]% A3 f6 `    }. [1 I  e4 K! }
    function withdraw() public {
4 G" w" ]$ @* {5 q# y2 `- K- U        uint amount = pendingWithdrawals[msg.sender];
2 n, M" L" ^! @$ X( d8 E1 F* J        // 记住,在发送资金之前将待发金额清零
- y* {2 a+ @+ D( E& h0 K1 P        // 来防止重入(re-entrancy)攻击
* i3 D9 G! f7 g, Q# }" a& g+ {        pendingWithdrawals[msg.sender] = 0;9 j! F+ y# p9 Q3 O
        msg.sender.transfer(amount);
5 Y  o2 B( D/ L. n) E4 c    }
6 D2 u7 w5 M: V  s" _}. ~! k# \/ Z4 l6 H: _; |1 S" V
下面是一个相反的直接使用发送模式的例子:7 k* q% J; Y+ G: m& G1 F
::, [" I/ `' l0 o& P$ D
pragma solidity ^0.4.11;; J1 H' U* S+ n/ @; y1 F
contract SendContract {
" i' d7 D* d5 H7 V- _* B' B    address public richest;
2 z5 {* A2 e- @: k/ `    uint public mostSent;
* K. d+ j8 o0 o7 d    function SendContract() public payable {8 h4 Y8 U) `6 i0 M3 B
        richest = msg.sender;
% T& {9 O" _, m/ A  C4 k; n4 q# ]) E        mostSent = msg.value;
& k9 C% n1 H: U. j; g* n% u8 x    }
% G/ ^- g: B0 g1 S    function becomeRichest() public payable returns (bool) {, g7 H1 D, L* q6 c- I# X4 O2 k
        if (msg.value > mostSent) {3 M% A8 ?/ ?0 c: o! D  A3 u
            // 这一行会导致问题(详见下文)1 @( m  y% l9 ^4 l' I5 h
            richest.transfer(msg.value);
/ r1 ]! K; i* E7 [            richest = msg.sender;# h- k/ e9 u( J+ o5 L$ I; H# @
            mostSent = msg.value;# B0 |4 Y1 k  ~& V  h' b, ~8 R: n+ m
            return true;1 F" s6 b9 S9 ?% B3 l3 f
        } else {4 K9 l8 O0 G* ^1 |
            return false;
0 @7 h* T! u# A* A        }
7 w) v$ P' c; g3 v$ y    }$ k4 U% ]& M! L# t7 z3 ^
}8 t0 Z: n( x" a0 |% Y8 }' s
注意,在这个例子里,攻击者可以给这个合约设下陷阱,使其进入不可用状态,比如通过使一个 fallback 函数会失败的合约成为 richest
4 c6 n. e0 u/ t* m(可以在 fallback 函数中调用 revert() 或者直接在 fallback 函数中使用超过 2300 gas 来使其执行失败)。这样,当这个合约调用 transfer 来给“下过毒”的合约3 Y4 ?! J) `4 q% N. p" J8 Y+ E; F& w
发送资金时,调用会失败,从而导致 becomeRichest 函数失败,这个合约也就被永远卡住了。* h( B% T8 U7 O7 M. v
如果在合约中像第一个例子那样使用“取回(withdraw)”模式,那么攻击者只能使他/她自己的“取回”失败,并不会导致整个合约无法运作。
/ i! a* a- b' h5 ?9 L4 e3 _& {, J$ I… index:: access;restricting9 z+ g1 e& |- j6 ?* Q. u
) Z% S/ k3 ^; @* i4 ~6 p
限制访问1 p3 T3 h2 V  Y
/ B" `3 n, s' T9 r% [1 q, l
限制访问是合约的一个通用模式。注意,你不可能限制任何人或机器读取你的交易内容或合约状态。2 v; q; M1 U% q0 i( N
你可以通过加密使这种访问变得困难一些,但如果你想让你的合约读取这些数据,那么其他人也将可以做到。) k9 A: t3 b- Z* s3 ~
你可以限制 其他合约 读取你的合约状态。
# h" Z5 O8 o, Z% q+ ]* a: P这(其他合约不能读取你的合约状态)是默认的,除非你将合约状态变量声明为 public。0 X6 }4 l2 |; }9 _
此外,你可以对谁可以修改你的合约状态或调用你的合约函数加以限制,这是本节要介绍的内容。) R4 }, Y: ^( q' F
… index:: function;modifier
; y1 ^% @* I4 Q$ }通过使用“函数 |modifier|”,可以使这些限制变得非常明确。
+ x# M; p2 P7 k. G+ ?9 k/ I/ P8 C::7 W# e. H* X2 u( G1 }5 }* y
pragma solidity ^0.4.22;
. x" ?! J, K6 G7 p% Bcontract AccessRestriction {* Z& v$ r1 c- n4 I/ j) U
    // 这些将在构造阶段被赋值1 _( Z+ j3 W" M( ~- D
    // 其中,`msg.sender` 是
/ D6 f) M# U. |8 Z    // 创建这个合约的账户。
4 m6 y2 o7 V; t) k+ A$ K0 K8 ^    address public owner = msg.sender;
% P) f0 Q* _. C4 L9 L# j0 A: d* b    uint public creationTime = now;
9 ]4 r: `3 ]- K. X' a& l    // 修饰器可以用来更改
( E2 M# Y. }! w! k" P$ g    // 一个函数的函数体。
" @3 v1 V6 P6 [* a2 j    // 如果使用这个修饰器,, x3 `, Q8 `0 t. g4 R/ u. F
    // 它会预置一个检查,仅允许; O9 a5 U; b1 O! [* A( }; Y/ W% B2 V
    // 来自特定地址的: K! `$ @( o4 P' k9 _
    // 函数调用。1 r. E6 g7 D% Z7 T, [: j+ B
    modifier onlyBy(address _account)
# h- Q) k) K# f' D1 F    {5 G; _* R- W0 T- d8 A& Z
        require(8 z: S2 N! _  l9 C. N/ }
            msg.sender == _account,
6 B% H/ s9 `) H1 K9 E. L, V* H            "Sender not authorized."
: t2 R& t" W# D6 P- O/ P3 h" W8 _        );  b4 y) f$ s$ W; ~- ~
        // 不要忘记写 `_;`!
7 |# }% Y; g7 C1 k) }! s8 \        // 它会被实际使用这个修饰器的
, s1 |# w# a0 \& l2 Q        // 函数体所替代。" L7 e5 |# _- W, x. y
        _;; J2 g/ Q+ {5 V5 S! Q$ z, w& t
    }9 S7 E6 p* t6 v5 }  Y5 m
    // 使 `_newOwner` 成为这个合约的
0 h: u' w  ]) h/ F" D    // 新所有者。
! r5 \! @, L" z0 c4 @8 o1 l4 B    function changeOwner(address _newOwner)- o& ^; t' ?  U% p( t7 z
        public9 e" T5 T% C9 f3 P
        onlyBy(owner)
4 }% y6 z5 `) v; x9 C    {1 R/ J& N7 m$ |. t7 u+ M/ Y# P
        owner = _newOwner;6 C/ {5 B8 t6 U! l
    }
7 {9 D* z- Y" {0 t; c% W    modifier onlyAfter(uint _time) {
. O8 v, B8 P& f  a0 Q2 C) Q; u        require(
: z; l/ e' s4 [            now >= _time,
0 }) B$ @5 B# z5 @/ T            "Function called too early."/ \5 f6 l6 `5 s. o) O. C
        );5 J1 B% T6 d: F/ }) O
        _;% @& |4 t9 x$ R# M8 U- Z1 X; Y
    }
$ N  [4 _9 p# \/ a6 z    // 抹掉所有者信息。* u9 E+ c( D+ ?; @8 I9 x
    // 仅允许在合约创建成功 6 周以后. n2 M% _! N+ `( v- h
    // 的时间被调用。
- B! |  ], ~6 l5 m" N    function disown()5 D+ K9 {9 _% }  Z
        public- z, s  ~0 X9 \! @2 m* X
        onlyBy(owner)9 r3 u3 g4 x8 o5 m3 i: b& _
        onlyAfter(creationTime + 6 weeks); g+ c. Z3 R, O' i% F# @) j; w$ W  e
    {2 R3 a8 J) ]) M  Q; M! O
        delete owner;
$ N% K3 {3 {' E    }
1 W7 y: w7 K. S8 ~! m0 ?0 n- c0 m    // 这个修饰器要求对函数调用
4 S' x( ?0 [. h0 ]    // 绑定一定的费用。0 A- R7 B6 q+ }; ]# A/ E
    // 如果调用方发送了过多的费用,
9 `9 b, w* }( I# e' ?    // 他/她会得到退款,但需要先执行函数体。
! t2 C3 }' a2 `/ f    // 这在 0.4.0 版本以前的 Solidity 中很危险,3 |; I" T5 q% ]+ ~+ o" u
    // 因为很可能会跳过 `_;` 之后的代码。
8 Y+ g  R8 g" S! }4 ?    modifier costs(uint _amount) {
+ g, R# r8 n% q8 \6 m& Y        require(
6 L' f! ~7 ?6 [. ^  C3 N3 q            msg.value >= _amount,% q' Z) N+ m2 q$ B% `  _) s
            "Not enough Ether provided."
4 h5 y7 F( O0 ~# |2 ]0 m# g2 |        );5 l9 T, t% N! X* z# z2 J  |* p
        _;
1 X* h; ]0 L% [" x. {0 a7 H3 t0 ]        if (msg.value > _amount)4 ]% H. X+ O1 t0 t- b) R: Y
            msg.sender.send(msg.value - _amount);
2 Y" O5 S0 L3 R    }
+ \- Z  J. Z% _$ V- w/ x    function forceOwnerChange(address _newOwner)$ n4 D) ]7 `, h& M6 A! M1 k0 t
        public& r: r+ h% Z) `7 Y2 E! }' @* e
        payable
, D* j3 Q7 n! e0 l' j: ^        costs(200 ether)
4 ^1 ~) d) H7 E2 Q. N    {1 Y- x  `- [. u$ h" G
        owner = _newOwner;
7 {% k8 t) V; Z$ e        // 这只是示例条件
. H9 G' F( z; K6 C) S1 P7 c% X( B; P2 G& {9 g        if (uint(owner) & 0 == 1)
5 t( u' V! B# C$ I( W! R+ C            // 这无法在 0.4.0 版本之前的% N- s; c  k- Q: f& A, {
            // Solidity 上进行退还。
6 X/ D: ~! s" K            return;
9 N% |! _$ o5 z' B5 i; n  `) d        // 退还多付的费用# ~$ ?; S. u( Z- H9 S' c
    }
/ i" _7 U- q+ ~( z}3 p' x3 z# z- d! n9 Z( s
一个更专用地限制函数调用的方法将在下一个例子中介绍。- M" ?+ H# G9 u  \% g  K# N. N
… index:: state machine
9 }1 }* U. b" c- x' b! y' w8 \3 L. i, W& Q; b+ d7 O' `) b
状态机0 I3 j. ]+ ~1 {3 e: F( h. g
3 \: N% c: t7 H% d7 r8 M4 }/ U
合约通常会像状态机那样运作,这意味着它们有特定的 阶段,使它们有不同的表现或者仅允许特定的不同函数被调用。8 a- z+ I9 k+ u! [
一个函数调用通常会结束一个阶段,并将合约转换到下一个阶段(特别是如果一个合约是以 交互 来建模的时候)。, r* e! {) G; z
通过达到特定的 时间 点来达到某些阶段也是很常见的。5 P  N6 x5 Y" ~- e: t& Q6 @
一个典型的例子是盲拍(blind auction)合约,它起始于“接受盲目出价”,
& H6 O- W! x8 q  c- q7 N1 n然后转换到“公示出价”,最后结束于“确定拍卖结果”。. P, R* [) G) c% Y( J# ?/ k
… index:: function;modifier* s5 a" v- C0 u7 y& f2 I5 W
函数 |modifier| 可以用在这种情况下来对状态进行建模,并确保合约被正常的使用。5 u2 H2 V5 M1 k" i' ]: T
示例
3 C# q: g2 S( R* o- F8 F5 y, B在下边的示例中, |modifier| atStage 确保了函数仅在特定的阶段才可以被调用。! I. K: f5 D( b( T
根据时间来进行的自动阶段转换,是由 |modifier| timeTransitions 来处理的,
, i+ c* u; m5 j/ _4 v9 Y+ e它应该用在所有函数上。5 [6 @/ q* C4 H2 @
… note::* E8 m  [* f. t
|modifier| 的顺序非常重要
0 G5 z# _, B  p5 s+ W如果 atStage 和 timedTransitions 要一起使用,, u6 S4 l. z4 N, j" i1 u- ~; l( x
请确保在 timedTransitions 之后声明 atStage,( _$ s  Z6 F4 D" m5 g
以便新的状态可以6 ]& H+ r& z% d6 h
首先被反映到账户中。
4 ]  V9 n  h( B* V/ {9 V2 a$ P( |最后, |modifier| transitionNext 能够用来在函数执行结束时自动转换到下一个阶段。+ x3 f. K* {! H3 o- \' e4 I& N* }1 |
… note::
4 R" {3 ?  U. J: W" j|modifier| 可以被忽略
. p- x: c" `6 X2 U6 @$ D以下特性仅在 0.4.0 版本之前的 Solidity 中有效:
5 q4 K1 p2 c0 p) V+ w6 W9 @, w由于 |modifier| 是通过简单的替换代码' O! l: R; C; Z/ P% B8 J
而不是使用函数调用来提供的,
- G) C: b2 w$ H! z5 m& z! Y3 k如果函数本身使用了 return,那么 transitionNext |modifier|
/ H2 \$ c7 [) c/ d的代码是可以被忽略的。
4 T8 v" s2 n. ^8 e如果你希望这么做," Y! g6 b! ~7 E4 c. o  c
请确保你在这些函数中手工调用了 nextStage。5 }  p/ W/ w) ]! h
从 0.4.0 版本开始,即使函数明确地 return 了,6 p: g8 o; |6 j" j3 b" y' ^
|modifier| 的代码也会执行。( P9 s  `! u6 m
::& c/ y9 w# g( Z! G4 b& w" B  H
pragma solidity ^0.4.22;) d, D  p4 L5 a1 }) e5 u) V
contract StateMachine {* s6 x, x0 D3 n* ?2 j) I6 T$ Q
    enum Stages {
& b) s9 r" X* ?* ^; B' _        AcceptingBlindedBids,
& V$ v6 \* g8 t- h        RevealBids,
3 l8 [: I0 a% I+ t( @        AnotherStage,
) Z2 ^5 ?; Z/ b* m$ n        AreWeDoneYet,
3 Y* H2 n7 F/ \2 R1 U* P        Finished
3 \6 v6 j0 Q% H    }
2 \$ M1 x6 m$ R; F8 H) Y& {+ r    // 这是当前阶段。  q4 o" F; |* h* y6 Q& |* G
    Stages public stage = Stages.AcceptingBlindedBids;
) P) r( H$ M* B; b, h* U    uint public creationTime = now;
/ S" R- R+ [! A" b7 A    modifier atStage(Stages _stage) {
3 [1 c* r0 b4 g; U        require(
+ E7 R! ]+ ^' p8 O  i            stage == _stage,8 c  E; L, P. m2 d* I1 u, Y+ Z
            "Function cannot be called at this time."" n$ Y- ?" f7 g: ?
        );
* F2 b+ C# q. z6 q7 G4 Q6 M0 t        _;
( J, y9 B9 R& H- F    }
! x% H  S9 }6 G3 _; A8 {    function nextStage() internal {
9 x7 W! D4 p  v+ O4 W6 z        stage = Stages(uint(stage) + 1);
9 j1 @9 z0 t# F! P  I4 w  e5 g7 t    }
- [6 f3 f+ s4 h) ?+ K    // 执行基于时间的阶段转换。
9 n9 c/ {4 ]1 o8 Z, z! d& M    // 请确保首先声明这个修饰器,
( G. B  g% ]& l9 J1 z3 v  r* h" \: G' x    // 否则新阶段不会被带入账户。
1 a5 A, G3 t1 {: ?9 n8 O    modifier timedTransitions() {
  [% [0 K- @0 G" R8 g! S6 z2 |        if (stage == Stages.AcceptingBlindedBids &&
7 x; ~) F% |  ]. L4 p9 C                    now >= creationTime + 10 days)4 A' b( D6 T3 g1 Y2 n2 ~# Y/ `* C# {
            nextStage();
6 I3 v6 m' b2 W, z+ C: j6 Y        if (stage == Stages.RevealBids &&
5 d7 i9 `8 H. Q( a+ h7 T                now >= creationTime + 12 days)
( Y, d3 y+ {, a  a- i9 c: E2 S            nextStage();( i) B5 h+ _3 `, g8 c% g% X
        // 由交易触发的其他阶段转换# V7 |! B- \  q. k4 ~2 _
        _;* H, x  O; i% ?% f" U
    }; o# n, c) U8 n. n7 _
    // 这里的修饰器顺序非常重要!/ E9 L. T9 w: S/ X& C
    function bid()
3 Q2 q& G$ B* O' \$ P        public& L1 r( X. L& g
        payable# C7 T# K9 X/ J6 i- W$ l6 X. A5 O
        timedTransitions# _& R/ B) k4 ?$ x; i( h
        atStage(Stages.AcceptingBlindedBids), F# n- Y7 m. V' _9 I2 h+ o( m
    {$ _* A: n" ~9 P2 U8 F( D
        // 我们不会在这里实现实际功能(因为这仅是个代码示例,译者注)/ }$ N' ^  w. p  ]
    }
  X" S/ y4 f% L3 T+ m9 s    function reveal()" v! c; i$ P' k( _2 k
        public
3 m, D# v7 |/ t        timedTransitions
/ k* r! l+ d7 d4 \/ B* B- O        atStage(Stages.RevealBids)
, w9 G8 a# I7 Z: E    {
) L" L+ g& C3 [5 z4 C% p    }
2 X7 q0 q/ E# t2 n- [    // 这个修饰器在函数执行结束之后
7 m  g5 t5 w3 ~6 i4 V    // 使合约进入下一个阶段。
1 @; d- E9 B' d. ]" c2 R4 X% P    modifier transitionNext()- \! O$ Q: C, F
    {4 d3 _* A- u' G8 Y9 Z
        _;0 M1 @0 p' k1 F% P+ f
        nextStage();/ R- R( m# K9 a7 `7 \) d3 h( f$ J
    }- f/ R( N+ \$ k
    function g()9 u' V5 ]0 \* f
        public: ~9 u7 I6 b$ S! p
        timedTransitions+ M$ ~( ~5 ^2 p' ~
        atStage(Stages.AnotherStage)4 k4 M0 |* |- F
        transitionNext
. e) U4 G% g/ }+ L7 M6 v. x# Z7 K" q    {2 z* q  E# j2 N' W5 T1 ~
    }
. a' K" S0 l6 a' ]/ Z+ f+ ]3 n. Z    function h()
+ p7 s  j" ]. K9 Z6 [2 X        public' g, Z! Z8 b  g4 x
        timedTransitions
( k  W( q4 f: n6 x  n1 e/ L        atStage(Stages.AreWeDoneYet): D; ~% B; c4 [
        transitionNext2 [- f' N5 _* N- }  y
    {) v; e) M; f0 Q1 l
    }9 C3 m0 X$ W( ?- N. X
    function i()
' S- u( m( }9 G$ d        public% c0 \; q2 J* G. W* N
        timedTransitions
. T' F% V- _! X4 ?        atStage(Stages.Finished)
7 ~. p8 g% |4 O+ L$ x% u8 {    {
$ A# n; P- m7 f  ~  ~$ z( A+ k    }  M2 o! p) A0 G# T( z( M
}
) e4 i$ I& C  ]/ T$ K6 I9 A原文:https://raw.githubusercontent.com/etherchina/solidity-doc-cn/develop/common-patterns.rst
标签: Solidity
BitMere.com 比特池塘系信息发布平台,比特池塘仅提供信息存储空间服务。
声明:该文观点仅代表作者本人,本文不代表比特池塘立场,且不构成建议,请谨慎对待。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

成为第一个吐槽的人

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

    0

  • 关注

    0

  • 主题

    13