Hi 游客

更多精彩,请登录!

比特池塘 区块链技术 正文

Solidity通用模式

温室小书生室d
185 0 0
从合约中提款
) O% ]1 ]# I; H/ A; _. Z
! z, k! _/ H4 P在某个操作之后发送资金的推荐方式是使用取回(withdrawal)模式。尽管在某个操作之后,最直接地发送以太币方法是一个 send 调用,
  u' h2 a/ r7 c- o6 Y1 A$ j但这并不推荐;因为这会引入一个潜在的安全风险。你可能需要参考 :ref:security_considerations 来获取更多信息。5 ]% z: p% q1 x/ V  L$ u- @- ?' W
这里是一个在合约中使用取回模式的示例,它目标是通过向合约发送最多的钱来成为“最富有的人”,
- d1 }: K2 T$ g/ J, j1 A) [其灵感来自 King of the Ether _。
, i4 D; a- D( T在下边的合约中,如果你的“最富有”位置被其他人取代,你可以收到取代你成为“最富有”的人发送到合约的资金。6 ?2 F# T; m; j3 R
::
( I2 v1 ]0 A2 {6 a7 ^pragma solidity ^0.4.11;
* D( O! y8 B" H% x) {+ Jcontract WithdrawalContract {! p! m% k) G3 Z
    address public richest;& \* h8 H4 z2 C. N
    uint public mostSent;
1 B6 v; a8 i0 ]5 @% @( V! B! u    mapping (address => uint) pendingWithdrawals;% i* N, o9 S$ W1 y7 ~
    function WithdrawalContract() public payable {
# p$ @, E; z) r" d! ]        richest = msg.sender;
) K7 h/ T3 o9 S2 F- I+ o        mostSent = msg.value;+ |7 D0 T$ l+ R4 ~) `
    }$ `& ]1 D7 n- L' {, l, Y2 C6 x
    function becomeRichest() public payable returns (bool) {" j- z3 K7 A) s$ _0 p/ G
        if (msg.value > mostSent) {, ]) j# I7 n# ~1 S, B
            pendingWithdrawals[richest] += msg.value;% n7 @( Q" T1 S5 L9 d
            richest = msg.sender;, P+ {5 p1 L% M, t0 Z
            mostSent = msg.value;4 ]1 z2 n& O9 x: F+ _3 e
            return true;
5 d& `+ j, W3 _( C: n- r        } else {
7 ^0 m9 ]9 d' S. A; f; q            return false;
6 |$ m; F6 H- _+ m! a* V& L) K        }1 w$ X: f0 ^3 j
    }
3 F# [  G8 G6 @1 p( C' h. s3 C    function withdraw() public {
" B1 B! L$ m8 l3 t! L        uint amount = pendingWithdrawals[msg.sender];
, g( Q9 l9 f5 X& ]0 m/ `' M' ^        // 记住,在发送资金之前将待发金额清零
+ K1 Y8 Z" h; X) W1 u. n        // 来防止重入(re-entrancy)攻击
: ^) h& i: C- e# x) o- D        pendingWithdrawals[msg.sender] = 0;
3 I0 z  b1 @. [2 ?+ Q/ d        msg.sender.transfer(amount);
+ i1 Z- x4 v/ r# o& U    }
: Z+ q: I7 ?( V9 M/ V- Q}
1 ?0 e" S: V' L下面是一个相反的直接使用发送模式的例子:) g' f( d4 O7 Y3 |
::( `+ A' D/ a) ?) o% g( \( Y) W
pragma solidity ^0.4.11;# N+ ]. p3 l$ }$ E$ t  D" H7 g
contract SendContract {) e! q/ G9 ]2 }0 k
    address public richest;
! h7 \. ~7 {1 N, |* {, B4 W- I    uint public mostSent;8 g- h& a: K2 m& [- z3 @( h/ \
    function SendContract() public payable {. Y9 h; z: L5 N+ R, c! F
        richest = msg.sender;' Y* j; R( s4 g; {, `! b. q; c3 l+ v
        mostSent = msg.value;1 @3 ^; T, n1 U
    }
/ g; N7 J; U/ W: S( o+ B    function becomeRichest() public payable returns (bool) {
8 [2 f' d2 a2 q! Y- H        if (msg.value > mostSent) {2 H: Y# O0 V9 n2 ~; g3 m
            // 这一行会导致问题(详见下文)
' F. w+ @' B! r4 ~+ z( [2 f            richest.transfer(msg.value);# g/ x- m, F: ^; w0 J3 c' W
            richest = msg.sender;
7 u4 X! d6 S3 D" k8 r8 l            mostSent = msg.value;& ?6 Y" z1 v: _7 X2 N
            return true;
/ x% X: r3 w7 [8 g5 ]1 c        } else {
; \! ]& U1 w  v0 c  y- I6 Q4 |            return false;" K# V4 F" q: b) b6 s0 F
        }) x  C5 V( m8 @% U- m
    }/ L6 Y' U* j6 N+ p$ P" k; W7 e5 [2 \1 j
}1 ^( d3 C# L( @. d% a
注意,在这个例子里,攻击者可以给这个合约设下陷阱,使其进入不可用状态,比如通过使一个 fallback 函数会失败的合约成为 richest
- u! p$ K8 \0 p+ O' _$ ?) h% |' |. f(可以在 fallback 函数中调用 revert() 或者直接在 fallback 函数中使用超过 2300 gas 来使其执行失败)。这样,当这个合约调用 transfer 来给“下过毒”的合约
  G  p; Y% b% ^6 T' i4 E发送资金时,调用会失败,从而导致 becomeRichest 函数失败,这个合约也就被永远卡住了。
" ]2 @- y2 \# ^6 L4 n如果在合约中像第一个例子那样使用“取回(withdraw)”模式,那么攻击者只能使他/她自己的“取回”失败,并不会导致整个合约无法运作。# h; V! B4 w  ]
… index:: access;restricting0 y6 R) E- m  z8 S
0 D4 v% J9 D8 \9 t
限制访问% }( b9 i4 U: Q9 r2 ^& U
0 Z) p+ `1 w8 _
限制访问是合约的一个通用模式。注意,你不可能限制任何人或机器读取你的交易内容或合约状态。2 i1 I) \+ K! h. N# j
你可以通过加密使这种访问变得困难一些,但如果你想让你的合约读取这些数据,那么其他人也将可以做到。9 p/ a# I8 s" n3 M3 _
你可以限制 其他合约 读取你的合约状态。. b. S/ t( H* D' a
这(其他合约不能读取你的合约状态)是默认的,除非你将合约状态变量声明为 public。
2 ^7 ]* {) Y: H; e6 }) y* H此外,你可以对谁可以修改你的合约状态或调用你的合约函数加以限制,这是本节要介绍的内容。/ y& e( I  F# h5 _
… index:: function;modifier
5 G3 C( O, D- E+ \! @通过使用“函数 |modifier|”,可以使这些限制变得非常明确。3 S) D/ S2 ~9 }* `0 @; Y
::2 E" f& v& f6 j6 l  f" c# r
pragma solidity ^0.4.22;
& p' ~9 d) a# u/ [0 h+ mcontract AccessRestriction {
( E, e  l' `$ v: h8 K7 `    // 这些将在构造阶段被赋值
) q7 C, l; P9 n$ m    // 其中,`msg.sender` 是$ X% b7 x# f( z4 s& p/ V0 g$ T' @
    // 创建这个合约的账户。( W% {: X+ D  X: X' m
    address public owner = msg.sender;( B! t) O* s' o6 W3 G
    uint public creationTime = now;
- Q8 n! j  y0 A1 f: \    // 修饰器可以用来更改2 s% B9 ]5 ?& F7 l. ?
    // 一个函数的函数体。" q; N: [5 A( C1 P% y
    // 如果使用这个修饰器,
( T9 @- M# ^+ e+ G    // 它会预置一个检查,仅允许
% V' C( N: y+ \( b& w9 I# X    // 来自特定地址的
6 i' j2 J- U5 L, t* f5 k    // 函数调用。
, L! k9 L2 V" o' t" e    modifier onlyBy(address _account)# Z8 k4 Z2 u3 u
    {
0 k" N8 o! }: y  H, b        require(
- `' `1 C/ L0 {, {7 c' {; q7 L. Q            msg.sender == _account,' O2 m  u' P5 [- e
            "Sender not authorized."/ Y$ U1 C' h7 e, F! @$ L
        );' Q+ e8 U0 N4 }, ^0 B
        // 不要忘记写 `_;`!
' b5 L; ^; @' i; T: @2 q        // 它会被实际使用这个修饰器的) i" w8 [3 T8 p8 v4 W
        // 函数体所替代。
" z/ ~/ X* I( r/ g        _;
( s/ f/ h* K) D* J1 E% d/ ]    }
* f2 n7 l& H' y# f    // 使 `_newOwner` 成为这个合约的% A! {! C% m8 @" j& q' d
    // 新所有者。
9 b) `3 b, s2 `- ]; l    function changeOwner(address _newOwner)  `1 g7 C3 j& }! B" _9 x3 Z) m
        public
  n4 b8 b8 [: J) e' q0 _" ~& ?        onlyBy(owner)
7 ~* {6 y# C0 E3 n" I- L3 j    {0 V/ @1 |. }) c1 S
        owner = _newOwner;
9 @/ \! Y: ~/ i& @' {  P    }
0 R+ Z/ o- [6 K2 Q9 W- a4 C    modifier onlyAfter(uint _time) {8 r7 |; y1 f$ p5 |) d
        require(3 U$ [/ {4 Z: h. Q
            now >= _time,9 g# O7 g# c6 }2 N5 c* t
            "Function called too early."% m  A  h0 o$ O& I
        );
- R" u1 G/ u0 D+ Z& e7 b% \' y        _;
: u! c/ G! \0 _) u0 ?( m  l$ c    }
5 A; d2 d- X! I( \    // 抹掉所有者信息。- M: f6 |8 D- p- y4 o7 p' ]& d
    // 仅允许在合约创建成功 6 周以后
) g4 O% D) r) ]6 N9 z    // 的时间被调用。- [1 o: ~3 n6 M8 W
    function disown()7 x8 j; z% f' H  {: E8 X+ M/ u
        public
9 ]# v+ \% ]& f4 u! W% A        onlyBy(owner)
& i% Y7 _& j/ Z. _- s% f        onlyAfter(creationTime + 6 weeks)2 A8 K' J8 z5 c( S0 T
    {- C4 Z4 T% V  N2 r' b
        delete owner;( u* E9 e9 q# M  |
    }6 p9 g4 D$ h6 e& f0 U: h& h
    // 这个修饰器要求对函数调用
8 P7 K% L: f3 E: h" c/ ]: s    // 绑定一定的费用。, d1 z+ I# G, ?3 D5 y
    // 如果调用方发送了过多的费用,
- A* \# V/ V8 D: ^: r, z  T    // 他/她会得到退款,但需要先执行函数体。0 ]; ~9 q- s. A: y9 }' ~! ?
    // 这在 0.4.0 版本以前的 Solidity 中很危险,) L9 m3 k- A  T' l1 c
    // 因为很可能会跳过 `_;` 之后的代码。' l1 `4 z- |" O7 y
    modifier costs(uint _amount) {" f6 T' p2 E% c/ D. ]- t% K
        require(
+ g9 m1 j" {: z/ I# R; v0 n- v# y            msg.value >= _amount,  z0 Q5 V  ~& V* s/ @
            "Not enough Ether provided."' C2 B5 z* q; ?0 O4 f
        );& ^4 j6 Q% M; l# d' [, _" z
        _;
( m3 e0 i% O$ G) B) ~7 l        if (msg.value > _amount)2 o7 t4 H( |6 y
            msg.sender.send(msg.value - _amount);
3 O3 G' x# F! K  @" |9 p    }
0 v) ~7 F: T* a% u! L4 q    function forceOwnerChange(address _newOwner)9 `+ `* X: v3 X: A% U& H
        public% M7 J+ e- w/ M7 u# @# P) Y
        payable
. Z6 v* g4 {2 G1 e2 L5 ~        costs(200 ether)0 P' A  \- x; Y8 ?1 W
    {
7 {. k9 k  t* F+ k        owner = _newOwner;6 p$ ]* B, p7 y5 a+ |
        // 这只是示例条件6 q$ {) q) X- _5 P3 c
        if (uint(owner) & 0 == 1)! t. w5 B; g2 u$ [7 h! I
            // 这无法在 0.4.0 版本之前的
( o5 [3 }+ e- h  H            // Solidity 上进行退还。) I! ^! T6 a5 v" E( z' W6 f
            return;7 _. n- Z; Y. A" q7 H
        // 退还多付的费用* K+ D2 r/ B0 `4 s; }
    }
. Z& v- ^, g- ]}0 {" b* A( m1 J4 g" \
一个更专用地限制函数调用的方法将在下一个例子中介绍。
6 \8 Q1 w* |9 d  ^8 g7 b* K3 H… index:: state machine
! R- H4 B+ b3 g3 F$ F5 z% Q8 A
! K& N8 ~/ A& z8 [状态机8 i1 M5 E7 N# C% E
7 x& m+ ~+ W, q4 h' `/ J
合约通常会像状态机那样运作,这意味着它们有特定的 阶段,使它们有不同的表现或者仅允许特定的不同函数被调用。
6 ]+ v. z# j  D4 z% ]( j一个函数调用通常会结束一个阶段,并将合约转换到下一个阶段(特别是如果一个合约是以 交互 来建模的时候)。
) y  L8 y' A( p% ~通过达到特定的 时间 点来达到某些阶段也是很常见的。
$ `( S# D. e- o" l一个典型的例子是盲拍(blind auction)合约,它起始于“接受盲目出价”,: T5 F' L% w4 x! r  f2 m
然后转换到“公示出价”,最后结束于“确定拍卖结果”。
: s* K! i1 H+ O8 S3 n# H% }+ E… index:: function;modifier; E" |  \' T, p+ z2 f! N+ f0 @: f
函数 |modifier| 可以用在这种情况下来对状态进行建模,并确保合约被正常的使用。* `! s* G' @9 ?4 I
示例
; T- j* k% k" K在下边的示例中, |modifier| atStage 确保了函数仅在特定的阶段才可以被调用。! h7 {7 Y0 ]5 t7 |
根据时间来进行的自动阶段转换,是由 |modifier| timeTransitions 来处理的,
$ {" Q  Q2 Q* |% f3 |5 ^: p$ i它应该用在所有函数上。
9 R. X" M6 M/ t- H… note::8 {2 ~+ y1 P& {9 s
|modifier| 的顺序非常重要6 Z) _, |& G  D
如果 atStage 和 timedTransitions 要一起使用,
( I9 E. B# ?' z5 n2 Q( d9 h请确保在 timedTransitions 之后声明 atStage,
9 b' X: A% c/ r  b8 G" _$ N以便新的状态可以  o: }, }' g; {  h* d
首先被反映到账户中。/ U0 R9 ~/ m3 M8 k7 E( C
最后, |modifier| transitionNext 能够用来在函数执行结束时自动转换到下一个阶段。
; I: P4 w2 b7 ^4 v… note::$ W( M% z8 ~4 R5 G  f
|modifier| 可以被忽略( L( O2 y( S* s  L3 D
以下特性仅在 0.4.0 版本之前的 Solidity 中有效:
/ z1 n7 H4 \, {2 t由于 |modifier| 是通过简单的替换代码7 |+ t* C: S8 w* U! K% A6 M) N
而不是使用函数调用来提供的," N- T# ~) M9 ?  s6 \
如果函数本身使用了 return,那么 transitionNext |modifier|' _& p( W: m9 K& f" C$ t2 q3 O
的代码是可以被忽略的。
; d+ D* A1 x0 \$ w如果你希望这么做,; S! h; ?2 o2 w
请确保你在这些函数中手工调用了 nextStage。
1 U4 `9 O' o' J2 @: {* X3 Q从 0.4.0 版本开始,即使函数明确地 return 了,
1 o: y: n6 b& Z& d- v1 p|modifier| 的代码也会执行。
$ L2 g, h" w3 M6 S% y::
4 p# T4 x0 W# X" qpragma solidity ^0.4.22;
8 O. B) Y# r. y7 t3 k, \( ^contract StateMachine {, @5 r6 r/ P3 ^1 w0 N$ `- b% J# P1 [
    enum Stages {
* L' F- r7 }: Q" m4 G        AcceptingBlindedBids,- `$ k& t( P5 W# ?) W* w9 b( j6 d% ]
        RevealBids,# t1 K. t. [9 y: J4 e) g8 }  s- [
        AnotherStage,6 G4 d) m0 ?; N: S  P
        AreWeDoneYet,3 ~4 ]" r; S0 Z) t9 j4 D! E( D9 U
        Finished
9 Y: ^. q! ~6 k    }. w7 X' K6 D  L- I' e7 L# Z
    // 这是当前阶段。
' E5 y. i, v' }4 X- d" C# j4 |    Stages public stage = Stages.AcceptingBlindedBids;
! ~9 f2 U7 \* Z    uint public creationTime = now;. u6 J- z. a3 z% U: h. h4 V9 v
    modifier atStage(Stages _stage) {
5 Q/ e5 a* [4 z; _. j        require(
3 ^$ Q; ~& S3 L- _; e. u2 M. ?/ M            stage == _stage,6 w1 b& T) g& P0 [5 Z, ?/ i* o+ n
            "Function cannot be called at this time.": Y2 O. K6 F" F' C! Q# k
        );
: ^4 ]5 e7 s) O0 \! D        _;$ ]: [* k7 J- T3 c6 Y, L
    }
- T1 `8 o. f. h( ?# l$ ^; b    function nextStage() internal {) U' b1 F4 ~7 {! {
        stage = Stages(uint(stage) + 1);
' n7 r. _0 `  J7 j; @2 M    }
( b  a& ]8 V* B' F/ M# Z& B    // 执行基于时间的阶段转换。
, `" c4 j3 i$ o( x) K    // 请确保首先声明这个修饰器,* Z" B- b* ?% _1 H" y& q' {! P
    // 否则新阶段不会被带入账户。
2 F- J' P. W" y    modifier timedTransitions() {
# e, e9 d# c, O9 P1 q        if (stage == Stages.AcceptingBlindedBids &&( h6 _& K5 Z5 y
                    now >= creationTime + 10 days)1 S6 d, K" {+ V+ e) C
            nextStage();
( w% X+ j& Z; B/ ]; B        if (stage == Stages.RevealBids &&
; m# R% a6 M" r                now >= creationTime + 12 days)6 j2 ~! o* o4 d2 d1 m6 `
            nextStage();7 F1 S) K' J. n3 ?5 m& C) I
        // 由交易触发的其他阶段转换* n$ i# c) e9 Z2 O2 `, O3 ?/ w! q
        _;4 x$ ^8 N* b* f" c5 j2 S
    }
' L) j$ A7 ^+ L# k" p/ W3 D    // 这里的修饰器顺序非常重要!
; h+ w+ A! t( J1 [+ {1 l    function bid()( c: r0 @, j1 r; Z; e- G# p
        public
' ~, d- Y& c# X# L# P/ `        payable9 v0 V  S5 B9 Z% A" B) M
        timedTransitions
: Y6 A% B! f2 R, K3 @$ ^        atStage(Stages.AcceptingBlindedBids)% ?, D( W6 j/ X9 y% ?; \+ D
    {
8 n9 p0 W; H, ~: n: p6 r; g0 u        // 我们不会在这里实现实际功能(因为这仅是个代码示例,译者注)
& @  r/ {$ z% Q. L    }. D9 j$ v3 O# y9 P2 R- C$ i# j+ B9 l
    function reveal()" G& j4 P1 k/ k0 |0 w
        public8 h% y1 ^* v' }% ]2 K- [( h5 `
        timedTransitions
. {  v; L$ S( T7 m        atStage(Stages.RevealBids)' b6 Z) a8 ]! ^# x  l+ M5 _+ R
    {
; i2 O( K( s& \4 x8 {1 i    }
# Z; K1 `/ \/ k' j5 _/ U    // 这个修饰器在函数执行结束之后# z$ Q* k% S1 {; W2 B8 P# X/ |! A: E
    // 使合约进入下一个阶段。2 w) G# s0 i0 F7 z5 R: p
    modifier transitionNext()
" S, A. _8 r+ l. K: E2 y: K+ H% i    {# v! }6 F' H( ]/ r
        _;3 J3 L  k, E( h: S, f4 f
        nextStage();
. h+ o* s7 ]0 G8 w# f6 u    }* \" j/ U# g( T6 {
    function g()
, w# V. P& C% M, t% c+ l        public
" i9 c8 N0 L. I+ y, o* X        timedTransitions3 N- u& s0 J2 E
        atStage(Stages.AnotherStage)! ~9 j, \8 T9 b7 K# C# ]/ A: `
        transitionNext* S9 ?1 n5 a% U# s3 D% ~. e9 K
    {4 ^; H# }1 X' o0 `# F  u% A6 J2 T6 Q
    }( {( G- S% S, R$ L/ o/ H
    function h()- e. w- ]' ~$ W$ V( H: v
        public
$ \% H* ^7 N+ f! _        timedTransitions
& e" L2 z  A, _/ l        atStage(Stages.AreWeDoneYet), `: ^0 v" f* K" U: g% L: Q: b5 g7 O
        transitionNext
  ~4 n  L1 e8 \+ }- D" w' r    {6 I. b) u% z* V7 M+ p9 B* B
    }
2 [# k+ y+ R) ?# }: `    function i()0 x$ \% f+ r8 I
        public  H+ U0 Y3 P; i( h" g" G
        timedTransitions; N+ \& c4 g; p8 e% _0 o, z( W+ L
        atStage(Stages.Finished)
+ \$ f6 H! D7 }& Y7 e    {% A5 A& }/ Y; Z1 @! r2 h
    }1 w5 G2 Y9 P) z5 h
}8 Y3 ~6 y+ u' g& N6 g% G
原文:https://raw.githubusercontent.com/etherchina/solidity-doc-cn/develop/common-patterns.rst
标签: Solidity
BitMere.com 比特池塘系信息发布平台,比特池塘仅提供信息存储空间服务。
声明:该文观点仅代表作者本人,本文不代表比特池塘立场,且不构成建议,请谨慎对待。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

成为第一个吐槽的人

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

    0

  • 关注

    0

  • 主题

    13