Hi 游客

更多精彩,请登录!

比特池塘 区块链技术 正文

Solidity通用模式

温室小书生室d
530 0 0
从合约中提款8 g$ b7 y6 U1 E
& c1 R% s2 f8 Q, ]( |
在某个操作之后发送资金的推荐方式是使用取回(withdrawal)模式。尽管在某个操作之后,最直接地发送以太币方法是一个 send 调用,9 ^% L5 q+ _# N9 ^: o, N
但这并不推荐;因为这会引入一个潜在的安全风险。你可能需要参考 :ref:security_considerations 来获取更多信息。) B" X0 \8 b8 }- F
这里是一个在合约中使用取回模式的示例,它目标是通过向合约发送最多的钱来成为“最富有的人”,
: C$ d7 ~1 b7 n# y2 S! x6 a其灵感来自 King of the Ether _。" d4 I9 W8 m! W
在下边的合约中,如果你的“最富有”位置被其他人取代,你可以收到取代你成为“最富有”的人发送到合约的资金。, x9 X; r- H( r2 }6 ^, O
::0 m. l# H) h6 f* o
pragma solidity ^0.4.11;
% G! s" @4 ^: P, g5 Bcontract WithdrawalContract {
! U. C& P( \5 y5 L' v: k5 J    address public richest;% U  w5 t# u7 w) q- L$ N  `9 Q& s
    uint public mostSent;+ r$ K/ O$ l- l: e  ~, T  M
    mapping (address => uint) pendingWithdrawals;$ n+ x4 |" S" g5 b7 I
    function WithdrawalContract() public payable {
2 W' v& H9 ^) g: W1 J        richest = msg.sender;8 b: h% F( @- P+ v* `
        mostSent = msg.value;
9 D( }8 A: o4 v: S% J2 M2 y    }
, r1 K" C& o. F1 J# `    function becomeRichest() public payable returns (bool) {
* e+ Q6 Z8 S+ ~* i5 Y+ z        if (msg.value > mostSent) {
7 n( w  ^" ?# |, v( z            pendingWithdrawals[richest] += msg.value;9 T- z& G) B3 b/ ~3 n- i/ y5 C
            richest = msg.sender;# H3 D( L5 _" t& v( u% D# h3 ?
            mostSent = msg.value;
$ L% {0 \3 f7 }/ C4 v            return true;
, f; |3 H) J1 k        } else {3 Z$ [8 o0 s3 m1 x) ^2 c. C- Y
            return false;/ e- x: d3 f" N1 U5 p' o
        }4 V8 v$ g& G! n7 k2 n
    }
8 ?$ K0 E0 M; O4 A    function withdraw() public {9 }& ]* ]2 e5 N4 x( \, T6 K' X
        uint amount = pendingWithdrawals[msg.sender];
* H5 a- w- @, _        // 记住,在发送资金之前将待发金额清零
$ Z& M# s' Z  y$ q- Y$ ]        // 来防止重入(re-entrancy)攻击3 [$ y; L& t' U; b0 k9 i' @
        pendingWithdrawals[msg.sender] = 0;$ P( R0 k$ A2 q5 \
        msg.sender.transfer(amount);: ^3 {" v( D9 e" Z! z
    }
4 Y  k, \7 E" C6 a3 i/ d}+ H/ w( ]) X2 a  f$ l
下面是一个相反的直接使用发送模式的例子:, Q/ h" Q- R0 a+ F1 x
::: m' W/ B# _2 s7 R# d3 e
pragma solidity ^0.4.11;
& N  Z* H7 W( O9 Ccontract SendContract {  S/ Y/ m2 {( B3 c
    address public richest;
7 T5 o2 v% P1 D) k. w1 f    uint public mostSent;& }3 d) ^- X' R% U9 o
    function SendContract() public payable {, ?7 q: a# m& r* s
        richest = msg.sender;
, o9 p' ]7 v. F& \        mostSent = msg.value;
- a# ]3 i# J% u0 u    }. u# o4 ^) B  C. U' I$ v6 y. t. q
    function becomeRichest() public payable returns (bool) {
  K0 I# y( I4 U, y2 v        if (msg.value > mostSent) {
9 O) C- V: d7 f% A2 \            // 这一行会导致问题(详见下文)
/ y7 v4 I7 k% O            richest.transfer(msg.value);
7 ^9 {: v* G. E8 x9 l5 l: `            richest = msg.sender;& C! v3 B) Z' d8 Z" }! n% o
            mostSent = msg.value;' H8 k  _' c# g& `3 C7 s1 H0 g# k
            return true;
' ], }4 _2 ]% Z, ]& s        } else {
3 g' `0 i( F3 i! y            return false;
; ]+ a8 T$ [4 r) E: q        }) o2 L- h, _6 R3 ^) V
    }' r  [4 h  e1 W
}0 @1 n$ }% A4 T0 z
注意,在这个例子里,攻击者可以给这个合约设下陷阱,使其进入不可用状态,比如通过使一个 fallback 函数会失败的合约成为 richest
; G3 @" \8 {6 ~: a5 a. [& @(可以在 fallback 函数中调用 revert() 或者直接在 fallback 函数中使用超过 2300 gas 来使其执行失败)。这样,当这个合约调用 transfer 来给“下过毒”的合约
! u; Y4 e" ^$ J/ {: E2 s发送资金时,调用会失败,从而导致 becomeRichest 函数失败,这个合约也就被永远卡住了。) t$ [0 V' n8 E/ t/ @& R
如果在合约中像第一个例子那样使用“取回(withdraw)”模式,那么攻击者只能使他/她自己的“取回”失败,并不会导致整个合约无法运作。) ~* I6 a3 }2 ]. k, J, \6 u9 i
… index:: access;restricting
+ L, l- |3 r# V1 p& I, ?1 y3 G( r& f1 i# N' Y. S
限制访问# P7 U/ w, B/ O& X% I

! Z0 V, l% t! S9 B限制访问是合约的一个通用模式。注意,你不可能限制任何人或机器读取你的交易内容或合约状态。  H8 t: x$ o& H4 D' y
你可以通过加密使这种访问变得困难一些,但如果你想让你的合约读取这些数据,那么其他人也将可以做到。
" h9 d( G- y- v6 n( Z. w7 U你可以限制 其他合约 读取你的合约状态。( s- j# x( \9 G$ j
这(其他合约不能读取你的合约状态)是默认的,除非你将合约状态变量声明为 public。
+ N* e  O7 X3 H此外,你可以对谁可以修改你的合约状态或调用你的合约函数加以限制,这是本节要介绍的内容。* ]3 U; {/ L; W
… index:: function;modifier0 S' J' Y' q/ J5 E6 c7 [
通过使用“函数 |modifier|”,可以使这些限制变得非常明确。3 U* B1 I% o0 u: j3 P0 F
::; _; x; w% }( A! _" ]+ k
pragma solidity ^0.4.22;; y! L0 N. N" |
contract AccessRestriction {
/ p6 e) |" L" z' c0 U) f! o) T    // 这些将在构造阶段被赋值
4 `' @( v6 n+ ]; J6 J% n, d    // 其中,`msg.sender` 是- y" X2 ~: f2 I+ r; o
    // 创建这个合约的账户。: }- j, b- j) M/ Y
    address public owner = msg.sender;
8 s4 S3 }9 w( {  V6 p' F) x) J    uint public creationTime = now;% N6 M4 v# F% A! e
    // 修饰器可以用来更改2 R  ^% A6 P+ O+ S2 s  F+ j
    // 一个函数的函数体。
8 B7 l, @9 X" Z3 j: v$ g    // 如果使用这个修饰器,
6 @( e0 [. M! F    // 它会预置一个检查,仅允许, V# ~8 O& y7 x/ X
    // 来自特定地址的
; A& D6 Y7 p. C  z* x* t    // 函数调用。
2 @- c3 |' x7 d( f. _    modifier onlyBy(address _account)
3 u7 C6 x- h  n/ {! {( L    {- v  Z0 U+ n! q3 I3 s( O
        require(: U5 P8 G0 f( E) C
            msg.sender == _account,0 ]# P5 L& X; y
            "Sender not authorized."0 N5 i$ ]6 T1 j5 Q" M: A0 J
        );- B2 J% Q$ Y0 H8 ~# a6 f* e% V2 v. G* J
        // 不要忘记写 `_;`!: {& o# L8 b( x3 ]; Q( y
        // 它会被实际使用这个修饰器的' C, ~! \% t3 R/ q2 q+ J
        // 函数体所替代。  l4 c! \& I' n% m
        _;
  [  |6 Q; J. s* [    }( b  n% w# k5 D% C1 s$ _
    // 使 `_newOwner` 成为这个合约的4 ?8 A: F. {- ]- o! g  r
    // 新所有者。% t; O5 \+ T: b
    function changeOwner(address _newOwner)
" A5 H* T/ {# M8 ]$ N        public
% P  j. f! q7 n$ s) {" @1 N/ S        onlyBy(owner)& j. F1 g4 h+ d- j
    {
0 Q8 D; O# g0 `$ x  x        owner = _newOwner;# I3 L9 D. A+ U# t9 w8 ~2 N2 l
    }
  I" y6 o' Y" ]: D! q8 z, B! K    modifier onlyAfter(uint _time) {+ O( x4 T7 y, R# V( |
        require(
9 \8 h" Y  ^# o- s' \# l1 ~0 }$ n            now >= _time,( p# x! B' h/ _+ V, U
            "Function called too early."
9 L# d. a- I+ A' A6 B( p5 f8 u        );! d, C, o& Q+ D$ m- v( U7 z$ l3 P
        _;. O7 ~# Q0 G% ^- V
    }7 }5 i! I6 c, a! `; n% ?; `
    // 抹掉所有者信息。5 O" V* g# Q: f: ?8 A
    // 仅允许在合约创建成功 6 周以后
  g' r+ k5 ]8 \1 `" g7 Q    // 的时间被调用。6 m1 F5 d2 r% o7 L' N
    function disown()7 P: ^7 O# W7 \
        public
8 Z3 Y. H$ e7 E& ?3 Y        onlyBy(owner)
* @' r" x6 l$ H7 ^8 [        onlyAfter(creationTime + 6 weeks)4 X! q/ z8 g5 S, H" A
    {
" F. @5 w5 X5 O+ \        delete owner;
! L" V0 T( ~/ P    }7 {# Z  L: T+ U
    // 这个修饰器要求对函数调用# X' N0 f* f' s- V' V2 m& v/ r
    // 绑定一定的费用。
' h% X  z5 b" }  r" c    // 如果调用方发送了过多的费用,) I( O; i5 ^. }- ?' A
    // 他/她会得到退款,但需要先执行函数体。
$ _( U( Z9 B9 l0 N& m. O- `' n    // 这在 0.4.0 版本以前的 Solidity 中很危险,5 l0 M3 ?" J+ h+ Q6 r
    // 因为很可能会跳过 `_;` 之后的代码。! b8 \: h: O5 g' y
    modifier costs(uint _amount) {
; y. I  J; q( f, }" f& j+ v5 f# B        require(2 U( _$ }+ F8 I
            msg.value >= _amount,
; o8 \, x  F$ h+ o$ z$ f1 p2 w            "Not enough Ether provided."
/ n( M: k" q* H# P8 _        );
, p, \& S9 v( L( s4 q7 ^  d2 B        _;
6 I, Q% F  h8 _) }6 `0 ]        if (msg.value > _amount)
0 t- O8 O# c  i. c            msg.sender.send(msg.value - _amount);5 G# U/ z+ m  S7 H* t
    }
& R/ g( B6 Y+ O    function forceOwnerChange(address _newOwner)
) }7 ?7 {4 Z2 U! X        public
# m! K- Z* n0 A- @! R        payable
/ g7 Z8 Q! p) l% T7 t. k4 A        costs(200 ether)2 ]5 f; Z& a" D+ x
    {
+ U; c: O  }+ h  o7 Z        owner = _newOwner;, N* I7 w- w+ }2 S( j
        // 这只是示例条件" O$ a4 h( y9 H9 i8 _; g
        if (uint(owner) & 0 == 1)
. {5 w7 A4 ]; G0 u' m2 c, q  K            // 这无法在 0.4.0 版本之前的
& @! }- c0 r+ C            // Solidity 上进行退还。% t# Z( o) b& l4 `
            return;0 T7 m- m& ^" l8 W& \1 Y1 {" Q, c
        // 退还多付的费用( `/ p0 r4 z2 ^" U6 L% `
    }
' s! S% j5 n% {" j, y}" _3 p4 b5 ?9 A* r( t
一个更专用地限制函数调用的方法将在下一个例子中介绍。( C% B9 K$ y  }2 o4 p- s. O
… index:: state machine
2 t! X- z2 N1 I. l. o4 ^/ U/ i. F. G4 @" v7 }
状态机
0 Z9 U2 [6 Z1 I" Z  C- a# j+ {% e! Y  X2 {& g: N6 k
合约通常会像状态机那样运作,这意味着它们有特定的 阶段,使它们有不同的表现或者仅允许特定的不同函数被调用。
. U5 m) f( m2 n* o5 n9 k0 o一个函数调用通常会结束一个阶段,并将合约转换到下一个阶段(特别是如果一个合约是以 交互 来建模的时候)。6 o+ J0 O4 O( G4 n  |: B9 A
通过达到特定的 时间 点来达到某些阶段也是很常见的。1 Z7 j, u4 X; f* r
一个典型的例子是盲拍(blind auction)合约,它起始于“接受盲目出价”,
5 ?0 N" ]5 l- [' o% N8 q然后转换到“公示出价”,最后结束于“确定拍卖结果”。9 P' ^0 I5 L7 \5 R$ r0 i
… index:: function;modifier* t/ R+ D- s9 M# `. y# p
函数 |modifier| 可以用在这种情况下来对状态进行建模,并确保合约被正常的使用。; u/ J& {: E5 v
示例
( R0 z3 e. x2 V6 G0 G在下边的示例中, |modifier| atStage 确保了函数仅在特定的阶段才可以被调用。! h& n9 \4 M# O& s) b
根据时间来进行的自动阶段转换,是由 |modifier| timeTransitions 来处理的,
8 B. M( Z3 b6 L1 P( o它应该用在所有函数上。
5 c! L: T7 a1 @7 ^7 \( |8 v! M… note::
, P. ~$ G) l& ^: E$ B: d|modifier| 的顺序非常重要
4 n+ y+ ?. G' j0 A7 e! w如果 atStage 和 timedTransitions 要一起使用,
- J( W/ ^: ^, O请确保在 timedTransitions 之后声明 atStage,
* A+ G6 s  x- s$ L+ v4 Q, s以便新的状态可以
. W8 y! U; D$ f( {6 F: E首先被反映到账户中。
( K5 X- \3 b3 h" p) o+ }3 g7 N" X最后, |modifier| transitionNext 能够用来在函数执行结束时自动转换到下一个阶段。# G# h4 j6 i9 O$ E: v5 Q
… note::
- U1 [; O6 C/ J" O: D* ~9 I|modifier| 可以被忽略
7 K) Q& D8 L8 q3 p3 C$ A以下特性仅在 0.4.0 版本之前的 Solidity 中有效:2 f, Y9 j9 D* M! v$ x
由于 |modifier| 是通过简单的替换代码
4 E2 @% Z0 H5 W, }而不是使用函数调用来提供的,
1 a. n% K: f" o; P如果函数本身使用了 return,那么 transitionNext |modifier|
, q: ]+ H5 ^& D( F, t+ s的代码是可以被忽略的。
$ w3 Y1 D& ^- \如果你希望这么做,
  V3 e6 p! m; ^2 ^8 ^7 ~请确保你在这些函数中手工调用了 nextStage。
: q. N* |" e0 e从 0.4.0 版本开始,即使函数明确地 return 了,9 a1 ^; m/ a# W$ Q
|modifier| 的代码也会执行。1 A+ T0 U& a- c
::
! T( m( o8 y5 d2 T0 hpragma solidity ^0.4.22;
; a9 \% X6 }8 Bcontract StateMachine {
/ W) S0 x6 O/ ]- B+ y3 ^1 O    enum Stages {5 y* A" l0 I5 u/ H; \( b
        AcceptingBlindedBids,
# i# n; j7 _6 X" \8 u$ y5 A! Q        RevealBids,
# k! R, A( y- m/ @! Q        AnotherStage,8 K, N7 z# q( g- j, P
        AreWeDoneYet,* W" A( r" S4 K! t9 m3 l
        Finished  C: [* j0 b; X: H8 \5 J' ]1 M3 [' o
    }
; R% q- F. Z1 a' c/ z. }    // 这是当前阶段。& s, m" U6 o, d, J4 {5 d, r& v
    Stages public stage = Stages.AcceptingBlindedBids;
- j" b' W3 C: o8 E; P9 U    uint public creationTime = now;
% ?! i* e8 h- P, D* L    modifier atStage(Stages _stage) {, d( c, T5 [3 d* o6 O2 N. \9 X
        require(6 }' d! F4 i8 s4 ~: k; f
            stage == _stage,8 F9 \% C( a* G% v, C5 S; e
            "Function cannot be called at this time."
9 V8 Z9 U' C6 @4 p6 y        );
% m: V1 O8 V0 q3 y) N4 d* M        _;& r2 o3 q0 O$ J8 m  K( c: q
    }
# D' i3 B" j2 S) o& B! T. \    function nextStage() internal {
7 q  r* X1 ?5 _4 f9 j        stage = Stages(uint(stage) + 1);& ^/ T% t% A5 T- R
    }
- K. F+ N) `5 Q1 R    // 执行基于时间的阶段转换。
" m3 k6 o2 B6 S7 ]' z4 y# [4 e    // 请确保首先声明这个修饰器,
, D2 X& \: j' u/ w    // 否则新阶段不会被带入账户。
' j% \9 b' h" A* B    modifier timedTransitions() {
( ]3 n# m0 h: D+ t" q        if (stage == Stages.AcceptingBlindedBids &&
! H+ B3 n0 o. m7 ^9 \, M/ ?$ Q                    now >= creationTime + 10 days)8 ]) `: K$ V! P) u
            nextStage();
0 C4 s. S: S- z$ J; S  P/ |        if (stage == Stages.RevealBids &&& `# x6 x1 w7 K/ L  [. Q
                now >= creationTime + 12 days)
9 ]: _2 b, O9 ]) ]! K( r/ W* y1 k6 w            nextStage();
8 \) G, e! t( `; [1 w        // 由交易触发的其他阶段转换. y+ w( y( `' a9 T+ V& k+ `
        _;
; a  }% R8 j4 w! p6 L, [    }) T- [' U4 K% U8 ~" f8 j7 G- ?
    // 这里的修饰器顺序非常重要!: W2 V9 X/ G: j1 r
    function bid()- d# {% r: a( I+ y- T6 q
        public
+ ]3 k; h/ `% H4 x5 h% l; Z        payable
2 B$ V# p% s1 Q        timedTransitions
: C4 W$ E; R, }2 h" Z        atStage(Stages.AcceptingBlindedBids)
! G6 c/ T9 {) N5 y7 o' D, i    {
& i1 ~3 t5 U/ O; x+ y) d" a+ u' @        // 我们不会在这里实现实际功能(因为这仅是个代码示例,译者注). w# h0 G3 }+ M( Y- G- i
    }
- i5 @. k5 p& A$ d. U' w    function reveal(). u8 E6 a% Z6 l9 ], \+ B4 n8 v
        public
0 Q) t5 i$ \) F5 g        timedTransitions
- ~# u; A1 n+ |+ i1 G2 x, {( X. O        atStage(Stages.RevealBids)
; u' h+ z5 [- L( i2 G    {
4 d  S9 I) q" y+ E( X    }  P: j2 C0 S: x* t' R5 D" L
    // 这个修饰器在函数执行结束之后
! U& ?# J8 ]4 R$ V0 H5 `# g    // 使合约进入下一个阶段。
1 d4 i- X! u9 e) a( r" I    modifier transitionNext()7 }' ~" Z# q0 A7 |, G/ }
    {+ Y3 b( Z- ^* X4 _" \) A, o
        _;
" w9 f( J+ l9 g1 S        nextStage();
  H( L3 B$ q1 A    }' v# o' `+ l- ~2 R
    function g()4 i2 w9 `3 S- Q$ [; z% s
        public+ S/ E6 k. V& R# `0 i% d
        timedTransitions" d  x+ a3 _6 q$ T" @/ g* o; J! U
        atStage(Stages.AnotherStage)% ~( O8 K5 d( P& H9 J, |; \
        transitionNext) q0 l0 }' E9 Y
    {1 ]$ D, G9 M/ r8 q9 F* S+ J2 D. n
    }
1 p% j2 G# J0 u1 E    function h()
# @$ u6 J* ]; S) E8 K( p        public
% W0 z+ K, g" s9 Z        timedTransitions
. V2 f$ L7 v6 d( c; X- }2 x6 A+ V2 m        atStage(Stages.AreWeDoneYet)+ k6 p$ i, p. F0 F
        transitionNext
" p* }  t. t) O; i    {
, J! E7 P6 B& _2 D0 n5 A    }
9 }: g5 y5 q: E+ z* r    function i()
: J' C: N3 x8 {! r' u% C        public
6 b! B# x  J5 n# Z" L+ ]6 }        timedTransitions8 O& a8 o# v3 l' N% R5 X3 }
        atStage(Stages.Finished)/ Z% e* ~8 \) P, a" ~5 {
    {
$ v" E0 N1 p- n    }1 l& z/ T" J: i' j
}
7 R! b" V; L) w5 k- e7 H原文:https://raw.githubusercontent.com/etherchina/solidity-doc-cn/develop/common-patterns.rst
标签: Solidity
BitMere.com 比特池塘系信息发布平台,比特池塘仅提供信息存储空间服务。
声明:该文观点仅代表作者本人,本文不代表比特池塘立场,且不构成建议,请谨慎对待。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

成为第一个吐槽的人

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

    0

  • 关注

    0

  • 主题

    13