Hi 游客

更多精彩,请登录!

比特池塘 区块链技术 正文

Solidity通用模式

温室小书生室d
525 0 0
从合约中提款
8 P% v" \  B  V
5 K3 Q7 g- N" s) n& }4 E在某个操作之后发送资金的推荐方式是使用取回(withdrawal)模式。尽管在某个操作之后,最直接地发送以太币方法是一个 send 调用,
4 Q$ S/ p& c8 ]$ S0 K! `2 [但这并不推荐;因为这会引入一个潜在的安全风险。你可能需要参考 :ref:security_considerations 来获取更多信息。) S( x& b$ l7 H+ @1 f
这里是一个在合约中使用取回模式的示例,它目标是通过向合约发送最多的钱来成为“最富有的人”,4 J9 |( p+ V  k0 a
其灵感来自 King of the Ether _。# P/ \$ y0 A! N) L0 O5 n! i
在下边的合约中,如果你的“最富有”位置被其他人取代,你可以收到取代你成为“最富有”的人发送到合约的资金。& F4 }6 B8 X9 B; f& `
::
% K9 }& F1 p# Epragma solidity ^0.4.11;% {. L( o- o' `1 \9 ^
contract WithdrawalContract {
3 W. L3 u% f' J    address public richest;$ A# e0 |, A8 Q
    uint public mostSent;
# F9 ?8 r# D5 g( _    mapping (address => uint) pendingWithdrawals;$ ]. z. ?0 u3 R+ Y0 d# b  X
    function WithdrawalContract() public payable {$ A: Y2 @% f6 V4 v+ }: C) b
        richest = msg.sender;
) _$ K( D1 `! h8 @  x) \& M        mostSent = msg.value;9 b5 r  B( e" V3 @1 ^
    }: ~( i+ X4 U3 k# B! p  y" n- }
    function becomeRichest() public payable returns (bool) {8 H8 ?+ H* w7 j" e$ `: x0 n" C
        if (msg.value > mostSent) {& l$ v, C7 L$ q& a& v1 @) v2 W
            pendingWithdrawals[richest] += msg.value;
' G3 L" r0 ?1 J0 X; F8 q/ H0 j            richest = msg.sender;
' \) u1 h% R2 ]) \            mostSent = msg.value;7 P4 g, ^) T6 H; w2 E) C/ t; D
            return true;
5 ^5 |+ Y+ h" U7 C, X2 C        } else {
1 x, C* O; y% i: L, P. V: b" k            return false;
+ U& q$ `, i- o$ V% B4 R; k' p        }
# \6 E, {9 F: x5 E- G$ U3 b1 Q    }
5 U: A! M2 g6 }- e7 R9 v    function withdraw() public {! w. u7 p+ |" r
        uint amount = pendingWithdrawals[msg.sender];: T$ q1 {' k. P/ H5 f& {
        // 记住,在发送资金之前将待发金额清零
6 S5 L# v3 H$ H6 o# v        // 来防止重入(re-entrancy)攻击9 `- {& ^: b/ w% a
        pendingWithdrawals[msg.sender] = 0;( Z; f) u) }, x1 C
        msg.sender.transfer(amount);4 p. o! W# q. [6 l
    }
% f6 k' x; n' y/ M/ h2 I3 a}
; K& V* `/ T( j3 T( u0 S( }下面是一个相反的直接使用发送模式的例子:" }# I6 h' }4 ~* A- f
::
2 l. }3 q' w9 o0 \% ^( ?2 N% ]/ R6 H$ vpragma solidity ^0.4.11;: V, }0 U, \3 C' _. I/ [% t! V3 R
contract SendContract {5 F+ y: W" i6 q4 V: D7 D+ p
    address public richest;6 Y/ d# x1 O4 \
    uint public mostSent;% ]2 X  L4 L9 t# A6 l
    function SendContract() public payable {$ B. t/ d3 u3 p
        richest = msg.sender;
3 y8 u( p( L) Z1 E        mostSent = msg.value;
' n. r, R, Y4 T. S6 }+ t    }
4 G: U1 x' U$ n* X    function becomeRichest() public payable returns (bool) {
/ ^$ i6 o3 U3 g/ T        if (msg.value > mostSent) {8 \: U4 [* A* J8 b
            // 这一行会导致问题(详见下文)
3 e" j. s% F  t: z- [            richest.transfer(msg.value);
2 |* g* q0 w% [9 v. c            richest = msg.sender;
4 c! U0 n3 V2 N' U! Q; _  f. O            mostSent = msg.value;
3 i! K" c* J: }$ ^3 t0 q1 p            return true;
% e& O8 j5 e  O! |1 w        } else {
+ @0 t$ a9 h8 b            return false;' d! f/ ~8 P" C8 V
        }
- |  I; F( b! Y  E$ q    }
2 K6 Q* `9 A9 B2 {2 K}
8 }  {( M4 R, I7 U5 u8 q% O) `9 V注意,在这个例子里,攻击者可以给这个合约设下陷阱,使其进入不可用状态,比如通过使一个 fallback 函数会失败的合约成为 richest9 w- {6 X- Z2 M
(可以在 fallback 函数中调用 revert() 或者直接在 fallback 函数中使用超过 2300 gas 来使其执行失败)。这样,当这个合约调用 transfer 来给“下过毒”的合约
' F1 [% P6 k' o+ \发送资金时,调用会失败,从而导致 becomeRichest 函数失败,这个合约也就被永远卡住了。
/ p* e' B; }. R) ]2 Z2 J1 v1 w9 U如果在合约中像第一个例子那样使用“取回(withdraw)”模式,那么攻击者只能使他/她自己的“取回”失败,并不会导致整个合约无法运作。; [/ X. F4 O, @" l; R
… index:: access;restricting
( z! W' x. Q' K2 \- X1 G" h# g! I5 {
" a, B, S' c% C) C  Z" R' ^  h限制访问
% C! ~0 R6 \/ F6 L0 P: o! V- p  V) u9 [% l, N% b) ^3 `( N
限制访问是合约的一个通用模式。注意,你不可能限制任何人或机器读取你的交易内容或合约状态。7 P, k+ Z& T# v# \# x6 X+ }
你可以通过加密使这种访问变得困难一些,但如果你想让你的合约读取这些数据,那么其他人也将可以做到。
. ?/ j/ {' `/ f! ]: T: T你可以限制 其他合约 读取你的合约状态。3 @" S  S4 g, h0 o
这(其他合约不能读取你的合约状态)是默认的,除非你将合约状态变量声明为 public。1 |* @3 F1 ~3 ]; w7 U" [8 c
此外,你可以对谁可以修改你的合约状态或调用你的合约函数加以限制,这是本节要介绍的内容。
; l3 U/ J# n8 F… index:: function;modifier
% u0 J1 w+ G' {1 Z2 R7 c通过使用“函数 |modifier|”,可以使这些限制变得非常明确。* h8 ?, ]) G+ V: E$ _$ t# T" Z
::6 \$ I2 t+ n# ?; k9 E9 T( d
pragma solidity ^0.4.22;
: B8 z8 R" G3 `% w* u* V; p$ scontract AccessRestriction {) s  ^8 `9 p. T
    // 这些将在构造阶段被赋值% G: u1 t/ V  U( w$ f" U
    // 其中,`msg.sender` 是
$ f8 c& ~9 t8 {, l6 o$ I. D    // 创建这个合约的账户。/ ?2 Q% G; \% @- X- \9 F: y* M* F
    address public owner = msg.sender;. h" E4 N0 Q. A7 l1 U: F
    uint public creationTime = now;
/ I2 ^) i* P" ]0 {  s5 @7 D8 P5 y/ o    // 修饰器可以用来更改2 K3 z4 n1 o5 ?. N
    // 一个函数的函数体。) k% ^9 T  P' W* K
    // 如果使用这个修饰器,: b. S& a; K5 p! x
    // 它会预置一个检查,仅允许2 m7 u* J( j6 u$ N' v/ m& d
    // 来自特定地址的
+ y. V5 N9 u. k/ Y+ {+ x    // 函数调用。6 q. U$ R1 U4 R3 ]3 Y% ^
    modifier onlyBy(address _account)
& t+ m$ d$ U5 A& P& z& p1 H) w- ]    {
* @& a  ?% {5 ?% P+ }/ o7 s& R/ ]5 h        require(% P$ E: f( ~4 q
            msg.sender == _account,
5 l! |" p- ?$ S0 V7 ?            "Sender not authorized."
- f* i: b5 [* M: D) B( W        );
% {' r& l) _8 b        // 不要忘记写 `_;`!
! j: s$ x$ Y" L$ f1 j        // 它会被实际使用这个修饰器的
' p7 x: z" N* ~, h6 ?" i        // 函数体所替代。
; a" y) Y. B# S; R) D. V        _;
" g' I: l) H8 \5 e    }
3 x" S5 c6 Q7 i' U+ p    // 使 `_newOwner` 成为这个合约的, D3 n( u3 Z. d% @7 f; P
    // 新所有者。
8 T" ], i" p4 {  a5 @& W    function changeOwner(address _newOwner)+ w' \( h' M% u3 Y- l5 q. b/ L
        public
. H' A5 D9 R1 z. K/ \: c5 z+ F        onlyBy(owner)6 s  E+ k8 _1 c9 T! y
    {8 m" {) Z+ y6 ?% H' f  a
        owner = _newOwner;7 _( t: j* Q. R( I4 A( u
    }2 }- Q5 A' A* O& Q+ m
    modifier onlyAfter(uint _time) {
) k8 s: y& d$ ], W  Q! Q        require(* S2 b8 x  L- A9 ]! z6 |) `
            now >= _time,( m$ p; v$ w+ |. c0 k
            "Function called too early."
- c3 b5 t% ^/ s4 e- {        );
. f% B- y, I8 Q1 ^$ z0 p  Q        _;
0 p# O& @5 L- i/ ^    }. O1 {& q) i* o9 V
    // 抹掉所有者信息。/ V" B$ x& f4 H1 I
    // 仅允许在合约创建成功 6 周以后0 s; [0 L; U- a0 A. z8 D) F" d+ Z
    // 的时间被调用。
4 k- [1 X/ z$ C" X9 P1 F+ O    function disown()
; d) }0 f0 p; H* S" X        public! b  A- ^! s1 [
        onlyBy(owner)* N" l8 C7 r+ W* a' |
        onlyAfter(creationTime + 6 weeks)1 L: @, O; D# R
    {
( {, x( M4 K4 \/ G- S        delete owner;: ^# a8 J5 W& S( p
    }* q: e: X5 o: I# T1 Q/ [  D
    // 这个修饰器要求对函数调用
! i; F" p# h9 ?    // 绑定一定的费用。: d" {1 Q: H9 {) v
    // 如果调用方发送了过多的费用,; o, p9 x# y- {) s+ L% ~
    // 他/她会得到退款,但需要先执行函数体。
; u8 X6 i% ?# w! o6 v( G    // 这在 0.4.0 版本以前的 Solidity 中很危险,$ f8 h% D' B( h
    // 因为很可能会跳过 `_;` 之后的代码。4 m* L5 Y1 f+ v+ h9 H* W; f$ l/ O
    modifier costs(uint _amount) {
; |8 g  S2 \; @& u        require(
% Y- i5 X% [0 W! {            msg.value >= _amount,
8 Z! |- B1 C9 d            "Not enough Ether provided."
: `* ?! g: j7 c8 N        );
' [' E& z2 M# e  t6 |3 p        _;# @( n, X& N$ b; y
        if (msg.value > _amount)
# j1 C7 i% d5 ]0 d& H7 u            msg.sender.send(msg.value - _amount);0 l- b1 _1 @. F  g( e1 ~
    }6 q9 c5 i5 O  p* L6 z
    function forceOwnerChange(address _newOwner)' ~8 Q; @2 Z; H3 U# w4 D
        public; z) J! |9 [+ t' {
        payable8 ]8 |8 t$ e: T2 X
        costs(200 ether)
1 {: c; {- E: i6 h1 u+ f; ^    {! ^! N  C; U5 t# U2 K) u6 ~
        owner = _newOwner;
- }) w0 H4 i1 U! q6 `) d0 H8 ~8 l        // 这只是示例条件5 {( J$ {. I- Q# ]
        if (uint(owner) & 0 == 1)
/ R( w% u. h( W4 C            // 这无法在 0.4.0 版本之前的5 \7 v' g3 {: D" Q' [( ^8 G7 k
            // Solidity 上进行退还。
4 x& q5 Y, v, q, v- G            return;
- E; i. \6 Q4 N) ^        // 退还多付的费用5 h9 X9 w$ A; ^
    }2 b/ _+ [5 m0 p8 i; P6 e
}
+ {; h2 \4 L) W# X* B' V9 g3 d一个更专用地限制函数调用的方法将在下一个例子中介绍。* S( E3 h( q- r9 ~- H
… index:: state machine5 B% ], p8 q6 [1 m3 I
1 J" c. C& z7 v' i
状态机" z2 q% g1 r4 r/ h

1 T- X. K( C- R3 ^) G4 O合约通常会像状态机那样运作,这意味着它们有特定的 阶段,使它们有不同的表现或者仅允许特定的不同函数被调用。; \: T( U9 }* m1 l. U
一个函数调用通常会结束一个阶段,并将合约转换到下一个阶段(特别是如果一个合约是以 交互 来建模的时候)。
7 P$ @  L. g, c( x+ G4 y6 W+ \通过达到特定的 时间 点来达到某些阶段也是很常见的。6 Z6 {: s  G, l5 D. v3 s
一个典型的例子是盲拍(blind auction)合约,它起始于“接受盲目出价”,
& d$ g- ~( m1 q  N. z8 \然后转换到“公示出价”,最后结束于“确定拍卖结果”。
0 j0 b/ w$ L9 f6 L1 I: x… index:: function;modifier0 j& m9 z" q1 {
函数 |modifier| 可以用在这种情况下来对状态进行建模,并确保合约被正常的使用。0 v9 `$ T3 W; D
示例0 ^1 k3 o6 c. P2 _' c
在下边的示例中, |modifier| atStage 确保了函数仅在特定的阶段才可以被调用。, T) R+ a) X4 M9 g9 V( ]
根据时间来进行的自动阶段转换,是由 |modifier| timeTransitions 来处理的,
8 X: L1 x  O8 k3 O它应该用在所有函数上。9 B2 S( K" L8 |/ v
… note::( v1 E8 X5 b4 j/ S" b
|modifier| 的顺序非常重要6 ?& D6 y1 g& n( F
如果 atStage 和 timedTransitions 要一起使用,
& ?3 a0 n+ g; |, F请确保在 timedTransitions 之后声明 atStage,& l7 u9 @  \9 u2 L0 W; _5 N
以便新的状态可以
; W6 w  Z( f* A- Y首先被反映到账户中。0 y2 Q  r  X9 B. e$ ?/ o/ l+ G
最后, |modifier| transitionNext 能够用来在函数执行结束时自动转换到下一个阶段。
. x: }2 P# ~: B" C- r$ Z& f/ Z5 c… note::
0 _, q( [$ r, Y! X# n|modifier| 可以被忽略* {5 u* d1 g6 Y' L1 M" o/ {2 L8 c
以下特性仅在 0.4.0 版本之前的 Solidity 中有效:
5 w+ V6 U4 Y# e, X由于 |modifier| 是通过简单的替换代码
5 T1 ?0 W4 B+ t5 L6 \而不是使用函数调用来提供的,
; ^* W6 A, E' a# `) h* s3 j如果函数本身使用了 return,那么 transitionNext |modifier|* B, t# D' i: e: @& P$ h  s
的代码是可以被忽略的。6 d/ b3 P4 }- r* ]- A+ U, S
如果你希望这么做,
  e) Y. U" c. D. u" j* o请确保你在这些函数中手工调用了 nextStage。3 v# @4 U* ~" j, F
从 0.4.0 版本开始,即使函数明确地 return 了,
1 u0 O  W" Q' R' @4 G: o" I% s2 j% o|modifier| 的代码也会执行。
' X; h2 E' k. Y' v4 C/ Y' k::0 U$ q, `$ S' ?; o
pragma solidity ^0.4.22;+ j* t: q5 N# V  v2 k- P& K
contract StateMachine {. \) c3 a8 g+ F( W5 b- }
    enum Stages {
- b1 \, X! _8 y: i( W. p- m        AcceptingBlindedBids,
- b3 O2 N+ m: L+ {% v. L6 G) A        RevealBids,
$ D" U1 s5 i# Q6 G" S  \8 S        AnotherStage,$ F) A6 k/ r3 C) c* P4 s# O: E1 f
        AreWeDoneYet,) K) B1 W9 n4 T0 N6 T
        Finished
9 U6 B! R! }0 Y) k9 ]    }! C& d7 O* S& I. x7 }
    // 这是当前阶段。7 t# U* X% v% B2 V3 u
    Stages public stage = Stages.AcceptingBlindedBids;- B# i3 R  g+ S9 [
    uint public creationTime = now;
3 t% U! z  \; q- M& \    modifier atStage(Stages _stage) {! k: g7 ?/ A/ R* Q  y
        require(7 K! I0 R+ c* s# I; a* e
            stage == _stage,
, s+ {9 {) g8 M9 H            "Function cannot be called at this time."
% i" m1 O0 Z) [& g9 C/ }% p1 N        );
  D% v) k; C# B% j4 c# N        _;
: o$ Q- C# y. h: M! f1 M. g    }* \  D  y' m7 C- N
    function nextStage() internal {
1 `3 n# h6 @3 [9 I3 H4 o        stage = Stages(uint(stage) + 1);
; R7 ~% Y" T) V6 u, ?    }& P( A4 c2 ~0 y4 |2 W$ }7 j1 L
    // 执行基于时间的阶段转换。- j' d! d6 g) O! E* w
    // 请确保首先声明这个修饰器,0 d5 H, p3 e, i, m
    // 否则新阶段不会被带入账户。( v% p% z! C5 W2 F0 W+ Q4 l
    modifier timedTransitions() {
! p. ~; y4 p/ u: g0 L" P% Q% ~2 ~/ z        if (stage == Stages.AcceptingBlindedBids &&% Y( I( D( U5 g9 y( S5 z
                    now >= creationTime + 10 days)
) @& @$ e! \. ]1 h- ^8 C            nextStage();
$ ]3 T2 q$ |5 o% o% y        if (stage == Stages.RevealBids &&
6 k& e4 @5 o' f  x) u                now >= creationTime + 12 days)" l2 s  W' T5 d% @4 ^$ q
            nextStage();  u3 u9 V2 x& K* a* }. x
        // 由交易触发的其他阶段转换6 d$ r; w$ B( R1 y, ~
        _;
. [  c* {& b' s! t# R% `    }
: _" e  A& k2 o, R* h3 \9 I3 ~    // 这里的修饰器顺序非常重要!5 P0 B( c2 `" w$ E& t3 M
    function bid(); @' d( F8 G' T8 K# y! m
        public" v' ^/ o" }8 m; W
        payable
, Y% o7 l. ]5 @( `3 N+ C        timedTransitions
$ C" ~9 m. U: w        atStage(Stages.AcceptingBlindedBids)
! h8 o1 r& T/ y7 P: P6 r. v6 G( ~% B    {& C2 y9 ^0 ^& K- i  [0 ^+ K& p
        // 我们不会在这里实现实际功能(因为这仅是个代码示例,译者注)1 W5 i' _  `2 w8 y$ I, {
    }
% x4 S( |7 k7 `6 V/ c9 p    function reveal()
9 v( ^+ |' Q8 E$ J( R        public
5 w' T, o9 w' O( {' ~; a& A: x9 \        timedTransitions; [# Q& p7 u% E4 O( _7 Y
        atStage(Stages.RevealBids)1 R* ~. F7 S7 E8 B3 U* G8 @
    {% z9 W9 Y9 E3 g2 o& w/ [+ a
    }! ~$ c6 \( V" [: F9 d5 H) A
    // 这个修饰器在函数执行结束之后
5 `, ^6 Y, I3 c( S2 F    // 使合约进入下一个阶段。
1 [8 J& ?0 S% @5 s- z- o* J    modifier transitionNext()) X: }- Q; |* e5 x
    {+ W2 r- Z# S4 l) |5 t$ W
        _;
' ]4 l' H/ b9 @$ G        nextStage();
1 T7 X8 b& w5 [% {    }) e9 m7 F* X" \
    function g()* Z: q% Q7 d# \. l4 k/ V8 z
        public' |9 d! ?: c2 b: B- r# Q% U
        timedTransitions8 `! n  J' N) ^
        atStage(Stages.AnotherStage)
9 C0 ^  O4 _7 a; l% H        transitionNext
: j  [, L& l# d  E9 o    {
+ s, k5 M9 ~" |1 H    }# `! C9 Z- U5 i5 Y/ R, h, p0 T
    function h()
1 S* |5 s* E8 s* F) Y        public* |# {9 f' }7 S$ f1 p( {/ Y5 ]2 Y
        timedTransitions
6 S9 c7 L9 o, v- {        atStage(Stages.AreWeDoneYet)
( K8 J$ b4 Z* t2 H9 ~$ x7 h5 W        transitionNext
$ p$ \4 Z/ _  i5 Y; {    {, [$ G7 Y6 J: S$ @# p
    }6 Q! Z' q9 @/ n9 V" w: ~. e
    function i()" ~% Q  ^8 u5 W; H9 W
        public
; A# A. `# B* j6 T        timedTransitions
3 `) H# M' d& k% g2 L9 a, l. d        atStage(Stages.Finished)/ P' z4 k- e  I, |0 ~+ J" x  t
    {8 V3 _$ z& g& H4 |8 M
    }
  _# Z" _7 c2 f# k* k}& Y) s# l3 O, O" m
原文:https://raw.githubusercontent.com/etherchina/solidity-doc-cn/develop/common-patterns.rst
标签: Solidity
BitMere.com 比特池塘系信息发布平台,比特池塘仅提供信息存储空间服务。
声明:该文观点仅代表作者本人,本文不代表比特池塘立场,且不构成建议,请谨慎对待。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

成为第一个吐槽的人

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

    0

  • 关注

    0

  • 主题

    13