Hi 游客

更多精彩,请登录!

比特池塘 区块链技术 正文

Solidity通用模式

温室小书生室d
486 0 0
从合约中提款3 W: d; v; `7 \4 m8 X
# p, Q- C1 ~# F
在某个操作之后发送资金的推荐方式是使用取回(withdrawal)模式。尽管在某个操作之后,最直接地发送以太币方法是一个 send 调用,
% Y7 k. ~" Y2 ?" X, z# t但这并不推荐;因为这会引入一个潜在的安全风险。你可能需要参考 :ref:security_considerations 来获取更多信息。
- P9 S; k% \$ C9 |4 o这里是一个在合约中使用取回模式的示例,它目标是通过向合约发送最多的钱来成为“最富有的人”,
  i4 j8 ]0 q# c3 @2 f其灵感来自 King of the Ether _。, C5 e4 ]! P$ Q  }7 d
在下边的合约中,如果你的“最富有”位置被其他人取代,你可以收到取代你成为“最富有”的人发送到合约的资金。  A: z: d' Q' N. }0 b  i
::) G" |- W( w) G9 i6 g9 s! P
pragma solidity ^0.4.11;# D7 z* K& D$ j6 L3 ]
contract WithdrawalContract {
5 G+ j3 O0 J" v9 N. o    address public richest;
; l/ T/ Q: X) Q  ~- ^7 J/ c    uint public mostSent;  [8 h/ K! x! y
    mapping (address => uint) pendingWithdrawals;9 A5 f7 T1 e* N0 S/ V  `7 z3 Q
    function WithdrawalContract() public payable {
1 s; Y9 a% Y: q- G& K2 f) @        richest = msg.sender;* T  x! Z" r; t8 l
        mostSent = msg.value;
" O4 s: U% K% n" _4 ^# q    }, D+ G0 C3 p) @+ n
    function becomeRichest() public payable returns (bool) {% j2 E" P7 {( j8 k3 P3 B/ M0 P
        if (msg.value > mostSent) {; z$ y/ z* t  c& {4 K5 N
            pendingWithdrawals[richest] += msg.value;! c% T5 {0 H0 h$ j; M% ?
            richest = msg.sender;
" k- K, a) m7 u7 d% e- ?            mostSent = msg.value;
1 Q7 t' A" O2 N% ^/ o% D) _            return true;! ]% h$ x, ?" x, ]: p
        } else {
( j9 g8 B0 |  j/ x' z            return false;- I7 |+ R9 d! d2 n- ~+ B8 e  c
        }7 V5 r: }2 B$ t
    }
3 s6 `. ^" b" d/ m% x" s! g4 X2 M0 l4 ?    function withdraw() public {
- k' r- U9 k4 D! v! ]% i% \        uint amount = pendingWithdrawals[msg.sender];) V5 F: _1 d% U, S
        // 记住,在发送资金之前将待发金额清零
' k; f4 t: F4 [5 l9 e$ _0 {& ]        // 来防止重入(re-entrancy)攻击
9 e% l3 R: I0 g& A$ P        pendingWithdrawals[msg.sender] = 0;( e' @# l2 r2 D+ U
        msg.sender.transfer(amount);
4 A& G: J, {$ N$ \& H- H* Z- ~7 ^    }
& B, \  ~5 n$ W) O# G/ d0 R}8 p& a& u6 e; B" C# P
下面是一个相反的直接使用发送模式的例子:
& @" a# v# O9 I% J) C4 L5 j" M3 e9 q::8 o0 m0 o. S3 t: a' O+ X4 _0 c3 _2 F
pragma solidity ^0.4.11;
: _8 r# {- _- d8 b3 Ncontract SendContract {4 W/ t1 o( H" Z& u: ?* Y- N3 v
    address public richest;
: y" J! x% c: p- a    uint public mostSent;
5 U, t: ]' k" L" ?    function SendContract() public payable {
: n, k& ~1 u4 J  b: \        richest = msg.sender;
/ |% l' ^( \% O- p& d1 }  O        mostSent = msg.value;7 V2 [: Y* h8 f  [& p8 v
    }
; G4 B# e$ n: H7 C. p    function becomeRichest() public payable returns (bool) {' N# B% q/ B$ W% v/ [/ z
        if (msg.value > mostSent) {
+ a" B: @; v4 x. F- j            // 这一行会导致问题(详见下文)2 \' z1 M2 |8 U) [7 \- Q) J1 I0 t& h
            richest.transfer(msg.value);4 J5 h0 u2 V' f
            richest = msg.sender;
) U' n& j3 O+ K' o9 R) b) g" y            mostSent = msg.value;
# K% z% b* C# B& Z( x            return true;
: R6 D, ^: @. L# P3 L* ?8 [        } else {
. ~7 E' i; a1 x! K1 e! P# L" f            return false;( m: a) `: d! v( }6 \' S! D& ]& C/ j# D
        }$ ~. Q, l4 D' v% r( e0 X) y
    }
( C! O) h. y- g5 s+ h}4 e. Y/ K* W( K. a3 z; `. Y
注意,在这个例子里,攻击者可以给这个合约设下陷阱,使其进入不可用状态,比如通过使一个 fallback 函数会失败的合约成为 richest2 ~6 a. m' U0 ]! G8 ^# K1 V
(可以在 fallback 函数中调用 revert() 或者直接在 fallback 函数中使用超过 2300 gas 来使其执行失败)。这样,当这个合约调用 transfer 来给“下过毒”的合约
. }4 \% R* d4 v0 y7 W% M3 V发送资金时,调用会失败,从而导致 becomeRichest 函数失败,这个合约也就被永远卡住了。
! j1 w/ b8 B! W' c8 j& }5 c如果在合约中像第一个例子那样使用“取回(withdraw)”模式,那么攻击者只能使他/她自己的“取回”失败,并不会导致整个合约无法运作。
; ?) Y2 W! u: Y' E5 D… index:: access;restricting
* u/ M; I; H2 V' @; i6 n. e) E: Z) j) `
限制访问- X  H0 z) _$ _! P7 |( o" @

; K: C7 i3 c( Q( t+ s' ?8 @限制访问是合约的一个通用模式。注意,你不可能限制任何人或机器读取你的交易内容或合约状态。7 J" i/ {, I* D. K
你可以通过加密使这种访问变得困难一些,但如果你想让你的合约读取这些数据,那么其他人也将可以做到。
* S" H( {8 j7 b) T  m! n$ c3 q你可以限制 其他合约 读取你的合约状态。  c& D) F, @# J8 C
这(其他合约不能读取你的合约状态)是默认的,除非你将合约状态变量声明为 public。
( [9 B% [# ^# p此外,你可以对谁可以修改你的合约状态或调用你的合约函数加以限制,这是本节要介绍的内容。+ J; Z% I! f- T, V
… index:: function;modifier1 |. J  y- W+ ^* Z2 x" Q
通过使用“函数 |modifier|”,可以使这些限制变得非常明确。
4 ?+ M: A5 d( A3 S, a::
; H5 u: {2 _, ~, v. gpragma solidity ^0.4.22;
0 r' v# x6 I8 Z* Pcontract AccessRestriction {6 M- c+ E; s, w5 G8 w
    // 这些将在构造阶段被赋值: ]2 e, ?8 m8 M+ w$ p
    // 其中,`msg.sender` 是
( D+ D- \( m/ X  C* x    // 创建这个合约的账户。
+ G. u0 T) J6 |' k$ y& `    address public owner = msg.sender;
/ G: C0 E; \$ P2 ]% w    uint public creationTime = now;2 u: @: q3 n- Q# K4 f
    // 修饰器可以用来更改2 j0 ~- L+ ?4 E3 z3 q( L, d3 p# F7 W
    // 一个函数的函数体。7 K/ i9 ^& e; Z# }
    // 如果使用这个修饰器,
/ U& W' J( ]% o. W* \    // 它会预置一个检查,仅允许' E" U5 j6 C- u1 O- r7 P9 m
    // 来自特定地址的1 o6 b+ a8 {) X: A: g
    // 函数调用。
% H+ g  d/ w3 |3 k; D7 K    modifier onlyBy(address _account)
5 |( a$ N8 M# I6 C7 O. v& g    {: x) k; B9 k3 _& w4 n4 N% G
        require($ A$ D0 k3 S3 C* u
            msg.sender == _account,
7 K( W" i* Q, R- k# N; h/ l4 B9 B            "Sender not authorized.", L. ?. q$ o2 s( a$ F# Z
        );
/ c" ]1 M# ^5 X4 Y        // 不要忘记写 `_;`!
* O! y. G: l) T& \        // 它会被实际使用这个修饰器的) ~  H4 m4 R( A5 g
        // 函数体所替代。
* ?  J  y8 D0 P3 }5 s( P        _;, v8 u1 ^: O$ ~- ?% Z/ |" S. ]
    }
/ Q2 G. J( o* `) M    // 使 `_newOwner` 成为这个合约的' e; A* Y8 ]! H$ E2 g5 K2 O
    // 新所有者。
' z5 q, T5 r% |    function changeOwner(address _newOwner)
/ V  s6 C1 g0 z  @+ c$ V        public
& _8 `! V% U/ e        onlyBy(owner)1 ?  R9 h; n! J1 l- n8 g
    {9 L/ Q, O0 n% {( d- g+ x
        owner = _newOwner;
7 T6 r/ H* a* w8 y+ _" P- D$ ~    }/ i9 L* C; v' R! x8 M
    modifier onlyAfter(uint _time) {
  J( X" b6 h; k8 F        require(
& V9 L6 i# a) q, O            now >= _time,
" L7 d( c0 f- G! }) T) R# l1 ^            "Function called too early."
' U5 d4 {7 B. t7 C8 {4 u: D" z) H        );
  z% n/ M8 E, [1 W$ K# A6 V+ X        _;+ B  O5 G- G/ i) a+ p0 i" m
    }
  L4 {! }/ d+ b. Q4 C9 M    // 抹掉所有者信息。+ G0 c9 [6 Z- d! l3 p
    // 仅允许在合约创建成功 6 周以后8 |* p8 H. m& L- N2 T" g
    // 的时间被调用。$ o+ _' f$ R: ~' [* X3 ?
    function disown()$ x/ \$ D5 h; q% ^: i
        public( x. Q( x5 ~$ V" A* a; v
        onlyBy(owner)
* B) u1 `) d8 }7 _0 F        onlyAfter(creationTime + 6 weeks)
' ?4 Q. N6 U' |( l' @$ f    {- {; `; B9 r: c9 |
        delete owner;
( o+ J/ c6 y: ^    }
; d/ o! Z5 ~7 S! e    // 这个修饰器要求对函数调用
; Z! ^: A" b" g    // 绑定一定的费用。
+ E( k: a+ j3 b! G2 t: C8 A    // 如果调用方发送了过多的费用,- x; w# n8 E( I& n, F
    // 他/她会得到退款,但需要先执行函数体。
6 [! q" g& K( s' R+ L5 @    // 这在 0.4.0 版本以前的 Solidity 中很危险,6 d" ?  T; C! |/ M) W+ S
    // 因为很可能会跳过 `_;` 之后的代码。
' n3 p2 U$ E' L7 `    modifier costs(uint _amount) {
$ {8 O  r* h1 s% Z        require(
+ S9 x2 |. o3 e9 Q' h            msg.value >= _amount,$ W3 e) n  G3 T
            "Not enough Ether provided."' ~, S' s, `+ {$ |# n0 y
        );
1 d' V& \1 z8 C& t. ]4 e) w        _;
; ]' U! ?' {! K        if (msg.value > _amount)
6 p! x1 u% v& _' \6 ]            msg.sender.send(msg.value - _amount);$ I; l4 a7 K  f0 N; T% {* D4 d! c% {, I
    }, H8 x; {4 J5 v! y% }
    function forceOwnerChange(address _newOwner)/ P6 _! f- Y- q% V& ^
        public: P% K) X5 g) y( A8 o. l
        payable
5 U2 X# b9 ?3 V* K        costs(200 ether)8 g+ n, W" ]2 a! v) E$ _
    {
$ z( ?6 {( e" [3 w1 M  @        owner = _newOwner;2 Q/ n4 ~2 R: K: ~4 U- C
        // 这只是示例条件1 G. Y  G& J7 u
        if (uint(owner) & 0 == 1)
0 P: s8 ^) \8 ~) J( {7 E8 k6 v            // 这无法在 0.4.0 版本之前的: _6 X7 b  ?1 A
            // Solidity 上进行退还。" N/ Z; c# X# e1 e
            return;$ N2 ~) I7 k8 |
        // 退还多付的费用- Q" F$ S- x1 L! n
    }" @" k% R0 Q5 u2 y/ v  P1 s( g
}6 v0 O9 Y0 t4 D, [) V' l  A( e. p
一个更专用地限制函数调用的方法将在下一个例子中介绍。
8 H! h3 \3 {& [/ C, i+ R; w1 e… index:: state machine
) ^7 U2 L- }- Y6 v1 c3 k
8 U% X' R" Q( H- }1 ~: ]& N状态机+ J$ J; [- e0 W% J! ^
# U# `- o* a) O# I; l8 h' p
合约通常会像状态机那样运作,这意味着它们有特定的 阶段,使它们有不同的表现或者仅允许特定的不同函数被调用。
+ {" L* E9 Z1 X- R一个函数调用通常会结束一个阶段,并将合约转换到下一个阶段(特别是如果一个合约是以 交互 来建模的时候)。5 C  s3 N' O  Z
通过达到特定的 时间 点来达到某些阶段也是很常见的。' v* G4 u3 `9 M2 t# b
一个典型的例子是盲拍(blind auction)合约,它起始于“接受盲目出价”,/ w  ?7 Q$ g5 N2 K
然后转换到“公示出价”,最后结束于“确定拍卖结果”。  }: A8 Q: e( ]% H0 x$ D
… index:: function;modifier+ h9 ?* w3 Y* X
函数 |modifier| 可以用在这种情况下来对状态进行建模,并确保合约被正常的使用。
- P9 E& v0 X; _6 ?& h示例
, \2 E2 f& Q5 V6 r: C2 V在下边的示例中, |modifier| atStage 确保了函数仅在特定的阶段才可以被调用。* a3 l/ q! U$ Z0 |. N; e
根据时间来进行的自动阶段转换,是由 |modifier| timeTransitions 来处理的,% s* d# p* ^2 y3 B1 y! L% ~& W( M
它应该用在所有函数上。9 @7 p" K0 w2 I7 [
… note::! J; E. A, u  d
|modifier| 的顺序非常重要
3 s6 P& @$ x4 \7 p& F, F& k如果 atStage 和 timedTransitions 要一起使用,9 A% s& A7 f, a& K. G4 B
请确保在 timedTransitions 之后声明 atStage,) v. x5 C6 j/ f7 n, _* z
以便新的状态可以
6 Z& s- f- T$ J- @0 c6 k& _首先被反映到账户中。- G- k1 O& x1 [& _
最后, |modifier| transitionNext 能够用来在函数执行结束时自动转换到下一个阶段。
+ q, Y- l$ k1 K# Z: p9 g… note::
9 B$ y$ F" ]* \8 v+ }0 k|modifier| 可以被忽略
3 V* q3 D) [4 J9 f/ S以下特性仅在 0.4.0 版本之前的 Solidity 中有效:! E+ Z0 J" W3 j7 m( N
由于 |modifier| 是通过简单的替换代码
2 x  C3 r7 G2 n7 a" S1 i2 ^而不是使用函数调用来提供的,& j6 b  U: e/ t  q: W
如果函数本身使用了 return,那么 transitionNext |modifier|- C1 X1 y" r7 |5 Y+ \$ O8 X
的代码是可以被忽略的。
0 H9 W& ?" ?  |. u6 F如果你希望这么做,
- v7 V1 d! j( e* f请确保你在这些函数中手工调用了 nextStage。
+ S6 P4 d  V3 p4 d- D2 L& m从 0.4.0 版本开始,即使函数明确地 return 了,
$ }+ ~' C  Z. q+ g0 k% w|modifier| 的代码也会执行。
. k5 p5 H/ u( ^) b0 T6 Y8 k) |::
7 t3 u( E4 f- |, q5 _pragma solidity ^0.4.22;( c( e+ D9 q8 z) o' \, d# z) K
contract StateMachine {
2 f+ i9 K$ R9 R! K( ]/ A# P6 \5 Y    enum Stages {4 w; i  t; I3 O$ a- E* n" Y% b
        AcceptingBlindedBids,3 J2 N8 q8 P5 B2 y) P
        RevealBids,, r8 N; G; |' z
        AnotherStage,9 i! A) _. J  p  e" |% Z' w
        AreWeDoneYet,
- b& Q/ y$ \4 G7 Y        Finished
7 o: a, ]5 h- W+ J    }
# @$ Q1 z" H  ~+ n    // 这是当前阶段。
. h  ]$ p$ P8 M, w" f    Stages public stage = Stages.AcceptingBlindedBids;
; z, g, O6 ]' Q* I/ K( }    uint public creationTime = now;
! P9 C+ _0 ~6 f: P. k    modifier atStage(Stages _stage) {
/ g: X1 S8 R. ^. u) O. N7 y        require(
+ j! a, b( l0 B/ |% Z  |+ Q/ g            stage == _stage,
; n5 R$ Y2 M/ q            "Function cannot be called at this time."3 i  H! B( }) O# C' [3 s0 N- J- Y
        );
* K2 S3 u( o# ^        _;
8 Y1 n1 g5 o7 Q" k  K1 n    }
$ ]2 }/ ]/ A$ i5 O    function nextStage() internal {
( u& X+ ^* t8 @0 [        stage = Stages(uint(stage) + 1);9 D$ R# L; |7 N1 m
    }; `2 W/ s- @' e9 E! |4 c" ~; d: C/ P
    // 执行基于时间的阶段转换。
; S# E; {0 z9 H( ~8 Y4 n& ~8 r2 i    // 请确保首先声明这个修饰器,
/ Y) K$ V' Z+ q0 o    // 否则新阶段不会被带入账户。8 Z% a2 o& r% D  G& n
    modifier timedTransitions() {
, `, X/ F1 K: `1 S% t3 ^( S5 H7 I$ k        if (stage == Stages.AcceptingBlindedBids &&
$ O4 Z4 I+ S$ D0 g                    now >= creationTime + 10 days)2 @) Q* C9 {3 b. }5 D
            nextStage();
2 t. i. f% V6 o1 y        if (stage == Stages.RevealBids &&
% x: ^* W' K8 m# W+ A                now >= creationTime + 12 days)
/ o! d2 c1 {% x, U) m            nextStage();" H; c- q$ i' U
        // 由交易触发的其他阶段转换
1 ~" g- D! y; `/ i# w        _;
) y& n' S4 N! k! S, }4 u* A    }
, g* k) l- e$ F  E7 e  p! E2 h! X    // 这里的修饰器顺序非常重要!% ]' `- ~" m7 a
    function bid()5 p1 ?" u- I$ a% }; D
        public
2 ^# t  k3 r6 W2 \        payable
  ?0 G- z5 r5 V  ?: O; `) f9 @) h        timedTransitions8 F5 _7 O" C% J( a3 w$ l
        atStage(Stages.AcceptingBlindedBids)8 G5 t$ X* W1 z( B6 N8 e) J4 I
    {) x/ k0 a$ N4 u+ H+ |
        // 我们不会在这里实现实际功能(因为这仅是个代码示例,译者注)
# H2 z2 x4 E! ^8 H* V. E1 V    }# e; z! h1 u! X2 }
    function reveal()
  P* ^' |7 O7 Y6 W0 `, v5 M( w  E+ u        public
# g+ K* `9 ^2 Q  x# O! z5 ?        timedTransitions
  C1 ?" D8 s# v! W        atStage(Stages.RevealBids)
9 l+ }0 J7 Y# }" N    {% ?+ N, O5 _: H9 K+ i7 L- D2 g
    }! c) W6 v" g# f$ h* n- V* \0 H. k
    // 这个修饰器在函数执行结束之后2 b( \0 _% O3 C, T( v+ u  P7 ]
    // 使合约进入下一个阶段。
& l7 ^& Q1 m. ^3 E, n. h3 m    modifier transitionNext()7 D1 Z$ U& t( X" l" M& X4 u: T
    {" L  {' Y" p- d5 {1 k% y
        _;
* L5 R5 G% R( [/ J        nextStage();
0 z. @( D, T% T  _5 C# t    }" n+ ~! c/ D1 v/ z: V
    function g()) M  d& S+ r: S
        public; s; p/ [! w* L
        timedTransitions
: m# z. I1 |; e2 b* {6 o        atStage(Stages.AnotherStage)" o: x. `$ K& O6 P0 W% i: m
        transitionNext
1 S6 K0 m- L$ _; y5 _. f5 v. H: n5 ^0 s    {6 Y. j% k3 L6 Y- o2 e% k  C6 m
    }5 a- T! j2 [2 ]* x
    function h()5 B. l) R: }" S  u
        public
- G; o: s$ [7 V* J7 n        timedTransitions' Q8 ~. k  O9 ~" ^/ I
        atStage(Stages.AreWeDoneYet)0 M$ j2 {5 H  k/ Q9 _
        transitionNext
; T9 _/ O) ^  p$ g3 Z5 j  M+ r    {
3 m  l* @8 w2 Y3 s+ @0 e    }
6 c4 c7 k! {5 \$ Z; @    function i()+ f7 A: b! Q2 @
        public4 w' b% x! o/ ?3 B" K
        timedTransitions
# S; G! o, ]7 W9 H+ L* i        atStage(Stages.Finished)
) j# r. w4 a4 W* ^& f    {
0 |3 f* b2 H3 `4 i' H5 Z/ K    }# b- l  \. T9 w
}3 ^* n! y8 f+ @3 I
原文:https://raw.githubusercontent.com/etherchina/solidity-doc-cn/develop/common-patterns.rst
标签: Solidity
BitMere.com 比特池塘系信息发布平台,比特池塘仅提供信息存储空间服务。
声明:该文观点仅代表作者本人,本文不代表比特池塘立场,且不构成建议,请谨慎对待。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

成为第一个吐槽的人

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

    0

  • 关注

    0

  • 主题

    13