Hi 游客

更多精彩,请登录!

比特池塘 区块链技术 正文

如何实现可升级的智能合约?

用香烟做的云
111 0 0
现如今,整个密码货币生态系统都是由智能合约所驱动!不管我们有多小心,或者我们的代码测试工作做得有多好,如果我们创建的是一个复杂的系统,那我们就有必要更新合约逻辑,以修补其存在的漏洞,或者添加必要的缺失功能。有时候,由于EVM虚拟机的更改或者被新发现的漏洞,我们可能需要去升级我们的智能合约。& o5 o! H& ]' ]( r8 w2 j 一般来说,开发人员可以很容易地升级他们的软件,但区块链的情况是不一样的,因为它们有着难以更改的属性。如果我们部署了一个合约,这就好比是泼出去的水。然而,如果我们使用适当的技术,我们可以在不同的地址部署一个新的合约,并使得旧合约无效。下面是一些最常见的,创建可升级智能合约的方法。7 r+ O% a) B# k* u& A* x $ f9 [4 h# j g% b2 ?: y) o 2 a& @* p9 E" v; O主从合约(Master-Slave contract)9 I2 l* @/ h9 v 1 Z; ?9 A; s6 D主从技术,是可实现升级智能合约最为基础也是最容易理解的技术之一。在这种技术当中,我们部署一个主合约,以及其他合约,其中主合约负责存储所有其他合约的地址,并在需要时返回所需的地址。当这些合约需要和其它合约进行沟通时,它们会充当从合约,从主合约那里获取其它合约的最新地址。为了升级智能合约,我们只需要在网络上部署它,并更改主合约中的地址。虽然这远不是发展可升级智能合约的最佳方式,但它确是最简单的。这种方法存在着很多的局限性,其中之一是,我们不能轻易地把合约的数据或资产迁移到新合约中。) b8 c; [; q. W/ Y; H% T ! D/ U0 t" t- [; { 永久存储合约(Eternal Storage contract, i3 Z$ Q: D' S' g + z$ G M3 Q' Z7 u2 U6 n! f. V 在这种技术当中,我们将逻辑合约和数据合约彼此分离。数据合约应该是永久并且不可升级的。而逻辑合约可以根据需要进行多次升级,并将变化通知给数据合约。这是一项相当基本的技术,并且存在着一个明显的缺陷。由于数据合约是不可升级的,数据结构中需要的任何更改,或数据合约中存在的漏洞,都会导致所有数据变得无用。这种技术的另一个问题是,如果逻辑合约想要访问/操作区块链上的数据,那么这个逻辑合约将需要进行外部调用,而外部调用会消耗额外的gas。通常情况下,这种技术会和主从技术相结合,以促进合约间的通信。, `* |' Z8 ` s8 {( p3 | 2 i5 Q6 `" F# K# R 可升级存储代理合约 - e' p [3 w( x, b% S4 ~; Y3 c% X& Q3 L4 P 我们可通过使永久存储合约充当逻辑合约的代理,以此防止支付额外的gas。这个代理合约,以及这个逻辑合约,将继承同一存储合约,那么它们的存储会在EVM虚拟机中对齐。这个代理合约将有一个回退函数,它将委托调用这个逻辑合约,那么这个逻辑合约就可以在代理存储中进行更改。这个代理合约将是永恒的。这节省了对存储合约多次调用所需的gas,不管数据做了多少的更改,就只需要一次委托调用。3 p; K2 `8 B: C1 r8 F. X2 Q& o 这项技术当中有三个组成部分: " r) }# g; ?" P! ]! D代理合约(Proxy contract):它将充当永久存储并负责委托调用逻辑合约;- s9 L- H+ o! B2 ] 逻辑合约(Logic contract):它负责完成处理所有的数据;& m3 d! S4 w; [ 存储结构(Storage structure):它包含了存储结构,并会由代理合约和逻辑合约所继承,以便它们的存储指针能够在区块链上保持同步;( s/ Q8 A/ O9 t( l' Y3 N 3 l9 F0 E4 T% f9 O* ?' J 5 U1 W$ D9 V9 C+ ] e/ K9 G . Z$ M" _/ k3 U8 ^ 委托调用 % B8 G: }- e5 }* p; Y7 Y7 g5 r) x 该技术的核心在于EVM所提供的DELEGATECALL操作码,DELEGATECALL就像是一个普通的CALL 调用操作码,不同之处在于目标地址上的代码是在调用合约上下文中执行的,而原始调用的msg.sender以及msg.value将被保留。简单说,DELEGATECALL基本上允许(委托)目标合约在调用合约的存储中做它任何想做的事情。 + D& u& @1 H! Y. s7 U3 a* r我们将利用这一点,并创建一个代理合约,它将使用DELEGATECALL操作码委托调用逻辑合约,这样我们就可以在代理合约中保持数据的安全,同时我们可以自由地更改逻辑合约。 % A6 @! c. E- i8 R- _* H 2 I- G# S H+ r- A, c 2 u& M: s3 M$ Q& l% t5 e% {1 \ 如何使用可升级存储代理合约 ! C# s: |3 A; j7 }! E3 k7 {3 u' I4 U8 X) Z, ?; E. y0 H# Q9 {% _, F 让我们深入研究一下细节。我们需要的第一个合约是存储结构。它将定义我们需要的所有存储变量,并将由代理合约和执行合约所继承。它看起来会是这样的: 6 T* d) u/ k0 K# D
  1. <b>contract StorageStructure { / j+ [& ~( O' ~1 E1 Y
  2. address public implementation;: `+ o' a5 F! f% Y9 C
  3. address public owner; , l {3 @0 B L2 ?, ~
  4. mapping (address => uint) internal points; & w" o, b2 D0 K) Y0 G( r' `6 R
  5. uint internal totalPlayers; i9 L, u7 l5 k* w5 I! q+ u9 x
  6. }</b>
复制代码
& \! a& Q- C9 E& H; g) r( R : \& U: ^) ?) F3 a$ u; p! q% P我们现在需要一个执行/逻辑合约。让我们创建一个简单版的合约,在添加新玩家时不会增加totalPlayers计数器的数字。 - a' _/ s; j D# z, U" x$ R- U: s/ U9 E' z9 Q! Q
  1. contract ImplementationV1 is StorageStructure { 3 E' B6 {# P2 A. ~2 K! t1 D1 r
  2. modifier onlyOwner() {: N5 x, ?3 [6 X9 B. y: L
  3. require (msg.sender == owner);+ N2 X/ T; J1 Z. [5 J/ J
  4. _;0 x* t4 h; V- S* i
  5. } , ^4 }8 n$ F, P( A+ y+ j
  6. function addPlayer(address _player, uint _points), y1 ^# F, Z% \
  7. public onlyOwner3 O% D' E; N0 k9 A0 R
  8. {4 D! Q4 Q5 R p/ U
  9. require (points[_player] == 0);9 X+ e1 N* ~, x" l& \
  10. points[_player] = _points; $ N9 p% n1 P* K K' ?. z
  11. } : |% h3 i3 s& D4 I$ B# [/ C
  12. function setPoints(address _player, uint _points) 9 X; Y5 X" F5 R& m
  13. public onlyOwner% I/ I3 V* b" |5 i, f4 _$ ]* x
  14. {. `4 ^/ ]0 ~' g# l. G
  15. require (points[_player] != 0);, Q2 m2 C K- V$ K& b- c# q
  16. points[_player] = _points; 7 m, b) X* n5 Y/ ?0 h
  17. }! D8 V# W( Q& ~3 {" I l
  18. }
复制代码
" m5 u: e# d( H; I' z 5 K' i- j; ^. a3 @5 d ( I( h0 U& f% C0 w) b 下面就是最关键的部分:代理合约;0 c1 J x" h0 h3 ?$ \: S8 B
  1. contract Proxy is StorageStructure { / A0 |/ {& T7 I; m$ M. j# d- D
  2. # L( d) l0 W; _2 D6 f' y5 E
  3. modifier onlyOwner() {$ o' x. C0 y4 L. f$ J
  4. require (msg.sender == owner); + [+ P. [ R6 z# S0 @
  5. _; / k7 h4 |1 H) z2 y) a
  6. } - z. y' V8 V$ [
  7. /** 2 U" S+ D, F( O" o E: M+ M! P
  8. * @dev constructor that sets the owner address $ J7 s) q1 e% g" ?9 z
  9. */5 O) Z( W7 q9 a3 W- Q
  10. constructor() public {0 U- K; v. U. T7 |+ Z: ?
  11. owner = msg.sender; : r: @7 R5 }, u" Q& Z) _2 O. t
  12. } I5 a m; D5 N/ f" @
  13. /**8 m7 x1 k7 R! k
  14. * @dev Upgrades the implementation address$ R0 E% ^! k6 u, Q/ V! w
  15. * @param _newImplementation address of the new implementation 0 ^7 ^; }- f0 j2 G
  16. */$ w3 F* e+ R$ l+ F- k3 |
  17. function upgradeTo(address _newImplementation) . X. D: ^, P! L/ r) z+ A6 i6 u' n
  18. external onlyOwner) ?4 l' F* b4 N, o
  19. {+ b7 o6 R) n. _7 O8 A
  20. require(implementation != _newImplementation); V' k. H- k) X
  21. _setImplementation(_newImplementation);# {1 y# d' s; O: n! Y1 Y( ~; p7 x f
  22. } ) `3 r8 U, c- _; L: x
  23. /** 3 n- O6 s+ n ?5 N& Z; u1 T% X
  24. * @dev Fallback function allowing to perform a delegatecall : e; x4 ], _+ p" j. b" q
  25. * to the given implementation. This function will return2 H' e W% s7 G9 h5 r9 U1 t
  26. * whatever the implementation call returns * m, M8 I! y) e( r
  27. */ % i* L% R8 P( T
  28. function () payable public { " E* G$ S9 [. g9 ]5 y
  29. address impl = implementation; : r( C b- c* v8 V) o
  30. require(impl != address(0)); 7 l5 g+ c4 C0 R
  31. assembly { & ~/ e" e; x+ m% p: X
  32. let ptr := mload(0x40)# W& M* w4 a+ W& u; i
  33. calldatacopy(ptr, 0, calldatasize) * i3 L1 @2 `7 |2 n( w4 ~
  34. let result := delegatecall(gas, impl, ptr, calldatasize, 0, 0)) P* c& J& C! W9 N; V# f
  35. let size := returndatasize& |, W* u& Z W: K
  36. returndatacopy(ptr, 0, size) 7 r0 V7 @' I. j. U( s: H* |! G
  37. switch result9 m L& R/ Z! N$ j9 T4 d2 V
  38. case 0 { revert(ptr, size) } - G; P, Q! E- D9 u
  39. default { return(ptr, size) } + c' `/ D D# i
  40. } ! o4 v# c& h" V2 r
  41. }6 i. H, u1 H$ n
  42. # x; ^! x+ U3 Q. c. o1 |
  43. /** ! m7 i- C' v7 d7 Q
  44. * @dev Sets the address of the current implementation 8 [% T( P% }6 _" v& G3 Y) L
  45. * @param _newImp address of the new implementation5 }( r. a+ N; m, K" F/ v- t
  46. */ _0 l+ R$ y; k5 b. J9 V% X
  47. function _setImplementation(address _newImp) internal {$ q* s- X4 i: ~1 r2 |. E- Z
  48. implementation = _newImp;6 b0 C8 Z* F, e9 [
  49. } ! `' h: Z1 ^: U* ]8 _/ y {
  50. }
复制代码
, ^5 L, a; |9 o6 d+ H8 R8 r- h- C3 h# } l& f ; ^2 y* I5 X5 N. |' A4 a 为了让合约生效,我们首先需要部署代理合约以及ImplementationV1合约,然后调用这个代理合约的upgradeTo(address)函数,同时pass掉我们的ImplementationV1合约地址。现在,我们可以忘记这个ImplementationV1合约的地址,并把代理合约的地址作为我们的主地址。6 G* t& y1 ], K- [% a5 `; `! Z 为了升级这个合约,我们需要创建一个新的逻辑合约实现,它可以是这样的:- c8 o, \5 ?5 ^3 q0 Q# @0 C
  1. contract ImplementationV2 is ImplementationV1 {' O, B9 `. |. I
  2. ) C6 ]: _- y+ Q8 d" v
  3. " O7 Y7 v) B% o0 e3 B; z
  4. function addPlayer(address _player, uint _points)) T4 X/ p- |5 [5 o
  5. public onlyOwner5 `; s1 T3 j9 D1 [: k2 u
  6. {0 a. x/ s" ]6 c1 S$ u/ l ]1 E
  7. require (points[_player] == 0);7 A4 o2 E) a5 e) Y4 r
  8. points[_player] = _points; ( m( r$ f9 J) @
  9. totalPlayers++;. D, ~) A! X9 G0 M! M$ K- j
  10. } % I$ }' b6 l9 p+ }2 _! Y y
  11. }
复制代码
* i! d: a( l# e" t8 ^' B 9 ~2 j6 Q8 s# u* E& Z8 e8 z9 Z 你应该注意到,这个合约也继承了存储结构合约(StorageStructure contract),尽管它是间接地。 0 ^, q# D: h( |1 r4 a9 s# s8 Y所有的执行方案都必须继承这个存储结构合约,并且在部署代理合约后不得进行更改,以避免对代理的存储进行意外覆盖。 . m) f p+ S3 V& W) Y% c- W 2 s5 H: B& \" d6 V为了实现升级,我们在网络上部署这个合约,然后调用代理合约的upgradeTo(address) 函数,同时pass掉ImplementationV2合约的地址。1 I S( M3 |5 p2 \; P* D# u 这种技术,使得升级合约逻辑变得相当容易,但它仍然不允许我们升级合约的存储结构。我们可以通过使用非结构化的代理合约来解决这个问题。 1 ?7 {% D0 L. }. x; D, P; t4 \2 ?$ Y2 p 0 l2 W/ G. u* P9 ~3 H , s/ [+ [* ]. i; ]7 i非结构化可升级存储代理合约) V: O8 n5 g. E* E W ! |3 W+ M1 V: K这是当前最先进的,可实现智能合约升级的方法之一。它通过保存合约地址以及在存储中固定位置所有者的方法,以实现它们不会被执行/逻辑合约提供的数据所覆盖。我们可以使用sload以及sstore操作码来直接读取和写入由固定指针所引用的特定存储槽。2 T7 P* L8 f, v" ] q. V) J& i 此方法利用了存储中状态变量的布局,以避免逻辑合约覆盖掉固定位置。如果我们将固定位置设置为0x7,那么在使用前7个存储槽后,它就会被覆盖掉。为了避免这种情况,我们将固定位置设置为类似keccak256(“org.govblocks.implemenation.address”).) {3 H2 w* x7 }) `' u* a 这消除了在代理合约中继承存储结构合约的需要,这意味着我们现在也可以升级存储结构了。然而,升级存储结构是一项棘手的任务,因为我们需要确保,我们所提交的更改,不会导致新的存储布局与先前的存储布局不匹配。 ) b& G# D; B. n# u9 M+ w; U) ] * h5 @- H- X6 k, M这项技术有两个组成部分。2 y7 c$ U3 c' l4 g* H1 q+ ^- S5 T 1、代理合约:它负责将执行合约的地址存储在一个固定的地址当中,并负责委托调用它; 2、执行合约:它是主要合约,负责把我逻辑以及存储结构;, L# i! J; L% P4 b$ |* T7 f 你甚至可以将这项技术用于你现有的合约,因为它不需要对你的执行合约进行任何更改。7 H; }# c; \2 X m 这个代理合约会是这样子的:1 W8 T8 l7 q% o
  1. contract UnstructuredProxy { 6 S7 z- f) E2 h1 e! c$ `
  2. " r" [% V6 l) i2 l
  3. // Storage position of the address of the current implementation! E$ N I2 v* z" P7 h1 P8 P
  4. bytes32 private constant implementationPosition = . ^/ T. q5 \" ~
  5. keccak256("org.govblocks.implementation.address");) k% z2 h7 O) _2 d% l0 v
  6. // Storage position of the owner of the contract * m% P8 A! Q- l
  7. bytes32 private constant proxyOwnerPosition = y5 a0 @, C' v1 e; H
  8. keccak256("org.govblocks.proxy.owner"); - H2 h- `+ j5 w. {) {- r! a& {
  9. /*** u. ?9 M/ r- e" K, w" j" w
  10. * @dev Throws if called by any account other than the owner.' ~0 } \% ^: K6 s0 y
  11. */ . ~: f% ]: J! g) b' Y
  12. modifier onlyProxyOwner() {4 b+ D9 V! n+ W0 B$ _# I, [! h
  13. require (msg.sender == proxyOwner());$ ^6 q$ W, C0 R; U3 t+ M
  14. _; 6 v& k9 ^( K7 Y: @* d
  15. }1 g' v" W8 w1 ?5 h
  16. /**- h' Q% T, e$ `5 G7 i5 |
  17. * @dev the constructor sets owner0 _/ |4 c/ [$ ?9 i; C4 b$ r
  18. */( Z* ~. j+ ?! r! W
  19. constructor() public { 0 s: G4 H# t, Q: b. Z! D7 e$ A$ e" \
  20. _setUpgradeabilityOwner(msg.sender); 8 ~$ c5 o0 @# H4 T& C. F2 Z* ?
  21. } 5 u# A; K4 Y9 Z0 b2 w9 `& M" H
  22. /**, K: \7 t; _) o) k0 C4 o
  23. * @dev Allows the current owner to transfer ownership( d: e4 }+ f3 y
  24. * @param _newOwner The address to transfer ownership to% R2 o/ z2 t) ^ o k
  25. */ + C( ?6 ?; [' i. z1 }' c6 ?
  26. function transferProxyOwnership(address _newOwner) 7 {" ]# w" V! E' R/ V
  27. public onlyProxyOwner8 s! I1 l" ^4 N; L4 b: @7 I
  28. { 4 n) A& N& F! A
  29. require(_newOwner != address(0)); * n5 ~1 W* k1 G5 P) l1 b( Y2 { V' ^
  30. _setUpgradeabilityOwner(_newOwner); 5 W! n& L3 i: V. z. I9 A% y! |" E* }
  31. } ) K/ {/ W0 M/ c$ Y, q# M
  32. /** . e6 R* O5 b# }# I; D8 W8 t
  33. * @dev Allows the proxy owner to upgrade the implementation' h0 |$ D, c( B4 I9 n: N }
  34. * @param _implementation address of the new implementation 1 h8 ^( g& H S! o" H6 P$ d6 T+ D7 [
  35. */: C; S; A( H; p
  36. function upgradeTo(address _implementation): M4 k% c; @: i5 d
  37. public onlyProxyOwner& ^! w$ c. k/ Y V/ |7 d4 \
  38. { , Q. V2 `/ X2 J1 o s
  39. _upgradeTo(_implementation); / B5 j: Y# x8 _2 E; l N' b' a
  40. } $ C; V! \* _: X9 b
  41. /** 3 B6 r) B. `$ x( L2 D( L; Z
  42. * @dev Tells the address of the current implementation / P/ F9 b+ {/ a
  43. * @return address of the current implementation g1 n! j5 s: G% H1 s& j
  44. */6 G1 _2 E# o) g
  45. function implementation() public view returns (address impl) { ; J/ z: S4 H: T5 z: d5 ~
  46. bytes32 position = implementationPosition; 6 ` z6 {# R) e
  47. assembly {+ ]! V/ T" X x& ]1 N5 q
  48. impl := sload(position) , _& D# H: G* Q; q3 [
  49. }6 D/ W: H" [: }$ K
  50. } 8 J( E5 i- P1 o/ b
  51. /** & a- {; [5 @6 B. u( c' B
  52. * @dev Tells the address of the owner9 H4 |. X+ U) a& z" A; I
  53. * @return the address of the owner 2 j' m* \& n' h8 C7 D) _+ e
  54. */ . K6 a5 q X3 @% r q" r5 u7 u
  55. function proxyOwner() public view returns (address owner) { 2 r- _7 z$ o$ C1 s
  56. bytes32 position = proxyOwnerPosition; / P( J( W' j7 N% [) z }
  57. assembly { 8 |% Q4 E5 ]# p6 i
  58. owner := sload(position) / N: S. q9 g H/ u) z
  59. }. Z3 v- z0 ]# N* @! p
  60. }0 x# U* k8 @, a: L; \
  61. /*** P6 L8 C( s' G! U4 @$ \7 e6 y
  62. * @dev Sets the address of the current implementation6 ?7 ]# @( Q0 w z4 J
  63. * @param _newImplementation address of the new implementation + o! w/ Y* f" N+ F) l8 |1 {
  64. */ ' P& { K- ~1 b7 V, j9 s
  65. function _setImplementation(address _newImplementation)3 h* w0 M8 |" \1 G7 O1 l5 N
  66. internal % d1 J D- T0 ]* [7 K, p3 L
  67. { 8 ?! { F, X( x8 g
  68. bytes32 position = implementationPosition; # p" ~7 G( Q" u) r, N( m, \* F
  69. assembly {; R* V7 [( d( m$ m
  70. sstore(position, _newImplementation)( _, `2 X" B* {) ~; q5 F z0 X
  71. }4 B F2 G6 ]4 C/ X( W
  72. }- Z1 r( K% w0 D; _2 T
  73. /**! y y7 L8 ]0 Q4 p" J% o; f
  74. * @dev Upgrades the implementation address 9 A6 W2 t- g" w" [- I5 w% V& d( R
  75. * @param _newImplementation address of the new implementation 5 ~ r' j. ]( k. I+ z" f& F. v/ V
  76. */ & ]# @' V" {6 L5 L5 O, p
  77. function _upgradeTo(address _newImplementation) internal {# G. W1 u' l! M; ?3 @& G4 q9 k
  78. address currentImplementation = implementation(); # Z. F6 N, _" t
  79. require(currentImplementation != _newImplementation);2 ?. V4 I: g- F9 M/ H
  80. _setImplementation(_newImplementation);' {4 x; @; J, {* M! P
  81. } 7 P4 ^; D4 _* a" c% M
  82. # b9 N N: j* ^
  83. /** " s: O3 W Z4 f/ C. j" o
  84. * @dev Sets the address of the owner6 @" Y" g/ t- d& b4 }8 }, c. ?( `. F
  85. */ 6 A2 @! X8 s/ D9 S: P
  86. function _setUpgradeabilityOwner(address _newProxyOwner)7 x% [0 X {! R
  87. internal & [ U- J6 s b: i D6 d& _/ l
  88. {5 {$ u# e6 y4 S ^/ s V
  89. bytes32 position = proxyOwnerPosition;( S( j# h0 D G, c" A: t; E* c
  90. assembly {% Z* T$ x$ W/ L% a, ]
  91. sstore(position, _newProxyOwner)8 R1 I4 A9 T" N `
  92. } $ p( B$ o" h3 e7 j6 B+ P. K# X
  93. }0 E& g; {+ @% _, O) W, F
  94. }
复制代码
4 k8 C0 N: M, s# E2 L+ ~# @/ r 7 l8 [4 P2 o5 ^5 z! B1 b 7 @ o0 E; Q3 p0 z2 H6 a7 A. Z如何使用非结构化可升级存储代理合约?% X; P+ o5 H/ o% v; A* L $ I9 D" e8 o' c* R' c# k, v使用非结构化可升级存储代理合约是非常简单的,因为这种技术几乎可以处理所有现有的合约。想要使用这种技术,你只需要遵循以下步骤: $ ?# b+ E* r2 q- D+ I部署代理合约和执行合约; . N1 `9 r# B, b调用代理合约的upgradeTo(address)函数,同时pass掉执行合约的地址。7 t# |* I& b5 ~( W6 H2 g+ V9 s 我们现在可以忘掉这个执行合约地址,然后把代理合约的地址作为主地址。 " G9 R4 @/ w; \9 i 7 h0 ]5 [3 v) {1 o3 B而要升级这个新实施的合约,我们只需要部署新的执行合约,并调用代理合约的upgradeTo(address) 函数,同时pass掉这个新执行合约的地址。就是这么简单!( n- H n1 g' G8 \ F3 N, F9 d 让我们简单举个例子。我们将再次使用上述可升级存储代理合约中使用的同一逻辑合约,但是我们不需要用到存储结构。因此,我们的ImplementationV1合约看起来会是这样的: # F' s& \0 s# X! Z R/ G# U
  1. contract ImplementationV1 { 5 Q) o( e& v8 |
  2. address public owner; 6 u: [3 O: u1 E
  3. mapping (address => uint) internal points; % x0 x$ T& f' p% J1 i( U$ C8 B
  4. & A; H/ R s! ]1 `/ n
  5. modifier onlyOwner() { 0 L* f, y* d, o5 S
  6. require (msg.sender == owner);/ N' `7 U2 b* Z- G1 C. x
  7. _;$ W: `4 q) A" C% u) Q
  8. }" n8 {. x+ i/ a3 H
  9. function initOwner() external {+ E6 H) N( a3 C7 V6 {6 E# k# W
  10. require (owner == address(0)); + o% R8 E2 m$ H. A* ~! O
  11. owner = msg.sender; 0 {: v7 U# {$ f" c4 U
  12. } / T8 Q; R6 B7 m! s% r; O
  13. function addPlayer(address _player, uint _points), o2 s' B# U! [& j
  14. public onlyOwner 4 }$ y& I* D1 \% R
  15. {- [, Z/ }1 N1 t7 N' x1 v
  16. require (points[_player] == 0); / d4 |9 Q) a u. Z8 }0 u! r
  17. points[_player] = _points;) y/ W! i; I( b. l0 D5 K% V* x
  18. }) U3 v" }( f7 V# I. R8 R8 j# @
  19. 8 p: Z; Y# k% P
  20. function setPoints(address _player, uint _points) 8 y( x- @: X9 n5 m' j1 z- |6 y% ?
  21. public onlyOwner( u2 n+ R, H9 t1 Y- E0 ?4 R
  22. {6 c; h( X: v' X4 y* O: Q! c
  23. require (points[_player] != 0);, w: s* b; q0 G' ]2 F" o6 F
  24. points[_player] = _points; $ ~: Z+ H% O( [. g6 p
  25. } " I% A9 {- ^, s5 N5 Y! l
  26. }
复制代码
5 y8 R0 L, |( O ! J4 q8 g) [7 H/ U下一步是部署这个执行合约以及我们的代理合约。然后,再调用代理合约的upgradeTo(address) 函数,同时pass掉执行合约的地址。 2 e2 X, w( L% p7 i5 P0 g & L* ?6 s/ g. A5 K你可能注意到,在这个执行合约中,甚至没有声明totalPlayers变量,我们可以升级这个执行合约,其中具有 totalPlayers变量,这个新的执行合约看起来会是这样的: , {$ P/ U! t4 v1 I0 S
  1. contract ImplementationV2 is ImplementationV1 { 2 f9 O& I: s+ q: `- v( \! w
  2. uint public totalPlayers; ! t7 t: m/ m" r% H5 f' [
  3. 9 r/ n4 \) Y* G: D
  4. - @' e3 q, K4 i2 m+ J e) z) p
  5. function addPlayer(address _player, uint _points) 2 a' y: v2 x* S3 `
  6. public onlyOwner8 V3 x; A1 l% T* X6 O. _& ]
  7. {! H5 k: b4 o" c
  8. require (points[_player] == 0); , a3 |% _# r" I f) A+ _+ f
  9. points[_player] = _points;* L( D! y. b) z0 [
  10. totalPlayers++; f- X t! n$ R# Y3 X/ G
  11. } & v2 X* q, ~5 K' g0 L( i' s: ~: z+ j
  12. }
复制代码
% n$ E$ \% v$ J8 j% b, S: ` * | u, u2 f' S' C# e 而要升级这个新的执行合约,我们需要做的,就是在网络上部署这个合约,然后,嗯你猜对了,就是调用代理合约的upgradeTo(address)函数,并同时pass掉我们新执行合约的地址。现在,我们的合约已演变为能够保持跟踪 totalPlayers,同时仍然为用户提供相同的地址。0 o6 \7 |8 y. M ! e/ Z" Y% X3 q这种方法是强大的,但也存在着一些局限性。主要关注的一点是,代理合约拥有者(proxyOwner)有太多的权力。而且,这种方法对复杂的系统而言是不够的。对于构建具有可升级合约的 dApp而言,组合主从合约以及非结构化可升级存储代理合约,会是更为灵活的一种方法,这也是作者所在的GovBlocks所使用的方法。7 S1 `7 L/ K+ w- p2 h" ]9 h 5 I" v6 e4 q8 o 6 j( ]4 @7 w+ Y/ {! N2 l& F, D 结论 :$ E( |6 W- b+ @$ U9 _& V ~ 非结构化存储代理合约,是创建可升级智能合约最先进的技术之一,但它仍然是不完美的。毕竟,我们并不希望dApp所有者对dApp具有不当的控制权。如果开发者拥有了这种权力,那这个dapp还能称之为去中心化应用吗?在这里,我建议读者可以阅读下Nitika提出的反对使用onlyOwner的论点。你也可以在GitHub上窥探到我们的代理合约。 5 `; A9 h- B2 p+ M( K希望这篇文章可以帮助你创建可升级的智能合约。1 a8 ~( h6 k9 l4 g" ? 同时向Zepplin在代理技术方面进行的工作致敬。
BitMere.com 比特池塘系信息发布平台,比特池塘仅提供信息存储空间服务。
声明:该文观点仅代表作者本人,本文不代表比特池塘立场,且不构成建议,请谨慎对待。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

成为第一个吐槽的人

用香烟做的云 小学生
  • 粉丝

    0

  • 关注

    0

  • 主题

    2