Hi 游客

更多精彩,请登录!

比特池塘 区块链技术 正文

Solidity通用模式

温室小书生室d
402 0 0
从合约中提款) h& |9 x% D; x$ d: g& G1 b* b
/ R0 C$ F# ~$ v; `& ], q5 r
在某个操作之后发送资金的推荐方式是使用取回(withdrawal)模式。尽管在某个操作之后,最直接地发送以太币方法是一个 send 调用,
0 |2 H" U$ c( s4 J# m但这并不推荐;因为这会引入一个潜在的安全风险。你可能需要参考 :ref:security_considerations 来获取更多信息。" i+ [4 l4 h+ l% l0 u8 x6 ~
这里是一个在合约中使用取回模式的示例,它目标是通过向合约发送最多的钱来成为“最富有的人”,
& Q; ]5 S' J/ q8 U其灵感来自 King of the Ether _。
& @& p8 a7 n; s" G$ G9 ~7 L( w1 M在下边的合约中,如果你的“最富有”位置被其他人取代,你可以收到取代你成为“最富有”的人发送到合约的资金。
  m4 Y1 V3 @* Q3 n. N::/ t+ u: t$ K! [' `% t
pragma solidity ^0.4.11;
8 Q" K; ^5 Z! bcontract WithdrawalContract {3 n$ V3 g9 b' f4 [! f+ G8 l+ G+ `
    address public richest;' @2 Q  i0 _( w+ k& z
    uint public mostSent;) S. e. k9 a5 I5 t* c# ^' I
    mapping (address => uint) pendingWithdrawals;4 r& x3 h, B, N' C
    function WithdrawalContract() public payable {3 z( Y% a' ]9 U
        richest = msg.sender;3 X4 ]' c5 P6 {: o. k0 O
        mostSent = msg.value;6 x* y& }  ~, ]+ e1 B" ]
    }
; d, h, M# ]) z3 Y, G3 z" d    function becomeRichest() public payable returns (bool) {( V0 |  a3 k7 Q' s$ v! q
        if (msg.value > mostSent) {) q) p( [) T. N- K8 h- c1 J- \
            pendingWithdrawals[richest] += msg.value;# F  ^1 K9 a& L. Y1 y' f0 A! v) W/ ^
            richest = msg.sender;4 K7 a5 q" C% G" `) v' G
            mostSent = msg.value;3 A% j8 n5 b1 c3 V8 Z5 q& c
            return true;' U8 E( S7 B9 x6 y# K- s& Y% D
        } else {6 R  S  J. Q" |. F
            return false;( S7 R* ?7 d* C; {
        }
4 P: H3 b4 a% I8 J  o    }6 Y4 N9 q" O: B% [3 g: n
    function withdraw() public {- G6 O- p  G+ Y" q  u2 ?9 T) ]4 n
        uint amount = pendingWithdrawals[msg.sender];7 n, b) t8 x1 i" z; h* |
        // 记住,在发送资金之前将待发金额清零
6 V: ]% x- i# V        // 来防止重入(re-entrancy)攻击. ^9 H0 C. p! h7 N& l/ e
        pendingWithdrawals[msg.sender] = 0;
( k7 ?/ r  i6 s5 ?8 n        msg.sender.transfer(amount);6 n! G/ E4 T" M8 I7 W: ]* C/ \
    }+ |' {) \# l" P' m7 k* l5 Z
}. o+ u  F/ V4 ?# K; h3 K( e! F
下面是一个相反的直接使用发送模式的例子:
' S  ?' ?3 Z+ [  z4 A+ P) I::
' v$ E+ [) X  O1 P! Fpragma solidity ^0.4.11;0 D0 Q1 c8 `* G5 k9 a& o$ G5 \
contract SendContract {  n6 [2 n. m* K. e0 {
    address public richest;/ U; p/ D" I! ^7 ~0 ?0 H& T5 ]* N& D% R! f
    uint public mostSent;) p% N2 i. J! i: F) O5 |
    function SendContract() public payable {
' ^+ v; q9 [6 N2 C6 E        richest = msg.sender;
+ x/ Z$ y$ [" g* v        mostSent = msg.value;  I: O8 ^$ `% P6 i: S; V
    }; L' A. x5 Y- O$ m% W+ T; D7 y# Q
    function becomeRichest() public payable returns (bool) {9 e  {2 a3 c' T* \" N, u
        if (msg.value > mostSent) {" O, @4 v: j* g5 @! ?
            // 这一行会导致问题(详见下文)
0 w) U8 \8 r7 N9 L3 W5 `            richest.transfer(msg.value);
. x. ^1 c3 m+ c: J            richest = msg.sender;9 i  l# z$ x: M; W: o1 `3 K
            mostSent = msg.value;8 \+ J" C  G% j1 B
            return true;  Q7 B7 ~! C& Q9 G  f7 ~- N
        } else {4 a: h. _5 x! h3 Z
            return false;
) F( J6 N. M6 v$ |# K% q        }
( _6 U3 O# A3 ]0 H* L9 e    }$ Q7 Q- a0 d& |6 n# z
}
- l8 L7 ?' ^- V/ w) S, C: e注意,在这个例子里,攻击者可以给这个合约设下陷阱,使其进入不可用状态,比如通过使一个 fallback 函数会失败的合约成为 richest& M0 _5 Q4 |* r" I8 Z, R' A! n: j! z
(可以在 fallback 函数中调用 revert() 或者直接在 fallback 函数中使用超过 2300 gas 来使其执行失败)。这样,当这个合约调用 transfer 来给“下过毒”的合约
* q' |% ^* l9 j9 M& Q# A发送资金时,调用会失败,从而导致 becomeRichest 函数失败,这个合约也就被永远卡住了。
2 R1 f! V% b2 E+ D' b( r: u- C& u如果在合约中像第一个例子那样使用“取回(withdraw)”模式,那么攻击者只能使他/她自己的“取回”失败,并不会导致整个合约无法运作。$ U6 U3 @: M; d3 U' P
… index:: access;restricting
. J  |1 l% }0 {8 S! }0 g6 B! b- z+ ?
限制访问/ b9 w4 R9 x' K' l! L9 ?

( K# Q% Z9 c; }1 H/ P4 g$ B- d" B4 i- b% p限制访问是合约的一个通用模式。注意,你不可能限制任何人或机器读取你的交易内容或合约状态。% X1 o# h/ k9 V4 P
你可以通过加密使这种访问变得困难一些,但如果你想让你的合约读取这些数据,那么其他人也将可以做到。: e. j( _1 `: M9 Q9 h- Z  N
你可以限制 其他合约 读取你的合约状态。
+ W0 u- |( E; W: Y" a! w3 C5 \这(其他合约不能读取你的合约状态)是默认的,除非你将合约状态变量声明为 public。$ e: N; A, q; u+ Z
此外,你可以对谁可以修改你的合约状态或调用你的合约函数加以限制,这是本节要介绍的内容。
; P. S; o- T5 G% M! u; V… index:: function;modifier
+ ~% q5 U  k8 K/ ]" D9 a- S; R通过使用“函数 |modifier|”,可以使这些限制变得非常明确。( U$ e4 w9 G( L4 C3 p2 }  Y' U
::
. X$ T5 a* D' P( I1 s! _3 [- ppragma solidity ^0.4.22;$ [1 P7 z2 J. V& x
contract AccessRestriction {
' V8 r' t1 i& d    // 这些将在构造阶段被赋值
0 L" O( D0 u4 F) g* k0 t    // 其中,`msg.sender` 是4 i$ E" W( X3 b8 X. f6 u
    // 创建这个合约的账户。
  b& H7 G6 a1 ]. I- j6 O1 M    address public owner = msg.sender;* m; P5 P% l, U; P/ b
    uint public creationTime = now;& o. s; \  L, P/ s4 Z/ p
    // 修饰器可以用来更改8 ]5 \/ u9 M& W* [; n
    // 一个函数的函数体。
! P9 A0 Z/ X( J: |    // 如果使用这个修饰器,( y( r3 d, M6 s4 z& g6 Q0 Z
    // 它会预置一个检查,仅允许( ^4 P) X" I6 K0 R5 \- _& n
    // 来自特定地址的
2 S6 M. y+ i7 o/ M    // 函数调用。' S- i; r, T& n- @
    modifier onlyBy(address _account)
/ n" {' ?4 n; X9 J3 b    {
5 |. w' p% t/ T" ?6 I$ g        require($ b! T. y. K+ D7 i  N
            msg.sender == _account,6 S5 K2 z8 t' t1 O0 Y
            "Sender not authorized."
+ @) J7 `0 P4 b6 X        );
- Y0 E8 n2 S1 _6 c6 _! z        // 不要忘记写 `_;`!
6 v. v. j* X& m+ E% x% d        // 它会被实际使用这个修饰器的
0 q6 X6 R& I: b        // 函数体所替代。1 {; J. J( d: {2 K8 f; S) h1 k
        _;0 g9 a% u. z/ k! o2 l4 C4 O& U( g
    }
# P# g4 s$ N2 G9 g    // 使 `_newOwner` 成为这个合约的. |' ^- N  ^9 u
    // 新所有者。
5 y* Z0 _( F1 O. S1 W    function changeOwner(address _newOwner)& z4 f( k3 ~) G, k/ r* g
        public
1 u, r- M! \; b4 V  x7 U        onlyBy(owner)
# G$ s# Y2 ]9 S' {4 |$ _- ?' ^    {
8 z, ^3 Z! S+ U$ p- s        owner = _newOwner;9 _" W1 w* v  i. @
    }  _6 P4 Y3 j& q4 s$ }& e# @
    modifier onlyAfter(uint _time) {
0 P8 a6 V/ A7 y) h* m# m, o$ U0 m        require(
( f6 M  z! q/ Z            now >= _time,
' J' R* b3 I5 k1 H) L, w; k            "Function called too early."
1 d8 P% d" }* @+ Q% I. v        );
$ Y, H. h: ?$ G- |        _;- m: h; L2 R( T" z7 R
    }
9 e+ V. L5 v$ f% {0 X- W7 K6 L    // 抹掉所有者信息。' R/ p9 b  T6 i$ d% i5 G3 U. p2 o
    // 仅允许在合约创建成功 6 周以后
0 ?, u+ g* s% R3 C) k' e$ S    // 的时间被调用。8 ^( [+ e: `( ]5 e% g# V
    function disown()
; N2 C$ S3 k7 E8 G$ S8 m        public% b7 a* p8 K/ S% A
        onlyBy(owner)
8 v* K) [1 \# G0 H7 `& w        onlyAfter(creationTime + 6 weeks)
& q6 O) p  u! E7 X8 ?  |# L! F; W    {
& J6 {0 T% r. f3 W        delete owner;9 H2 R- n( Z) n  c
    }
) d8 ~6 i- U0 j( R+ N; e+ I    // 这个修饰器要求对函数调用
  O) `$ i! Z, u' V4 `" |1 G    // 绑定一定的费用。
, A5 a! f& L8 D; ~# Q( G  `    // 如果调用方发送了过多的费用,
, ^! Y$ B$ Z5 W0 y. L# [    // 他/她会得到退款,但需要先执行函数体。  _5 d/ M9 Q  B' Q$ m. k
    // 这在 0.4.0 版本以前的 Solidity 中很危险,  z, v) J7 \1 O, k/ I' X% L4 K, L9 ]
    // 因为很可能会跳过 `_;` 之后的代码。
+ F  P. J. t8 g& V5 R! m    modifier costs(uint _amount) {
) |& P, A/ A# y        require(
/ D. E! R- j( k/ G- o7 b            msg.value >= _amount,
9 X# ^* b) N! V! c3 P            "Not enough Ether provided."
; t/ P9 P  I; v/ ?/ V        );
4 G4 c1 k9 E$ ^4 G+ A( l: F        _;
" o* o, J2 k9 L5 N1 V1 M        if (msg.value > _amount)" u1 q2 G& H& n$ n/ Y
            msg.sender.send(msg.value - _amount);$ u& I9 F- T$ ?6 Q- l$ ^" M
    }& p6 c3 k0 L) q, c% @$ J
    function forceOwnerChange(address _newOwner)8 M& z3 I4 {) j$ ~
        public* i) V9 e! f% l
        payable  H# y3 o  W5 J! m1 b. X
        costs(200 ether)" H7 g6 R- u/ r% p
    {
1 H/ O* V* ]; q1 K1 I, e. ^        owner = _newOwner;
5 z+ l0 \0 x2 u' ^* A" y2 R' \- `        // 这只是示例条件9 V" u& t1 h; }' Y
        if (uint(owner) & 0 == 1)0 {. T/ ]' y- _, S0 m4 m
            // 这无法在 0.4.0 版本之前的
" {% h' m, O# W! n( N            // Solidity 上进行退还。
. f( u7 @2 s3 K, x# d3 x            return;1 U$ U- C$ j* I8 B
        // 退还多付的费用
% ^  C9 x8 R* f& D# Z% k. \    }; U  Q& V# \4 q- l* m( b
}7 _$ m! \3 U8 V7 x- ~, T+ Z4 E
一个更专用地限制函数调用的方法将在下一个例子中介绍。
" P! s* t# A/ o2 w+ I# S! H/ b… index:: state machine
$ ]7 H" c* i2 B' ~9 S1 @! Q# s9 j( H% `- I( M
状态机  Q6 H) `% _+ X# x

5 X* |: e, V" x5 i( [6 G7 A合约通常会像状态机那样运作,这意味着它们有特定的 阶段,使它们有不同的表现或者仅允许特定的不同函数被调用。: B0 ^/ D5 M% D/ z
一个函数调用通常会结束一个阶段,并将合约转换到下一个阶段(特别是如果一个合约是以 交互 来建模的时候)。
6 a3 K- n: V" Q8 s9 [9 m2 V$ m) d通过达到特定的 时间 点来达到某些阶段也是很常见的。
$ E+ x! ]* k: N5 Q* w  [+ v7 G( T$ M一个典型的例子是盲拍(blind auction)合约,它起始于“接受盲目出价”," u0 t3 ?8 |1 b; C4 @
然后转换到“公示出价”,最后结束于“确定拍卖结果”。
4 z' y# W% c: ~! R8 l: Y! J… index:: function;modifier$ ~6 @2 v) {! a/ F
函数 |modifier| 可以用在这种情况下来对状态进行建模,并确保合约被正常的使用。
" z! G- y: [2 [. k+ ], Z示例
+ k# _0 P* h2 P5 e3 P* k" ?在下边的示例中, |modifier| atStage 确保了函数仅在特定的阶段才可以被调用。
* U- f) t! @; h' j6 q) S6 @9 z根据时间来进行的自动阶段转换,是由 |modifier| timeTransitions 来处理的,6 N: n& ^/ p4 N5 p7 P
它应该用在所有函数上。
% B9 k0 W5 I+ F. _0 t+ q… note::, _1 L) R3 \4 w" c& t( E- l
|modifier| 的顺序非常重要3 n+ X( ?; b6 H" |5 L
如果 atStage 和 timedTransitions 要一起使用,
: `) t3 e% U* c7 K! X4 M请确保在 timedTransitions 之后声明 atStage,
& s4 @/ L9 z3 _6 P2 E以便新的状态可以
: W0 p* M# f1 w) S8 k首先被反映到账户中。9 g: W% b0 N2 D
最后, |modifier| transitionNext 能够用来在函数执行结束时自动转换到下一个阶段。" o6 g+ g* ]2 f) K2 N
… note::
! V+ ?9 S. ]& ?  g|modifier| 可以被忽略% d/ h/ \+ j- k( I0 ]4 }
以下特性仅在 0.4.0 版本之前的 Solidity 中有效:) ?' z6 _, s. \# W. C4 I
由于 |modifier| 是通过简单的替换代码# }* h8 z% l. a) a4 b
而不是使用函数调用来提供的,, X# a* y5 f( D" O  g
如果函数本身使用了 return,那么 transitionNext |modifier|' B( U7 R$ ~5 j1 z
的代码是可以被忽略的。8 L9 C" X5 C1 ^7 Q
如果你希望这么做,
$ `/ x9 f' S% ~, E5 m: b8 ^" U请确保你在这些函数中手工调用了 nextStage。2 l( B) R! f# ?/ H
从 0.4.0 版本开始,即使函数明确地 return 了,
% p/ @- E6 r" ~- F) Q|modifier| 的代码也会执行。
! P7 A5 x& G5 j5 o::) ]: k9 c% e! K
pragma solidity ^0.4.22;
" U# F2 n- s* Ocontract StateMachine {
5 E) ?( m0 x5 k3 ]; A  y& |    enum Stages {
: s" Y0 u( X! d9 a7 }        AcceptingBlindedBids,0 v2 l# J2 `! m( f3 i3 q
        RevealBids,
( j; H% r. w9 _$ l8 }6 o4 G- k3 X        AnotherStage,
8 a5 I; k! Z; }. e6 Q        AreWeDoneYet,3 e- |3 _; J" A$ L  C# }
        Finished
7 F7 D' e4 _3 I" o' `8 g    }
+ N1 C$ i: d1 F0 k    // 这是当前阶段。, x, n& ~  G6 U) e
    Stages public stage = Stages.AcceptingBlindedBids;
# o& N, `! q1 w' X4 x$ M    uint public creationTime = now;! T! {5 ^* H2 d6 h. U8 [
    modifier atStage(Stages _stage) {
3 {2 ~  O8 o3 T8 V, Z& o        require(
- u; Z0 E. V5 J% ?8 {9 P            stage == _stage,
( l$ P/ s5 W! W            "Function cannot be called at this time."
: t2 E% ]' {( c* s/ L        );
" X) R3 m  p& q' L7 J( b! V: n        _;
! x, u) ?  a7 H/ Q! @    }% j) B- {( [% f$ w4 v! |) {
    function nextStage() internal {
' |0 t% i$ [9 }        stage = Stages(uint(stage) + 1);* v8 p6 N; H3 T9 i+ M8 u/ h. G& e9 B
    }( u. l; }; y, x& [) P* R; n
    // 执行基于时间的阶段转换。
9 A8 C" v9 p5 j8 ]    // 请确保首先声明这个修饰器,
4 g. x$ `# [4 T* T/ D- K$ S    // 否则新阶段不会被带入账户。
) w! d/ A! o  L# [6 P    modifier timedTransitions() {
5 G, ?0 {* k3 h% F7 ~        if (stage == Stages.AcceptingBlindedBids &&
7 L% ^1 m) d" L" a3 a                    now >= creationTime + 10 days)$ B- ~% S) Y1 T/ S
            nextStage();8 f$ W6 a. W1 T/ |; Q
        if (stage == Stages.RevealBids &&0 x: O9 s* Z9 ^% D& ~
                now >= creationTime + 12 days)
; y0 a. V  Y0 S" u8 e/ Y2 W            nextStage();
7 ^8 w0 l; [# C; J        // 由交易触发的其他阶段转换' U( Z; |! p' A6 \" f( Z1 @8 x
        _;: E9 e9 |6 j7 y3 e
    }
6 \1 C0 Z  V# |1 J  [- r! }    // 这里的修饰器顺序非常重要!2 g9 \! ~7 I1 i: B% K% J
    function bid()
. M) a2 g6 F( o- [. N, y7 y, z  D        public
2 E  z7 Z4 C, N! y6 I0 E        payable1 h2 U# h3 J) b1 L$ c
        timedTransitions( G4 h% f/ M5 U* V% h& K( ^, V! x
        atStage(Stages.AcceptingBlindedBids)1 u2 o/ o% l6 C2 V2 z
    {( [* E4 W6 E& h1 [  U- u9 Y$ J
        // 我们不会在这里实现实际功能(因为这仅是个代码示例,译者注)% Z5 ?) J- c  @9 j
    }
6 s7 E' d! u( c    function reveal()8 {! p2 u# Y2 e1 y! A
        public' ?2 k0 y% }0 g6 k4 ~, x# [/ t9 c2 l
        timedTransitions' T, u1 z5 F5 p- q8 r; L
        atStage(Stages.RevealBids)
: i0 B: T4 K, d, v4 |* P    {/ C& q+ V& ]# ~( w, I8 h- t
    }
8 s2 A* h; Y' Q3 G& S( A    // 这个修饰器在函数执行结束之后: M; v2 J2 g3 v' m2 H( C$ U
    // 使合约进入下一个阶段。: ?; p" q* @; l5 |# a8 N
    modifier transitionNext()# W4 B" h+ e3 q) t6 g8 ]
    {8 p1 f4 m8 z( Q' a% a
        _;; ~. A/ H; W: G* |
        nextStage();% E: S( u1 A- C9 u+ A  J
    }
' O9 p2 K/ {- [  B5 H    function g()
( M( A. S. o% [+ \/ }, T: G; Y        public; S( `/ x' U- p  A
        timedTransitions
8 @% N# t* j0 d8 H9 F: a        atStage(Stages.AnotherStage)
4 ^8 `' f6 L3 j: U0 `        transitionNext
; q2 s/ r2 `  }8 z# q$ g0 V    {
( d% E0 F; D/ P% P4 Z' E/ w    }( V- T( t' o9 }" c8 G  J
    function h()$ T/ N1 {! K6 P/ ~9 b
        public
5 Q5 X+ N% M! X5 \2 U  U: Z        timedTransitions
3 |2 ?* b1 T) I. W/ u- a9 r% j2 @        atStage(Stages.AreWeDoneYet). o9 @( G$ ~1 p
        transitionNext% R. s& {. h: p0 j
    {- n1 g4 M2 m! F4 i5 v! w$ k
    }, K( G* X$ d# S' G' q. _
    function i(), X( ^& S4 m! ?
        public
6 y, K1 O  X& B3 c( _* _, P        timedTransitions
2 {+ W. S4 F* k8 ^/ p% S        atStage(Stages.Finished)  A, f3 c) s" Z- O
    {* A1 S5 r( ]5 v! l6 Q9 F; F' w
    }
7 z# s/ F" a9 U- I}2 h8 [& n9 ?: ]* m; l& }
原文:https://raw.githubusercontent.com/etherchina/solidity-doc-cn/develop/common-patterns.rst
标签: Solidity
BitMere.com 比特池塘系信息发布平台,比特池塘仅提供信息存储空间服务。
声明:该文观点仅代表作者本人,本文不代表比特池塘立场,且不构成建议,请谨慎对待。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

成为第一个吐槽的人

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

    0

  • 关注

    0

  • 主题

    13