Hi 游客

更多精彩,请登录!

比特池塘 区块链技术 正文

Solidity通用模式

温室小书生室d
371 0 0
从合约中提款
) b  ^" n! t+ G/ L; Z* M8 a
2 Q9 x# j( x: ^3 z  A6 p4 K在某个操作之后发送资金的推荐方式是使用取回(withdrawal)模式。尽管在某个操作之后,最直接地发送以太币方法是一个 send 调用,
; Y& c, N3 U) k8 y% X6 |, Z但这并不推荐;因为这会引入一个潜在的安全风险。你可能需要参考 :ref:security_considerations 来获取更多信息。
2 b' J6 B8 c- E6 o这里是一个在合约中使用取回模式的示例,它目标是通过向合约发送最多的钱来成为“最富有的人”,
, y* e! y; H& ?% R8 Z; j! R其灵感来自 King of the Ether _。
6 \- M9 v# j, J; M( i) x/ p在下边的合约中,如果你的“最富有”位置被其他人取代,你可以收到取代你成为“最富有”的人发送到合约的资金。
, r* {0 \* S" _% D3 U1 }::
* S8 I# s* Q. Y- qpragma solidity ^0.4.11;1 K. {/ j7 y/ R' [8 Q8 l
contract WithdrawalContract {
7 Y0 W9 ?1 l& l; H5 q    address public richest;
+ |: e4 O/ [7 |2 M. F    uint public mostSent;- x4 O% F/ n( o2 E3 m$ J0 A
    mapping (address => uint) pendingWithdrawals;
$ [0 O; n! J+ ]6 w7 f+ t' E    function WithdrawalContract() public payable {5 G# a* ~4 ]" I* p. v
        richest = msg.sender;  m: M) S: f& ?
        mostSent = msg.value;  W# e$ }$ N: B4 s$ K8 z
    }1 m. D  M" Z* o7 A% ]) P2 P
    function becomeRichest() public payable returns (bool) {- i4 e4 h4 U) ]+ [- d  z- s# @2 y% `
        if (msg.value > mostSent) {
; F/ F2 M" e0 w8 ]7 V7 p8 q            pendingWithdrawals[richest] += msg.value;
; ?( Q! N6 s) I6 D6 N. r            richest = msg.sender;
  L. d$ B4 y5 ]) c0 ?            mostSent = msg.value;. ^" n+ b+ c9 b( F
            return true;
) r: \& L% A6 X* n% D- a2 ?        } else {
! {, d+ V2 l$ S0 S0 f% k            return false;9 T$ f/ L9 V- w& x5 b+ _! ]- F, J
        }$ Z1 u2 g" F4 [1 u2 C! P* ?
    }4 o' {8 t; L" i: w6 U6 C0 m
    function withdraw() public {
) n. h, k4 m0 {5 x        uint amount = pendingWithdrawals[msg.sender];( ^* r9 J+ ~5 Y+ J( _
        // 记住,在发送资金之前将待发金额清零# a+ }! u' w( L/ _, [6 a& d
        // 来防止重入(re-entrancy)攻击  [$ t* x0 w2 D3 K4 L$ q4 k' O
        pendingWithdrawals[msg.sender] = 0;
  F0 d5 I) j& C8 D" }; U% c        msg.sender.transfer(amount);- s  W$ I6 \' o5 u2 w: H
    }5 l; ~3 _+ r$ ~
}% f+ I4 h3 H. p, j! P
下面是一个相反的直接使用发送模式的例子:/ A% l' q  T# `" p- E# z  S
::
  w2 x9 q* ]+ O  }. ?: Apragma solidity ^0.4.11;
- Z$ x% a9 r' }/ j; F1 hcontract SendContract {
& H4 B% f) a! B/ J    address public richest;1 u/ t% ?  @# X9 S) l) N
    uint public mostSent;
' {# ~8 K5 `  s6 E    function SendContract() public payable {
6 _# p" N: L8 R2 K% S        richest = msg.sender;: q9 V! R# c+ G" ?+ P) T' d
        mostSent = msg.value;; n& ]' o, j% q
    }
7 ^. c& D) M2 A    function becomeRichest() public payable returns (bool) {
) {& ]1 e0 G, S8 s        if (msg.value > mostSent) {1 h' L$ c; `6 {8 ~1 T, c; m2 Z. H
            // 这一行会导致问题(详见下文)7 B9 A' w# U0 o# u+ \+ G+ {
            richest.transfer(msg.value);6 K8 f! _/ F% h4 Z7 ]) n
            richest = msg.sender;
, z% M8 {; {( Y; v            mostSent = msg.value;2 o" `' G7 h* q2 T: ^, Z$ |
            return true;" Y9 R5 H8 t4 t2 R" `& y! b0 d
        } else {
' I7 O: h0 b" a! p            return false;
( F5 H6 h0 K$ ]& M, T        }
! `7 `$ \0 ?3 X( Q2 A" G* ~    }% r) j4 s& ?" f8 Z" V8 h4 w- k/ Z
}8 j9 _1 O4 c) C# C: N
注意,在这个例子里,攻击者可以给这个合约设下陷阱,使其进入不可用状态,比如通过使一个 fallback 函数会失败的合约成为 richest
4 N! y( f% o4 N. z0 R(可以在 fallback 函数中调用 revert() 或者直接在 fallback 函数中使用超过 2300 gas 来使其执行失败)。这样,当这个合约调用 transfer 来给“下过毒”的合约
) b/ D, d0 D1 j. a6 D" J发送资金时,调用会失败,从而导致 becomeRichest 函数失败,这个合约也就被永远卡住了。
, s% K* o  c; L* b如果在合约中像第一个例子那样使用“取回(withdraw)”模式,那么攻击者只能使他/她自己的“取回”失败,并不会导致整个合约无法运作。! t% _1 Y& a7 N9 D; ?
… index:: access;restricting: d% \% x$ K; x. ^& \; Y
6 m6 w. s; S  w! n3 ?
限制访问
* t3 y  ~7 |# ~2 J! j
8 l1 Z* ]/ T. O, Q% ]( ~. W% v限制访问是合约的一个通用模式。注意,你不可能限制任何人或机器读取你的交易内容或合约状态。/ E0 w9 c& @' h
你可以通过加密使这种访问变得困难一些,但如果你想让你的合约读取这些数据,那么其他人也将可以做到。
* J! k, h& P' X( I+ z) q* K9 i你可以限制 其他合约 读取你的合约状态。
/ ^  N; \7 p3 y* M这(其他合约不能读取你的合约状态)是默认的,除非你将合约状态变量声明为 public。6 \9 P- N" A# b4 J
此外,你可以对谁可以修改你的合约状态或调用你的合约函数加以限制,这是本节要介绍的内容。
8 c5 |( p* T0 J- H- S& [… index:: function;modifier
; }+ B' {- k* k3 J9 o通过使用“函数 |modifier|”,可以使这些限制变得非常明确。4 E' Z+ K2 H0 ?9 I9 o
::
# H$ J1 T4 N5 `9 n- \( M: }. q# B2 e9 Rpragma solidity ^0.4.22;5 S  k' g4 d$ v. E9 i
contract AccessRestriction {& {  [7 J. t+ D2 p, u" T  K- G5 w
    // 这些将在构造阶段被赋值
, K- ?0 r+ A- T  v) v7 [1 {    // 其中,`msg.sender` 是' }0 T: u0 `$ z* c+ _  F
    // 创建这个合约的账户。; W) v. u5 `9 E7 W
    address public owner = msg.sender;
& V$ X4 A$ q) F7 d8 K    uint public creationTime = now;: c5 B+ M" C1 d6 ~- z# X& v( L
    // 修饰器可以用来更改9 S9 w! n' o6 r7 @% E  f
    // 一个函数的函数体。
8 h: s/ u* m1 j" O! z  ?    // 如果使用这个修饰器,
4 l4 |6 K) s. }' g    // 它会预置一个检查,仅允许# ^# [; e( d6 b0 q
    // 来自特定地址的/ o$ v) k% \: L
    // 函数调用。
2 M8 O* Q8 w& x4 s- ?* A    modifier onlyBy(address _account)
" G9 t; |5 v' l    {/ W( K% {5 h1 x7 g# ]
        require() {; l% L2 Y3 _, Z
            msg.sender == _account,1 M$ z% t; O7 q  \- u5 j' |6 B
            "Sender not authorized."
, u) Q$ _/ k. ?* `        );. q. C9 l7 X5 C+ h
        // 不要忘记写 `_;`!: m5 M* L  W8 w3 \3 U
        // 它会被实际使用这个修饰器的% {3 }+ V# l& i* p
        // 函数体所替代。
7 l* t/ ]# F/ j        _;
2 w4 s9 p1 I/ ~$ p8 e+ B+ J    }6 ]5 A7 @. y/ U7 K* J; ]* ?
    // 使 `_newOwner` 成为这个合约的' S, Y: q. A# P* E3 W* B$ d
    // 新所有者。
* M' m) d2 u  ]. y    function changeOwner(address _newOwner): X0 w8 e6 @7 t. y
        public+ i2 M* g1 r$ I$ X) H; b9 `
        onlyBy(owner)
8 g4 H( A5 m3 ^& b: m! O* D* w, S    {& o' ^$ J4 E$ B
        owner = _newOwner;
! p* e) M' o' c0 n) A; G0 D    }
6 C  i" Q" k, D    modifier onlyAfter(uint _time) {0 Y7 i- T- z+ j7 Q2 T$ o% r3 G
        require(0 @  i0 a1 w) ^2 I% `
            now >= _time,0 H9 A4 x0 `& D/ b2 H0 N3 f
            "Function called too early."7 u! d$ Y2 @, X% E
        );  r- R! Y  W7 v0 x  R
        _;+ c" X0 Y8 p, s* j/ }' {0 L- ?
    }9 F, ?% ]7 I, ]  O( S# U
    // 抹掉所有者信息。
5 h3 O3 G/ c; k6 g& ^: Z    // 仅允许在合约创建成功 6 周以后
4 ]% [( ~* X% @0 Y0 ?9 z    // 的时间被调用。
3 d; K" O: D1 D& M* j: D  T    function disown()! b0 |9 u1 {% {% t( R' ~0 G- }
        public1 ]2 O: D9 s. o, h
        onlyBy(owner)
; i" z2 ?+ E9 w        onlyAfter(creationTime + 6 weeks)
# R$ [8 O1 i5 ^0 O7 K2 q2 H* f# R    {
/ n+ T% r; g0 ~& a. u+ |        delete owner;
+ }3 d  j6 T' h8 I# Y    }
  d# t4 Q7 B2 X" R    // 这个修饰器要求对函数调用
  w+ |" @( R/ I7 j% _9 K+ i8 I    // 绑定一定的费用。! g3 Z0 I  t$ n' u3 `* E6 x) _
    // 如果调用方发送了过多的费用," D) _2 k% U4 s. m3 R/ }6 G' W% H
    // 他/她会得到退款,但需要先执行函数体。5 C, \4 e9 _2 ^* \0 M
    // 这在 0.4.0 版本以前的 Solidity 中很危险,. p% Q; }6 H* Y7 y' d
    // 因为很可能会跳过 `_;` 之后的代码。  W2 f. W5 F" D1 j  ^4 @
    modifier costs(uint _amount) {
4 y; \, Z$ w6 Q8 O$ E% O        require(( i% u' j/ }# O
            msg.value >= _amount,
+ R  a# c; T' ]0 M. H            "Not enough Ether provided."
% Q: ?, g0 g" g! v2 ^1 l        );
' ^* y- \7 g: \        _;. g$ W0 l. t- t" Y8 W2 \; O6 N5 |
        if (msg.value > _amount)4 o8 e! a7 |# N. e4 u" x( K
            msg.sender.send(msg.value - _amount);
; f1 t* k2 P. c7 [    }
4 _) M- o; @2 m! F6 f    function forceOwnerChange(address _newOwner)
) I  k! h$ M, m        public
" S( e) {* ?+ t) ^" d        payable
2 O* \& R! P# S3 }" E! e0 s        costs(200 ether)% t' w' K3 K! z1 j6 S
    {3 Q/ `  ~1 ]9 v6 a! X( b
        owner = _newOwner;5 m/ p) z+ t/ r& d1 N# h( a+ a
        // 这只是示例条件; ]- h1 U; C- d
        if (uint(owner) & 0 == 1)' Z1 H- g) v; J7 Q
            // 这无法在 0.4.0 版本之前的
5 Z; d! K0 i- l  [' v* Z            // Solidity 上进行退还。% X( q) D; ]9 U" V* ?# U
            return;
5 W% j( Y% _5 S/ O) R, n, c! m7 k& \        // 退还多付的费用4 a/ b# Q, g0 L0 _
    }4 J) ~$ ]* g/ Y5 e0 p  t
}
' _6 \' j7 Y, y5 m8 W" q一个更专用地限制函数调用的方法将在下一个例子中介绍。: R; @+ D# l, w& I* ^" E- A
… index:: state machine
) X+ `* T. a+ ]& [& ]* V* J6 Q5 I% k' N) b
状态机7 G  w/ N8 v: |% Y
- X5 B' r; l! s/ f1 r" J! J4 T. n$ ]
合约通常会像状态机那样运作,这意味着它们有特定的 阶段,使它们有不同的表现或者仅允许特定的不同函数被调用。
5 R8 j* S2 ]  b, `. _' \  A一个函数调用通常会结束一个阶段,并将合约转换到下一个阶段(特别是如果一个合约是以 交互 来建模的时候)。
4 f* o/ z. i9 I1 Q+ S$ y通过达到特定的 时间 点来达到某些阶段也是很常见的。1 E% x) V: ~: _7 Y3 G7 J
一个典型的例子是盲拍(blind auction)合约,它起始于“接受盲目出价”,
2 }3 p/ q5 F7 e然后转换到“公示出价”,最后结束于“确定拍卖结果”。6 O4 H' L( J. `
… index:: function;modifier# O0 H) C" ^3 F
函数 |modifier| 可以用在这种情况下来对状态进行建模,并确保合约被正常的使用。/ K! u9 O0 i4 g& ]! n7 h5 n
示例, ?% H: D) p1 c+ Y. G7 [
在下边的示例中, |modifier| atStage 确保了函数仅在特定的阶段才可以被调用。4 x7 g& h, w9 _& @
根据时间来进行的自动阶段转换,是由 |modifier| timeTransitions 来处理的,
% s4 b5 M; G) A; W+ Q5 \: v( t/ F它应该用在所有函数上。+ V1 y" M1 a# X
… note::
0 \% i, r/ l* ]0 `. `|modifier| 的顺序非常重要7 {" I7 |- ~/ _9 X! O& X2 N9 u
如果 atStage 和 timedTransitions 要一起使用,4 T, K5 c( P) q% G
请确保在 timedTransitions 之后声明 atStage,/ H: _5 _4 v. y& p& [* I4 B/ W
以便新的状态可以
; [7 ~* _8 B- D5 n! h$ e) |; ]; H首先被反映到账户中。' V4 h" W1 A$ Y8 M! {, m: y3 T
最后, |modifier| transitionNext 能够用来在函数执行结束时自动转换到下一个阶段。: x! k+ U% S' _9 U
… note::% ]2 `& d) `& Z# A
|modifier| 可以被忽略
# {. t- K: e, V以下特性仅在 0.4.0 版本之前的 Solidity 中有效:
9 O. T* o* n, h+ O# N' \1 n4 U由于 |modifier| 是通过简单的替换代码7 K5 c2 l5 {2 ^1 b1 z* ^
而不是使用函数调用来提供的,, a- t# v% i- A$ ]8 e5 q
如果函数本身使用了 return,那么 transitionNext |modifier|
6 p+ c: f' ?& c) N0 H8 G0 E9 s, R+ ~的代码是可以被忽略的。, \! ]% x! S) A
如果你希望这么做,
! A0 S( O3 ]( H/ o5 y. M7 f: [请确保你在这些函数中手工调用了 nextStage。! i0 Z( Q% Y# {- E2 ]1 l& B
从 0.4.0 版本开始,即使函数明确地 return 了,
7 E+ U* T9 S! c3 f4 l; A* w|modifier| 的代码也会执行。( ~7 K' a8 L# x: w8 B
::/ c( G0 Q  [+ d; r
pragma solidity ^0.4.22;
" b. ]) R6 \" G6 y9 f- \contract StateMachine {+ R2 i. {% e/ b3 R6 |" B/ S8 `
    enum Stages {
" \+ u3 A7 b" N/ ^+ W        AcceptingBlindedBids,
- |4 n8 `% l9 l6 e# K        RevealBids,
. o" g. H8 m* c% n/ U        AnotherStage,
2 y4 U) z3 |; @! Q9 a- n- [        AreWeDoneYet,
3 x! r8 R! @1 a: m+ C8 C        Finished
" {! J! a7 C2 o9 G3 v' z; o0 C6 K    }
# f9 a4 q4 \, ?# c- }! y    // 这是当前阶段。" D! n0 |! w6 I3 R( I) Z/ b: }2 }
    Stages public stage = Stages.AcceptingBlindedBids;5 A6 V: K& F- U' {+ o
    uint public creationTime = now;
" g+ s. W, v* F0 B5 J    modifier atStage(Stages _stage) {8 ?  Q0 {1 ?. c+ c- N
        require(. V6 T$ O4 _1 ?
            stage == _stage,2 I) u; H5 c( d/ c6 ~5 ]  x: K
            "Function cannot be called at this time."0 x1 n% l4 T( l
        );6 p9 G  q, f1 q+ R
        _;' ^# @1 X$ {& z
    }4 L, i# M0 x3 z7 i; L) i9 t2 m
    function nextStage() internal {
, F1 k- I3 J0 ~        stage = Stages(uint(stage) + 1);
. k2 F' [/ }8 Y  ?" J    }
, d: ?- F7 C. u. ?  D    // 执行基于时间的阶段转换。" x4 z$ S, Q7 m2 |
    // 请确保首先声明这个修饰器,1 m; y* j: x% H* ^$ b" d
    // 否则新阶段不会被带入账户。. m  G; S6 x5 T) O  L4 v- w
    modifier timedTransitions() {% J: Q6 o0 e" B
        if (stage == Stages.AcceptingBlindedBids &&
" \" y# _2 \5 L/ [                    now >= creationTime + 10 days)
( e: ?* {- ]5 [1 B2 V; h- r) s            nextStage();/ W+ W+ ^+ X: N, Z$ A, Q# Q! L
        if (stage == Stages.RevealBids &&: Z4 z3 ^6 A5 j3 z7 Q. p9 q, p+ k
                now >= creationTime + 12 days)
3 p- Z! a8 s+ P) l3 I* |, Y( @3 @            nextStage();) x* d9 f) i/ {" M# N5 W
        // 由交易触发的其他阶段转换* k5 a, I+ W" S1 D' l; x
        _;
9 \4 D* A4 U, Z    }
0 s7 L$ ]# ^: C* d. i. |. x- n    // 这里的修饰器顺序非常重要!
9 G8 ~" e5 d* f$ f" ~    function bid()+ H! d( ~2 L9 v5 w1 u
        public
2 c  A, O0 V, G- o6 K, q        payable
8 \2 _6 i/ A  U, _; Z8 W        timedTransitions
' b- \. ^+ v9 }" O2 X- F+ [) K        atStage(Stages.AcceptingBlindedBids)
5 H+ R1 M* W- @2 N6 k4 C9 _, U    {6 j' `: x& i: a) M9 U+ J5 G9 _
        // 我们不会在这里实现实际功能(因为这仅是个代码示例,译者注)
/ ^0 ^* a( g- G# k6 H3 R( X3 x    }& d* r  f' `& x$ |) n1 v; Y8 x' r' j# C# C
    function reveal()
/ m6 @2 b7 y: w+ ~. N1 G        public
9 C: a9 ~; Z0 o* c        timedTransitions' I3 y$ z+ |7 V( K' b* ]
        atStage(Stages.RevealBids)
* n2 n, {6 a  y# O    {
( B8 _+ N. L8 _    }
! o0 ~) c" z/ ?0 }! i    // 这个修饰器在函数执行结束之后
# [. Y. j# r9 @    // 使合约进入下一个阶段。8 U3 j6 V: \5 a1 Y3 }* h
    modifier transitionNext()
& O- O) d5 i  q7 s" N; W0 K    {
( ^  M* \' L+ P/ N( |        _;- ^: `' L, n4 }- m" m* c
        nextStage();% G' q0 i% s7 p. }& v
    }
8 P5 ?; M" X( v: X* i& F    function g()
7 p+ B9 K4 F5 B2 [        public
( C, }8 q* j9 h1 v" `        timedTransitions
- X0 t! Y' l( N) p+ G        atStage(Stages.AnotherStage)6 s3 x$ s4 w1 b4 }* D* `- h) A4 v
        transitionNext
7 {0 K* f) U8 l6 W! D7 x( X    {& q, X& d9 Y* W& R, l
    }* h  C; R+ t* A1 \
    function h()
- Z' E( I: }& J        public* L; r8 v5 l; M- j; c' w
        timedTransitions3 g% b, r; s0 @" d
        atStage(Stages.AreWeDoneYet)
  D& t' i) u; _5 ~; U        transitionNext' U8 n6 W  E* t
    {! Q0 r$ k9 o0 |" P) w; a/ e5 I; W
    }8 ~( w+ B: U/ j
    function i()
% K; U* s+ r( t# l/ O4 K        public7 `% v6 _! }1 Y' v7 e% o
        timedTransitions, H; Q+ {: w; ]) X5 y' S% S" y0 A; g
        atStage(Stages.Finished)7 @; a2 ?4 h) m4 F5 U) g
    {
6 j% \0 \8 c7 ?6 |" a    }
  S5 R! V8 x( b+ A6 m- ?/ \}
- s5 h3 E; q5 q" a原文:https://raw.githubusercontent.com/etherchina/solidity-doc-cn/develop/common-patterns.rst
标签: Solidity
BitMere.com 比特池塘系信息发布平台,比特池塘仅提供信息存储空间服务。
声明:该文观点仅代表作者本人,本文不代表比特池塘立场,且不构成建议,请谨慎对待。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

成为第一个吐槽的人

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

    0

  • 关注

    0

  • 主题

    13