Hi 游客

更多精彩,请登录!

比特池塘 区块链技术 正文

Solidity通用模式

温室小书生室d
366 0 0
从合约中提款
3 k& G* F' h& R. k7 D$ ]  Y
& r' c7 k6 {6 N, Q在某个操作之后发送资金的推荐方式是使用取回(withdrawal)模式。尽管在某个操作之后,最直接地发送以太币方法是一个 send 调用,
& ?9 N5 m7 ]/ p+ ~6 |! y" y: B但这并不推荐;因为这会引入一个潜在的安全风险。你可能需要参考 :ref:security_considerations 来获取更多信息。. ^8 V. @1 \& U
这里是一个在合约中使用取回模式的示例,它目标是通过向合约发送最多的钱来成为“最富有的人”,0 w0 L2 @+ a) b" _' j9 M; k
其灵感来自 King of the Ether _。
/ k& s' B& Q/ O6 E7 E) ~* k在下边的合约中,如果你的“最富有”位置被其他人取代,你可以收到取代你成为“最富有”的人发送到合约的资金。
9 x% `& e# _. m; Z::+ E. h. @6 O" }7 _; H0 U
pragma solidity ^0.4.11;/ V+ \6 u# H4 Z, T
contract WithdrawalContract {
( G; n& i; v( L' \9 d    address public richest;
8 m4 ]) h/ ^& M    uint public mostSent;2 z- M; K1 s; N+ ?; v
    mapping (address => uint) pendingWithdrawals;
. r/ r+ b( A" {- ]    function WithdrawalContract() public payable {, Y( r+ r% p& E
        richest = msg.sender;7 Y  d! [- z$ u
        mostSent = msg.value;. L. p1 z, s$ Q) y3 |8 K
    }
; z" D* S' n+ p+ ~2 M    function becomeRichest() public payable returns (bool) {
% ^  P) b4 R1 G" E% k4 J, g+ O) i        if (msg.value > mostSent) {  K  G. y; X* L; F  N& d2 K
            pendingWithdrawals[richest] += msg.value;
$ V1 n5 O/ \+ u7 f/ Y" [( w            richest = msg.sender;! f- C) b3 b: s: ^, I+ |; T
            mostSent = msg.value;$ F; Q2 Q( q: ~- w( J. S
            return true;' t  Q$ c# I6 @: K
        } else {( @8 `7 D: ]$ C$ N
            return false;# K! v9 B8 C7 b. Q3 W9 [* i& K& M
        }
; C2 l: ^+ ~* i$ @. A. w    }: a6 o: g0 W) Q/ K) r4 B
    function withdraw() public {
8 M4 l# C: U# V, X        uint amount = pendingWithdrawals[msg.sender];
' J' N4 x. K2 s( x2 c3 R        // 记住,在发送资金之前将待发金额清零
& f( j& q, v  C        // 来防止重入(re-entrancy)攻击
0 H3 A" w9 z0 M  M! ~) }/ i        pendingWithdrawals[msg.sender] = 0;
7 I6 B: {. @7 _1 h        msg.sender.transfer(amount);& A* g. J8 z+ a& W. Y; ~$ u
    }# V3 S& U+ Y* B. r
}5 r, c  \$ p/ L1 O% w1 j# c
下面是一个相反的直接使用发送模式的例子:
# Z. \8 R6 p+ J- e' V% x::
  U# P# x% r# f( ~9 p5 Xpragma solidity ^0.4.11;) P% t/ T6 h+ [% ~! \& L
contract SendContract {+ o7 |5 A) f" }0 Q/ F( m" w, N
    address public richest;- Q4 O+ s1 p4 u4 S
    uint public mostSent;
- S) m& `2 w0 G, E' n0 k& B    function SendContract() public payable {
; a4 C/ ^! I/ w  h2 F        richest = msg.sender;+ o6 _( N% H! p) F# T" D+ c
        mostSent = msg.value;
" v$ h1 e* i; U$ C6 h    }
+ P# W* ?7 @4 b, w% L' k    function becomeRichest() public payable returns (bool) {+ j  N& M, ~" N% g9 U  v( a- r) F
        if (msg.value > mostSent) {5 f4 S. _% q$ f0 m* c5 w& p) X2 f
            // 这一行会导致问题(详见下文)* u5 a- ~/ z! }3 \6 Q- }0 i, z
            richest.transfer(msg.value);
: Y" M' d9 G- n) h6 n5 M. U            richest = msg.sender;) P* Z) |" o! W2 s$ ]. \
            mostSent = msg.value;
0 z$ C) L8 `% h6 v; A9 s& h" ?2 J$ u% J            return true;
. J# C) z0 n+ T% l. a% u        } else {( p/ G, Z/ l: s- @* k7 y) b
            return false;
7 o/ l- x1 p1 P. g. E4 u* F2 _        }0 P5 r* ?  }9 Z0 n6 ?
    }* p0 _& e) ~3 Q. `7 J1 ^( f
}
# V1 q1 m2 k& |) e: p" m2 V& u! T' ?注意,在这个例子里,攻击者可以给这个合约设下陷阱,使其进入不可用状态,比如通过使一个 fallback 函数会失败的合约成为 richest/ K. o- R2 \) R; e" p
(可以在 fallback 函数中调用 revert() 或者直接在 fallback 函数中使用超过 2300 gas 来使其执行失败)。这样,当这个合约调用 transfer 来给“下过毒”的合约% W! g) Q" [- }0 n
发送资金时,调用会失败,从而导致 becomeRichest 函数失败,这个合约也就被永远卡住了。/ m) R9 x4 h8 v
如果在合约中像第一个例子那样使用“取回(withdraw)”模式,那么攻击者只能使他/她自己的“取回”失败,并不会导致整个合约无法运作。) G* k& Q9 k5 U) Z1 D* a; k- b8 L7 I! y' l
… index:: access;restricting
3 r2 s$ T6 b! ]2 b) [, [
  P  Y9 m$ U% B0 _& ]  q' Y% P# d限制访问
$ G+ ]% `0 P2 R6 ~" g7 N4 Y. c7 p3 O, i' e8 [0 V6 p
限制访问是合约的一个通用模式。注意,你不可能限制任何人或机器读取你的交易内容或合约状态。0 @4 r5 D  X1 [" k9 N7 J
你可以通过加密使这种访问变得困难一些,但如果你想让你的合约读取这些数据,那么其他人也将可以做到。
; }' a8 j" z$ @你可以限制 其他合约 读取你的合约状态。8 ^9 K9 X2 m0 g, i" L! K
这(其他合约不能读取你的合约状态)是默认的,除非你将合约状态变量声明为 public。
* U2 @" }& `+ v此外,你可以对谁可以修改你的合约状态或调用你的合约函数加以限制,这是本节要介绍的内容。
, A+ q* T- g7 B6 G& N" J… index:: function;modifier
  s3 ?- G+ t# M! T6 Z$ F通过使用“函数 |modifier|”,可以使这些限制变得非常明确。" O! _0 {( V7 D- _) ?; ]" \
::; w& Q8 Q2 d1 T0 z- d) [% L0 V1 d4 d5 y
pragma solidity ^0.4.22;
2 T5 C" H0 H5 o' econtract AccessRestriction {
8 a7 p; R/ g5 K+ R    // 这些将在构造阶段被赋值% X' R8 y/ T5 e
    // 其中,`msg.sender` 是4 G* [0 w5 M) V; K! R. G$ A' p0 L; V
    // 创建这个合约的账户。2 ]7 x6 t. j5 X" H+ w0 c1 r( G' q
    address public owner = msg.sender;  {% [- D1 m. }' U+ n1 x; Y- j
    uint public creationTime = now;
: D# O& G  a# k: B( J    // 修饰器可以用来更改- `  ?! |5 T" n! V6 D" L& v
    // 一个函数的函数体。. w8 R9 U, P$ K$ F/ P
    // 如果使用这个修饰器,
7 N% f  W# z3 q9 C( G& i    // 它会预置一个检查,仅允许4 F, w7 Y- G, w
    // 来自特定地址的! ~) s, D! P' N7 t& O
    // 函数调用。) y  d8 \0 ?8 |' |
    modifier onlyBy(address _account)* A7 ^2 ~5 O0 t- @9 a! y
    {7 A+ x. h7 ?- I5 L# p% p
        require(
2 W  a& V( {* E- X5 g7 @; C4 e            msg.sender == _account,
& M, J% L; t) j! ~# R            "Sender not authorized."
: {2 S% b  `$ q        );' a! q% m: w# K
        // 不要忘记写 `_;`!
/ m7 C7 T' @  m        // 它会被实际使用这个修饰器的
$ `) b4 w( X- \        // 函数体所替代。
+ v/ N) J0 O% t        _;4 s3 l3 g# R' c7 c: B
    }9 [% N9 m7 f4 C+ T$ h
    // 使 `_newOwner` 成为这个合约的: l8 A' L3 U& H  c9 D  G
    // 新所有者。
% u1 ]5 C7 i) f+ d. N. R    function changeOwner(address _newOwner)1 h* u% \" x. K0 S0 s1 G2 t* z
        public
+ v2 _; i- G8 ]; `  I! d2 B        onlyBy(owner)
3 m& D- d% r7 `+ K: X    {
& V' G) J' |. E* m        owner = _newOwner;
( r3 [9 n0 b+ n7 T    }
% V* v  M, X! w6 s    modifier onlyAfter(uint _time) {; z9 w6 r1 v* [- t4 b! J0 f
        require(8 d4 c; i! r2 l" Y' @$ l
            now >= _time,, m" k6 X' M: \. D* N1 H2 C
            "Function called too early."+ R7 e- s- x$ B2 C- z
        );3 f! r: R2 I' ?/ Y
        _;, w$ g7 |/ [& ]/ F8 w- i
    }' v# n1 E1 }! [
    // 抹掉所有者信息。1 n; V* d- n( p2 `; F. x1 S
    // 仅允许在合约创建成功 6 周以后9 ?2 ~. A' a4 g7 R/ y
    // 的时间被调用。
- W! \' @. X4 Z& y3 z/ T    function disown()  f( b( r8 ~6 I' a: y3 L1 Y: a( E
        public* C  x$ W% k4 t6 j
        onlyBy(owner)/ h! x6 ]  x5 F# u4 r
        onlyAfter(creationTime + 6 weeks)7 ]6 a& ^0 ~0 ]. N" M
    {' g+ W( {7 @+ U% Y2 s4 X8 z2 O; P" V
        delete owner;
; W3 L- Q: ]7 _# U' g* x    }
6 `$ `6 r9 H6 I& h. W# A    // 这个修饰器要求对函数调用
& I. n4 m' K6 d! z    // 绑定一定的费用。2 z. i2 k% l: }) L! _+ U
    // 如果调用方发送了过多的费用,
* I+ U; M. |. W. R9 d) [; N    // 他/她会得到退款,但需要先执行函数体。
" [- _5 [. e5 y' Z: t% f    // 这在 0.4.0 版本以前的 Solidity 中很危险,
( X2 X% \+ l) \! K/ M* S: `    // 因为很可能会跳过 `_;` 之后的代码。3 g3 D. ^2 M6 c1 W2 N2 v+ @; k* ~( ~
    modifier costs(uint _amount) {/ {9 s+ C  h! O+ @$ G7 H
        require(- [9 b* m7 f7 V% _0 q
            msg.value >= _amount,9 E5 q& m+ j2 B7 |- K
            "Not enough Ether provided.", ?7 [, W# F, r7 F
        );
, x. [. c3 I+ k0 H+ c: S        _;1 i( G, L/ I% p% B
        if (msg.value > _amount)
6 U- [% `; H7 B9 w            msg.sender.send(msg.value - _amount);2 x2 C& |. [. W0 }4 @
    }
' ], C3 X+ l2 H    function forceOwnerChange(address _newOwner)5 V9 b) q8 I' P# z' @, A- G) x
        public* T% E& z+ {9 J1 j$ _2 `
        payable2 A2 a1 {2 K+ ?
        costs(200 ether)# K5 x! S( g! p
    {: X+ j1 b& V/ H5 u4 t( a" L
        owner = _newOwner;
: b5 Y9 q9 H& Q3 r0 J2 [4 O) X        // 这只是示例条件8 \  ]% b2 Z, p% a/ e1 e: ~+ `' h
        if (uint(owner) & 0 == 1)
7 p, B+ Y+ R8 @            // 这无法在 0.4.0 版本之前的8 \" }8 e7 y+ f8 j0 Q: J; n- W
            // Solidity 上进行退还。
+ L+ k1 L5 ]& ~$ D+ K1 w6 Z" X            return;
) Z) m& m& V/ ?6 {8 P' }  _        // 退还多付的费用* t$ p: h0 g/ Q9 W) {' \
    }
3 N5 n& ]& D$ L8 B8 w/ }# i}
, g  Y6 P, o: ]$ H( x; W一个更专用地限制函数调用的方法将在下一个例子中介绍。9 b& R3 E  X" T; ^) G; y" \! N
… index:: state machine1 I1 \$ A! g0 ?, _& B( l

( G- I6 K1 z! v: E3 @( a7 q状态机
6 e" H' u3 s  O6 A  [/ I8 c5 d! S6 v$ C; x5 \2 ?4 g5 L
合约通常会像状态机那样运作,这意味着它们有特定的 阶段,使它们有不同的表现或者仅允许特定的不同函数被调用。
3 n; d' U8 N- _  E一个函数调用通常会结束一个阶段,并将合约转换到下一个阶段(特别是如果一个合约是以 交互 来建模的时候)。8 d& x+ ]4 B7 E- H" s, K3 G: n
通过达到特定的 时间 点来达到某些阶段也是很常见的。" l0 G7 |% B/ n7 J0 Q
一个典型的例子是盲拍(blind auction)合约,它起始于“接受盲目出价”,0 i- d# `% i( g/ g
然后转换到“公示出价”,最后结束于“确定拍卖结果”。* D5 Q, z; e3 u! n6 @" |3 b7 D& W2 X
… index:: function;modifier# G/ M- o+ y2 f
函数 |modifier| 可以用在这种情况下来对状态进行建模,并确保合约被正常的使用。& X9 X8 Z8 `& X6 K0 k
示例
6 Z/ N* [; x1 U6 i( e6 }' _在下边的示例中, |modifier| atStage 确保了函数仅在特定的阶段才可以被调用。
4 B' ?& s( e' s" }6 U根据时间来进行的自动阶段转换,是由 |modifier| timeTransitions 来处理的,
, ~  H( }+ a5 `) I# l+ M& ]  d它应该用在所有函数上。- @- `9 f5 |$ f* s9 P+ T
… note::
) _% @/ @$ x4 N* ]9 I& p|modifier| 的顺序非常重要
7 \/ m3 H( x: f% S如果 atStage 和 timedTransitions 要一起使用,
5 Y- \% I) L( L) T6 m1 l请确保在 timedTransitions 之后声明 atStage,0 ?( G$ o  V! k+ g; \6 L+ Y$ |
以便新的状态可以3 T, G' S. [  }# ~3 `7 c4 b2 s" N
首先被反映到账户中。
2 W2 H9 Q1 |9 V* j最后, |modifier| transitionNext 能够用来在函数执行结束时自动转换到下一个阶段。5 C; x; r7 I$ P
… note::
0 R; l5 c2 F5 C) `|modifier| 可以被忽略/ F% V0 ~# E. B2 \
以下特性仅在 0.4.0 版本之前的 Solidity 中有效:& |# u. n& S. y3 G
由于 |modifier| 是通过简单的替换代码
4 w5 y5 j3 H0 g$ q' R" ]而不是使用函数调用来提供的,$ X8 [6 v9 x" M% ^+ ^  A: K
如果函数本身使用了 return,那么 transitionNext |modifier|, i4 ]" x! L, v
的代码是可以被忽略的。7 c9 Y: K1 L6 v7 k* `& `& G5 Y1 a
如果你希望这么做,
0 L6 r9 Z% l6 F, V9 ]' |' H4 K请确保你在这些函数中手工调用了 nextStage。/ K: b, ~- p7 s0 J0 I1 p5 Y2 W
从 0.4.0 版本开始,即使函数明确地 return 了,
. z) f1 }% b4 f' V|modifier| 的代码也会执行。+ e0 A. }4 l( e, ]% o0 |
::: h7 E$ P( Q/ q2 ~% S1 K- N
pragma solidity ^0.4.22;. c. [7 U% w/ K0 l1 A
contract StateMachine {
: r: o' Y+ `3 h9 W* E! Y, t    enum Stages {3 S! t- `5 y- [+ Z0 d
        AcceptingBlindedBids,
! O) }- p; l6 f4 g8 y3 ]8 \        RevealBids,/ x. X. J$ s1 A4 h/ n
        AnotherStage,
; S1 ~/ c/ q+ e; H0 @        AreWeDoneYet,0 \* Y# J% S, N8 s9 g; J
        Finished
: a; @. J6 l  R7 x( S; M+ i7 A    }
( g% ~+ f) U; a! F1 S. |3 C' p    // 这是当前阶段。0 R2 O' m3 H% r1 ?  D
    Stages public stage = Stages.AcceptingBlindedBids;
2 K+ t+ ]$ E& l1 o: {6 U    uint public creationTime = now;9 r5 v. F! ]$ C8 @
    modifier atStage(Stages _stage) {" u$ _. ?/ v* t+ i& j0 w" J1 L; E
        require(: D" A$ n' h' t# f3 q( D/ L- m( a0 U
            stage == _stage,
* h- z$ ]& `1 {2 x: @" o            "Function cannot be called at this time."; ^, _' p9 G. M; B: u1 ?
        );$ h, X5 B/ X% D" |5 J. G
        _;  [! N8 [) f7 D  j! k3 o* p
    }/ Y/ Q' I4 s: q2 ?; x! Z# _: @4 h
    function nextStage() internal {
: ^6 C' Y8 K0 e$ N        stage = Stages(uint(stage) + 1);
2 x2 n2 C+ ^/ o: ?5 ?    }+ E8 e; g2 S3 S
    // 执行基于时间的阶段转换。
# K2 F: u+ s4 l$ B! _$ c+ \' H    // 请确保首先声明这个修饰器,
2 L! ~# s1 g0 h  l6 G' e    // 否则新阶段不会被带入账户。
" O' @1 U9 g! R6 w9 j4 r    modifier timedTransitions() {! S- A! O/ w/ g+ H/ ^( g) p
        if (stage == Stages.AcceptingBlindedBids &&) b1 X' }5 g% d$ e: x! H3 O$ E3 @
                    now >= creationTime + 10 days); x, y" y  A# `: q3 J! V
            nextStage();
- S+ o: y2 b/ ~+ U' s. N        if (stage == Stages.RevealBids &&
& U8 {5 V  e8 I; g* i3 e* A$ W                now >= creationTime + 12 days)6 j& R; u% Z$ z
            nextStage();" E6 \  Z5 U" |6 q" j
        // 由交易触发的其他阶段转换$ w) p3 x2 ]8 c& l# j/ _4 V7 R" {
        _;
. F  _5 ?6 d/ u. f9 X; T7 O    }
( d3 D; e) J. j' X9 F0 p3 t. i    // 这里的修饰器顺序非常重要!
6 [4 h! f5 _, B( }    function bid()
3 e# W9 l6 f+ D+ w% y        public7 b- {' N% \. b- S' [
        payable
( I& H6 J2 Y8 |$ H        timedTransitions% U3 @- O7 v- r! d1 ^  _
        atStage(Stages.AcceptingBlindedBids)
( W9 ^6 x6 W, E* ~# l  H4 Y. K: P    {
# `( M4 w) R0 f0 w: e, l& d        // 我们不会在这里实现实际功能(因为这仅是个代码示例,译者注)
6 T/ [* A: L# ?/ X3 N    }
0 w/ a3 Q( D7 w8 w8 ~, R) Q    function reveal()
5 F& q: t( L1 {+ a+ B; V; g! a        public
& K' e& L' s: ~$ h        timedTransitions
, r% R3 z0 X/ Q8 o        atStage(Stages.RevealBids)
, n/ v' b+ H" u    {/ F. b8 u# [& U* M/ E( g* Q1 V
    }
5 N4 j% A0 @3 E    // 这个修饰器在函数执行结束之后
0 f( a1 B( F9 L) k- U9 P2 d    // 使合约进入下一个阶段。
% ~3 X1 j5 r- ]- K5 W! W    modifier transitionNext(). ]1 u4 a' j* ^$ e! J
    {4 R- C- p3 G3 u/ U
        _;
+ g* _6 y5 G5 K" j$ W! N. x        nextStage();1 f! A* e0 [7 S5 Y- Q& s
    }2 O  f: @! M+ q- ^
    function g()
/ m5 c- W6 C$ \5 Q7 ]. m" G+ n        public
; @2 T3 {) A$ a6 T& T        timedTransitions8 C* V$ E2 b6 o8 {
        atStage(Stages.AnotherStage)+ l* d( V% R5 v0 S- H
        transitionNext1 {; [4 r: j( o0 ?) B6 q
    {, o! N; @8 J- Z( D
    }
. @2 ]% {' z* Y1 n5 ^. `    function h()
5 _: R, d9 d' A        public5 v1 T8 O( e2 J# E
        timedTransitions. H  K% w& R% G' {9 w% b  W! M3 Q8 |
        atStage(Stages.AreWeDoneYet)
. B  N4 C& X+ V+ x! h5 o6 i        transitionNext4 u7 x+ K" J8 M* U3 E; C" a3 K! @
    {9 W. x: b- U% I* q& K; a5 k
    }; }$ ?* Y  O8 V# H% E
    function i()
' y( C% H7 |8 y: Q+ g; S5 W' _( V        public
& k9 F1 `  _' b& h. h2 e2 x7 J5 _+ _        timedTransitions8 C4 F2 L$ a% y8 ?1 O
        atStage(Stages.Finished)1 t( f% |: ~. P% a
    {
$ s0 \/ z% _& q    }
- n$ f* }; I, g}+ Q% o  N  y  C9 n  j
原文:https://raw.githubusercontent.com/etherchina/solidity-doc-cn/develop/common-patterns.rst
标签: Solidity
BitMere.com 比特池塘系信息发布平台,比特池塘仅提供信息存储空间服务。
声明:该文观点仅代表作者本人,本文不代表比特池塘立场,且不构成建议,请谨慎对待。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

成为第一个吐槽的人

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

    0

  • 关注

    0

  • 主题

    13