Hi 游客

更多精彩,请登录!

比特池塘 区块链技术 正文

Solidity通用模式

温室小书生室d
332 0 0
从合约中提款6 c3 j% F. W, J8 B% S, L6 t
- X0 F" R! D+ Y
在某个操作之后发送资金的推荐方式是使用取回(withdrawal)模式。尽管在某个操作之后,最直接地发送以太币方法是一个 send 调用,$ _0 Y9 O! Y2 @  b; `) _& {  A
但这并不推荐;因为这会引入一个潜在的安全风险。你可能需要参考 :ref:security_considerations 来获取更多信息。2 Z6 L$ N+ {7 R. L! o+ W
这里是一个在合约中使用取回模式的示例,它目标是通过向合约发送最多的钱来成为“最富有的人”,7 [7 z4 q4 P" |& J, s6 \
其灵感来自 King of the Ether _。
" I. s$ i+ {8 }7 ]在下边的合约中,如果你的“最富有”位置被其他人取代,你可以收到取代你成为“最富有”的人发送到合约的资金。
. G6 _  W$ [% C8 L0 A::
: U% i$ g" q2 ?5 B! G% p' `pragma solidity ^0.4.11;& i' a0 d! L9 |0 L! |8 v
contract WithdrawalContract {
& L$ s  V! Z1 ]+ g    address public richest;
, E$ ]% K8 {7 k! v) m1 c' d3 J    uint public mostSent;+ b7 {# R& z- p& b; l6 g+ Y8 \2 P, t
    mapping (address => uint) pendingWithdrawals;
8 Q0 O. s- C3 ?" Y6 w. F; i    function WithdrawalContract() public payable {3 G5 m5 k* O$ _8 }5 H: |# w; M  P- H
        richest = msg.sender;
- q9 v, `: X9 a9 ^( l        mostSent = msg.value;/ ^. k/ n" T4 O( L2 w7 n
    }( w  M: E3 T0 s- X
    function becomeRichest() public payable returns (bool) {" B6 r1 Z5 I' c1 X% f# _( X
        if (msg.value > mostSent) {
' \) O9 I" y- n# N5 {7 `  b( T            pendingWithdrawals[richest] += msg.value;
( I- s/ m0 i1 z            richest = msg.sender;. u- V! d' t$ s, b
            mostSent = msg.value;$ x. d: ]0 r6 C2 N. V8 Y( r* m
            return true;
- E" m2 ^% H; n6 i        } else {: M. n% N- n2 W
            return false;! F* W6 ]; x; m% }4 d
        }0 |6 z6 e' x& ]
    }6 V  l& T+ M4 z
    function withdraw() public {
+ O0 E; U7 O2 O$ Q6 c% `. m9 D        uint amount = pendingWithdrawals[msg.sender];; t' s/ d( x+ z6 f* N. U  A2 g& H
        // 记住,在发送资金之前将待发金额清零# f# m& f+ M5 t) r7 n$ c
        // 来防止重入(re-entrancy)攻击
' {1 z' Z7 o" m7 f        pendingWithdrawals[msg.sender] = 0;8 ~3 T' J8 Q. g% t0 h. }8 I9 R1 K
        msg.sender.transfer(amount);4 X  c" H/ ^$ O2 r7 ~5 Y% I' d; ^, D7 D. @
    }
/ g( m; l# N$ u0 b9 X}! d' ]# u# `( `; U0 p+ j1 |- T
下面是一个相反的直接使用发送模式的例子:
6 d  _6 O; k5 j# g::
9 t9 a7 ?# F3 q' d" lpragma solidity ^0.4.11;; l9 {/ W" z0 }, v! Z: N
contract SendContract {
. f8 k5 |; |- U+ O6 ~  s+ E    address public richest;
( F# a. H0 g. P0 J9 ]7 M    uint public mostSent;5 a; M1 e$ @) q5 B4 v# r
    function SendContract() public payable {$ V3 \" k" |% I  E. L% f, x
        richest = msg.sender;
+ u) m3 b* V! L$ m5 a! n+ z& F; p        mostSent = msg.value;
  p: T8 O& H* Z% O$ P    }
' q4 @, C) A$ f; f5 _* R: M% M) ~6 i    function becomeRichest() public payable returns (bool) {  V: i6 D3 n) ]$ r0 S7 Z0 Y! h
        if (msg.value > mostSent) {/ \: j8 Q0 x% h+ Q
            // 这一行会导致问题(详见下文)
5 b5 t2 J! Z2 O; p8 h  `+ M            richest.transfer(msg.value);( v+ B! W+ A) c6 `8 I: d
            richest = msg.sender;7 S6 \  Q4 }& [! D5 k: h
            mostSent = msg.value;
3 R5 H+ o1 g" i9 `, ]( E            return true;& ~7 M; j3 J1 y; H( g
        } else {+ h5 H- ~! z- o! Q) I
            return false;4 ^( p! J+ r" n$ t7 o) Q/ P7 y: u) a! R
        }
" a2 `  X; W4 T: ?( F( H    }) G6 }4 F0 g% O+ g' N, l
}* E$ n6 L# J! X' n5 u+ Z: }
注意,在这个例子里,攻击者可以给这个合约设下陷阱,使其进入不可用状态,比如通过使一个 fallback 函数会失败的合约成为 richest$ q7 E% o1 b4 o5 l/ a# h
(可以在 fallback 函数中调用 revert() 或者直接在 fallback 函数中使用超过 2300 gas 来使其执行失败)。这样,当这个合约调用 transfer 来给“下过毒”的合约
1 e7 v, O9 T/ K& x& [* K+ p发送资金时,调用会失败,从而导致 becomeRichest 函数失败,这个合约也就被永远卡住了。; j# ?8 ]9 v; X, j2 o# d7 R! U
如果在合约中像第一个例子那样使用“取回(withdraw)”模式,那么攻击者只能使他/她自己的“取回”失败,并不会导致整个合约无法运作。; K9 v* u. |* [7 C! ^6 z
… index:: access;restricting
: \% |( x! H* O( Q; `/ i0 i; n, F8 F; ^# P
限制访问
" O$ n- m0 J0 g. K0 W' F4 l
/ {  P+ V8 z$ [2 G# ~% X限制访问是合约的一个通用模式。注意,你不可能限制任何人或机器读取你的交易内容或合约状态。6 A4 q# D' J, k2 g* h+ G# W* D
你可以通过加密使这种访问变得困难一些,但如果你想让你的合约读取这些数据,那么其他人也将可以做到。+ N7 g- y: n6 m" y$ r$ o+ W
你可以限制 其他合约 读取你的合约状态。
; j3 e. y) h$ {1 R5 \这(其他合约不能读取你的合约状态)是默认的,除非你将合约状态变量声明为 public。1 I7 {2 j# _( @+ v. P6 @: R7 K
此外,你可以对谁可以修改你的合约状态或调用你的合约函数加以限制,这是本节要介绍的内容。; S9 C5 R) Q! _& Q6 J5 O# G8 e
… index:: function;modifier
2 G; b: l9 \( K+ [$ N通过使用“函数 |modifier|”,可以使这些限制变得非常明确。
4 c- ?4 i8 }" c$ W::
7 }' E* K9 Q  B, @- x3 @& R% Ppragma solidity ^0.4.22;
) P( z1 l* G' J! |$ Jcontract AccessRestriction {* \* L( q) T, N. P  y# V( t
    // 这些将在构造阶段被赋值" X) P+ z7 J# i
    // 其中,`msg.sender` 是) Y# `9 ]. q4 R1 c% L
    // 创建这个合约的账户。% y( n8 O6 u8 q
    address public owner = msg.sender;6 Z7 O) M1 n( Q$ a! [+ n' X' N
    uint public creationTime = now;
. R3 z. W% R. q2 K( u9 G3 t    // 修饰器可以用来更改
2 O; a* ~2 q5 G% H. a& E! H8 r( u    // 一个函数的函数体。
' c: ?5 V2 p. H, |1 G$ E" V% E    // 如果使用这个修饰器,
! }, E$ v) c( X  Q" I" a; u2 Y    // 它会预置一个检查,仅允许
& W; l- [' Y! n# r, a% M    // 来自特定地址的
/ D% D+ E  x( |8 Q, w! z    // 函数调用。
9 l& U7 C% _& ?    modifier onlyBy(address _account)6 ]2 h; F  E  G
    {
* c, t3 {1 t4 A* M        require(
7 d1 B& k- ]8 U            msg.sender == _account,0 I9 v# n2 U6 q9 _
            "Sender not authorized."
2 e! [/ u  T$ D/ c0 r        );0 z, V. U: m' h& T8 F; g* I
        // 不要忘记写 `_;`!' d' A7 k7 h+ P% H. G4 N
        // 它会被实际使用这个修饰器的
3 K" X/ \: j5 ?1 Z9 Q$ P- q' E  N        // 函数体所替代。
- l7 o/ A# W, v% Q        _;
& r- I/ H( d# p) Q    }
+ {4 _7 K! r9 t9 V' ~# f    // 使 `_newOwner` 成为这个合约的) J. }/ p. W  s5 S$ l
    // 新所有者。
+ v; _+ N; \8 x9 R# i8 m    function changeOwner(address _newOwner)
4 j2 {4 `* j7 |& J+ k        public  ]& k1 c1 \$ K% w: m# q9 ~
        onlyBy(owner)
5 ]7 n. |* i1 D8 v1 l  t& n4 F) h    {
" o# X9 t1 F7 c7 |1 d- x        owner = _newOwner;
. N$ K. D+ L- @* `; |, _  T    }
% j% u4 A# z- _4 i; e3 ^1 v    modifier onlyAfter(uint _time) {
6 l- J9 j8 H5 N/ v( ?        require(
2 j9 C( g9 S5 {, x$ }0 l            now >= _time,( c6 Y* B) e/ m3 |- R/ D: N) c6 ~- |+ G
            "Function called too early."
- q5 R7 T9 N$ T6 V        );
5 H7 W, E4 M: `9 L2 b9 f9 v( R        _;* @8 W. T0 N; U6 x3 P8 I" V2 ]3 F$ q
    }
7 Q' a) l& i0 F$ M. {    // 抹掉所有者信息。* D! s. A3 p& A# T/ j3 H
    // 仅允许在合约创建成功 6 周以后
: ]1 K% e( |+ J; r+ ?    // 的时间被调用。- q/ M% K$ q* Y' k9 {* f2 z
    function disown()
  N1 s% m8 h) n        public0 g4 b* r/ V, @7 W; L$ _* s1 V
        onlyBy(owner)
: x8 U# T0 p/ ?  V8 Z        onlyAfter(creationTime + 6 weeks), V/ C4 Y6 d3 f( l, _
    {
  y. Z/ P4 _' F& i8 M        delete owner;
! {1 f+ i5 T  j( [# @8 x2 A% n4 H    }
* _8 ~4 R9 e& _( f0 c2 u; W+ W$ E* I% a    // 这个修饰器要求对函数调用
0 c, e  l. h7 a    // 绑定一定的费用。
7 z0 f7 n1 [  N# Y" d    // 如果调用方发送了过多的费用,, `9 G! p. S5 k
    // 他/她会得到退款,但需要先执行函数体。
& M( K! G! c& F    // 这在 0.4.0 版本以前的 Solidity 中很危险,
2 F8 [- Y, d% ?    // 因为很可能会跳过 `_;` 之后的代码。
" {; o) t5 m; x; @/ v, D; K* I    modifier costs(uint _amount) {! V2 j  e6 F" w  X6 a
        require(  J$ ?3 B$ L4 U1 h
            msg.value >= _amount,
+ k" V& r( e! u- b" D/ N) N/ c0 V) [            "Not enough Ether provided."
# F- C9 J! w) b! p4 H        );, E3 m# l) m. ~# g% \
        _;' ]; W6 u! Q7 ~5 D7 q+ L
        if (msg.value > _amount)
& p1 N% W# M: W8 b# ^- w" y4 p) M            msg.sender.send(msg.value - _amount);
7 K% L, o" B4 y( t! i    }% m3 Q( g* m' `5 T  Z/ u8 Y
    function forceOwnerChange(address _newOwner)* }2 V0 |9 w. _( {! M
        public- H$ i6 U& n- I: a
        payable
& n1 J5 T/ {3 ]        costs(200 ether)) H" Z' @$ e8 [+ d7 a
    {# t. _: |7 M8 u5 g' X& V5 P
        owner = _newOwner;
$ M6 c/ T2 j8 h! k9 }2 @        // 这只是示例条件
  ?/ S# m4 y7 p7 I$ H. v" C1 U& n        if (uint(owner) & 0 == 1)
: G( ?; p( r- p. r" ?) [2 A            // 这无法在 0.4.0 版本之前的7 ^, O3 O9 |, b" g1 S$ r
            // Solidity 上进行退还。% K" ^" \1 _7 C* E
            return;
# v4 p3 r8 [3 ]1 ?% [        // 退还多付的费用
5 G0 i% b! O5 I" i    }3 O& v; L) `! j# D# H  H4 z( B0 O
}
# p4 C0 u  n$ U- Y. E一个更专用地限制函数调用的方法将在下一个例子中介绍。9 D+ i9 ^2 g8 r7 Y0 a0 W! Y
… index:: state machine
' h, d% ^- ^4 P4 v0 }. u* S
9 Q: S8 _* j+ p' q5 U) L/ ^状态机7 {- z7 W5 X% c! E
9 n: y  p* F; Y
合约通常会像状态机那样运作,这意味着它们有特定的 阶段,使它们有不同的表现或者仅允许特定的不同函数被调用。
* {0 s- r6 X1 Z  z+ d* W  l/ {一个函数调用通常会结束一个阶段,并将合约转换到下一个阶段(特别是如果一个合约是以 交互 来建模的时候)。
  G; U0 S( C  k6 C* f通过达到特定的 时间 点来达到某些阶段也是很常见的。
: \$ g1 l' c' f' i一个典型的例子是盲拍(blind auction)合约,它起始于“接受盲目出价”,1 x( R& r6 q8 N& V9 t
然后转换到“公示出价”,最后结束于“确定拍卖结果”。, ?# m  \$ r5 |& H% o0 t
… index:: function;modifier
& F- c7 ]2 g) X" D4 h. s函数 |modifier| 可以用在这种情况下来对状态进行建模,并确保合约被正常的使用。
7 z/ P- H0 h: i! H& S6 `  `8 Y: e示例
* L. K% G8 O- ~+ V; X* ~在下边的示例中, |modifier| atStage 确保了函数仅在特定的阶段才可以被调用。
, E& j- D+ X; z根据时间来进行的自动阶段转换,是由 |modifier| timeTransitions 来处理的,8 u4 N& W) D! p' m9 V% ~6 v! [* t
它应该用在所有函数上。7 D0 [+ E/ u5 P  c
… note::/ P! e- o: n7 X
|modifier| 的顺序非常重要& ~/ G; T" Q. ~+ P2 [; z) q$ ]4 z
如果 atStage 和 timedTransitions 要一起使用,
' b& v. n+ n$ s( P, F+ J  R请确保在 timedTransitions 之后声明 atStage,
5 B: `0 l1 G; D6 T8 u. K9 y+ w以便新的状态可以
3 j8 {" P. K& |6 a首先被反映到账户中。
& R, ~* g: Z4 ]: B! p0 _4 r2 ?; c最后, |modifier| transitionNext 能够用来在函数执行结束时自动转换到下一个阶段。; ]; D" O4 l4 c& D" }
… note::, |" q+ y- u: v5 T, ?' h
|modifier| 可以被忽略: h/ r3 C) Q" K9 ]0 W2 z4 i
以下特性仅在 0.4.0 版本之前的 Solidity 中有效:
" z) G4 @- A3 l7 c由于 |modifier| 是通过简单的替换代码' d0 D% i( J- E: k% j% R1 k8 X8 C
而不是使用函数调用来提供的," C* u4 H6 o% c
如果函数本身使用了 return,那么 transitionNext |modifier|( f3 `8 @' d: P2 V
的代码是可以被忽略的。! R3 {, P0 w5 h( P; t+ n
如果你希望这么做,& _+ g+ s' j2 i, ^
请确保你在这些函数中手工调用了 nextStage。
: b2 s7 l6 s! ~+ Q2 Y- h4 P- ?1 c% z从 0.4.0 版本开始,即使函数明确地 return 了," K0 ]! k% }3 [
|modifier| 的代码也会执行。
4 _7 R3 w) X2 b, Z. h0 E::
( S. I, O% b! l1 c8 j1 b; Ipragma solidity ^0.4.22;( U! v4 m+ d; d& c  h% ?& Q2 L
contract StateMachine {
. ]5 N7 r1 p/ n+ u: P    enum Stages {
3 y- P+ q4 c: l0 X' r, c' z        AcceptingBlindedBids,% j0 m- P% b& P% ]4 X9 f' [% O
        RevealBids,
$ ]4 f0 T. b" W: s0 z  u; [8 x        AnotherStage,4 }% U0 j; Q' b7 k, `
        AreWeDoneYet,3 m; D5 J1 W  T) l. u
        Finished
# W) r; ?1 v; k" ?2 [1 p% d- S    }
4 s6 z* V1 B; w/ z, o; d0 ~    // 这是当前阶段。( \, j6 H# q1 d
    Stages public stage = Stages.AcceptingBlindedBids;& s7 r& V7 o4 ^2 G
    uint public creationTime = now;* V- ^  H# W- r) r* G. w
    modifier atStage(Stages _stage) {
; r1 h& x" k  i0 M4 v' i* z2 z        require(( R0 T* T( x1 `+ _7 ]$ b7 a" E
            stage == _stage,5 Y5 x$ c3 m9 v4 E# Y) n
            "Function cannot be called at this time."- |$ h; |8 h- D' _- C' u
        );
* t; c5 ?) E8 |  G+ ?( X7 I        _;
3 z* w9 P6 }: N) ^3 o. P    }
7 P6 k& j! W- F    function nextStage() internal {5 m9 S, U8 Q9 ]5 D7 q% x/ f
        stage = Stages(uint(stage) + 1);2 z+ @8 U, Y5 Y& I' S8 x
    }5 V1 B: R7 S; n6 {( ~/ E
    // 执行基于时间的阶段转换。
  E# ]* Z' N5 Q9 B6 B  u; P* s    // 请确保首先声明这个修饰器,* ?; _! |% R& G6 {  T7 o$ `
    // 否则新阶段不会被带入账户。5 \# b6 D/ M* R- S$ H
    modifier timedTransitions() {
) Q$ l7 v8 Y; l1 A- k( S        if (stage == Stages.AcceptingBlindedBids &&
; f3 _( b, C* w  e' |' O% F                    now >= creationTime + 10 days)
9 H* {% E# O) x4 S3 k            nextStage();  l/ z& O7 S$ E: h* p. b0 q. Z
        if (stage == Stages.RevealBids &&
0 U9 P# V) i# R4 x; m+ J0 @% G                now >= creationTime + 12 days)
$ y! u$ @4 f1 f* l3 L+ y            nextStage();
1 x$ a6 P) \3 b" p; s        // 由交易触发的其他阶段转换, V1 U  Q- X- K! D  s6 {+ G: d/ B
        _;
% |/ y$ v  J' z% @4 v    }
6 u1 @; V: i* t2 C7 Y    // 这里的修饰器顺序非常重要!7 |: Q1 Q& ]1 x( y1 a
    function bid()
9 W, j8 G1 T0 I( o+ ~/ C, |) F        public! @% m8 O( ~7 u# I( Z; P
        payable
3 Z$ i' E" w! j% ^* C- y        timedTransitions4 K! C1 p5 z/ f6 T6 N9 q0 ^
        atStage(Stages.AcceptingBlindedBids)1 o. \; f3 P4 P9 k- t
    {, U% k* c4 g5 J: [
        // 我们不会在这里实现实际功能(因为这仅是个代码示例,译者注)
7 ~7 I& h1 I. W1 l* \8 a    }
% s! O" H. a# g+ \, x    function reveal()$ u( ?2 k$ w% @1 a, R! @
        public
/ ^4 t1 z: l+ j2 ?% `        timedTransitions
4 D' W; |$ D5 `8 {, ^1 C5 l. ?) X        atStage(Stages.RevealBids)& @/ e9 g$ G0 g7 a
    {
( |; u" W7 w) I    }
" Q) A# {6 G$ K  k. D" Q- c) `, v& e    // 这个修饰器在函数执行结束之后
# t/ L& q. f/ o3 q4 e    // 使合约进入下一个阶段。! t1 o& k5 o( V5 U2 ]
    modifier transitionNext()
! A4 F8 b* Z0 R/ f. E    {, q& r& e$ p% e3 ?9 E/ O
        _;- Y6 l$ U9 l* A; N) Q
        nextStage();
' V  ^8 H9 X0 B4 ~0 r! K    }
" \1 K* m# ]7 r" o) A    function g()
. D0 }3 }( B  t9 B# b( p" r) L        public1 i) p" S2 A5 ?
        timedTransitions
: C$ Y9 ?# Y- Q6 }        atStage(Stages.AnotherStage)
0 Z6 g5 L) P/ F* y" A        transitionNext! l; c1 Y' c  c+ ^, C
    {# G& w& p9 u1 u$ M
    }
7 G0 w" r  u. n2 g    function h()( y% [, {; M+ ~1 p( |- y
        public
  H" }! u; s, w9 ]0 e4 C        timedTransitions. A( B5 b& T3 W
        atStage(Stages.AreWeDoneYet)
% b9 X+ i$ z1 G# Q        transitionNext
+ k. U. D+ O, C  B$ ?& [8 W    {, t, n7 L9 b0 w
    }
9 s: e3 W$ p, ~9 @    function i()% A3 Y' r; A0 R" D
        public
( J0 r+ l" N6 |9 B) @        timedTransitions4 Q; ]7 y% k: s, H6 Z6 v) E
        atStage(Stages.Finished)) n3 _: [& m, h6 _' q
    {
% P! w% u) e+ ?7 ]5 m    }
$ x1 O6 y2 O7 o0 ~}6 t' {- T5 u' s$ J$ X
原文:https://raw.githubusercontent.com/etherchina/solidity-doc-cn/develop/common-patterns.rst
标签: Solidity
BitMere.com 比特池塘系信息发布平台,比特池塘仅提供信息存储空间服务。
声明:该文观点仅代表作者本人,本文不代表比特池塘立场,且不构成建议,请谨慎对待。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

成为第一个吐槽的人

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

    0

  • 关注

    0

  • 主题

    13