Hi 游客

更多精彩,请登录!

比特池塘 区块链技术 正文

Solidity通用模式

温室小书生室d
412 0 0
从合约中提款
3 x" A- Q( y! u
% @8 _0 [* j* ]9 J( q+ B( U9 p在某个操作之后发送资金的推荐方式是使用取回(withdrawal)模式。尽管在某个操作之后,最直接地发送以太币方法是一个 send 调用,
7 y1 j+ [7 L' N! Z! H8 V6 @但这并不推荐;因为这会引入一个潜在的安全风险。你可能需要参考 :ref:security_considerations 来获取更多信息。
9 c  b7 S, ^. I- ]0 I* _: g  Q- v这里是一个在合约中使用取回模式的示例,它目标是通过向合约发送最多的钱来成为“最富有的人”,4 L/ I7 v" `. W4 d" C! |# u* M
其灵感来自 King of the Ether _。
' m; I, M! {  U! Y- N& g4 ?在下边的合约中,如果你的“最富有”位置被其他人取代,你可以收到取代你成为“最富有”的人发送到合约的资金。8 q9 {& u* @, t; A5 n8 f% A
::
( y' I% P. m+ ?" H1 Q2 T: Npragma solidity ^0.4.11;
2 F9 }# H: U# ?/ H' _' {9 s( i) Fcontract WithdrawalContract {
& H0 h) [+ i9 D( N, _& }    address public richest;  s5 _, G$ ]1 H& `$ H
    uint public mostSent;
! y  F( n; r6 W* I6 [    mapping (address => uint) pendingWithdrawals;
: ~8 z9 z  P' ^* G( f& f. q    function WithdrawalContract() public payable {
5 o% i2 i: o, \, K        richest = msg.sender;( Z& ?3 U$ W) n; {; K
        mostSent = msg.value;! d& h+ ?8 V! C
    }; K0 v* P8 r, o- ~* \
    function becomeRichest() public payable returns (bool) {( x4 [' Q( z3 \
        if (msg.value > mostSent) {- H3 t( Q! K) b, @" S8 z& R
            pendingWithdrawals[richest] += msg.value;
( H# Q! C4 {2 x7 x            richest = msg.sender;4 k1 _9 X4 b9 t7 H9 O
            mostSent = msg.value;1 C( ~8 Q: k" f' E' `& F7 |" a
            return true;
1 r; ]# K& a7 A1 N. G        } else {
" @" {( f% W9 n- n4 }, L: x            return false;
* H. a4 d9 A& B1 G3 s+ m$ z# f( f        }6 N. x; |1 R3 \  \
    }1 N& O5 m/ [7 V6 ]( r. m
    function withdraw() public {3 C9 y/ w# X& O. W; I2 B
        uint amount = pendingWithdrawals[msg.sender];: K  D/ F: V$ w& N% Z- g8 I6 ^* r
        // 记住,在发送资金之前将待发金额清零9 ]% W# h8 J% X: k+ C
        // 来防止重入(re-entrancy)攻击
6 |: q9 x5 m! w8 t: S        pendingWithdrawals[msg.sender] = 0;7 x# w1 k+ `5 R8 A
        msg.sender.transfer(amount);
% F6 e) q: G8 E) F3 K7 j& r    }
: S. Q- N! }2 J3 W; _8 Z}# }4 c' X' w' M+ v3 h" [& j8 T
下面是一个相反的直接使用发送模式的例子:, i. o  b/ l! i" Y3 |
::& l( S+ U/ c% E4 B
pragma solidity ^0.4.11;
$ h5 [) }/ Z! P. A5 q" Kcontract SendContract {4 f% ^8 s( Y3 `
    address public richest;
' G4 m2 `2 `! m5 x2 `, k    uint public mostSent;
% |" c# p& {4 M, ^8 c+ |    function SendContract() public payable {
( o( M: g& S6 \3 H& E        richest = msg.sender;$ ^( H0 r) F& g0 R4 r" x! W
        mostSent = msg.value;
2 U# V* \$ R9 F, g% ~5 g    }
  h4 i0 F: @) W5 s+ a8 N1 [    function becomeRichest() public payable returns (bool) {
( z1 u. g% P* t. T8 B( ?        if (msg.value > mostSent) {
1 S: o; E! I6 P1 G! M            // 这一行会导致问题(详见下文)
' \7 j; `# T7 I  S            richest.transfer(msg.value);
2 q. N. m" y# W- j: a& q% @            richest = msg.sender;
2 w4 {& z  P  s' G7 z. Y: a            mostSent = msg.value;3 F; H1 I3 k, {7 [# P4 b8 Y
            return true;- R* p$ r% M) H. P* m: a
        } else {
: Z5 w( R! m! K3 Y            return false;
5 [- y& T" p9 R% r( h2 n        }
3 z$ _% n% Q) f    }
4 K9 H/ ?! G1 P3 T}
' |% R* G; ]& m, W注意,在这个例子里,攻击者可以给这个合约设下陷阱,使其进入不可用状态,比如通过使一个 fallback 函数会失败的合约成为 richest
( p3 L2 W( k, }% P; V% r(可以在 fallback 函数中调用 revert() 或者直接在 fallback 函数中使用超过 2300 gas 来使其执行失败)。这样,当这个合约调用 transfer 来给“下过毒”的合约
  L; B, U, t" Z8 A& a发送资金时,调用会失败,从而导致 becomeRichest 函数失败,这个合约也就被永远卡住了。( [; ~; |: V* A
如果在合约中像第一个例子那样使用“取回(withdraw)”模式,那么攻击者只能使他/她自己的“取回”失败,并不会导致整个合约无法运作。6 _3 _( t& ]! _" ]) ~
… index:: access;restricting
. H: e$ y- n' a6 i# J' o+ }- G* V
3 \# \2 {# {+ F2 b8 [5 I限制访问
& a- e. o- {$ _# G! y( k
  z9 B8 D& H! j+ F2 _限制访问是合约的一个通用模式。注意,你不可能限制任何人或机器读取你的交易内容或合约状态。
  ^3 r4 j9 {) V) o: @你可以通过加密使这种访问变得困难一些,但如果你想让你的合约读取这些数据,那么其他人也将可以做到。8 y+ `: W8 U! @) B) ~
你可以限制 其他合约 读取你的合约状态。
& I* ^$ h  C+ R$ H- r" q+ ?这(其他合约不能读取你的合约状态)是默认的,除非你将合约状态变量声明为 public。
: y; o7 B7 D: b5 V8 |; r+ _此外,你可以对谁可以修改你的合约状态或调用你的合约函数加以限制,这是本节要介绍的内容。% O9 ^5 _8 F8 r4 O! ?
… index:: function;modifier( v. h  m3 C) I! B& f
通过使用“函数 |modifier|”,可以使这些限制变得非常明确。; O( m% R- {" ]6 \( a/ f! V
::
+ ]2 t, n$ b; ]8 S4 `! spragma solidity ^0.4.22;
7 s+ L2 Z5 i4 B6 y! t0 n5 g5 Ccontract AccessRestriction {% M2 a( z; L( g
    // 这些将在构造阶段被赋值
" R- ]9 l' n# _6 Q% L" N) p    // 其中,`msg.sender` 是
0 ?# ^- s$ t* v6 Q    // 创建这个合约的账户。0 g  s: ?* {6 T: `. `% g
    address public owner = msg.sender;
) X+ a& `4 t6 l0 Y- X4 D. R    uint public creationTime = now;4 m8 |) i1 c+ m; U
    // 修饰器可以用来更改" e' o( ?' z0 f3 [# y* c1 \
    // 一个函数的函数体。
3 d2 g# A1 z0 Y8 L. L    // 如果使用这个修饰器,
+ @1 u4 E1 Q8 e7 G9 p2 J! A7 U8 T5 r    // 它会预置一个检查,仅允许
& e/ k* q$ V- h1 L    // 来自特定地址的
. P6 l" t% t5 f2 Q    // 函数调用。
  T2 m. v  w8 J    modifier onlyBy(address _account)
) ^3 ~( \" v( L  j. O% Z4 l    {
& n) M$ V7 q' \5 j# w& f        require(
7 |) h' ~5 Z' \; a6 u            msg.sender == _account,
9 a+ V! \; D+ U& @. W& T3 U            "Sender not authorized."
) u, \  N9 B* M! A$ B        );9 a" [: r8 ]6 r9 x" x
        // 不要忘记写 `_;`!
; i: i2 P4 \, \" ~5 t" O! t; Q$ h        // 它会被实际使用这个修饰器的
3 m4 _- E  d, O: z' r        // 函数体所替代。- l8 t$ `! V3 q! D- X. v; N- G4 O
        _;; G7 h. R# }. S3 Y" y0 C$ j# o
    }
8 G* D! c' v$ H% y% f/ H: k    // 使 `_newOwner` 成为这个合约的0 [5 q7 q! i* n" N4 [7 D
    // 新所有者。3 K' Y# C$ k" w
    function changeOwner(address _newOwner)
+ K: Z" w: ]( ?6 @! y1 X7 e: @        public7 U! c* ?% w$ C
        onlyBy(owner)/ J2 {$ @& A* H2 H5 ^* ~  p1 e
    {  h' D& p: v7 w3 I0 j( l
        owner = _newOwner;$ o2 `  Q) }* c
    }
( Q' X2 x$ G6 E$ e    modifier onlyAfter(uint _time) {* {; ], s; G$ {# m$ r
        require(
4 H2 |! m  t2 s( e7 ?4 U            now >= _time,
+ t$ K8 G" M' S  O            "Function called too early."
9 r+ `: x. C3 j! Z# B        );
( B4 d* [4 Z5 P2 q7 |( C        _;+ V$ R( }6 w3 Q) x1 G
    }
9 y9 c( T# \- h9 L5 }    // 抹掉所有者信息。( ^% I7 ~- O; v: R
    // 仅允许在合约创建成功 6 周以后
/ B% k1 R0 ]. l- }* b/ ~    // 的时间被调用。# M% [' ^% }7 s, L/ R, @
    function disown()
# B5 o1 @* b! v% [. r* V4 e        public1 G! g$ b) X  m+ i/ f  ?
        onlyBy(owner)
$ r* ?+ `6 r  L8 r$ H: x        onlyAfter(creationTime + 6 weeks); y" V/ b, N. v0 F
    {
7 v2 R1 |9 A. u% I' s% R- p( l        delete owner;
. J" h" }/ w# \2 y- g5 P! c! K' H    }
4 `3 C( }( v; g3 S8 ^- B    // 这个修饰器要求对函数调用
/ S! @8 X# p1 Z1 ]; V    // 绑定一定的费用。  A8 N8 I+ T5 d2 o7 [
    // 如果调用方发送了过多的费用,
* w- [" U; P$ o% P    // 他/她会得到退款,但需要先执行函数体。
, R" L1 O$ |$ v3 M    // 这在 0.4.0 版本以前的 Solidity 中很危险,
9 v4 u) p% G1 H9 c( Q    // 因为很可能会跳过 `_;` 之后的代码。5 [6 t- R6 Q- |4 z
    modifier costs(uint _amount) {
- q5 P: b/ @7 n, L2 O4 t# U  p        require(# A8 Z: G3 V3 n3 e& E: A
            msg.value >= _amount,
" O6 {, [  s7 a8 P! z8 ~& Y$ J            "Not enough Ether provided."# Q# E7 z) X) h/ y
        );
- {5 H1 x; d, J        _;
0 C# A+ D* @4 j2 h8 K        if (msg.value > _amount)
$ U9 G2 B% V# X4 y; c$ l: V2 f            msg.sender.send(msg.value - _amount);
. {' M6 O& m1 N+ T% h& U$ y    }
3 E$ n( F7 \" }2 t8 n    function forceOwnerChange(address _newOwner)7 j8 S/ j6 v6 @" Q! y
        public
) v( g) \' m% D/ c        payable
: L: @8 l" M6 C0 V        costs(200 ether): E+ w9 x/ S0 h+ {
    {
9 ?, }# O$ S! ^# \0 e        owner = _newOwner;) L) j2 e0 v) O7 Z7 @
        // 这只是示例条件7 H0 u& `' n; {" L" z
        if (uint(owner) & 0 == 1)1 F& z' j- ?$ O" d+ u
            // 这无法在 0.4.0 版本之前的$ o" r  a' O" r6 k( A/ q
            // Solidity 上进行退还。; L* P0 r7 o3 A: R* W3 p# m
            return;
- C+ e$ o! F' H        // 退还多付的费用
3 ?3 \" w1 d6 {, t% s    }6 s: Z5 ~2 n0 S8 [5 ^& a! j
}; H$ ]5 U' K' }; h% e* m
一个更专用地限制函数调用的方法将在下一个例子中介绍。" B* u2 }) @& Q2 q8 M1 ?# E
… index:: state machine1 A& n7 q, T: W7 x# I
) ]5 v2 ^+ D; }0 h+ Z7 M! K
状态机
, l6 [. l5 {; H  K7 X* E# C' `" k9 p; X  s% |. @' l- K5 N0 Q
合约通常会像状态机那样运作,这意味着它们有特定的 阶段,使它们有不同的表现或者仅允许特定的不同函数被调用。
+ S  B/ B: ^3 I3 \一个函数调用通常会结束一个阶段,并将合约转换到下一个阶段(特别是如果一个合约是以 交互 来建模的时候)。/ k) v7 B4 Q( a" J5 t! q. P
通过达到特定的 时间 点来达到某些阶段也是很常见的。
+ G. F  `' E* {+ K; n& g* O  A一个典型的例子是盲拍(blind auction)合约,它起始于“接受盲目出价”,
; l* _; C# h7 z) h) e, _然后转换到“公示出价”,最后结束于“确定拍卖结果”。
7 ]; ]) V4 Z' Z4 E, E+ g# j… index:: function;modifier  U0 Y2 H# X) v6 t
函数 |modifier| 可以用在这种情况下来对状态进行建模,并确保合约被正常的使用。
/ @! Q2 X! Y: y3 J7 L1 N示例, o9 r! O# J/ R4 }( Z- s- f5 R
在下边的示例中, |modifier| atStage 确保了函数仅在特定的阶段才可以被调用。
9 m  ^. }! x2 Q; |7 J7 ]根据时间来进行的自动阶段转换,是由 |modifier| timeTransitions 来处理的,
2 u2 ^4 O# y0 x# r它应该用在所有函数上。  U- ^: g/ u/ u! s/ y; K4 F
… note::
3 @) h3 {7 s8 w! p+ |$ V|modifier| 的顺序非常重要
/ }8 E- v/ R8 R& E, u* ]9 y4 `( b如果 atStage 和 timedTransitions 要一起使用,8 I0 Q1 }, i* @7 Q; _3 A
请确保在 timedTransitions 之后声明 atStage,
1 t4 t8 k% n2 Z- p以便新的状态可以
4 z4 m3 Y0 G5 l  f9 @首先被反映到账户中。2 m# x/ v  @. I: z+ y% A7 H
最后, |modifier| transitionNext 能够用来在函数执行结束时自动转换到下一个阶段。) {* ~3 W& P4 Z. ?1 t$ W
… note::
) t/ l2 x# g2 l( w3 ^% p2 J$ P|modifier| 可以被忽略
) i) Y9 }6 ^0 A  e以下特性仅在 0.4.0 版本之前的 Solidity 中有效:9 i' r* x6 c: c% B% H* t
由于 |modifier| 是通过简单的替换代码. I2 w; `/ g, N6 Z! `
而不是使用函数调用来提供的,
/ m- D7 U0 C6 ~# F# [7 i( [如果函数本身使用了 return,那么 transitionNext |modifier|6 y$ K* E  n8 ^. O) u" B# }+ W
的代码是可以被忽略的。
4 x: T* x- ?. G0 [' L# L如果你希望这么做,( h4 I8 ]5 ^, S$ h' Y
请确保你在这些函数中手工调用了 nextStage。8 y2 C2 V" I3 q0 L& {  p" d, [
从 0.4.0 版本开始,即使函数明确地 return 了,& |- A; A6 Y; ~" v, L9 {) e
|modifier| 的代码也会执行。) f2 C7 D# x& h& E. g
::
) P1 s6 |; h9 n7 Y. ^" R: s8 vpragma solidity ^0.4.22;" a# J. r* S1 j' ~- u  K* [
contract StateMachine {6 `9 f* i: T( S( e1 u. ?0 j
    enum Stages {2 k, y2 r9 H9 A% M" I
        AcceptingBlindedBids,
5 [4 ?! J/ s9 s        RevealBids,
5 k, x; ?* m4 K: n% d        AnotherStage,8 C! X+ `- O% L4 ~# `5 d
        AreWeDoneYet,
1 R& u. l& v$ u6 Y$ o5 a        Finished6 g9 ?' Z' r$ M4 M! b  D# q4 E
    }
% [8 D8 W/ g# c( ^! t    // 这是当前阶段。  E: {* E- n) w  S8 n
    Stages public stage = Stages.AcceptingBlindedBids;
7 S) G1 \. ?$ m: {    uint public creationTime = now;
- u7 |  g1 r- V3 ?' v    modifier atStage(Stages _stage) {* T1 n4 k+ I5 I' a# p! k- \
        require(, t7 e* `( z& ^
            stage == _stage,
" F: n8 R/ u* c8 N6 f6 F" k: v            "Function cannot be called at this time."
& {1 n( d* Z  u, x) \2 F0 G        );% `  t  e3 ]3 k- `% n$ P+ A
        _;
0 P0 t' R) y# F( z2 R6 |    }. `5 k3 R" N' l9 k
    function nextStage() internal {$ e) a2 r( N8 q2 t- g4 q9 S
        stage = Stages(uint(stage) + 1);; q" l& d& Q5 m0 r8 b% w
    }
1 t1 S8 i. m7 R2 a; q2 h    // 执行基于时间的阶段转换。
& S, L% `4 F; D4 h# Y0 ~5 ]    // 请确保首先声明这个修饰器,! @0 e& ^! d1 L* [& U
    // 否则新阶段不会被带入账户。
2 ^6 M; p3 I+ s. N    modifier timedTransitions() {; ~9 F7 y, m4 b
        if (stage == Stages.AcceptingBlindedBids &&& p$ `) G& x( N, e8 {* B& W$ M9 G
                    now >= creationTime + 10 days)
7 e' N7 J6 ], N. K7 J6 m            nextStage();
) |" x8 o$ K7 G1 }1 W- y        if (stage == Stages.RevealBids &&
! o  \2 H$ ]! j# _                now >= creationTime + 12 days)8 `0 c* w0 ~% e
            nextStage();
4 C9 Q' v1 L. b; x: J/ G( D2 |        // 由交易触发的其他阶段转换
- }- `4 Q  u) h- d8 r        _;
, `6 {" |3 `* Q8 P    }
; k6 f2 u' e: O$ P( e    // 这里的修饰器顺序非常重要!
, c2 A  A9 q  o) j! a( e% S    function bid()
9 O! j& y( a& G1 [* v; }        public. }" F/ }/ p% O6 A
        payable
3 T3 \! X, ]' t2 D. @9 P, h4 l) x        timedTransitions5 p9 R2 N/ S) q% O
        atStage(Stages.AcceptingBlindedBids)
$ g* z$ G. P8 N, k& m    {
6 L( B' Q( W/ w- Y! A) e        // 我们不会在这里实现实际功能(因为这仅是个代码示例,译者注): X+ n9 S; L( E7 `; {3 K- D6 }, i
    }
8 d5 {1 B: I9 c' L& ^0 `. Q1 \! V    function reveal()
- U8 f) H/ u" i# M0 w6 ~& a- r% R        public
+ K7 h7 }, x3 J6 @- X/ W        timedTransitions
' Y6 [1 c$ U  P9 T: Y0 f: ?5 N/ H* B        atStage(Stages.RevealBids)3 i: I6 C% U1 y5 }6 n- J
    {: w+ j  J' n4 E; |$ h; L* j
    }
" ?( j% b% a! d, ~& t0 d0 }- t, D4 A    // 这个修饰器在函数执行结束之后. j6 K- v) y2 a
    // 使合约进入下一个阶段。' E, h: f/ n3 g  y
    modifier transitionNext()3 A0 C2 Y% p. Q4 M: n
    {
) ~& L& R! E9 N        _;
* E( `# `$ q& V' s) x        nextStage();/ e5 q2 X& x5 f1 u; l% {
    }% Q- R# V) `' J0 I
    function g()
  m9 N- O9 K3 x3 T        public
) F4 }, G  X) A2 d$ K        timedTransitions
' d$ h, a- G1 U: T% y. ~+ k        atStage(Stages.AnotherStage)0 @, y. A+ s# |4 n5 u% F5 I
        transitionNext
3 @# N; M0 t/ B  T$ b    {
$ Y, V& `# I. @8 k) \    }
) f2 M- w, f- W( f    function h()1 G% o$ a- S: _9 l3 B, P+ m: V
        public
: p9 D8 \. @8 p% E        timedTransitions2 k% F, n/ @$ V
        atStage(Stages.AreWeDoneYet)
: o1 _, v4 @- z6 T        transitionNext5 a' o1 {! y! N4 ?9 g% Y. y
    {
. i& j' r! X' n# S    }9 \% a" j& t. c! l/ e/ W5 a% h) H
    function i(). ?7 I. K) @3 d/ U- V
        public2 J. d5 g2 w& a+ F3 b2 O% T
        timedTransitions( Y9 V4 K. I) ^5 k% k+ j
        atStage(Stages.Finished)
% o1 X' l* j( I/ J1 s" G/ {    {
8 a0 C$ R( F! J2 J! X2 H    }
5 x0 V; b6 Z8 ]) O. ?# U8 c}
+ x* E  s/ a; U1 w  b原文:https://raw.githubusercontent.com/etherchina/solidity-doc-cn/develop/common-patterns.rst
标签: Solidity
BitMere.com 比特池塘系信息发布平台,比特池塘仅提供信息存储空间服务。
声明:该文观点仅代表作者本人,本文不代表比特池塘立场,且不构成建议,请谨慎对待。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

成为第一个吐槽的人

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

    0

  • 关注

    0

  • 主题

    13