Hi 游客

更多精彩,请登录!

比特池塘 区块链技术 正文

Solidity通用模式

温室小书生室d
421 0 0
从合约中提款) m4 z9 J, K$ d' j. b: ?1 J7 X
! J* }# _6 R: p, [
在某个操作之后发送资金的推荐方式是使用取回(withdrawal)模式。尽管在某个操作之后,最直接地发送以太币方法是一个 send 调用,
  n+ ~4 C6 v. `+ _但这并不推荐;因为这会引入一个潜在的安全风险。你可能需要参考 :ref:security_considerations 来获取更多信息。
9 n1 L- y. _3 ^) m这里是一个在合约中使用取回模式的示例,它目标是通过向合约发送最多的钱来成为“最富有的人”,% T1 k+ |' K9 G
其灵感来自 King of the Ether _。
" u0 c" K8 V; p4 u8 c! Y: J在下边的合约中,如果你的“最富有”位置被其他人取代,你可以收到取代你成为“最富有”的人发送到合约的资金。6 _' q# z" S% s% n
::/ G& _) N, Q% ^% U- |) H
pragma solidity ^0.4.11;
+ P" y$ u  J0 Z2 Y4 g0 h( [contract WithdrawalContract {
  p9 }' s; T0 x8 P* _    address public richest;
, h3 S* a5 M: s% I4 E    uint public mostSent;; i- I5 z% q" Q5 _) z# e0 q
    mapping (address => uint) pendingWithdrawals;& _4 P9 p, E+ Y8 p: c
    function WithdrawalContract() public payable {1 @7 j8 j2 B; m+ N
        richest = msg.sender;
9 v2 R/ P- B( {        mostSent = msg.value;/ H5 G& Y. {0 d! x
    }9 [- C% f- ?2 h6 k8 T
    function becomeRichest() public payable returns (bool) {
% k9 U1 ]  V" C4 ?4 x        if (msg.value > mostSent) {
7 n& h. n: i' B8 |+ `- S            pendingWithdrawals[richest] += msg.value;! p$ T4 }5 A& V6 x9 M4 l" |% k/ a: S
            richest = msg.sender;9 ~/ j2 E" k9 Q3 A" l7 z# \
            mostSent = msg.value;
2 Q, r8 ~% G. ^1 I            return true;1 w4 R* O0 c: o) X/ ]$ P+ Y  v
        } else {
3 y; O, E' n. e            return false;+ z* Y$ o: N8 ^8 P3 K+ N  E- M* u3 Y
        }  ~' ?! @' K0 ?8 t% @
    }: |+ y4 C9 q; l$ B( ^& Y2 D# n
    function withdraw() public {
# O3 X; S4 l. O9 U1 e& Q, p        uint amount = pendingWithdrawals[msg.sender];9 e) @+ I* L% t6 e% _
        // 记住,在发送资金之前将待发金额清零
, B7 E, @+ G5 [        // 来防止重入(re-entrancy)攻击
( t' |4 ^+ h( l9 W+ ^$ i* O        pendingWithdrawals[msg.sender] = 0;% Z$ y# X& k8 S- A0 F
        msg.sender.transfer(amount);+ K) {, ~- Y+ W% r# {) ~: f
    }
$ P9 W( i) B7 U( N}
5 ?& _$ W0 B7 y2 s下面是一个相反的直接使用发送模式的例子:
$ V- n, O- N+ E::
* Z6 y2 z' g0 g8 u' k1 xpragma solidity ^0.4.11;
- S3 ]3 e/ i) ]' Ocontract SendContract {
0 X9 d; S# x6 J0 K7 O    address public richest;
6 s' s' R! I  S6 i' U$ e# J2 v    uint public mostSent;1 P! ^5 f, }# i! T# b; y
    function SendContract() public payable {
" n, v0 |" u4 Z( q8 W4 {/ d        richest = msg.sender;
0 a- C5 u  R+ W& _8 m2 F* i; P7 r        mostSent = msg.value;2 {$ u1 G1 G7 G1 f# X  J0 N
    }
: _# Z( Z: r$ j. l    function becomeRichest() public payable returns (bool) {
: G( Q+ q% ]. ^* J2 A        if (msg.value > mostSent) {
2 T- H, w9 i3 T1 v" e, S            // 这一行会导致问题(详见下文)
/ s, ]* x. g8 u( ~$ b            richest.transfer(msg.value);8 R) V) u! H% d
            richest = msg.sender;' ]" F7 C  h: i1 {
            mostSent = msg.value;3 n+ s4 G" @" D5 V
            return true;
6 O+ g4 T) f1 \/ H; k7 i        } else {6 g8 G$ I; A" O
            return false;' d, t( o2 d# h3 H0 f8 C
        }
1 a. o" W+ v2 ?  c1 b. W: u    }. v- t7 J8 r/ R5 l+ b! X
}& w6 t7 g! `# o0 ]( G# b! j
注意,在这个例子里,攻击者可以给这个合约设下陷阱,使其进入不可用状态,比如通过使一个 fallback 函数会失败的合约成为 richest
, k. r! l  p  Y6 l$ @(可以在 fallback 函数中调用 revert() 或者直接在 fallback 函数中使用超过 2300 gas 来使其执行失败)。这样,当这个合约调用 transfer 来给“下过毒”的合约0 I" n  K4 }/ J
发送资金时,调用会失败,从而导致 becomeRichest 函数失败,这个合约也就被永远卡住了。) g- i! w' i6 y' A% Z" [. |+ g
如果在合约中像第一个例子那样使用“取回(withdraw)”模式,那么攻击者只能使他/她自己的“取回”失败,并不会导致整个合约无法运作。
: O* s, P# o9 O3 N* f* r2 V/ U… index:: access;restricting
& ]3 g  f  q/ K, x# E/ R4 ^1 z
: P8 ~2 g! v% @; i1 z) s3 Y$ |2 @限制访问
! D& T5 D* u6 w3 S( w6 E8 n
8 Z. s1 d7 r9 j& d限制访问是合约的一个通用模式。注意,你不可能限制任何人或机器读取你的交易内容或合约状态。
  q$ N  [2 o" m6 J& W! ]: F! N你可以通过加密使这种访问变得困难一些,但如果你想让你的合约读取这些数据,那么其他人也将可以做到。) m: Z+ l& [0 d. B' @
你可以限制 其他合约 读取你的合约状态。0 p8 n- P8 J8 P/ y* a# G+ g
这(其他合约不能读取你的合约状态)是默认的,除非你将合约状态变量声明为 public。
6 w, B( g. F% k9 Y, _/ d  F此外,你可以对谁可以修改你的合约状态或调用你的合约函数加以限制,这是本节要介绍的内容。
4 r3 q& T  T! M7 p+ f9 F+ ?+ u… index:: function;modifier0 p0 Z. ?' K9 @1 N
通过使用“函数 |modifier|”,可以使这些限制变得非常明确。3 |" F2 s" m5 w& ?- @* X
::
  D* T( y  {& K3 E' x, |pragma solidity ^0.4.22;( Q! u6 i2 S, e" K
contract AccessRestriction {' V0 Q  }- P* e5 d& B
    // 这些将在构造阶段被赋值
3 P* j6 k9 y3 t, h) _    // 其中,`msg.sender` 是4 w6 K' M. p( w0 I" n9 D' c
    // 创建这个合约的账户。
8 D  O4 J5 h: G    address public owner = msg.sender;
7 @5 }% x" }: X5 c2 ^  f. I    uint public creationTime = now;% ]6 x2 Y! F4 Z# x" K
    // 修饰器可以用来更改
- ]) Z+ Z* E+ _. I3 p' l$ D    // 一个函数的函数体。" k2 ?5 s& e; ^, z
    // 如果使用这个修饰器,& n" h$ F- v( Q- I
    // 它会预置一个检查,仅允许
! f9 t) F0 s9 ?  f' ]) v    // 来自特定地址的
% ^$ B0 Q" J! E* J& ?) s7 f5 X    // 函数调用。
6 [* b: D- r$ X7 f& a( A7 }    modifier onlyBy(address _account)
) [$ [1 h2 W* C. q! c7 L    {7 w( |# J7 b# B
        require(% w7 D7 }% E% z" B1 u1 @
            msg.sender == _account,
  E  d6 g, _, S( S* l' |            "Sender not authorized."2 d; a* e- I! v: N6 t' |& R
        );
0 @9 `3 Z' m3 t; B3 k# c6 z        // 不要忘记写 `_;`!: t+ ?$ m% o, K; Q# W! f/ e: X4 j, g
        // 它会被实际使用这个修饰器的
5 o; r" h! O( m2 a        // 函数体所替代。5 @, W4 G  s! ~  M8 p
        _;
! W5 E8 Q" o% K0 A4 W4 D+ b    }6 a% K/ n* R$ E& M
    // 使 `_newOwner` 成为这个合约的; t# j1 M8 g5 ^( k
    // 新所有者。6 D$ x, w* m. {/ x/ \9 y8 |7 r
    function changeOwner(address _newOwner)
8 a9 q: O7 K; b' V6 D. P" c        public/ }- E2 v' B6 b5 g
        onlyBy(owner)+ {+ i, N: ?- @8 o
    {$ o9 ]9 q$ v7 g' V
        owner = _newOwner;$ ~) W  _* n0 D) e; B5 \/ ^
    }9 w8 b* F* n) q" I( C3 X
    modifier onlyAfter(uint _time) {% A$ a. G5 z! b+ ]* y7 r: e+ f
        require(
1 r2 s: q( m/ Y- d  l- t: J1 b            now >= _time,6 M8 v1 @" u' ~4 F8 T
            "Function called too early."8 |) q) N) }! o
        );/ Z5 N6 |& ^  T  d
        _;
! F' ^! E+ D4 d! @    }6 ^8 g: E2 \( R3 I6 y
    // 抹掉所有者信息。
' b/ k9 s$ f4 V    // 仅允许在合约创建成功 6 周以后
, V5 S: C* {( Q    // 的时间被调用。
& u* W' K4 K! J% h9 F0 J* b2 |    function disown()
  ~% |! {  [8 L$ |        public
# P' N7 V: M  R8 h* J% ^+ ?        onlyBy(owner)
1 V# B+ K$ U- Q- y, B; ~$ Q        onlyAfter(creationTime + 6 weeks)6 ]" r1 n/ T- M- v9 e
    {8 |; l" f) U% K- ?: ?9 a9 E
        delete owner;
8 L: _/ K& }) ?- ~: Z! j% ?; \    }
3 a( g9 g/ N7 O, k' r    // 这个修饰器要求对函数调用
* J. P) J* B" p, d    // 绑定一定的费用。7 [3 Q( |) i% b2 g% q6 `! U
    // 如果调用方发送了过多的费用,
$ H2 `6 R6 y! B4 u* Q/ K' f    // 他/她会得到退款,但需要先执行函数体。
3 M. W/ ~' L8 j' n3 E# R7 m, {    // 这在 0.4.0 版本以前的 Solidity 中很危险,
0 a) ~( G- C" m; [2 }3 J* n. ^( t    // 因为很可能会跳过 `_;` 之后的代码。
) G3 G* F: ~1 `    modifier costs(uint _amount) {/ }( p5 @  C5 `+ w/ h6 e
        require(! P: ?+ E( G$ h) c. Q( X
            msg.value >= _amount,9 A) y6 {7 }9 c3 Y
            "Not enough Ether provided."% \6 x! S! U2 ?1 ^0 A: c' h1 l# s2 ^
        );3 A# r; \" I0 v, ?7 O5 i
        _;- R' B; v' _% \' }9 e& I
        if (msg.value > _amount)/ V& O! ~1 ~6 x  ~; b; _
            msg.sender.send(msg.value - _amount);
- p4 V% }; Q- V/ A    }
8 c' D0 ?. G3 e" B( N, R& F# h. o    function forceOwnerChange(address _newOwner)$ s3 o  Y: R% Z
        public
: G6 F' k$ x8 T' ^5 y0 M& P" c        payable
7 p% n2 s* J( ^; e        costs(200 ether)
# i6 ~7 a8 z5 ^( X. j% N7 S8 s    {- E' Q* s1 V4 l7 _  `1 K
        owner = _newOwner;8 B( M+ k4 M! |9 ?0 w( X
        // 这只是示例条件# h$ S: y* \! \/ c% g9 P
        if (uint(owner) & 0 == 1)
/ A$ r0 R: I$ ?0 O! R            // 这无法在 0.4.0 版本之前的
" e' F" [! T; H0 o3 Z* z# m! |. @. y            // Solidity 上进行退还。
, d8 L) K! u. k7 {0 z  i: H            return;" P3 B* o$ M' r4 t& R, c
        // 退还多付的费用
9 n6 W' ^# }* m0 H# I    }
1 u/ x3 Y: h. y1 |$ k3 L3 B: A}
; Z% V$ Q/ F0 L# O一个更专用地限制函数调用的方法将在下一个例子中介绍。
4 T8 A: m# d+ }7 w) e) x… index:: state machine
4 x) O% O% z) b* x; ^: Y7 {* T' K. a$ x% E5 c  J( V0 c, I" V7 B
状态机9 B. D$ Z2 l7 E3 p
8 a6 D4 a- P" L1 Y* U1 ^4 L
合约通常会像状态机那样运作,这意味着它们有特定的 阶段,使它们有不同的表现或者仅允许特定的不同函数被调用。3 [5 `; `6 ]  G  a+ C% ]6 v
一个函数调用通常会结束一个阶段,并将合约转换到下一个阶段(特别是如果一个合约是以 交互 来建模的时候)。
4 s& i6 B) g* P6 I通过达到特定的 时间 点来达到某些阶段也是很常见的。
# r' M# R) u2 c1 J# }# }! Z一个典型的例子是盲拍(blind auction)合约,它起始于“接受盲目出价”,* |' `7 i2 c9 i4 l* S6 E2 s
然后转换到“公示出价”,最后结束于“确定拍卖结果”。$ P6 b5 Z- K2 t$ s1 f9 t$ b3 R& t
… index:: function;modifier
: C+ [: W: S# T( d. R+ n函数 |modifier| 可以用在这种情况下来对状态进行建模,并确保合约被正常的使用。
8 F1 s' _! W' \/ _* G示例
) K' m7 X2 K5 a7 A' T在下边的示例中, |modifier| atStage 确保了函数仅在特定的阶段才可以被调用。8 R- Z, i& T6 d% p
根据时间来进行的自动阶段转换,是由 |modifier| timeTransitions 来处理的,) R' y. Y2 h- p) q
它应该用在所有函数上。3 e3 G( ~( \) |* K5 I, t1 f
… note::
& v/ ~: s5 d  j1 Y! v2 E- f& t|modifier| 的顺序非常重要
" ?  R  U! G9 n$ }" f如果 atStage 和 timedTransitions 要一起使用,. ]  m& g8 }5 P/ z+ k" w
请确保在 timedTransitions 之后声明 atStage,
- d. B( x7 b( o' u7 w7 y  j以便新的状态可以; a1 W) M2 W) E# N
首先被反映到账户中。- R7 g- u' o8 E; i* F! G
最后, |modifier| transitionNext 能够用来在函数执行结束时自动转换到下一个阶段。" U& e5 W! N, S4 q4 c3 `5 ^0 w
… note::
1 f0 n2 R2 j: T! b4 ?|modifier| 可以被忽略) B2 c6 |& O% J' G6 V/ \7 x  O+ X
以下特性仅在 0.4.0 版本之前的 Solidity 中有效:2 ~* F" ]' v" F5 D* m' @
由于 |modifier| 是通过简单的替换代码# N) s: Q9 N! b* [
而不是使用函数调用来提供的,
, F" \1 J& [  w3 m/ h* B如果函数本身使用了 return,那么 transitionNext |modifier|
1 r3 l1 p7 @4 w& ^9 [: F" h0 ~" K的代码是可以被忽略的。2 D6 |! A8 h5 W% n, f
如果你希望这么做,
2 e9 ?  r+ c2 ^" n9 @3 ?- B请确保你在这些函数中手工调用了 nextStage。
6 W  s) ~$ T. b6 U6 }/ a从 0.4.0 版本开始,即使函数明确地 return 了,9 K' i+ O. ^. F0 K, V: @
|modifier| 的代码也会执行。
0 |( H* D5 g% u::
: N9 G2 w+ I! u6 U2 ]5 e3 q; Bpragma solidity ^0.4.22;: m0 {( v8 X% f1 p. x3 e. n
contract StateMachine {& t. B" j1 z' C3 _8 f! m4 b8 w. C) [" o
    enum Stages {
9 Z4 x4 R% |% S0 c1 ~  q        AcceptingBlindedBids,! D; ]5 W% H2 D, ]& L/ A
        RevealBids,
$ m7 {" }" M( m/ f+ {3 D        AnotherStage,+ D2 h* c) i, L" p) I4 ~8 Y+ q
        AreWeDoneYet,
  J& }% E* c$ T' Z8 C) Q1 K        Finished
( S1 I; w9 E5 D/ G: u( C    }
3 Z7 f( y4 O* ~2 H" v    // 这是当前阶段。) I& T: M4 h9 j
    Stages public stage = Stages.AcceptingBlindedBids;& e/ X* W& s" }& j: M4 a
    uint public creationTime = now;
! `. p' D) K; d# G    modifier atStage(Stages _stage) {, g3 _/ n" l( j: J
        require(
+ ~/ w! c8 \6 D# b, [            stage == _stage,
6 L7 S, G  @. `& W" I. J            "Function cannot be called at this time."
3 r4 L$ Q; L# w4 [# S1 X        );
3 a3 [: W# ^* c- P$ N6 f        _;
9 g6 b! p9 U- M/ M% z8 }    }! z; j  y: d; B; U" x% ~0 I
    function nextStage() internal {
4 l# `6 K; M( O        stage = Stages(uint(stage) + 1);" M. e( |( D- u! K$ f
    }
% R$ m" |: G) G8 D+ B- d    // 执行基于时间的阶段转换。# w% U! C+ _& \- l! a0 u
    // 请确保首先声明这个修饰器,8 @4 l4 D7 _, o6 t
    // 否则新阶段不会被带入账户。4 n& b( X# \- v! R. l
    modifier timedTransitions() {) e$ V8 S% K3 q$ j- |( R
        if (stage == Stages.AcceptingBlindedBids &&
" f( E% ^! [, o, U0 q' s                    now >= creationTime + 10 days)
: z3 h$ B/ B: d6 t& N+ S            nextStage();
% _0 C. e. X8 k5 A+ H) |! A% v        if (stage == Stages.RevealBids &&
( {# C& }% l0 i" L+ @* x                now >= creationTime + 12 days)- P8 K" ~- c; o. k) F5 s: [
            nextStage();0 E3 G  z( p+ q/ `3 ~
        // 由交易触发的其他阶段转换- h0 R( P& E1 C# k% s; K
        _;
" q4 }; O+ g7 G" S- u    }, i7 B$ h3 Z( j, T" g2 u2 ^2 ]' ^
    // 这里的修饰器顺序非常重要!; ]# k) b9 D/ ^1 O# m4 v
    function bid()
2 _. Y4 x  `4 t5 k7 B' _3 q8 l# \. ~        public. _* F8 H# P& K: L) L% y4 v
        payable/ p& `0 M, J, K3 y( S6 z# [
        timedTransitions' H/ L8 ~, C1 D7 R3 b
        atStage(Stages.AcceptingBlindedBids)- t  a& W7 s1 T( e9 }" _' J
    {
& `7 t, C) _# `& c2 L; l        // 我们不会在这里实现实际功能(因为这仅是个代码示例,译者注)1 S$ x) O/ [7 \- C5 j
    }
5 z2 k! P$ ~( m. t    function reveal()5 W8 f2 O% w& l2 M
        public
! \4 n5 L1 S% r5 F, H  \3 l        timedTransitions8 q7 |3 J$ ]  k$ a' f# Z2 ?
        atStage(Stages.RevealBids)
# z6 y7 K% U2 }    {
, G: k4 V3 J! f' t2 @$ }+ R" C    }% L$ F% |5 G$ d6 k) q# E
    // 这个修饰器在函数执行结束之后9 v( _2 }9 b6 J0 ~
    // 使合约进入下一个阶段。! ~' F0 R* J1 p4 G, ]+ i
    modifier transitionNext()0 s* m0 `7 y1 h; f& {& n
    {
- b) G/ Z4 P% w# D        _;. B$ G6 {; Z# g9 c: h# M
        nextStage();
. W" t, p3 Y8 `8 B; w    }& i& w6 V% N6 U3 D0 v2 c
    function g()
. }4 M* U9 \  {2 R        public
3 f  k4 ^0 }- b        timedTransitions
& [4 d/ ~7 I& Q, S        atStage(Stages.AnotherStage); Y7 |& c: t) }& N
        transitionNext
* g- @* }5 N3 `8 t* |    {) Q1 M# W+ l- P# L1 W) ]
    }
' i6 M9 P9 f. [. p! s    function h()
6 N4 F9 ?9 ~+ R3 p. b7 X2 q        public
# d+ n: F; K! l( g" \! B, j2 _        timedTransitions
4 g; Y( q8 {% `* c        atStage(Stages.AreWeDoneYet), e+ R! f4 \% W3 g( J8 I: c$ I+ o
        transitionNext
, ?: E3 `$ j# z    {2 Z4 i) M: V3 B0 u
    }
0 q2 x! ~) j$ t0 q; ^$ A6 x    function i()7 R, M0 Y, N4 z$ V5 f% s  S
        public% o6 B% @  ?( o
        timedTransitions  `% A# s: L/ y6 `
        atStage(Stages.Finished)
2 N/ V, _# F0 Z8 w" ]0 k/ G    {
. g6 T' N( C6 I0 M- K, |    }
; k. M1 ~0 M7 O0 M}- O2 S) ~1 O) N/ W9 R/ \) C
原文:https://raw.githubusercontent.com/etherchina/solidity-doc-cn/develop/common-patterns.rst
标签: Solidity
BitMere.com 比特池塘系信息发布平台,比特池塘仅提供信息存储空间服务。
声明:该文观点仅代表作者本人,本文不代表比特池塘立场,且不构成建议,请谨慎对待。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

成为第一个吐槽的人

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

    0

  • 关注

    0

  • 主题

    13