Hi 游客

更多精彩,请登录!

比特池塘 区块链技术 正文

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

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

本版积分规则

成为第一个吐槽的人

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

    0

  • 关注

    0

  • 主题

    2