Hi 游客

更多精彩,请登录!

比特池塘 区块链技术 正文

Solidity通用模式

温室小书生室d
532 0 0
从合约中提款
  e$ u- v& y. l' c2 z8 Y5 g7 o# r1 O1 ^. q& I
在某个操作之后发送资金的推荐方式是使用取回(withdrawal)模式。尽管在某个操作之后,最直接地发送以太币方法是一个 send 调用,
) P/ h4 R0 k6 h! G+ H; g但这并不推荐;因为这会引入一个潜在的安全风险。你可能需要参考 :ref:security_considerations 来获取更多信息。
6 m/ J; K. T& M6 r这里是一个在合约中使用取回模式的示例,它目标是通过向合约发送最多的钱来成为“最富有的人”,1 _- D' j4 ?! r* V' `" M
其灵感来自 King of the Ether _。
8 E0 e3 d! S5 v# y& v. V在下边的合约中,如果你的“最富有”位置被其他人取代,你可以收到取代你成为“最富有”的人发送到合约的资金。
, p- s6 H9 K/ Q! `1 \::; \% f  q. W$ B$ U# [
pragma solidity ^0.4.11;
# B5 m0 g' Y' A' U* v- B# h+ p3 [2 Zcontract WithdrawalContract {- Z6 A5 u+ ?' f4 z' [! `" u* o
    address public richest;
% f6 P9 e: ~8 A4 y$ d    uint public mostSent;5 N+ _. m$ H; C; w
    mapping (address => uint) pendingWithdrawals;5 J7 n: n! V' q
    function WithdrawalContract() public payable {
; N2 o! O0 m! C        richest = msg.sender;
& B* |9 e9 L# \) k- y+ b9 A        mostSent = msg.value;! t& k. d  B; s5 Q
    }
# j, u4 t: f4 G$ m    function becomeRichest() public payable returns (bool) {3 i: g; P/ M9 e% k
        if (msg.value > mostSent) {
8 |, }7 j/ o# y8 O            pendingWithdrawals[richest] += msg.value;; W( T! y! @8 l1 `" b" D
            richest = msg.sender;3 `( I. b) a% e: I& o
            mostSent = msg.value;
/ P4 X& h1 Q5 t4 i( A! X  q            return true;
: f' `8 l# z# c1 Y- S        } else {
% [; A8 {, V- w6 m# O" B            return false;! R- @# \  i: _! u, N* q( a1 c4 F$ G
        }; r/ R) w6 J5 X/ ^+ ~8 ~- C, L
    }
# y8 i) e3 v6 z! X    function withdraw() public {: }1 t( Y. {$ F7 x1 O9 n
        uint amount = pendingWithdrawals[msg.sender];
4 u: A0 D' N8 c+ f- P+ `/ _        // 记住,在发送资金之前将待发金额清零2 r' A/ \4 Q+ q* o: U
        // 来防止重入(re-entrancy)攻击5 H$ v$ K6 t+ ^& l) g
        pendingWithdrawals[msg.sender] = 0;
9 C, ?$ s* T7 Q2 L4 Y        msg.sender.transfer(amount);
/ e9 R2 Y" v( r9 C6 U    }; S% a  k# o/ `7 c  f- P2 m
}
6 a/ D- o' p/ I* C! P$ m& r下面是一个相反的直接使用发送模式的例子:
2 C& O) t" s- g::+ M8 A. F' f, R4 x$ H: q
pragma solidity ^0.4.11;4 y2 R: E& E$ O/ O5 E
contract SendContract {6 _8 n: n5 w( ^4 T+ E8 n
    address public richest;
# `) K' {* B% m  p3 {& k& i+ s    uint public mostSent;; |# i* m2 t( H: m
    function SendContract() public payable {/ i& s1 }( x9 `2 n
        richest = msg.sender;
" f5 o  ?& X: ?9 C9 j1 i" f- c+ @        mostSent = msg.value;
% F3 t, F# ^) z  j    }& A0 }# V" z# g9 z9 O9 w
    function becomeRichest() public payable returns (bool) {
: g. |. Q5 x9 _' \% q        if (msg.value > mostSent) {" T- \/ s' R3 y3 K
            // 这一行会导致问题(详见下文)
8 Z' s0 o9 C: K: T, P/ Q            richest.transfer(msg.value);# G5 _1 m( O( x1 C) m. }- l7 c
            richest = msg.sender;+ J4 T  ]+ \, e4 j0 C* C
            mostSent = msg.value;# [: H0 S, m! \5 V$ m/ i
            return true;
8 R+ m3 J7 ?( p' R, x        } else {7 J6 n8 }* e7 y  a: G0 C; R
            return false;
% k6 c) i, T0 C2 Y        }; X2 [. }9 b6 I1 l! G& a
    }; W! I9 L4 g. `0 L( [
}
6 t2 B+ g9 C& `! v, [注意,在这个例子里,攻击者可以给这个合约设下陷阱,使其进入不可用状态,比如通过使一个 fallback 函数会失败的合约成为 richest4 J3 f1 Z% S2 T1 s
(可以在 fallback 函数中调用 revert() 或者直接在 fallback 函数中使用超过 2300 gas 来使其执行失败)。这样,当这个合约调用 transfer 来给“下过毒”的合约0 v; k( z* T: M) S( l
发送资金时,调用会失败,从而导致 becomeRichest 函数失败,这个合约也就被永远卡住了。
7 r& t& V( O4 w! O4 E如果在合约中像第一个例子那样使用“取回(withdraw)”模式,那么攻击者只能使他/她自己的“取回”失败,并不会导致整个合约无法运作。
2 v1 G& a- u# C  c… index:: access;restricting
) k; Y8 x  V" q0 z# O0 t# u9 G7 u# N2 z4 f
限制访问- J! A: ~' @+ D+ J; a

1 Q8 m; K& @; B" Y6 Q- Z) y限制访问是合约的一个通用模式。注意,你不可能限制任何人或机器读取你的交易内容或合约状态。% l! Q( v- p, O" f8 F* d
你可以通过加密使这种访问变得困难一些,但如果你想让你的合约读取这些数据,那么其他人也将可以做到。
. v( w* T8 C9 @" P; l4 u% \你可以限制 其他合约 读取你的合约状态。% x( |8 `8 w' R& F, l1 j' ^
这(其他合约不能读取你的合约状态)是默认的,除非你将合约状态变量声明为 public。
; m5 }5 t4 Z5 `7 y8 q; H此外,你可以对谁可以修改你的合约状态或调用你的合约函数加以限制,这是本节要介绍的内容。
* v8 |0 q2 l" P: }7 p; j( r… index:: function;modifier
; j7 |1 l5 N8 C4 b& ]9 c通过使用“函数 |modifier|”,可以使这些限制变得非常明确。" z7 A4 H3 G+ g6 A) _5 p9 u, P! F. A
::
: F) g  q( P% |; rpragma solidity ^0.4.22;
/ k* z5 N4 ~! q9 zcontract AccessRestriction {
# Z8 k, A- e; {0 s- H; V    // 这些将在构造阶段被赋值
& y" S& g$ B8 n% U) F8 e# G* f" @/ ~    // 其中,`msg.sender` 是* A9 J2 X( a$ d0 j* t: l
    // 创建这个合约的账户。
; e" }& {- ^7 u$ l    address public owner = msg.sender;
# H- B1 t: }1 q5 L' D9 y    uint public creationTime = now;
6 y6 d8 X- y- v$ y% L. F  C: w    // 修饰器可以用来更改5 Q* l( x) S& V0 ^6 Z
    // 一个函数的函数体。
1 B  `  I- c3 h8 P* ]    // 如果使用这个修饰器,
. L1 I! w7 H" H+ k0 m' x    // 它会预置一个检查,仅允许3 H/ i, [# f. l. O
    // 来自特定地址的
" y' K* J* y8 L2 M* f    // 函数调用。
  {% V8 B" Q( w9 ~    modifier onlyBy(address _account)- O+ B5 q, c) I7 W; Z% A
    {
6 r6 Z1 s  B4 e# T$ S" b* M/ e        require(6 T* ?  L: z! G
            msg.sender == _account,8 t7 G9 C# I: E
            "Sender not authorized."
% C4 b/ ?+ ^# t; t        );6 G% l4 H* Z% e! n- I: ]
        // 不要忘记写 `_;`!
) E: `& w4 I/ Q: G+ v+ e        // 它会被实际使用这个修饰器的
/ Z$ [) F! G- {5 `  m' i        // 函数体所替代。
/ ^, M4 f% f8 A: M& A3 p! g        _;
+ a5 K4 J7 \) v% I    }
  W! i6 c. G& A0 R2 \    // 使 `_newOwner` 成为这个合约的
2 B5 N" ~4 A: b4 l( G% l( y1 [6 h    // 新所有者。9 Y2 q; n& f- d2 p8 i
    function changeOwner(address _newOwner)
2 d. h; q) M0 O; M% `1 k: k% h        public
! q! e: M, g1 U/ ^2 \        onlyBy(owner)
1 \* P% m& e' `/ `2 M6 p3 s/ _    {
  w* b+ V8 `/ {& T& {        owner = _newOwner;
0 ^; H& _6 {$ r/ z8 B+ w; Y    }
7 |6 ]4 x& K" r, e    modifier onlyAfter(uint _time) {
! L2 Z+ X5 h" w$ Q; r, z: K" X        require(3 j) J* B" [* ?/ G
            now >= _time,
  u% O- R) u& o6 K8 m2 \            "Function called too early."
( e. _3 ?5 j' r1 [1 q3 j' L/ o( s        );
! M( v! U; A+ I) \0 P        _;
$ L* N9 p/ }5 C6 [. r" ?$ R4 N    }! d/ V) T- x5 q+ @; d1 x
    // 抹掉所有者信息。' T; b/ h) O9 Y
    // 仅允许在合约创建成功 6 周以后$ |: T; I; G; V  Q# v, K
    // 的时间被调用。
8 p% S8 \- |# g! X- }* q& N9 _    function disown()3 Z3 ~! f( }1 N3 {( u5 |/ Y
        public
8 S" M8 W8 b% [' p' N# o        onlyBy(owner)1 a) U" \2 q$ D- ]1 Q) Q+ W8 ], @3 a
        onlyAfter(creationTime + 6 weeks)
- j; d0 R& B* n; r% T, J  S    {6 S: Z! S; O: ^
        delete owner;
* F- n" X9 I; `    }
9 A, `9 U, c- |9 K# ?- m  }    // 这个修饰器要求对函数调用
0 _6 A0 j9 R- j0 o1 k8 o+ y+ ^    // 绑定一定的费用。! }: _+ k" V& n3 w! a0 ]+ ^
    // 如果调用方发送了过多的费用,
- Z& f: `# V) q) n% `    // 他/她会得到退款,但需要先执行函数体。9 q! V/ }" L5 r3 }+ T1 H. N% j1 d
    // 这在 0.4.0 版本以前的 Solidity 中很危险,
) ?' o# d, f  |% D, P7 j    // 因为很可能会跳过 `_;` 之后的代码。
0 I* n5 D/ w5 E; F) A; u9 d    modifier costs(uint _amount) {" D  Q/ q, ?7 W5 r6 ^0 m
        require(
* C6 z0 N# a4 `# @1 {            msg.value >= _amount,2 i7 |/ @0 W( Z3 K
            "Not enough Ether provided."
7 i% @- \6 N! o) H! q: Y5 H8 E  ^% h        );
: U. ^; R$ X6 v& c        _;' P& d2 a/ T% H$ I$ B+ g
        if (msg.value > _amount)3 S+ o2 _4 H/ e! {* U8 l; M
            msg.sender.send(msg.value - _amount);- A1 Q" x- S7 A6 |) v" Q
    }
) L$ Q+ @& j$ X8 _    function forceOwnerChange(address _newOwner). \& d7 g5 a9 q& m- R0 n9 B
        public
- R7 ~% Z+ ^& i1 e! r& e. s        payable( R- J1 O4 K# s1 h' d$ E
        costs(200 ether)
) V& X2 f3 c. A/ ~# h8 `; ^5 T    {  [' G) _' i! ?# P' B; [
        owner = _newOwner;
' D5 p+ `9 e& G. n+ s  D* W        // 这只是示例条件
( B: ^: m7 i' w" Y        if (uint(owner) & 0 == 1)1 t5 t6 B; Q& H
            // 这无法在 0.4.0 版本之前的
1 m# c$ l$ x( {/ G            // Solidity 上进行退还。# w7 I% k  ~0 O5 T9 m% K
            return;$ V1 O- Q) k; v) F3 \- E
        // 退还多付的费用, h6 e  _% Z( }: R
    }% u, J6 u3 _, J# G; @0 M' q( L
}
! g. Q: z: b+ m1 E/ e一个更专用地限制函数调用的方法将在下一个例子中介绍。8 n; U% L2 `  u. R; s- b# w8 K
… index:: state machine
8 k5 i: j# i0 x" d  p8 o! n
; |& ]  E; [! q. b% @状态机
/ L, Q0 [; Y9 A; i. |9 i1 P, a8 O8 `- H. K6 p, ]" Y
合约通常会像状态机那样运作,这意味着它们有特定的 阶段,使它们有不同的表现或者仅允许特定的不同函数被调用。
4 N: g, l! R" B( s! M9 J- i一个函数调用通常会结束一个阶段,并将合约转换到下一个阶段(特别是如果一个合约是以 交互 来建模的时候)。
6 g" s6 C: `) X* w通过达到特定的 时间 点来达到某些阶段也是很常见的。
2 ^0 x( Z% P, l一个典型的例子是盲拍(blind auction)合约,它起始于“接受盲目出价”,
! n: J  ^) A5 D然后转换到“公示出价”,最后结束于“确定拍卖结果”。6 R- ]3 X+ S* m# S; k+ E
… index:: function;modifier
, J5 C7 O% k) y4 V' o0 D. [3 V9 q函数 |modifier| 可以用在这种情况下来对状态进行建模,并确保合约被正常的使用。
# S$ j1 z/ F/ p) t. e; v3 i  Z2 Z5 ?示例# [. e! ?: e) Q& v: _% F5 K
在下边的示例中, |modifier| atStage 确保了函数仅在特定的阶段才可以被调用。. k9 r, y# }" ?
根据时间来进行的自动阶段转换,是由 |modifier| timeTransitions 来处理的,+ k: `" U2 L+ y6 k4 a, B
它应该用在所有函数上。
, K& H/ N6 j" K$ ?… note::
2 R; ]: B. S- e3 ^3 ^, V( _|modifier| 的顺序非常重要2 G; z; i" W% x+ }" ?. p: ]0 Z
如果 atStage 和 timedTransitions 要一起使用,
5 m* Q+ w# \0 `$ w% {- x) Y+ p8 Z请确保在 timedTransitions 之后声明 atStage,
6 [* K3 D% a% u2 B以便新的状态可以) J" a% ^: S8 u" ?# Y; ?4 M
首先被反映到账户中。& G: K: e% j! I
最后, |modifier| transitionNext 能够用来在函数执行结束时自动转换到下一个阶段。
; `( `" D9 d. r; n0 v0 c- s1 Y… note::$ h) S7 d) N4 ^
|modifier| 可以被忽略! ]2 N( Y# w- _7 }' t; P+ B* O, R
以下特性仅在 0.4.0 版本之前的 Solidity 中有效:: ~8 K7 V  J& \3 F! S( y# M4 s
由于 |modifier| 是通过简单的替换代码! J8 Z* o) i1 g" Q& x6 A5 v3 r
而不是使用函数调用来提供的,
' D( R5 t6 Z* J" w/ m& d如果函数本身使用了 return,那么 transitionNext |modifier|
9 Y+ z. ^+ t$ H- o的代码是可以被忽略的。8 U6 Q; f6 W+ |& s
如果你希望这么做,1 ?: T5 i- S2 r& G8 ^) r
请确保你在这些函数中手工调用了 nextStage。) r0 Y( J% F) I; _6 \; S
从 0.4.0 版本开始,即使函数明确地 return 了,
- _* X. }  v2 B, z# }  r( ?5 Y|modifier| 的代码也会执行。0 H3 p" s" _6 ~" Z5 L
::
7 f1 }/ `* U' bpragma solidity ^0.4.22;
5 R- [/ X1 s8 w/ ?7 T; w4 Xcontract StateMachine {8 I# P- m& S6 R2 l) g6 v" k( H
    enum Stages {' u7 d* j* c3 r$ a2 e
        AcceptingBlindedBids,4 Y2 |8 C2 E8 O8 y( S" x9 _
        RevealBids,# J1 k2 n  v5 K: i
        AnotherStage,
; [: {7 _5 U* I' A) t' s8 E  v3 u+ T        AreWeDoneYet,6 U5 Q! D" n  r% R: Z% _# a
        Finished. u" [) }+ t. h! z
    }0 F1 X' F' Z2 W& \* C$ n" J& h
    // 这是当前阶段。
7 b, b$ y& Q8 v' H    Stages public stage = Stages.AcceptingBlindedBids;
3 w1 Q; l+ R+ R, g8 a( R! u1 e    uint public creationTime = now;: {4 N( C; X+ I' |- V
    modifier atStage(Stages _stage) {
3 @  o9 j  r- P1 C; J- f+ w/ d        require(
' s  l; C' `6 Z, M% k9 K            stage == _stage,: H$ r- @( A3 h: z# t  X; d0 m
            "Function cannot be called at this time."! E- a4 ^+ {! k0 C
        );
  b3 y, a6 l" c$ b1 b) x        _;
. b/ C8 v/ B) H3 P% @; `    }
/ P* d- v" Q0 W$ f" V; x; `    function nextStage() internal {7 C" u5 H8 a# r& u& m
        stage = Stages(uint(stage) + 1);  S; Q9 m3 I+ h6 _% V$ p! @( E
    }
6 v* o/ T2 v6 Q/ i7 M    // 执行基于时间的阶段转换。1 S7 a( f, B% I' v
    // 请确保首先声明这个修饰器,
2 K1 }! U: `- j8 @1 x" h    // 否则新阶段不会被带入账户。7 l$ O" w: d6 x( ]& z/ ^/ k. g
    modifier timedTransitions() {
0 K" E. p* X1 @0 S7 f; b7 y        if (stage == Stages.AcceptingBlindedBids &&
, J$ ^# y" P1 |                    now >= creationTime + 10 days)
! @! S3 N+ {, f- D8 c7 l            nextStage();
2 }0 }0 G) d$ E) [2 u2 e" _        if (stage == Stages.RevealBids &&/ U# u+ v& Y, A; ]5 i8 S3 R" S
                now >= creationTime + 12 days)
- |1 V4 l& R, P* e$ ^7 a& R% _2 p4 f            nextStage();
1 ]- M$ w! R) r, E  l. h        // 由交易触发的其他阶段转换& T1 P* R  |+ {/ T3 W$ R7 t# V* q
        _;
( o% J; r7 c! P; B    }
3 D" j4 K8 n0 o9 j; S    // 这里的修饰器顺序非常重要!
' g, s8 k2 v% H' ~/ _" o    function bid()& w) N1 g, i* |+ k  X
        public( \; r  [5 @5 F4 N% [
        payable. y0 w  y: p7 i7 V' X# V0 n
        timedTransitions
: `/ _0 i& j8 Z' _6 i        atStage(Stages.AcceptingBlindedBids)
# g6 l$ }% k+ t. x; W. N    {
, C- J) O- O6 _        // 我们不会在这里实现实际功能(因为这仅是个代码示例,译者注)! e. W. ?" q# O! t6 X* F& E
    }
& O- {4 O; D, I# W    function reveal()
- f# p0 U( n0 H+ e! E; Y7 ^0 ]        public
- m; K" ~8 H5 B# q/ G2 y        timedTransitions
' q+ n4 r2 f  E4 X4 z/ x2 p        atStage(Stages.RevealBids)
6 B1 d# Z  C; B5 Q( S! w- A* m    {
7 A9 e8 d& `8 ]/ W    }
) P9 P5 I5 K1 f$ a' ~    // 这个修饰器在函数执行结束之后  }' t- g9 s; P* n3 p
    // 使合约进入下一个阶段。8 R2 [# v- G( T0 _1 Y6 T- E
    modifier transitionNext()- P) p, _$ I' S" F3 m
    {4 q1 a) l& {0 c9 w
        _;
* J- \$ G8 E1 I; S+ T        nextStage();
' w- y- U) q& X" f0 S6 `. Y7 Q    }! U4 d% W9 k1 n
    function g()
, R, h) o1 s: i" L8 b+ J        public( U8 P" b1 p6 `# d0 ]
        timedTransitions
; V6 t+ q6 Z1 `, H        atStage(Stages.AnotherStage)
& N( O7 }; `# U9 F; X        transitionNext  |8 [/ l) z6 o$ c
    {: d$ o* ]/ O9 Z0 M, p
    }
8 Z0 u0 W# l% n8 u( |/ G    function h()
: c  l/ t% t& T% k+ V        public. _9 I) w- I+ ~3 L, m9 \9 R
        timedTransitions& e) G& [9 N# f4 B0 R
        atStage(Stages.AreWeDoneYet)3 }5 @4 B' k$ K
        transitionNext
5 x5 d3 O# W6 i+ w    {3 G  d, |9 I, n1 F6 Y3 V5 m
    }
* l8 n9 D! D. {+ [" R: G& t    function i()
7 ~: X- j( M5 r; `* k7 S3 A        public6 }0 A$ g. ?7 _5 i8 e) d
        timedTransitions
1 k4 k/ E) \+ L7 }7 p        atStage(Stages.Finished)  w; @$ e# ~2 j* S! W$ G. ]
    {5 c+ ?- @% O, x2 U3 C
    }
; j. R$ \( e. F( H* o}
9 A, d1 @7 o3 Y, C( |: X原文:https://raw.githubusercontent.com/etherchina/solidity-doc-cn/develop/common-patterns.rst
标签: Solidity
BitMere.com 比特池塘系信息发布平台,比特池塘仅提供信息存储空间服务。
声明:该文观点仅代表作者本人,本文不代表比特池塘立场,且不构成建议,请谨慎对待。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

成为第一个吐槽的人

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

    0

  • 关注

    0

  • 主题

    13