Hi 游客

更多精彩,请登录!

比特池塘 区块链技术 正文

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

用香烟做的云
142 0 0
现如今,整个密码货币生态系统都是由智能合约所驱动!不管我们有多小心,或者我们的代码测试工作做得有多好,如果我们创建的是一个复杂的系统,那我们就有必要更新合约逻辑,以修补其存在的漏洞,或者添加必要的缺失功能。有时候,由于EVM虚拟机的更改或者被新发现的漏洞,我们可能需要去升级我们的智能合约。 + f3 \+ i, N4 b _0 I2 M) C, [一般来说,开发人员可以很容易地升级他们的软件,但区块链的情况是不一样的,因为它们有着难以更改的属性。如果我们部署了一个合约,这就好比是泼出去的水。然而,如果我们使用适当的技术,我们可以在不同的地址部署一个新的合约,并使得旧合约无效。下面是一些最常见的,创建可升级智能合约的方法。 ' F/ d0 ?7 i# e0 b* m5 I" i' c 6 A! X, g# _! h) [. k* A% N, m8 I! H7 P/ {. L 主从合约(Master-Slave contract)1 l2 f8 s5 C* D1 f" s' F 3 a: m- V3 j5 z2 z/ d 主从技术,是可实现升级智能合约最为基础也是最容易理解的技术之一。在这种技术当中,我们部署一个主合约,以及其他合约,其中主合约负责存储所有其他合约的地址,并在需要时返回所需的地址。当这些合约需要和其它合约进行沟通时,它们会充当从合约,从主合约那里获取其它合约的最新地址。为了升级智能合约,我们只需要在网络上部署它,并更改主合约中的地址。虽然这远不是发展可升级智能合约的最佳方式,但它确是最简单的。这种方法存在着很多的局限性,其中之一是,我们不能轻易地把合约的数据或资产迁移到新合约中。9 D8 Q- r9 n1 C$ l % o/ K5 G$ o2 k 永久存储合约(Eternal Storage contract5 c' B; z0 z% O0 g, ?+ s2 V ! j# a9 n" u, Q& I 在这种技术当中,我们将逻辑合约和数据合约彼此分离。数据合约应该是永久并且不可升级的。而逻辑合约可以根据需要进行多次升级,并将变化通知给数据合约。这是一项相当基本的技术,并且存在着一个明显的缺陷。由于数据合约是不可升级的,数据结构中需要的任何更改,或数据合约中存在的漏洞,都会导致所有数据变得无用。这种技术的另一个问题是,如果逻辑合约想要访问/操作区块链上的数据,那么这个逻辑合约将需要进行外部调用,而外部调用会消耗额外的gas。通常情况下,这种技术会和主从技术相结合,以促进合约间的通信。+ j" v8 ^* S" Y# S+ u 3 q) h. w5 U3 p- d- a 可升级存储代理合约 / C3 I# x0 T1 K8 a, E$ M 2 |0 L3 w- |7 Q+ J( Z6 ~我们可通过使永久存储合约充当逻辑合约的代理,以此防止支付额外的gas。这个代理合约,以及这个逻辑合约,将继承同一存储合约,那么它们的存储会在EVM虚拟机中对齐。这个代理合约将有一个回退函数,它将委托调用这个逻辑合约,那么这个逻辑合约就可以在代理存储中进行更改。这个代理合约将是永恒的。这节省了对存储合约多次调用所需的gas,不管数据做了多少的更改,就只需要一次委托调用。8 h6 i! o: x4 v, d. | 这项技术当中有三个组成部分: . z1 E1 ^6 Y4 y& ?代理合约(Proxy contract):它将充当永久存储并负责委托调用逻辑合约; 5 l5 ~4 g i+ f( Y' i% m逻辑合约(Logic contract):它负责完成处理所有的数据;3 q5 U: U8 W0 K! H+ c 存储结构(Storage structure):它包含了存储结构,并会由代理合约和逻辑合约所继承,以便它们的存储指针能够在区块链上保持同步; 1 P% a! i9 c# K+ a3 X/ N+ M+ x. Y" A8 a# K _8 H; o! `; G7 J. Z6 Y& `0 N1 C Y6 x3 d 委托调用 + G- Q$ T' @6 W' Y, r & G& S& t; P, A1 x该技术的核心在于EVM所提供的DELEGATECALL操作码,DELEGATECALL就像是一个普通的CALL 调用操作码,不同之处在于目标地址上的代码是在调用合约上下文中执行的,而原始调用的msg.sender以及msg.value将被保留。简单说,DELEGATECALL基本上允许(委托)目标合约在调用合约的存储中做它任何想做的事情。) V1 c( ]: M* M8 P 我们将利用这一点,并创建一个代理合约,它将使用DELEGATECALL操作码委托调用逻辑合约,这样我们就可以在代理合约中保持数据的安全,同时我们可以自由地更改逻辑合约。 : d6 N# [2 i" b6 H+ u. j* f' ?) }; S! s5 P - e0 k% T/ l: w- ]/ Y/ N; c/ Z如何使用可升级存储代理合约9 ~! d8 ]" t& k5 X- | $ A0 P/ f' v/ |1 `让我们深入研究一下细节。我们需要的第一个合约是存储结构。它将定义我们需要的所有存储变量,并将由代理合约和执行合约所继承。它看起来会是这样的:9 l5 \7 Q7 t' K( b! e0 ~, \
  1. <b>contract StorageStructure { + G2 h; q) c1 P% v
  2. address public implementation; 8 y4 k: t% {- Y5 m1 M8 a
  3. address public owner; - V/ v9 j$ a$ Z o
  4. mapping (address => uint) internal points; 0 W9 a/ T ^* I/ `3 {
  5. uint internal totalPlayers; j" W- c4 ?; I' S
  6. }</b>
复制代码
1 m) C* ~* b; t! D7 m H6 F/ h/ _: Z1 C, A' R/ {) p, I m* h 我们现在需要一个执行/逻辑合约。让我们创建一个简单版的合约,在添加新玩家时不会增加totalPlayers计数器的数字。 4 c; P9 [$ I4 m$ n$ i( |( l, V , y8 e; d; K* y* F
  1. contract ImplementationV1 is StorageStructure { 4 _$ G9 `* K' R; e
  2. modifier onlyOwner() {( N, U4 ^& O) G y2 F$ m
  3. require (msg.sender == owner); ( Z' j, G$ M: F% ?( o
  4. _; + ?2 X m, f$ x4 J2 l* ~# a
  5. }' u" B0 |7 G3 I& k
  6. function addPlayer(address _player, uint _points)' j% E. P5 {2 b3 p5 z: y' }1 t
  7. public onlyOwner/ t. g: {- k/ s1 t1 X
  8. {# j+ _( u0 q5 m' {: t5 }" V% |6 k
  9. require (points[_player] == 0); ! l* G$ v2 ~" p
  10. points[_player] = _points; , q& R3 _$ |7 ~+ D# @6 {: L
  11. }& b! x. @7 ?) V2 U8 L$ Y
  12. function setPoints(address _player, uint _points) 1 ]$ o j+ N, \; I* ^& I' G
  13. public onlyOwner% v( v; ]6 H' a* y) L* b A: S
  14. { * }) A% `8 K4 x) U( G
  15. require (points[_player] != 0); ; h' F/ o8 _" o, n5 ^+ D' j
  16. points[_player] = _points;; p! G- w2 O( G/ g* H, G* u
  17. }4 o9 l# D# b' z/ i$ ~5 x$ T7 Q
  18. }
复制代码
3 V7 @5 U" f; i1 D; Y8 j7 T) ~6 T$ T9 _2 ~ . E/ z) E% _5 y# H 下面就是最关键的部分:代理合约;8 U5 w1 [* M- U
  1. contract Proxy is StorageStructure { ! L) O, Y+ {2 j) C0 P$ b: ]
  2. h$ } s8 t; q' D5 I
  3. modifier onlyOwner() { ( K; I( }; m% L) }
  4. require (msg.sender == owner);; k5 h6 C: x" f* y" J; M6 ^
  5. _;* t* a) o. T! v4 E. \. `
  6. } . g* E! l3 S" f7 o" ^$ C
  7. /** 4 a( a# p$ d- A) |$ `; T
  8. * @dev constructor that sets the owner address2 `3 U) `6 L8 ~2 h4 ~# _
  9. */ z; l; u: J. J
  10. constructor() public {5 o7 n0 d3 K# t, ^
  11. owner = msg.sender;. }7 X/ e2 t1 m1 {+ Z
  12. } 8 x$ I4 f3 A3 s" l+ G, t
  13. /** , ]) j0 b2 E& c' X
  14. * @dev Upgrades the implementation address5 C+ b7 R( o0 I
  15. * @param _newImplementation address of the new implementation% \0 K- C3 l) B" x9 Y* N+ p
  16. */$ F9 B1 t$ e- j) k L
  17. function upgradeTo(address _newImplementation) ( g+ g4 J. O( z% D. C) S5 B) _2 e
  18. external onlyOwner & o+ }" j+ T. E
  19. { 8 g% _0 x p4 B4 o% f) {
  20. require(implementation != _newImplementation); ! {7 U8 h/ w3 F; ~0 ?/ ~
  21. _setImplementation(_newImplementation);% s7 a9 y- m! Y0 e+ I
  22. } : \' B) T' O2 @; D' c
  23. /** Y6 i r0 |* G$ z" t
  24. * @dev Fallback function allowing to perform a delegatecall& L) k: h8 C p$ O5 O5 N0 X$ E
  25. * to the given implementation. This function will return 1 ?! A; k4 B2 {, Q0 d% x/ G
  26. * whatever the implementation call returns & A2 _. Z9 L) X8 ^8 K% z. w
  27. */ : K; T3 E& S, g( Z
  28. function () payable public {, V4 }( H! u ~
  29. address impl = implementation; & G' d; r$ h; ?" x5 p; \. P
  30. require(impl != address(0));- I+ Z% Y& s8 g4 M8 K; A1 D6 {8 R
  31. assembly {+ v8 ^- W% ]8 _& d
  32. let ptr := mload(0x40)# F/ ]" u: C. ^$ w' u
  33. calldatacopy(ptr, 0, calldatasize)4 Q* e4 g" R" Q( G; A* t3 n
  34. let result := delegatecall(gas, impl, ptr, calldatasize, 0, 0) , ~; j1 T; t0 ]' q
  35. let size := returndatasize - ]; J& w) I/ ]1 w0 J& R" Y
  36. returndatacopy(ptr, 0, size)# D- R& z# ]' u) v6 ` I3 z
  37. switch result ' Y5 b6 o7 q% Q4 q" M7 z- z
  38. case 0 { revert(ptr, size) } * X5 x7 z& j# H/ u, z( u0 T4 [
  39. default { return(ptr, size) }. A& c2 T2 u# x1 Q- {
  40. } 0 Y% J" I# V) l7 k+ e/ L
  41. }& X& U! b) r5 }9 g4 q( B9 Y# m
  42. - @9 R' G7 |, O
  43. /** " v& _9 |3 I! p! ]4 R$ J( O
  44. * @dev Sets the address of the current implementation5 A# h$ L" l0 W, g/ G
  45. * @param _newImp address of the new implementation ]$ m5 x @! @% t- X
  46. */ 6 F0 x- ?! n0 H; {2 N3 i
  47. function _setImplementation(address _newImp) internal { + H* p' C3 F* F# r9 \
  48. implementation = _newImp; ) { `2 _/ ~3 l2 ^% f( K( O
  49. }* C5 K3 D; C/ Z+ O! T7 _ l2 S
  50. }
复制代码
~+ A d7 ]4 [! B 2 `0 r9 W% T D% o( g" s I S3 ]% w4 r" P: h为了让合约生效,我们首先需要部署代理合约以及ImplementationV1合约,然后调用这个代理合约的upgradeTo(address)函数,同时pass掉我们的ImplementationV1合约地址。现在,我们可以忘记这个ImplementationV1合约的地址,并把代理合约的地址作为我们的主地址。 / ` M$ X5 K! F. R: m2 D- o2 c为了升级这个合约,我们需要创建一个新的逻辑合约实现,它可以是这样的:; d( F1 H; z6 V% _) ~+ L
  1. contract ImplementationV2 is ImplementationV1 { . C- C& [) C c/ p* z; @
  2. ; H4 ~: H3 a- c
  3. 5 |# _3 { R m) _
  4. function addPlayer(address _player, uint _points) & k/ F" i' K) e/ o, g
  5. public onlyOwner1 c. U/ v7 ~3 C( _6 F3 w9 \2 ~
  6. {2 r# e, O0 r3 v* X
  7. require (points[_player] == 0);6 q o% A. A2 _! H2 F! n2 z3 n
  8. points[_player] = _points;( m& ]$ u% a( t3 Y
  9. totalPlayers++; ( R& o$ K/ {. W. w0 J
  10. } : j) r) _" R; v& d, D2 e& s
  11. }
复制代码
/ y1 I7 x: u* s6 m5 Y. Q- j & s. z0 O$ s i2 }# ^. |你应该注意到,这个合约也继承了存储结构合约(StorageStructure contract),尽管它是间接地。0 [. ^: t5 i* k. q: [$ I4 b! A# i: l 所有的执行方案都必须继承这个存储结构合约,并且在部署代理合约后不得进行更改,以避免对代理的存储进行意外覆盖。. x3 Q3 b7 W! F$ |% h5 o 6 [! C- L/ o# ?为了实现升级,我们在网络上部署这个合约,然后调用代理合约的upgradeTo(address) 函数,同时pass掉ImplementationV2合约的地址。, u ^2 m% ?, e! C' v 这种技术,使得升级合约逻辑变得相当容易,但它仍然不允许我们升级合约的存储结构。我们可以通过使用非结构化的代理合约来解决这个问题。 l `+ K$ q0 S/ J b3 j! L" B 2 | p! Q+ M7 P7 c9 d 4 q" R5 }# i3 M* y : R' `0 H9 e3 G% s, l1 l+ R3 x9 s' l3 m非结构化可升级存储代理合约+ I4 b! u/ X- x& m' P5 k 0 [4 X2 {0 a/ h V; Z4 ~. u6 Q这是当前最先进的,可实现智能合约升级的方法之一。它通过保存合约地址以及在存储中固定位置所有者的方法,以实现它们不会被执行/逻辑合约提供的数据所覆盖。我们可以使用sload以及sstore操作码来直接读取和写入由固定指针所引用的特定存储槽。 . P- |( C) ?' r5 f0 [# y1 v9 P此方法利用了存储中状态变量的布局,以避免逻辑合约覆盖掉固定位置。如果我们将固定位置设置为0x7,那么在使用前7个存储槽后,它就会被覆盖掉。为了避免这种情况,我们将固定位置设置为类似keccak256(“org.govblocks.implemenation.address”). 2 i2 t% G/ H6 {9 c/ @- h这消除了在代理合约中继承存储结构合约的需要,这意味着我们现在也可以升级存储结构了。然而,升级存储结构是一项棘手的任务,因为我们需要确保,我们所提交的更改,不会导致新的存储布局与先前的存储布局不匹配。 , S- p/ Q0 G- F! L, N% _1 F 2 S4 \! U* A( B4 S/ l这项技术有两个组成部分。: y2 Z+ w4 t: ^" j 1、代理合约:它负责将执行合约的地址存储在一个固定的地址当中,并负责委托调用它; 2、执行合约:它是主要合约,负责把我逻辑以及存储结构;9 F! w8 A: p: k& R- s" f+ s 你甚至可以将这项技术用于你现有的合约,因为它不需要对你的执行合约进行任何更改。 5 m2 D, T7 J1 z3 Y0 I4 s" ]+ \0 K这个代理合约会是这样子的:+ l u% v" U$ ]: c' h' w8 ~
  1. contract UnstructuredProxy {2 U; x7 q. X: R8 v# `2 G4 n* S
  2. ! I x& y h# v6 S9 z. |$ B
  3. // Storage position of the address of the current implementation 9 \0 Z0 t/ y4 ~/ ~8 N
  4. bytes32 private constant implementationPosition = / k& n/ ?3 G: Q; U- a1 c
  5. keccak256("org.govblocks.implementation.address");2 b2 D0 y( [: G& l5 P9 u
  6. // Storage position of the owner of the contract- t* z; c6 h/ c
  7. bytes32 private constant proxyOwnerPosition = ' Z. a& @& h7 Z
  8. keccak256("org.govblocks.proxy.owner");' C* L5 B# H+ u' J2 y
  9. /**5 D. U E) G/ y& T; d/ f
  10. * @dev Throws if called by any account other than the owner. + ~2 K* C" e. y7 r' r/ ~3 k
  11. */1 X2 s8 J) J, H: H" R2 G
  12. modifier onlyProxyOwner() {6 [8 e+ O4 o# H" z3 y
  13. require (msg.sender == proxyOwner());5 ~3 Z l5 a7 ~ M/ u$ q
  14. _;$ c; X' C% \/ D5 F/ H
  15. } ; Y4 ]. C0 u( q A" e
  16. /** 7 Q( [4 F% t; ^. ^3 [" ]
  17. * @dev the constructor sets owner 3 l; v8 r+ _9 ]5 {$ I' h0 f
  18. */ 3 {- d8 P4 _) I, J! M
  19. constructor() public { , C) u. b; j! h- r3 [5 U, U- w
  20. _setUpgradeabilityOwner(msg.sender); # s) M9 @9 Q7 o- n Z8 K
  21. }: g" C; o# A. D# I
  22. /**# X$ v" `4 V7 X/ h" g
  23. * @dev Allows the current owner to transfer ownership , W9 ]; B" I O) S! U2 Z& g0 V
  24. * @param _newOwner The address to transfer ownership to ) }, R8 u' F( w/ K
  25. */ ) w; j0 U5 O. m2 ?# L! o$ ]
  26. function transferProxyOwnership(address _newOwner) . d2 E- T9 c, f: j3 J. O
  27. public onlyProxyOwner 7 Z$ U9 z3 w0 ^" G+ C
  28. {! D$ G6 }+ @7 L. m' n$ e1 Z9 c
  29. require(_newOwner != address(0)); * R2 @/ x( X; A! Q2 r4 K5 N# q: ~
  30. _setUpgradeabilityOwner(_newOwner); 2 l" {2 }; M4 Z1 T7 v3 g
  31. }( v9 M8 B b2 _* N8 j& f, e
  32. /**1 [: P8 T$ w( @4 ?/ C
  33. * @dev Allows the proxy owner to upgrade the implementation ) Q/ `" b# v6 o1 m5 U6 X0 l% P; k
  34. * @param _implementation address of the new implementation 8 Z5 o+ G' {; f* y
  35. */! s0 K/ U/ Y R6 E) J; ^
  36. function upgradeTo(address _implementation)% ?& f8 C( _* c; k: v9 @
  37. public onlyProxyOwner P( n! ^- h/ }! t! ~. l. z& V' {$ L
  38. {3 D1 k4 |% o' A6 m4 ?! b3 n
  39. _upgradeTo(_implementation);/ V2 V. g, f7 D% }" r# q2 n
  40. } ! E/ K2 U8 U5 w. ^' \4 u9 \' |
  41. /** % h6 `7 P4 ], I, b$ E5 }, x" ?) e
  42. * @dev Tells the address of the current implementation2 y% U3 S+ U) F* O2 W
  43. * @return address of the current implementation 3 L* w/ D$ m8 i+ T: r- t$ H: s b
  44. */& {3 F Y3 z* Q
  45. function implementation() public view returns (address impl) {& f! s2 ~. z# ~/ p- ?
  46. bytes32 position = implementationPosition;: y; t; X# f! L
  47. assembly { + M6 \5 ?+ y- U) M
  48. impl := sload(position) , \5 a% D# g, v# @
  49. }4 g) K8 [: M# w {, v* z5 Z) \! C
  50. } . S( R- _6 i2 s9 a! x% H! F
  51. /** 6 q* R/ R, V6 m6 n4 F- V
  52. * @dev Tells the address of the owner 5 {; j, R! A; i7 E* f( R
  53. * @return the address of the owner7 }( v& p5 H3 l/ J; {
  54. */ 6 Z5 T: Z* ]; P! M9 E$ U4 a
  55. function proxyOwner() public view returns (address owner) {! K4 B; L$ p! W0 ]- x& }) J
  56. bytes32 position = proxyOwnerPosition; , I w8 x# n; Q( _
  57. assembly { 4 P) k% z, {( P/ Q. N# p/ B
  58. owner := sload(position)* Y* U9 s! N; V# D6 `8 s4 c/ o% K
  59. } 6 L$ j0 g O, s. T
  60. } ' [3 k7 `8 S$ ?3 B1 p8 S$ c5 {
  61. /** / w0 Q8 J% W2 }, q1 s4 z, x
  62. * @dev Sets the address of the current implementation 5 k7 n- V; j* y/ u8 G- c- q1 F
  63. * @param _newImplementation address of the new implementation # v W) _. m* Q1 f
  64. */ 4 B1 I5 x) P% q' \) A) r
  65. function _setImplementation(address _newImplementation) 2 E W! r2 G7 R! P
  66. internal ! k: l! t. B* m* ]9 ~
  67. {) w) ]5 K; V. S% k6 V3 w
  68. bytes32 position = implementationPosition;9 G7 S; _# `" i1 R
  69. assembly { # Q9 E/ ^1 K" D& z5 s
  70. sstore(position, _newImplementation)3 @& K# z: y: b4 B
  71. }9 q3 X$ z/ L% _9 L7 X6 L
  72. }! L$ e- y6 G$ A4 n! \: n3 q# H0 T
  73. /**7 X+ m& w. |+ i
  74. * @dev Upgrades the implementation address ( j9 N. ~: A5 {1 g) K0 h7 l6 p
  75. * @param _newImplementation address of the new implementation5 d0 q. e! o/ Q
  76. */; b+ W5 N: |4 B: Q# K
  77. function _upgradeTo(address _newImplementation) internal {2 l3 H2 ]6 b) F, i4 f1 z
  78. address currentImplementation = implementation();: P" _1 J4 I! u
  79. require(currentImplementation != _newImplementation);0 t g! l9 H% \( F: e
  80. _setImplementation(_newImplementation); ( j( n' b7 j1 s2 j2 S
  81. } ^+ H( ^4 e7 _% s3 O
  82. 3 e1 g4 _. _1 i5 f" g1 ^" `
  83. /**# ~9 c: y8 M8 O; I
  84. * @dev Sets the address of the owner/ B2 k3 w+ b0 w+ x8 ]
  85. */1 U* o, l. p0 C( b! t0 d
  86. function _setUpgradeabilityOwner(address _newProxyOwner)2 x* O/ z9 v3 U
  87. internal! y: d' s7 F4 X
  88. {- R3 m- r3 W9 K5 H- X3 p9 V0 t# m5 Y9 l
  89. bytes32 position = proxyOwnerPosition;/ l1 O$ o! ?4 } U- S& B5 ^( d( E
  90. assembly {1 O1 {! X4 @3 K/ ~* J
  91. sstore(position, _newProxyOwner) , q3 U; k" y* U% {
  92. }+ V+ N# o; ^' B4 w5 o3 m8 o
  93. }2 \2 w$ S) q7 T8 v+ U* H
  94. }
复制代码
( {% [2 x: j% f- n# d# |% U( T* B3 ]: Z; I# G5 d3 J8 V' f+ y . J+ K* H$ k4 A6 u如何使用非结构化可升级存储代理合约?4 @$ V p) V. b, Y& g# G: j U # u: s$ p/ p% |, c9 ~" w1 j2 l y使用非结构化可升级存储代理合约是非常简单的,因为这种技术几乎可以处理所有现有的合约。想要使用这种技术,你只需要遵循以下步骤: 9 \, ~# z3 N8 Y( x) p6 t6 Q部署代理合约和执行合约; ' X4 Y* l+ P6 C/ `) o& r( ?调用代理合约的upgradeTo(address)函数,同时pass掉执行合约的地址。 9 W+ l: r' c- J- A1 w: _我们现在可以忘掉这个执行合约地址,然后把代理合约的地址作为主地址。 & i1 X& \* k# `. M! s7 j 5 Z9 _- B$ o' l/ ] t0 [! }8 Y而要升级这个新实施的合约,我们只需要部署新的执行合约,并调用代理合约的upgradeTo(address) 函数,同时pass掉这个新执行合约的地址。就是这么简单! 4 b) M7 Y) i% A5 `* S: L& k让我们简单举个例子。我们将再次使用上述可升级存储代理合约中使用的同一逻辑合约,但是我们不需要用到存储结构。因此,我们的ImplementationV1合约看起来会是这样的:( a$ p4 E. ^$ B) W- Q6 I) x
  1. contract ImplementationV1 {9 P3 V$ k3 i$ @' Z
  2. address public owner; ) ]6 q! D2 s" U0 i. e
  3. mapping (address => uint) internal points;# U! _4 Q" W# \6 J! v# V0 N
  4. % W: @7 F6 \2 H$ Q- g( l* v
  5. modifier onlyOwner() {3 r3 U* L, K+ ]! [
  6. require (msg.sender == owner);! g% ~: o/ j6 t" h6 y6 J% X
  7. _;% P3 @, w- a0 a) U7 L# o& r
  8. } ) `3 L1 k6 j# r! Z9 X0 o" ^5 u+ L
  9. function initOwner() external { 6 v# `% e7 @. Z) v! U
  10. require (owner == address(0)); - `' _$ v! ]) l
  11. owner = msg.sender; $ V( V* E0 m8 q$ z$ x* t
  12. } % y5 b0 [: y& N H' i7 X' |
  13. function addPlayer(address _player, uint _points) C3 I/ A( \1 @( j& |8 d
  14. public onlyOwner% c/ P7 N6 }2 P
  15. { 9 n" D" m4 c; C( L/ W4 o6 @
  16. require (points[_player] == 0); }, R6 G) d$ p
  17. points[_player] = _points;% ]- W) t! I2 M( Q% ]
  18. } . y5 D! I5 `! S. V4 z) m
  19. : R* d" s! P4 D( W
  20. function setPoints(address _player, uint _points) 7 Q' I. o3 D5 F5 d, `9 h
  21. public onlyOwner) U3 S$ a5 }/ s" H0 S9 S+ o8 p4 l
  22. {% N% L1 `# u: H1 t( T+ g/ Z
  23. require (points[_player] != 0); 8 ]+ D$ D8 a. k$ N$ m. J; X
  24. points[_player] = _points; 1 w) q) ~) k7 i! P% V7 o( ~
  25. } $ i* t4 n. n7 j( q, n- w; e5 T! r
  26. }
复制代码
5 x+ U6 V$ C J0 l) A' z7 ~ x* g5 R( i& j7 Z 下一步是部署这个执行合约以及我们的代理合约。然后,再调用代理合约的upgradeTo(address) 函数,同时pass掉执行合约的地址。 " s' [; f: K( V$ F/ W, Y6 Z0 Z3 \ ( e: |; g; ]% @) D# I& e, `, U你可能注意到,在这个执行合约中,甚至没有声明totalPlayers变量,我们可以升级这个执行合约,其中具有 totalPlayers变量,这个新的执行合约看起来会是这样的:$ E `$ Y; {3 k0 }" l
  1. contract ImplementationV2 is ImplementationV1 {( m4 t: u. U* H+ | Y7 ]; j4 j
  2. uint public totalPlayers;7 s* Z# X# w2 i# R% {" @& z5 e2 U
  3. ) C$ k6 h2 d2 [
  4. + |9 Q9 ]" o0 R3 M
  5. function addPlayer(address _player, uint _points) 5 N: g' }4 T/ H# \: @
  6. public onlyOwner: L3 A2 ^ ~9 ?" m+ m. \' e' H
  7. { 9 V' o* B. N* X9 ]1 g* a: b
  8. require (points[_player] == 0);/ v- \) e A7 F; @$ R
  9. points[_player] = _points; 6 ~. t5 l1 f4 `5 j
  10. totalPlayers++; X1 T% C4 `6 S5 {* r* P
  11. }9 R, m2 F7 ], p' f7 ?
  12. }
复制代码
# z$ _- w+ S- R7 c0 \% V( j1 k ! M2 b: y+ B$ U# ?- E而要升级这个新的执行合约,我们需要做的,就是在网络上部署这个合约,然后,嗯你猜对了,就是调用代理合约的upgradeTo(address)函数,并同时pass掉我们新执行合约的地址。现在,我们的合约已演变为能够保持跟踪 totalPlayers,同时仍然为用户提供相同的地址。8 f: f% f" m, x& q* m + V! R( `; ^" e% ? 这种方法是强大的,但也存在着一些局限性。主要关注的一点是,代理合约拥有者(proxyOwner)有太多的权力。而且,这种方法对复杂的系统而言是不够的。对于构建具有可升级合约的 dApp而言,组合主从合约以及非结构化可升级存储代理合约,会是更为灵活的一种方法,这也是作者所在的GovBlocks所使用的方法。/ \% L& H/ ^: }" z- C+ V: ? ( V( D e' @' V# J9 h& W! h' e+ m A( O( H S8 A F, A 结论 : % A7 |* ?, w" U& ~" m 非结构化存储代理合约,是创建可升级智能合约最先进的技术之一,但它仍然是不完美的。毕竟,我们并不希望dApp所有者对dApp具有不当的控制权。如果开发者拥有了这种权力,那这个dapp还能称之为去中心化应用吗?在这里,我建议读者可以阅读下Nitika提出的反对使用onlyOwner的论点。你也可以在GitHub上窥探到我们的代理合约。3 e- i9 O x) d* f! V 希望这篇文章可以帮助你创建可升级的智能合约。 # f6 E' o1 j' T: c0 o6 f& J同时向Zepplin在代理技术方面进行的工作致敬。
BitMere.com 比特池塘系信息发布平台,比特池塘仅提供信息存储空间服务。
声明:该文观点仅代表作者本人,本文不代表比特池塘立场,且不构成建议,请谨慎对待。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

成为第一个吐槽的人

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

    0

  • 关注

    0

  • 主题

    2