Hi 游客

更多精彩,请登录!

比特池塘 区块链技术 正文

Solidity通用模式

温室小书生室d
433 0 0
从合约中提款
; c& r  V  U% L1 e6 j' N
  b+ ?7 e0 k9 N) K在某个操作之后发送资金的推荐方式是使用取回(withdrawal)模式。尽管在某个操作之后,最直接地发送以太币方法是一个 send 调用,
) v* {" D- n& i" X& O! V但这并不推荐;因为这会引入一个潜在的安全风险。你可能需要参考 :ref:security_considerations 来获取更多信息。! P0 C7 I6 h6 @/ \
这里是一个在合约中使用取回模式的示例,它目标是通过向合约发送最多的钱来成为“最富有的人”,
" D/ b1 @% N: T3 H% {) T其灵感来自 King of the Ether _。8 i2 e8 p/ }( c9 V) v
在下边的合约中,如果你的“最富有”位置被其他人取代,你可以收到取代你成为“最富有”的人发送到合约的资金。1 K4 E1 z0 c" Y( x
::$ C8 }' e# \, O: E
pragma solidity ^0.4.11;
  z! ~, q6 y# c# Hcontract WithdrawalContract {' k/ n% T0 M. d( h5 Z) Z* Y- j
    address public richest;
( H% H7 S7 F  a$ V( l4 Q( g    uint public mostSent;5 _+ }2 n6 C, ^: g
    mapping (address => uint) pendingWithdrawals;3 F5 r' v) U3 u, X* H
    function WithdrawalContract() public payable {- r) m5 ^$ z7 E& T6 F" ]2 M
        richest = msg.sender;
: {4 d: ]: w0 j' {        mostSent = msg.value;
5 P* ?% h6 ^8 h    }
; h! a1 ~' j2 r! D' j$ i) O2 s    function becomeRichest() public payable returns (bool) {
/ Z( _, F% a0 _: F        if (msg.value > mostSent) {
" ^5 i  N( r! s0 c0 h            pendingWithdrawals[richest] += msg.value;/ T& k( h9 S, r" B
            richest = msg.sender;% r+ \) z, d! Y, K' z+ \8 d
            mostSent = msg.value;( y/ H4 u" O, u. h) R) b0 d
            return true;3 r, x8 w% O9 N$ |5 m
        } else {
+ }6 b% N3 o2 F: Q            return false;( A! Z% X- L, F* C1 b
        }
' ?0 ^0 K. R, d& L5 C: G. e. j, S( E    }! K; S. W  [$ x" w
    function withdraw() public {
% Q0 O, o; H, ?5 ?. f) |( ^$ ^# p        uint amount = pendingWithdrawals[msg.sender];8 q' s5 e4 R/ J  L: Z
        // 记住,在发送资金之前将待发金额清零
2 @" p, F7 |  C1 j        // 来防止重入(re-entrancy)攻击8 q) X8 i" T$ a
        pendingWithdrawals[msg.sender] = 0;" }7 R7 ^$ C( O1 _1 ?
        msg.sender.transfer(amount);# m+ N, f1 y, ]* z3 W! q  E7 x
    }  f2 z/ L+ q: ~& p) W2 n1 q6 i* L
}! O( R; G" o+ E$ y
下面是一个相反的直接使用发送模式的例子:# Y- f+ y/ Q, j3 Q
::) P( w4 L) w. n4 z* N
pragma solidity ^0.4.11;7 I/ G% o- H( c) q3 ]
contract SendContract {. ]2 }/ `0 b0 X( X+ w
    address public richest;" c, G% S. r6 F/ F1 ^5 n% r# U2 V9 _/ o
    uint public mostSent;
* j5 g7 E: b6 s; e    function SendContract() public payable {7 w8 v8 J' k$ N- {4 u% Z' R4 x
        richest = msg.sender;+ F% M7 p# R2 {3 V9 d, E" Q+ N
        mostSent = msg.value;$ V4 m* c7 L) p2 M! ^+ r% H6 H0 ]7 `
    }
+ c6 G5 g' J2 V4 N/ W4 X6 H    function becomeRichest() public payable returns (bool) {$ }" G0 {* A2 ?/ s0 \+ R9 O
        if (msg.value > mostSent) {
/ U  N5 F+ i# L( h8 t9 v! U$ z            // 这一行会导致问题(详见下文)
- D, H; \5 l" R/ g  z( T) Q& J            richest.transfer(msg.value);+ E7 @& l' ?- S8 g# Y
            richest = msg.sender;7 H- V9 d- k2 c0 h! R
            mostSent = msg.value;
( W% ?, {  g0 ]            return true;
! M2 g9 {. [* ?+ n! M        } else {
0 _: I. j$ ?% a! \            return false;0 v- v, R% f4 H/ Z# P8 {
        }; ^0 |) K5 {' \& T/ D! W6 U- |
    }
+ Y. t1 m0 Y+ X" M: i' I}
. i+ h" `7 L6 D2 z0 A2 P& h注意,在这个例子里,攻击者可以给这个合约设下陷阱,使其进入不可用状态,比如通过使一个 fallback 函数会失败的合约成为 richest
) X, ~" v# ]4 \9 y, e7 ?(可以在 fallback 函数中调用 revert() 或者直接在 fallback 函数中使用超过 2300 gas 来使其执行失败)。这样,当这个合约调用 transfer 来给“下过毒”的合约) N2 }" y( I- Y: ?
发送资金时,调用会失败,从而导致 becomeRichest 函数失败,这个合约也就被永远卡住了。' W+ y% p$ L9 L2 D
如果在合约中像第一个例子那样使用“取回(withdraw)”模式,那么攻击者只能使他/她自己的“取回”失败,并不会导致整个合约无法运作。
0 r! ?. t4 y: M/ J… index:: access;restricting" ~6 e. d' i) m' x
9 G0 n; ^: L, `- v
限制访问5 s5 q. r: e- \

" S. i5 v4 H4 n. M限制访问是合约的一个通用模式。注意,你不可能限制任何人或机器读取你的交易内容或合约状态。" R; j, \/ e8 M) `0 C- Q
你可以通过加密使这种访问变得困难一些,但如果你想让你的合约读取这些数据,那么其他人也将可以做到。
$ h1 ^3 i# M% ~你可以限制 其他合约 读取你的合约状态。' s5 s! T) O* B) H6 e; t
这(其他合约不能读取你的合约状态)是默认的,除非你将合约状态变量声明为 public。3 H; ~- {8 g8 {6 S( x
此外,你可以对谁可以修改你的合约状态或调用你的合约函数加以限制,这是本节要介绍的内容。9 G) [! H" t8 i0 c% U8 W5 _
… index:: function;modifier
* h" U/ k3 C6 o& L# p2 h通过使用“函数 |modifier|”,可以使这些限制变得非常明确。) i0 ?* @' s$ m3 w
::
6 T' i  w' R! t3 l6 ]+ |pragma solidity ^0.4.22;, {  I8 k" H/ {
contract AccessRestriction {
9 P. @" w( A% r: J    // 这些将在构造阶段被赋值
* h& z4 R0 {* ~- M$ q9 X    // 其中,`msg.sender` 是" Z/ k/ ]1 F  h( ]
    // 创建这个合约的账户。
% v# B1 Q/ S: G/ S# o    address public owner = msg.sender;/ B9 `% [# ^* E! @5 f
    uint public creationTime = now;
" N9 v0 ?8 L* Q8 ]    // 修饰器可以用来更改" W, ], t+ r  x
    // 一个函数的函数体。6 E3 b3 U' Z- Y8 z
    // 如果使用这个修饰器,$ K7 O/ k& M" V" |
    // 它会预置一个检查,仅允许
% `1 t2 |( v' j% e8 N; {    // 来自特定地址的
* @4 L+ J! i# X# Q" D& g" F; n    // 函数调用。
: Z  `3 y7 u/ g. B  [" \    modifier onlyBy(address _account)
: k5 x5 S5 x" S- K: I6 ?    {
4 d2 _6 y# v4 M0 u        require(
& F1 E2 L) `9 O" x  \- ^            msg.sender == _account,
) r! g2 z9 i; E. e4 ~            "Sender not authorized."/ L8 q1 g* ?" s  ]4 B' I# z
        );
% B& X3 I8 Q1 b2 i7 A" ^$ j8 O# e        // 不要忘记写 `_;`!
; [/ \7 r0 r" H, N1 @% R& O2 m        // 它会被实际使用这个修饰器的0 p3 G$ d+ F2 q$ V' R- N
        // 函数体所替代。
: E& \- I! e" q        _;+ y  d7 G4 B; G6 X* j: r, B
    }
& ~' V* ?+ q" s9 y* s$ X' @    // 使 `_newOwner` 成为这个合约的
2 t: [) R2 Z+ A! \5 p6 P    // 新所有者。3 V8 Z) T* K" E6 O6 B! G3 p- O7 T
    function changeOwner(address _newOwner)0 h$ h- K  ~1 g( F
        public
! s/ E7 |" i: @9 _( v/ a9 L- y( r: z        onlyBy(owner)1 I1 ^! h; P0 G8 E- m1 G6 I
    {$ l9 |  N$ n/ B( e: P
        owner = _newOwner;
5 y3 L) O" R- _0 j$ W2 `    }7 M9 u: c" I& D% G
    modifier onlyAfter(uint _time) {8 R  Z5 P- q( B% h- ]9 n
        require(
/ s7 p, h6 u2 \. o            now >= _time,
* R* Z4 l& F4 L( z0 x) H            "Function called too early."
# h1 S! l; c$ V( T0 d; j6 K; _3 [        );
- E8 X4 p1 ^2 d' K  ?& w' e        _;  a1 l! |, g/ c) m# B5 q# W! p
    }5 [$ Y  c- w; w9 O4 w' e3 X: F
    // 抹掉所有者信息。9 A3 o3 W4 Q) A  o0 I# n: b
    // 仅允许在合约创建成功 6 周以后, W+ q8 E- r9 m* T) C; ~, _- x
    // 的时间被调用。
" P2 v; M/ u6 v0 _3 ]% W    function disown()# {) \" P2 t5 X  Q- Q
        public4 X) H/ k' N; R: Y: u3 Y3 l
        onlyBy(owner)
: X! i% Y, E1 a- |* ]) R        onlyAfter(creationTime + 6 weeks)
9 [2 t, n- _7 o& _% o& s2 X    {
' c7 O0 J. y) s7 {" J0 N        delete owner;2 v  n( r5 D7 j3 Y( ?/ D# P
    }& [  K" T! z: t0 L
    // 这个修饰器要求对函数调用. ]& J+ Z' Z; t" L  U
    // 绑定一定的费用。9 A2 [* j4 k# Q1 O
    // 如果调用方发送了过多的费用,/ Q: l. m/ h( u! y3 Y$ K9 T
    // 他/她会得到退款,但需要先执行函数体。
% i. W( C4 q3 \/ r$ L    // 这在 0.4.0 版本以前的 Solidity 中很危险,2 }& @2 n) Z4 h2 _. i1 S. F5 [) P
    // 因为很可能会跳过 `_;` 之后的代码。2 C( f0 ^& x* B+ d) P  H6 O
    modifier costs(uint _amount) {
# A# t+ k& O5 G+ |& U        require(
( n& P0 l' s1 F, V4 f            msg.value >= _amount,3 O! e" A2 `1 q7 \* [7 d
            "Not enough Ether provided."
' W" l* h: x$ D; L7 F/ o, w: m9 z        );
+ J" E2 G4 u; C- L( `        _;8 c& Z4 e& }; i6 o* P) ~$ F2 u
        if (msg.value > _amount)9 b0 S3 w% U; z$ |: b' V( [% S( `
            msg.sender.send(msg.value - _amount);2 {: O3 v6 S6 Q; O* ?
    }
4 f' {8 e: e" n% G+ k    function forceOwnerChange(address _newOwner)4 r& E3 U. P7 z' r
        public& [# F6 f1 h" {- c
        payable
4 x, {* }7 B- y7 x        costs(200 ether), q7 u+ G# U2 |/ m+ @5 R7 {
    {6 y7 C% c! Z6 y7 `+ }
        owner = _newOwner;
4 g7 Z. T" A. }! l- o8 I" X7 V& ?        // 这只是示例条件
! y7 q6 p; Y3 S3 Z2 f4 i' Q/ L$ x& ]        if (uint(owner) & 0 == 1)
9 e" T' R+ p6 U' G' L/ o2 {            // 这无法在 0.4.0 版本之前的
$ Y7 G5 a- h( O: d% H            // Solidity 上进行退还。
) X! I/ I! t" b; _5 S            return;
; `, H+ o6 n0 K2 m        // 退还多付的费用! o3 G& v) d( x* o, ?) x
    }9 i$ x9 v( g+ |+ j. g
}- Y$ N) t# ]4 P: y1 e; v7 l2 p
一个更专用地限制函数调用的方法将在下一个例子中介绍。4 V+ X. B1 M1 K  C4 E- z
… index:: state machine
& x8 Z: @+ w5 i! S/ j9 O# {3 q9 H' d
状态机) p4 H, G) p+ R/ J5 J

! p( r6 ^$ [: h4 h7 D, M( v) |合约通常会像状态机那样运作,这意味着它们有特定的 阶段,使它们有不同的表现或者仅允许特定的不同函数被调用。  x: ~0 N' }: ]" X' x
一个函数调用通常会结束一个阶段,并将合约转换到下一个阶段(特别是如果一个合约是以 交互 来建模的时候)。
1 [& N) B9 h9 D3 E# S5 {0 L$ k通过达到特定的 时间 点来达到某些阶段也是很常见的。0 L% z6 G1 {/ l4 f6 L) ]( g
一个典型的例子是盲拍(blind auction)合约,它起始于“接受盲目出价”,
9 G' @8 o' j2 Y$ J, V+ l然后转换到“公示出价”,最后结束于“确定拍卖结果”。
# f% Z! e. b, ]4 Y) d5 ]' b… index:: function;modifier
! d9 h$ E1 u  }2 c函数 |modifier| 可以用在这种情况下来对状态进行建模,并确保合约被正常的使用。% p) Y' f: k& G- ?
示例
3 F% J* ?# i+ G3 [在下边的示例中, |modifier| atStage 确保了函数仅在特定的阶段才可以被调用。
* v' d1 }  W) u( [6 U0 \" v根据时间来进行的自动阶段转换,是由 |modifier| timeTransitions 来处理的,: k! R- [. z$ Y% @' e/ q. R
它应该用在所有函数上。
; o8 k8 O, _  M9 [" l… note::6 i/ \0 y5 q' |% \+ b, x% c
|modifier| 的顺序非常重要
$ h& K( u$ s& q& l6 G! O, ^如果 atStage 和 timedTransitions 要一起使用,
5 {" B' l  _7 A, f请确保在 timedTransitions 之后声明 atStage,
* c0 M0 U% I4 R以便新的状态可以' a+ ^& Y% {9 m1 |; q" @% e+ h
首先被反映到账户中。
; _* v9 d% f) h$ c最后, |modifier| transitionNext 能够用来在函数执行结束时自动转换到下一个阶段。: H7 b; |  @1 s0 |% X2 }# y! ?
… note::
3 F4 q( ~! Y. q6 W& {+ {$ C6 R|modifier| 可以被忽略$ e; [" A5 H4 M' P
以下特性仅在 0.4.0 版本之前的 Solidity 中有效:, {2 l4 Y7 Z8 b# X
由于 |modifier| 是通过简单的替换代码0 }" X# K% R1 H+ `6 B  Z2 B
而不是使用函数调用来提供的,
  w' t; N- u; U: g1 x$ B如果函数本身使用了 return,那么 transitionNext |modifier|
4 q" y2 M0 z7 F, a1 E- l, o2 f1 E9 d的代码是可以被忽略的。5 Q* a9 s; u+ {7 {9 l& E3 `, Q
如果你希望这么做,
- F4 O- F, E4 b. ?) L请确保你在这些函数中手工调用了 nextStage。0 T' a3 ~8 M0 B% |4 s( }
从 0.4.0 版本开始,即使函数明确地 return 了,
5 S/ q& E* T: ~' z# u|modifier| 的代码也会执行。
4 k, ]7 J7 c& k  u1 b0 |::9 k2 r+ ?0 S, _. |
pragma solidity ^0.4.22;
8 T9 f' C" x) B* b) H0 J# {contract StateMachine {" z* G% F3 H+ k$ Q
    enum Stages {2 T3 b9 O1 c' r( U! c6 U# {
        AcceptingBlindedBids,1 w& Q  O$ R/ h% ]8 E: q0 ~5 ]/ c
        RevealBids,
) I2 ]& \  Y4 d2 T( [        AnotherStage,; G* }& c" P, D/ {+ O& F
        AreWeDoneYet,
7 g3 i: k9 z6 O- E        Finished2 r2 S+ E$ c- p% G; h& w$ ?% ~
    }
8 I) B; j+ T! k( q) n    // 这是当前阶段。, Q, I6 w' M: o, W# T: \& o6 `
    Stages public stage = Stages.AcceptingBlindedBids;7 F7 l& I! U( n4 ^$ A( v/ k) D7 s' d
    uint public creationTime = now;
2 S7 a& ^5 _0 A+ O' K# X    modifier atStage(Stages _stage) {
! k9 P" R; J6 `& D/ w) Z        require(9 I0 y0 i, t0 ^* k9 k, b/ P3 O
            stage == _stage,2 b* I# M; c1 F* K1 k9 B
            "Function cannot be called at this time."4 ]( G; X* b! ~$ a% T& [8 q
        );
) X& N) |- w6 x( l        _;
# C# G, A& z0 x- Q  H/ B" O    }& ?2 d2 F) `- w; L
    function nextStage() internal {( n8 W" y1 I! _3 N& I9 \* J
        stage = Stages(uint(stage) + 1);! g) Z" E2 F' m7 E9 y7 o
    }5 {4 P; a) r# g1 h3 g1 G* \& a- C
    // 执行基于时间的阶段转换。
1 `1 a) t8 k* L. Y    // 请确保首先声明这个修饰器,2 B. r+ Z+ ]5 I0 `
    // 否则新阶段不会被带入账户。
- e7 u2 L9 Y; }, }' @, O    modifier timedTransitions() {
, q1 |! ]1 }6 H1 b" B, Z1 i        if (stage == Stages.AcceptingBlindedBids &&
  B6 E9 Y1 R  t+ R0 b; r                    now >= creationTime + 10 days): P' V! n1 C, L# H: w
            nextStage();0 [9 H; C: [% P2 X! t1 {- H, G3 Y- {# t
        if (stage == Stages.RevealBids &&
0 T7 m# @8 B  d                now >= creationTime + 12 days)+ s3 j8 c+ `/ |# N
            nextStage();5 e! V* L& W' n7 X+ V
        // 由交易触发的其他阶段转换8 [9 \  @$ }  B8 N$ g7 |  D8 W
        _;
6 p4 H' q6 F( y* L    }  c. m6 x* ]" W, F% K4 u  R
    // 这里的修饰器顺序非常重要!
! g6 r4 T( j2 [- T8 z% d* e    function bid()
  ?0 P) C( d1 e2 E2 a        public  f) a, u6 n2 J& c% k0 w0 a8 g
        payable
' O& y- I: x! ]) ?        timedTransitions
, y! q2 c" S" W+ O1 U, }' L4 ^        atStage(Stages.AcceptingBlindedBids)' L0 @% ]& G. I
    {, U8 p; |. [2 D/ }
        // 我们不会在这里实现实际功能(因为这仅是个代码示例,译者注)1 E  S4 p( Y+ s/ C' F. B
    }* ~' z' J! R' s% G0 S
    function reveal()& G% z8 u) f( j2 q
        public
+ F' k" }2 r+ f" _1 u: h0 c( j) I        timedTransitions
5 i& u$ F4 U& m1 X. e3 A        atStage(Stages.RevealBids)6 \+ D( [2 X/ o* s- u6 `
    {
- G5 r2 e, M1 k" @5 c* e9 N    }$ W' ^9 ^% C- F) z7 a3 V$ v* O
    // 这个修饰器在函数执行结束之后+ k$ Z! r" q9 a# A, B
    // 使合约进入下一个阶段。
7 r/ x/ j$ n% b4 K) G! n    modifier transitionNext()& E0 }: `1 R" G# K+ }/ z
    {
; V) o1 J5 h* N8 u! A2 G9 l        _;
2 t: ~/ \4 X: `* G6 y9 d- {- l, U        nextStage();. x& p2 n$ l9 Z
    }
' u( |9 @; |. b0 _7 ]    function g()9 l# T3 s8 W$ e( }1 t
        public( O; g: G' J2 y2 b& h5 K
        timedTransitions
7 c! n3 W4 e4 ~3 `% _0 c        atStage(Stages.AnotherStage)% a5 M7 G6 ]& P% H$ E$ U& C! O
        transitionNext6 C% c& O- \  J0 m% b% S
    {* n% C9 B' O' l. `4 I+ u5 [
    }
$ K8 z0 m) A: ~- p( c) `; x    function h()
5 t! x3 z  W6 F) ?        public. Q0 s- M! y/ ~
        timedTransitions9 D5 y8 p: ~1 X; ]/ [% j' p
        atStage(Stages.AreWeDoneYet)
6 e% N5 Y/ `5 U2 x% `) H8 G+ ]        transitionNext
  l8 |5 H8 t$ i9 o, x    {
' E; @0 {) T$ O9 h    }9 T4 V0 T  `' l3 t
    function i()# Y! Z  M. o' g' W# S
        public( b. j# n6 w' v* `) i
        timedTransitions
' b7 X; v! I0 Z. |        atStage(Stages.Finished)$ E3 r" N! L5 p
    {
- Z+ A, L/ \- D! `2 ?- @" j    }
3 ^0 a4 O) h* \}
2 c% f, Q& ]4 k$ g6 A1 N" f原文:https://raw.githubusercontent.com/etherchina/solidity-doc-cn/develop/common-patterns.rst
标签: Solidity
BitMere.com 比特池塘系信息发布平台,比特池塘仅提供信息存储空间服务。
声明:该文观点仅代表作者本人,本文不代表比特池塘立场,且不构成建议,请谨慎对待。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

成为第一个吐槽的人

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

    0

  • 关注

    0

  • 主题

    13