Hi 游客

更多精彩,请登录!

比特池塘 区块链技术 正文

Solidity通用模式

温室小书生室d
523 0 0
从合约中提款
1 {5 H$ y. ?# ?) Z
8 _; d/ p" }) r; g9 V: J7 H在某个操作之后发送资金的推荐方式是使用取回(withdrawal)模式。尽管在某个操作之后,最直接地发送以太币方法是一个 send 调用,% o3 f. p2 j- c# {0 c" H5 {
但这并不推荐;因为这会引入一个潜在的安全风险。你可能需要参考 :ref:security_considerations 来获取更多信息。
% V4 u) w  J6 }. @, w这里是一个在合约中使用取回模式的示例,它目标是通过向合约发送最多的钱来成为“最富有的人”,
3 }& H& C, p/ w- j6 J& b其灵感来自 King of the Ether _。: |. M9 J( a0 ]! O/ k
在下边的合约中,如果你的“最富有”位置被其他人取代,你可以收到取代你成为“最富有”的人发送到合约的资金。& z) }' C* _, b2 Q7 e
::- P& r( g0 I8 H# C+ `; s* L: b$ M
pragma solidity ^0.4.11;
6 ~* b: i% D; z. n7 M# Lcontract WithdrawalContract {1 }2 m- L& V, v8 Z1 @
    address public richest;! W: {" c; W6 j8 g3 e% ^- `7 V" M0 d
    uint public mostSent;  o4 Q: P4 ?$ Y, a
    mapping (address => uint) pendingWithdrawals;
* }, ^2 v( a0 V: L* F* U/ E- D    function WithdrawalContract() public payable {
% z- H. A1 j! O4 p9 d        richest = msg.sender;
# f; S. a  S0 Z- v$ p" E        mostSent = msg.value;3 G9 C6 a) ], |! p% J7 L
    }
* j7 f$ r( E% h$ I5 j    function becomeRichest() public payable returns (bool) {
% B+ b% K& o" \; W$ |        if (msg.value > mostSent) {2 M8 [$ b! h9 |, B  e
            pendingWithdrawals[richest] += msg.value;
- P2 w. W% K3 a# Z4 m. {            richest = msg.sender;. f) ^( T- L/ @& L
            mostSent = msg.value;
/ d' b* p* T. n+ l$ ?4 z  @& i            return true;
# _8 Y4 i' s; s6 W9 k8 g5 }( j* g/ p        } else {
! D+ T2 ?( R9 T5 L$ o3 R  z% L            return false;/ p4 l9 e; `/ o- F
        }) O6 w( z- F0 m( }# V
    }! n5 c* c; {- |' e
    function withdraw() public {
7 o0 p' Y% O" x% Q' y        uint amount = pendingWithdrawals[msg.sender];
7 C; j& v7 T2 e* P) i        // 记住,在发送资金之前将待发金额清零' X4 X- a3 k5 P! N
        // 来防止重入(re-entrancy)攻击3 n( s6 M! P0 J# R1 P% T3 F5 V
        pendingWithdrawals[msg.sender] = 0;
: F7 C- g, Y+ Q5 E  a        msg.sender.transfer(amount);
: V4 j3 C: }$ {0 N- g/ ]; U: P1 L# Y    }
4 |) L/ Q; {! m8 `; Z}! g" ^: ~# ]3 k1 G
下面是一个相反的直接使用发送模式的例子:% N! Y. k4 H. r: |* A: l8 S' x
::* `1 S- ]" c2 N' y6 b8 P
pragma solidity ^0.4.11;; r; ?; J7 p4 b% s9 Z' l1 z
contract SendContract {
. v* [! W4 e9 d3 u9 o0 W7 Q    address public richest;
( ~: I' t: C  y2 W, ]; v    uint public mostSent;6 L' `* @5 V8 j' y
    function SendContract() public payable {& W# M3 ~) A. G, G! t
        richest = msg.sender;
2 z  y9 O( S8 T. g' P. `        mostSent = msg.value;
0 G' ~8 ?+ F6 A" A4 |, U" W    }" x. N0 i0 E4 g8 r+ N! @/ b. U
    function becomeRichest() public payable returns (bool) {
, d. b+ E! s7 e' j        if (msg.value > mostSent) {
) a8 C0 a+ n" Y5 j* f! F- r# y9 H            // 这一行会导致问题(详见下文)
* ]) g' W1 c8 @            richest.transfer(msg.value);# S8 ]" }% \9 `& r& r
            richest = msg.sender;
7 r! @5 g* u( P" U9 [0 s" ^5 W$ A            mostSent = msg.value;* c5 }: ^& X& Y4 E  p8 a
            return true;7 x1 s3 X1 W/ e& E5 ]0 s
        } else {
/ q2 D. u  A/ j2 E) {            return false;9 {, [3 o6 b3 g) R' T
        }: o* k/ G+ e4 R8 G( t" A* y7 }5 Q# ]/ f
    }
) U  ~# o% M. M  g5 Q/ X7 Z}2 W: f3 _3 r5 }' E1 g6 y& a
注意,在这个例子里,攻击者可以给这个合约设下陷阱,使其进入不可用状态,比如通过使一个 fallback 函数会失败的合约成为 richest+ O" l2 L& ]) i
(可以在 fallback 函数中调用 revert() 或者直接在 fallback 函数中使用超过 2300 gas 来使其执行失败)。这样,当这个合约调用 transfer 来给“下过毒”的合约
6 \) \+ R. \( o2 \/ c2 V发送资金时,调用会失败,从而导致 becomeRichest 函数失败,这个合约也就被永远卡住了。
8 Q" P0 I6 Y( J+ c0 B# E如果在合约中像第一个例子那样使用“取回(withdraw)”模式,那么攻击者只能使他/她自己的“取回”失败,并不会导致整个合约无法运作。
- X+ L2 T5 @* _8 z) _! l" t2 e2 @/ A# M… index:: access;restricting9 n& [: a+ b6 O0 K
5 l3 V4 f' |) F* p" j6 v+ V# @
限制访问
4 l" M6 F# q, O0 W- i
8 f# q2 ]4 p% A: V( s限制访问是合约的一个通用模式。注意,你不可能限制任何人或机器读取你的交易内容或合约状态。
+ o3 g. e8 N+ I4 N  b: S4 m( z& U你可以通过加密使这种访问变得困难一些,但如果你想让你的合约读取这些数据,那么其他人也将可以做到。
, b) v$ T' V# B* Z# H) L' C7 ~你可以限制 其他合约 读取你的合约状态。
8 z  e  q- a) _% t( |' V这(其他合约不能读取你的合约状态)是默认的,除非你将合约状态变量声明为 public。  d8 m9 |' i# K! p7 n) o+ J9 T
此外,你可以对谁可以修改你的合约状态或调用你的合约函数加以限制,这是本节要介绍的内容。6 r/ D! t- l2 M6 i7 `
… index:: function;modifier  k$ O: _' E9 s, Y; E
通过使用“函数 |modifier|”,可以使这些限制变得非常明确。1 W: @! O* H" X; j) G
::
$ f/ ]8 b, g# X  R. S5 _) d; S( h4 rpragma solidity ^0.4.22;
( W8 Z& s9 Z7 q" v+ k7 Q0 Z, Ycontract AccessRestriction {
% |( }' x6 Y* `1 D' ^0 Q; i. q    // 这些将在构造阶段被赋值
$ S( r  x$ ]8 f9 C9 I% U    // 其中,`msg.sender` 是; h* @! r+ u" S8 X
    // 创建这个合约的账户。" e1 v$ ]! s- _1 T! Q) ^
    address public owner = msg.sender;
2 b& I2 x) Z" ?& C, h; P$ l    uint public creationTime = now;4 h+ d7 X2 k# ]
    // 修饰器可以用来更改% g9 f3 u' H9 T( R. n" }
    // 一个函数的函数体。; }- Z  J* D  z+ z
    // 如果使用这个修饰器,# R5 ^8 @$ d1 k6 O9 h, x
    // 它会预置一个检查,仅允许1 q7 j8 Z  a8 g# o2 q# Z/ Z: b
    // 来自特定地址的
* W+ X8 I) m. |* _' m! ^    // 函数调用。: O, u) Q; p" D9 K  g% P
    modifier onlyBy(address _account)5 L) Q# y  w$ @) j: o0 j5 H
    {
8 ~3 p0 p. I0 o        require(
4 s8 [, i4 h3 F/ N) i" D0 f4 D+ ]            msg.sender == _account,
6 k2 s( x+ \" S- W5 ~$ A            "Sender not authorized."
7 |9 T% J; f/ `4 C2 I5 x        );$ K! E8 t, t" C4 ?: T& W1 d
        // 不要忘记写 `_;`!
4 ^( p1 Y# @/ D6 B: k8 `8 D        // 它会被实际使用这个修饰器的! W; D# L0 [) Z! u
        // 函数体所替代。
: k& j$ w1 j0 |        _;1 t5 K0 m2 z8 I8 E" n! @9 G% l
    }
2 ~9 K: p/ K% b1 U" G    // 使 `_newOwner` 成为这个合约的) M5 D+ _/ q6 R. t) x
    // 新所有者。
. t% U: ^0 [, i    function changeOwner(address _newOwner)
; j: q& }) t" \        public
$ F4 R. {2 p( D1 y        onlyBy(owner)
# \2 \9 y. |, r3 y    {5 R; b/ y/ l1 z; R, k0 w  E
        owner = _newOwner;
, Y' T  s2 [7 g8 o: F" o. t    }  K# y" `3 i9 l9 W0 I* K0 M
    modifier onlyAfter(uint _time) {. A: C+ a2 h- J, A4 r
        require(
% a% N2 p4 _+ X4 Z7 N- B  o9 F            now >= _time,( t/ U" P( |# h& g& w3 V' h
            "Function called too early."
% v1 F- `- w0 f, J& C( @6 z/ {# \        );
, p6 _/ Z$ g$ f$ t4 m- X; }        _;
2 w2 @* s/ h; E+ l- q: P0 U    }
! P* _8 L: T" c, J6 v' ?    // 抹掉所有者信息。
1 y9 j' v2 h+ T    // 仅允许在合约创建成功 6 周以后. S, r4 x3 V! S
    // 的时间被调用。
$ [7 C+ M7 C; ]! o    function disown()& T; o! K1 P% r- f9 T% W
        public( c- a; M0 ?: H8 J+ B6 u
        onlyBy(owner)  V! v3 F3 ]: K: ]. p% i* d; Z
        onlyAfter(creationTime + 6 weeks)
. k( p+ F& N6 o5 U% R    {
, O2 w3 |" x8 l/ C4 v$ ^7 i6 W7 w        delete owner;
4 x9 y+ ^" f, Z+ |( R$ A    }
$ z$ y; i9 S% n4 K* n5 |! q: U  v    // 这个修饰器要求对函数调用
5 o+ x  D+ k% R: |9 ~% N' u9 L    // 绑定一定的费用。2 X7 Y, A+ S  d+ N: T
    // 如果调用方发送了过多的费用,
9 K- k# O/ A. U! h8 S9 E    // 他/她会得到退款,但需要先执行函数体。
) D. ~% q6 B$ `5 \    // 这在 0.4.0 版本以前的 Solidity 中很危险,
; w$ B# o! m  {" }- ~; B$ b    // 因为很可能会跳过 `_;` 之后的代码。
1 ?7 r; ?: z6 b    modifier costs(uint _amount) {& W0 N' D+ Y1 _: f- j4 F1 e
        require(6 B  }/ {2 C0 N& T
            msg.value >= _amount,2 X3 @: Q9 o  ]4 Q
            "Not enough Ether provided.") c, F# ^( l7 ~$ D( t/ R
        );
5 h! h8 k. {, P4 a7 t        _;1 w* L! \  u5 ]1 u, M4 T8 m$ C! w
        if (msg.value > _amount)* P7 s9 @5 D4 X" p
            msg.sender.send(msg.value - _amount);
; K  ]: Q1 W$ b  v7 x' r4 l    }" J6 J  j% V2 n5 e# O5 N
    function forceOwnerChange(address _newOwner); E. u* P8 @* ]9 G2 t
        public6 j7 y$ O& j( h2 s$ Y
        payable
# G. e0 k& p3 O- a) d        costs(200 ether)
5 g# \+ B7 x0 k5 m+ T( Q  z    {3 e/ E4 l* F) W7 F
        owner = _newOwner;
8 ], q- t5 y: S        // 这只是示例条件
8 V" Y$ g) ~% r" Z5 Z" Q        if (uint(owner) & 0 == 1)
6 {# V4 ^% d# x- _! q4 @& a8 Q            // 这无法在 0.4.0 版本之前的
: \, ?3 F5 s9 }: E7 T: X3 ~            // Solidity 上进行退还。
  @( E& A3 b/ v            return;% ]4 F3 `: R6 Y9 [5 I" b
        // 退还多付的费用
3 o3 y: v' O  Q8 ]; A    }1 I, z& r+ l3 b% o4 T4 X$ l
}
9 `6 J3 w( P% f一个更专用地限制函数调用的方法将在下一个例子中介绍。/ u3 s) P+ G7 e/ M% y% ~1 [1 l
… index:: state machine
" R. v# C  i5 f: o, m: P8 c9 C/ a5 S. c
状态机
8 b, q: w& c: ^1 C  G- S+ a+ }
/ k5 U( K$ k1 A; x合约通常会像状态机那样运作,这意味着它们有特定的 阶段,使它们有不同的表现或者仅允许特定的不同函数被调用。
0 P# |% `+ T1 W一个函数调用通常会结束一个阶段,并将合约转换到下一个阶段(特别是如果一个合约是以 交互 来建模的时候)。
: K' Y7 M* d# g* e1 R通过达到特定的 时间 点来达到某些阶段也是很常见的。' p( l/ X+ m' `! V7 ^- j
一个典型的例子是盲拍(blind auction)合约,它起始于“接受盲目出价”,
: }6 Y3 R2 i9 t! k4 T$ @5 o然后转换到“公示出价”,最后结束于“确定拍卖结果”。: y2 X! ]7 _5 n% ?3 u7 ]  `. |' P2 a
… index:: function;modifier, T: h/ @8 @& n8 E
函数 |modifier| 可以用在这种情况下来对状态进行建模,并确保合约被正常的使用。+ W* X" t& d' K* ?
示例* O* E- G5 ~5 W0 z: G$ Y
在下边的示例中, |modifier| atStage 确保了函数仅在特定的阶段才可以被调用。  i# _. w4 K4 k& y; x  e6 |# }
根据时间来进行的自动阶段转换,是由 |modifier| timeTransitions 来处理的,
0 b: `' T5 W9 E+ e6 l/ z它应该用在所有函数上。1 @2 N# k0 V4 i' h
… note::
3 _( K  J; u8 r. `1 l|modifier| 的顺序非常重要
6 F8 w% U& F- G! A/ b如果 atStage 和 timedTransitions 要一起使用,
+ u, H& Y# `1 k7 \. u9 S请确保在 timedTransitions 之后声明 atStage,3 q* A! P$ w1 H5 G2 F/ y
以便新的状态可以
! l2 j  ~$ O% x首先被反映到账户中。$ \5 }# j7 Q5 R+ h. f
最后, |modifier| transitionNext 能够用来在函数执行结束时自动转换到下一个阶段。
6 p- |6 G1 L0 C… note::
8 t8 c/ k3 {/ [( V' G' Z|modifier| 可以被忽略
0 ~$ B3 P8 b, ]* P& D以下特性仅在 0.4.0 版本之前的 Solidity 中有效:
1 {# k7 o4 `5 G0 g1 ~3 ]由于 |modifier| 是通过简单的替换代码  I; x3 P/ ^! o+ P& H$ E( J
而不是使用函数调用来提供的,
" Q' p! ^. I$ ?2 w$ t* Y如果函数本身使用了 return,那么 transitionNext |modifier|
. b8 j8 Z6 A0 U& `! T! l: L4 l的代码是可以被忽略的。
' w' |7 [7 x+ B2 |7 b  v如果你希望这么做,
9 B  x% m' R; |+ S请确保你在这些函数中手工调用了 nextStage。
( F9 H. x9 f  D5 ^从 0.4.0 版本开始,即使函数明确地 return 了,. J9 O, Q9 I5 q+ U( {' T$ n& l5 i
|modifier| 的代码也会执行。
6 y) j6 o& ^4 s) r! n& Z::9 j  P4 B8 j* s# F; {
pragma solidity ^0.4.22;
# O0 A7 a+ S7 N, l1 M. icontract StateMachine {* }9 _$ y5 n/ L- d. L. f4 d* t* b7 V
    enum Stages {
/ P( H/ K8 s' I$ v" o/ x        AcceptingBlindedBids,
+ b  ?1 L2 D; a3 `+ v  F        RevealBids,! Y" m8 L6 M+ y; E# w
        AnotherStage,1 ]2 O- [6 l$ t
        AreWeDoneYet,
# G- R8 E" H- M+ \/ g! B1 k        Finished
, s4 I8 V' P  e$ T3 Q# h    }
! H0 h' v4 {3 r) o1 a1 C0 ~    // 这是当前阶段。
- q3 ?: n. d. g    Stages public stage = Stages.AcceptingBlindedBids;; [1 @3 M9 J9 @/ T: X$ e" P
    uint public creationTime = now;, ]% @, k* Y6 G& F; W: v
    modifier atStage(Stages _stage) {% d' v7 H7 s0 c% V0 K
        require(
0 y/ ?( y# A8 o9 J  O5 F) r- L# _            stage == _stage,
& C9 v" q+ k3 R! M            "Function cannot be called at this time."
5 J9 T8 v+ C9 [* V        );
# m3 k% Y! E2 F; d7 r- S2 z6 M/ ^7 i9 T        _;
/ C/ c; ~) c0 J2 Q6 F! T    }
' H+ [$ ]) F# w6 S$ G% f( @    function nextStage() internal {
3 m: E. B6 N4 t+ G        stage = Stages(uint(stage) + 1);- p5 ?9 I: m$ Y
    }
: c: U1 E  h9 E# n3 T7 P* s    // 执行基于时间的阶段转换。
( b- L1 M* f- }+ i/ T. p2 t    // 请确保首先声明这个修饰器,
: x% Y6 \& N8 J3 j: W" ~* R    // 否则新阶段不会被带入账户。' Z5 ]9 k& F/ G( ]. [4 B8 o
    modifier timedTransitions() {
* Q" G# z7 h* p) ?9 X& U" `        if (stage == Stages.AcceptingBlindedBids &&
$ a# Q9 d; _+ s                    now >= creationTime + 10 days)
' U( ^% p: x5 u8 y( u8 K            nextStage();
1 i" I$ d& Q3 O  j        if (stage == Stages.RevealBids &&4 H* u" r  `9 P! o$ P  J  }9 j
                now >= creationTime + 12 days)" G/ t. L. g" v( Z9 P
            nextStage();+ b5 o- T) Q$ H/ e8 w2 d( r( D+ l
        // 由交易触发的其他阶段转换
( n* d/ g9 U7 E, u$ i7 P7 \        _;
1 u( V9 ]0 d  ^; G; E    }* C+ ]  r( T/ N2 P
    // 这里的修饰器顺序非常重要!
8 ^. p# x7 |8 a6 L/ e+ t    function bid(), U3 K/ Z. d& {8 Y
        public
: Q: M5 P. N9 n0 z$ x        payable
' q( n  B' o& j6 t        timedTransitions+ k- X. Z$ m5 U# U7 b& h$ ~
        atStage(Stages.AcceptingBlindedBids)
6 ^& R' X4 a1 }# [    {& ~5 y2 \9 q, R+ R& X" q5 D& E
        // 我们不会在这里实现实际功能(因为这仅是个代码示例,译者注): }# V/ N4 \  G
    }
* k3 @7 O$ Y7 P4 t/ F    function reveal()& E  W3 Y8 X- k. E
        public
+ f) x% ?3 B: W4 y+ k        timedTransitions
7 s9 Z! s  R4 m0 Z+ G        atStage(Stages.RevealBids)
% l, ?+ ?1 d' ^9 c# B9 ?" G    {
/ R9 `8 r9 O, U# t  ]    }* ?) {% t! T7 d; @8 T8 a
    // 这个修饰器在函数执行结束之后
1 d6 @5 i7 Z- N3 c% c. X    // 使合约进入下一个阶段。% P- n0 j& d; ]
    modifier transitionNext()* z, r" `7 g$ [5 z6 O" ~% ]
    {( n2 l. R. D, A3 z. Z4 I( a9 P
        _;
3 j4 c, a; m# _5 C$ f2 z, v2 L) Q        nextStage();$ o) j8 z* t* c: G6 g8 b
    }' f" ]0 |" O# p: u/ i
    function g()
4 l6 T5 h$ l% z( ]: |1 N& f0 x( p# |        public
% l* t' \" a0 A3 ]        timedTransitions
: ]0 I+ Y" _3 h        atStage(Stages.AnotherStage)
, W6 g. K( ]/ g3 O6 _1 T$ X& [9 W        transitionNext6 v' z% N9 I: ?: ?3 Z
    {
" L1 i" f5 D0 O& W$ o    }" u4 N3 L% X- k% u- R5 V0 e
    function h()5 t; W3 X! g" }% O- t8 t* b+ W
        public, M% _; U; s! `7 ]$ W
        timedTransitions
$ Z0 j" i! Y- _9 T        atStage(Stages.AreWeDoneYet)
/ X8 _& I4 T4 w! J4 P, \3 p        transitionNext; w, P6 t' [" u! x$ F
    {% Z% |$ w  F/ o. s
    }; [7 h( Y' |1 C  z: Z, k/ c
    function i()
# O. _: A2 \3 U( {' c        public
" G6 F- Y- v7 O& y5 n        timedTransitions8 U& f( ?1 q% J/ [+ w
        atStage(Stages.Finished)
) E& o' {( U& p# r' T' {  r    {& @$ I9 R( n" V. }: f
    }
% u6 s1 b/ y: W' V1 F& B: e" D}
, ]' t; V( Y7 d# i) p5 Q原文:https://raw.githubusercontent.com/etherchina/solidity-doc-cn/develop/common-patterns.rst
标签: Solidity
BitMere.com 比特池塘系信息发布平台,比特池塘仅提供信息存储空间服务。
声明:该文观点仅代表作者本人,本文不代表比特池塘立场,且不构成建议,请谨慎对待。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

成为第一个吐槽的人

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

    0

  • 关注

    0

  • 主题

    13