Hi 游客

更多精彩,请登录!

比特池塘 区块链技术 正文

Solidity通用模式

温室小书生室d
413 0 0
从合约中提款; o/ z) M4 [* t. o
+ Z$ G0 Y! [( ~: {9 `: R6 p
在某个操作之后发送资金的推荐方式是使用取回(withdrawal)模式。尽管在某个操作之后,最直接地发送以太币方法是一个 send 调用,& ^( j: G) h4 w# X  ^" _
但这并不推荐;因为这会引入一个潜在的安全风险。你可能需要参考 :ref:security_considerations 来获取更多信息。
' l2 k! g: `# f0 b8 Y6 q, v这里是一个在合约中使用取回模式的示例,它目标是通过向合约发送最多的钱来成为“最富有的人”,
) a5 n0 @$ O* r' |1 n6 {' n其灵感来自 King of the Ether _。
3 J+ w( t! A/ H& [* a. J' z2 b在下边的合约中,如果你的“最富有”位置被其他人取代,你可以收到取代你成为“最富有”的人发送到合约的资金。2 L+ j8 U5 O# O! v9 `( B
::- k6 u2 [' d7 ?+ d4 V( ~: `9 Y
pragma solidity ^0.4.11;
8 P3 z: e* o9 F  t( j2 q( z" H# econtract WithdrawalContract {! J* D4 U8 y' y, L3 `8 a
    address public richest;
5 B8 o' f, I- F) D' N    uint public mostSent;1 P: n5 }: H) y7 H
    mapping (address => uint) pendingWithdrawals;( `" G1 B( R) Z! H. X1 `
    function WithdrawalContract() public payable {( e( f/ {) @0 {" S  N( {7 p6 C
        richest = msg.sender;
1 G/ O5 ^% H: i        mostSent = msg.value;
. w% u7 i% J* F. S- F    }
! ?+ a$ u1 l# K2 R1 {! b% ^3 s    function becomeRichest() public payable returns (bool) {; b7 k  N0 m+ {2 t
        if (msg.value > mostSent) {8 u& i% M% U+ V7 j! c5 r
            pendingWithdrawals[richest] += msg.value;
5 W5 n) R& g) o, R  A            richest = msg.sender;
- B! h7 Z1 Y' t0 P$ G$ H            mostSent = msg.value;
. c4 {' J" J% v            return true;1 M5 W( O" _1 {+ Z/ V+ `8 |
        } else {
; h1 e  Y! L' A7 I+ V; K            return false;/ r  e; J0 r! l2 B5 ~0 ]
        }
: u, n5 I, ?6 Q. f9 ^; `    }
- x0 H8 k1 B+ h$ k9 q9 ]# C    function withdraw() public {, }% {7 o6 r2 {; j' N
        uint amount = pendingWithdrawals[msg.sender];
5 f* ?  X+ Q, u& o9 \  P        // 记住,在发送资金之前将待发金额清零
2 G* I. {/ Z, f        // 来防止重入(re-entrancy)攻击
/ O0 Y2 {' m; r/ D        pendingWithdrawals[msg.sender] = 0;
8 F, u* ?( C/ x  K- p        msg.sender.transfer(amount);& `1 k; a6 m- c
    }( f. j! N0 O: w/ @+ A
}
  `% W. v5 {$ w# D* h& g下面是一个相反的直接使用发送模式的例子:
* G5 n7 o& k8 u* H. R) Y6 T::
2 Q, P3 P5 F' x! T0 Kpragma solidity ^0.4.11;
; N; W4 m* A7 [contract SendContract {: `9 j3 Z! b; \% `7 d# X4 ^
    address public richest;
& [+ g) _  c' G% ^. d1 L1 l) L9 @0 X    uint public mostSent;
  z5 [/ r; g5 M) j) C    function SendContract() public payable {
9 Z4 W5 g0 F" Z4 V1 y9 @, @        richest = msg.sender;
) x9 ^* U/ ]& j) H6 `        mostSent = msg.value;
  e7 J8 y7 c7 I    }
, v  L4 n; F, }# Y! z# d    function becomeRichest() public payable returns (bool) {+ ?# c( p; s0 u1 w
        if (msg.value > mostSent) {
  o8 V. X( I; k; r- ]) r            // 这一行会导致问题(详见下文)6 t& l; W% s1 O7 p
            richest.transfer(msg.value);+ u5 R+ R0 O( o" [  [
            richest = msg.sender;8 {, b) k' U& G" W( Z& N
            mostSent = msg.value;
/ W* `6 C' d" ]5 s0 _% V            return true;; C4 z1 E& ^" I
        } else {8 P% C3 f# x( z* D, t" @8 G, L
            return false;( `" T" c# x, \' y" I% i4 {5 K
        }
6 M( W1 o- F9 V: M. o* U    }4 Z6 Q* A+ m" D2 Y* m! V; r
}
9 \6 m( r; g$ m注意,在这个例子里,攻击者可以给这个合约设下陷阱,使其进入不可用状态,比如通过使一个 fallback 函数会失败的合约成为 richest( P7 _/ B" i2 i
(可以在 fallback 函数中调用 revert() 或者直接在 fallback 函数中使用超过 2300 gas 来使其执行失败)。这样,当这个合约调用 transfer 来给“下过毒”的合约5 @) S' Y  }; X
发送资金时,调用会失败,从而导致 becomeRichest 函数失败,这个合约也就被永远卡住了。/ c3 `2 E: e7 I0 S* a7 X! y
如果在合约中像第一个例子那样使用“取回(withdraw)”模式,那么攻击者只能使他/她自己的“取回”失败,并不会导致整个合约无法运作。7 j, v! O1 E6 {; m6 Q1 c; Y* L
… index:: access;restricting
3 R# k: c" _0 J' ?" u5 V5 q0 p$ U6 R( K7 d
限制访问
+ O2 C6 H+ }0 N# h4 {- `" c4 D
& }+ }1 D, @6 R/ J* l" D2 F; h: W4 C0 r限制访问是合约的一个通用模式。注意,你不可能限制任何人或机器读取你的交易内容或合约状态。
3 y. o: A4 E' e' w  ^' j你可以通过加密使这种访问变得困难一些,但如果你想让你的合约读取这些数据,那么其他人也将可以做到。6 v. @% w' r6 W" E6 x, r1 W$ L
你可以限制 其他合约 读取你的合约状态。$ m; }9 U# ~) W
这(其他合约不能读取你的合约状态)是默认的,除非你将合约状态变量声明为 public。  P; u/ y$ p3 ^$ }# C" W% {
此外,你可以对谁可以修改你的合约状态或调用你的合约函数加以限制,这是本节要介绍的内容。
4 @2 j5 F& \) ]6 b… index:: function;modifier
, _- s  q$ n  N: u, }通过使用“函数 |modifier|”,可以使这些限制变得非常明确。9 w" M! u, Y8 P2 Y; Y/ s" \7 e
::
( X, p6 y# }+ D  a3 L  p* ipragma solidity ^0.4.22;8 }' ~, r" h. V$ U: m) M; Q
contract AccessRestriction {* n  Y+ T0 l2 m- h* l- v6 M
    // 这些将在构造阶段被赋值
) p: T# o7 o" p& n& n. f( l& M    // 其中,`msg.sender` 是
9 i" z" R; w8 z; e, c    // 创建这个合约的账户。
/ q- L* h# P+ J0 b# v, R, b    address public owner = msg.sender;
- e5 g4 q. @6 _$ C5 R2 L0 Y& i    uint public creationTime = now;* r4 v% ~' Y( Z- Z1 r2 P
    // 修饰器可以用来更改9 T2 J& A2 o8 G0 a5 |8 W
    // 一个函数的函数体。1 P: M1 X- r& @* w
    // 如果使用这个修饰器,! j, z5 b, G3 b
    // 它会预置一个检查,仅允许
8 F! M$ @* \' G1 x    // 来自特定地址的
' w/ Z6 R( @. f/ B1 m    // 函数调用。
9 J0 F* K' Z' C    modifier onlyBy(address _account)' A; c7 ~- I3 G2 H0 U" K8 k# S) Q
    {
. q2 ~" |* d1 o8 f% T3 [( E6 M8 G        require(3 ]! g+ f$ {+ Z  s7 s; Y; u$ I
            msg.sender == _account,. {6 O* J7 k% w6 b: l# M
            "Sender not authorized."
2 e; `& N& G- M. Y        );
- W4 \, }# i8 _+ m( y  Q2 c        // 不要忘记写 `_;`!1 c/ B$ G: }3 D9 |9 o
        // 它会被实际使用这个修饰器的
9 g& k, s/ N( [" u: L" v+ L* p1 g" U        // 函数体所替代。, M% }' `2 L" Y* l: {3 X/ F; o* I
        _;9 H3 N1 g9 P$ E- l  ^$ {
    }; L/ `- D4 f4 _- k( x0 G
    // 使 `_newOwner` 成为这个合约的
+ k1 X% l/ U  V$ z, }. @- J    // 新所有者。
/ E+ c+ q3 |6 z, {7 z    function changeOwner(address _newOwner)( I8 D" J/ O1 G) c: a  b1 n
        public4 ?9 W, J" D5 q: m. Z
        onlyBy(owner)' k) n: ?+ C7 u5 \, C* R. X$ {
    {
+ u8 t5 o: S* `- s1 Y; @        owner = _newOwner;" n6 t. c' U. b! E
    }
: Y, Q! v+ H7 ?    modifier onlyAfter(uint _time) {1 Y; x6 L$ |; U& Y/ y  e
        require(
" k# \  P# F( s  Z8 w1 u4 p            now >= _time,
- ?* g4 H. W) W9 X) r8 u. m! @. h            "Function called too early.", y0 N: I0 }3 j0 ?0 a  x9 C, v' ^
        );+ ?" N$ d3 q) ~9 |" l
        _;
# ~) }& ?7 l7 W% c7 Y5 G    }
5 `. L/ r+ S3 _1 v) A; m    // 抹掉所有者信息。$ X! z' m! ^# }% R
    // 仅允许在合约创建成功 6 周以后
5 q6 D9 x. u: i! s: f5 g2 S% U    // 的时间被调用。
' m8 K# C% M! u5 O. B    function disown()
5 L8 t; p( B8 x* h7 c        public
7 O1 ^6 {  Z1 m0 l& s; `2 S        onlyBy(owner)* m7 A4 [3 r' Y8 y3 R8 X) ?
        onlyAfter(creationTime + 6 weeks)* E7 E: F  R8 R+ y
    {
+ Q$ D; C! D4 _: t, r" C1 j- o        delete owner;6 ?) X2 r6 r! z: H2 j9 w% P
    }
0 P1 L4 K/ A  T1 V) w    // 这个修饰器要求对函数调用
  a& h$ l/ y/ C    // 绑定一定的费用。
$ O' m$ E' t' u0 c9 V    // 如果调用方发送了过多的费用,8 r+ u$ G+ y+ a# Z  X- E
    // 他/她会得到退款,但需要先执行函数体。- D6 ^4 z+ M) G' n1 f
    // 这在 0.4.0 版本以前的 Solidity 中很危险,
$ _/ y. v9 I' q; N& a. `    // 因为很可能会跳过 `_;` 之后的代码。
8 [! F+ [3 [% |* P0 }( W    modifier costs(uint _amount) {
1 C1 g1 F  S' ^2 o* H        require(& V, Q% r! f( ?
            msg.value >= _amount,
+ s, |  h8 _) Y7 w- e, \            "Not enough Ether provided."
" a7 Y& z2 r' v' i3 O# p        );" u3 k. i0 u3 x% R; n" i5 X  {
        _;. V$ x/ x  A  Z& e9 {+ j
        if (msg.value > _amount); B* x5 f: R6 o
            msg.sender.send(msg.value - _amount);
: a2 Q  G! D" U$ x* Z8 j    }! g( X# V" o; b2 w, o) ^# e5 h& D5 g
    function forceOwnerChange(address _newOwner)
7 d5 i  X6 D+ C6 ~8 i) @5 Y! p        public( V* U* y4 I' T: g5 h
        payable& @5 M- K0 m8 q. Q, f1 c
        costs(200 ether)
2 Q4 |8 n2 @5 U) J$ V    {
2 {# W/ C. s# x        owner = _newOwner;) @* Y/ W: X  e! h1 Y
        // 这只是示例条件( d% }( y) K' l$ c+ y* T& z$ t  a$ U
        if (uint(owner) & 0 == 1)
1 M9 n9 O% N+ R            // 这无法在 0.4.0 版本之前的
" ]1 z/ m, I5 P9 |" h4 Y  l6 C            // Solidity 上进行退还。1 R# U& w8 Y; M" z3 _: C
            return;* C' Q6 e+ k# k
        // 退还多付的费用& }3 L- P0 b- Y. T
    }
% W" }2 @, {$ T9 A}9 }' w4 z# r7 t" P
一个更专用地限制函数调用的方法将在下一个例子中介绍。  s  D- e7 {3 G& v. N
… index:: state machine
* _  F8 ?, W9 _. ~& l; |, @- R6 K" D1 ]: [4 n* b4 _8 t% e
状态机/ ~6 a: g+ w2 h) m) t, u. M3 m
$ x; s4 J! A8 t
合约通常会像状态机那样运作,这意味着它们有特定的 阶段,使它们有不同的表现或者仅允许特定的不同函数被调用。; j. B, M! A) m8 L0 S- y
一个函数调用通常会结束一个阶段,并将合约转换到下一个阶段(特别是如果一个合约是以 交互 来建模的时候)。
) }4 q5 p( {) i: }$ z" I! `! |通过达到特定的 时间 点来达到某些阶段也是很常见的。+ A3 }- [! f, K3 t; _& ~: M
一个典型的例子是盲拍(blind auction)合约,它起始于“接受盲目出价”,
1 a1 T1 ^0 S: a/ I( J4 H然后转换到“公示出价”,最后结束于“确定拍卖结果”。
7 W) X, o/ R0 r) p0 P4 I+ Y… index:: function;modifier- q( C: p( {- @5 Q7 M" E7 y3 `) p% O
函数 |modifier| 可以用在这种情况下来对状态进行建模,并确保合约被正常的使用。
$ `! |' `( a2 ^9 B0 ^示例
4 w+ i' N* {* J. U" X" Q# {0 k- F在下边的示例中, |modifier| atStage 确保了函数仅在特定的阶段才可以被调用。
; R$ U6 x2 @, Q2 B根据时间来进行的自动阶段转换,是由 |modifier| timeTransitions 来处理的,
; Z: [, y' Q5 S/ E3 Y& }4 m8 A0 R它应该用在所有函数上。- Y2 h& q" }( v7 h
… note::; n. e/ K. X. o
|modifier| 的顺序非常重要
) c) d: x" P, @$ F) }: h$ y3 C5 i如果 atStage 和 timedTransitions 要一起使用,
" H  Y" o7 k. g2 O8 a请确保在 timedTransitions 之后声明 atStage,, t; d, l( ^! D9 q' {. ^6 N1 |
以便新的状态可以
  U9 y* ]5 N) D$ u首先被反映到账户中。
& Z2 q5 l6 w# O* J7 T% x' G, ~最后, |modifier| transitionNext 能够用来在函数执行结束时自动转换到下一个阶段。
4 _& z1 A6 F+ ]9 j( d  p… note::
/ j6 J1 ]6 I. a2 N! H% x|modifier| 可以被忽略
+ T+ e/ O4 U9 b& x( [以下特性仅在 0.4.0 版本之前的 Solidity 中有效:
" m- t! k& \3 M由于 |modifier| 是通过简单的替换代码# x9 L0 F: N9 f  s! y7 @- q# M( a
而不是使用函数调用来提供的,3 ]- Y& Z0 \8 Y. c6 G1 s) }
如果函数本身使用了 return,那么 transitionNext |modifier|$ d4 n% i0 V. e- O" i' x, v* D6 f
的代码是可以被忽略的。4 N$ o; u+ k  b3 z7 o$ w1 J8 X
如果你希望这么做,
3 ?* P) I& Y, W4 \: s请确保你在这些函数中手工调用了 nextStage。/ q! {6 h1 r% D1 Z# M( p$ b
从 0.4.0 版本开始,即使函数明确地 return 了,3 e. z* Y# ?+ c" @. V  o- c2 r9 L
|modifier| 的代码也会执行。5 |# `0 i% m7 V3 N7 b
::
' ?1 o4 B" \3 npragma solidity ^0.4.22;
9 B' o* V  N  Z! Bcontract StateMachine {
; w, e  a& m& ]% V- r6 g' P    enum Stages {1 Q7 G5 \4 Z% D9 Q& g& I' i0 A
        AcceptingBlindedBids,/ l8 q. C- x3 |; y( \
        RevealBids,% [( X! z' J& e7 ?6 i* \
        AnotherStage,; @: W( S3 P9 n  j
        AreWeDoneYet,
3 w! `) }# e2 T: n* q% d$ D* q        Finished
! E- @# t3 S% F! H3 M4 [3 r3 a; [# d    }
7 J( g+ X: B. ?: D    // 这是当前阶段。
" y" g6 M- s* d: o8 z- s    Stages public stage = Stages.AcceptingBlindedBids;9 _% N$ n) j1 S$ ^+ p
    uint public creationTime = now;
1 l- P0 B, i2 u7 C! W2 D: T8 ^    modifier atStage(Stages _stage) {
8 E/ k# B! `: A& F7 q; E        require(
7 @2 M# S( o- N7 ~0 q            stage == _stage,* J2 Z* ]) ~6 N* g6 M4 O+ ^
            "Function cannot be called at this time."  f; v# z. M# H4 a3 m! A  h3 p
        );& |; f/ y1 c6 i" L" G; S
        _;
4 G) ^6 n3 f& P) v    }
& Y& f' r! |" E' ]+ X% t    function nextStage() internal {
, Z/ c3 ]8 ]" g        stage = Stages(uint(stage) + 1);
7 l; W* n  o4 B8 m; |6 u    }
9 o7 t) L9 U9 o" X( Q    // 执行基于时间的阶段转换。
6 u/ ^, b; g5 u2 O, p9 S9 c5 N& d  G, C    // 请确保首先声明这个修饰器,
. Q" G; W$ Z1 `3 h+ Z; b1 _    // 否则新阶段不会被带入账户。
+ l& `, A) P  v% p4 Y& B7 j    modifier timedTransitions() {5 r' ~. ^# X$ s
        if (stage == Stages.AcceptingBlindedBids &&- ^  k4 z, V  w, F- u/ m4 u" J
                    now >= creationTime + 10 days)4 {8 {* I/ k# O% P4 `6 j
            nextStage();3 L, L. W: I+ E' \$ Q: A, R' U# T
        if (stage == Stages.RevealBids &&
, t& m8 h# B/ H; G: ]3 q/ G                now >= creationTime + 12 days)8 t& A9 f. G' s4 ]
            nextStage();
9 p6 X  F% `9 m! G' `' N7 v        // 由交易触发的其他阶段转换
8 f3 e4 O! L1 ]: @; U        _;( ^0 x9 u9 w" G$ u
    }# M' P: W5 a& p' _7 [3 R# S
    // 这里的修饰器顺序非常重要!( F1 U  N4 L4 {  \; ]; V
    function bid()
; t: d# j# v: Y- t2 G8 R        public
- A" k- R: i' y' D; N) ]        payable/ U, l5 y! y; C9 O
        timedTransitions# ?2 N3 B* R% c4 n0 r" P9 \: T
        atStage(Stages.AcceptingBlindedBids)+ w9 d  m& x# H5 f, k
    {6 K/ V8 R5 x& |  f& y1 O  @
        // 我们不会在这里实现实际功能(因为这仅是个代码示例,译者注)3 a' z& @; q; ]* |7 M3 k4 B
    }
& ]% `; m8 o3 T% b* p    function reveal()
$ c' X  w4 _; U# p# f7 m        public& x2 o- q( ~* \# }1 i+ b+ s
        timedTransitions6 V% Z7 B2 W  N  a
        atStage(Stages.RevealBids)
2 @& {5 |& v2 X6 v    {% D  |$ i9 z. ^3 x, V! G
    }" }4 l7 B0 p. O/ N  F- H- ?0 O" o
    // 这个修饰器在函数执行结束之后0 F* r3 X1 n3 y) k: w3 y
    // 使合约进入下一个阶段。0 M* N& S8 P( c( A/ w# s
    modifier transitionNext()
# w) f! B. m6 P# |5 D5 W    {
$ X3 t6 w1 Q6 `0 ~/ q  m        _;
# D3 j+ V% l! T! g; K; Y5 x        nextStage();4 K) T* D: ?" F/ j& ?( p
    }& ~* y: @% a$ r1 g% S
    function g()
( C' _; w  C8 {1 e        public; g5 f, R' ^: p7 k
        timedTransitions2 l1 Y4 y* c3 B/ K% {* [9 v
        atStage(Stages.AnotherStage)
3 m* m% @& W2 w        transitionNext
( m, M/ W$ `; r4 J1 ]    {
6 T8 Y) {* a: X6 l) }! D2 _    }, ~0 [& G6 Y: H6 E1 o. q
    function h()# g2 j$ O. a* `# i' X- D
        public
- F. G1 S, h1 J3 r& K        timedTransitions; N4 z) F$ ?# w/ @8 ?
        atStage(Stages.AreWeDoneYet)
/ n. H) s& m8 r        transitionNext
& I2 x% q# Q: X9 e4 M/ c: B* w% p    {% c* Y* L) l& e& }) D: U
    }
4 T7 U3 ^" s; O9 R0 E# [    function i()1 w- O$ u) I+ |5 _
        public
$ n) N  v$ \4 `! t# ^. C        timedTransitions& Y" D, G1 F6 B* }7 ~
        atStage(Stages.Finished)& o1 Z! {3 e. |( x" M
    {
5 t2 I# `% [+ z- t8 u% h1 R6 N    }
$ M3 F& I/ x' w5 p}
, t' S# @9 r: N& w/ j  L原文:https://raw.githubusercontent.com/etherchina/solidity-doc-cn/develop/common-patterns.rst
标签: Solidity
BitMere.com 比特池塘系信息发布平台,比特池塘仅提供信息存储空间服务。
声明:该文观点仅代表作者本人,本文不代表比特池塘立场,且不构成建议,请谨慎对待。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

成为第一个吐槽的人

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

    0

  • 关注

    0

  • 主题

    13