智能合約升級模式介紹 — 入門篇

以太坊最大的優(yōu)勢就是烟馅,每一筆用來轉(zhuǎn)賬涛浙、部署合約或者和合約交互的交易(事務(wù))都被存在一個(gè)叫做區(qū)塊鏈的公共賬本上康辑。一旦交易發(fā)生,就再也無法隱藏或者改變轿亮。這帶來一個(gè)巨大的好處疮薇,就是在以太坊中的每一個(gè)節(jié)點(diǎn)都可以去驗(yàn)證任意一筆交易的合法性和當(dāng)前狀態(tài)。這使得以太坊成為一個(gè)非常健壯的去中心化系統(tǒng)我注。

但是隨之而來的是按咒,它還有一個(gè)最大的缺點(diǎn),就是智能合約一旦部署之后但骨,就再也無法改變源碼励七。開發(fā)中心化應(yīng)用(比如facebook或者Airbnb)的開發(fā)者,都已經(jīng)習(xí)慣了奔缠,為了修復(fù)bug或者引入新的特性而頻繁更新產(chǎn)品掠抬。但這種方式卻不適用以太坊。

還記得當(dāng)面Parity多簽名錢包被黑導(dǎo)致150000以太幣被偷的惡劣事件嗎校哎?在整個(gè)攻擊中两波,就因?yàn)殄X包中的一個(gè)bug導(dǎo)致很多巨額錢包的資金被清空。而唯一的解決方案就是嘗試以比黑客更快的速度闷哆,利用相同的漏洞攻擊剩余的錢包腰奋,來把以太幣重新分配給它們合法的所有者。

要是有一種方法可以在智能合約部署之后阳准,還能對它們進(jìn)行升級氛堕,那該多好...

引入代理模式

盡管想升級已經(jīng)部署的智能合約中的代碼是不可能的馏臭,但是可以通過設(shè)計(jì)一個(gè)代理合約結(jié)構(gòu)野蝇,這個(gè)結(jié)構(gòu)可以讓你可以通過新部署一個(gè)合約的方式讼稚,來實(shí)現(xiàn)升級主要的處理邏輯的目的。

代理結(jié)構(gòu)模式就像下面這張圖一樣:所有消息通過一個(gè)代理合約來間接調(diào)用最新部署的邏輯合約绕沈。如果想要升級的話锐想,只需要部署一個(gè)新的合約,然后在代理合約中更新引用新的合約地址就可以了乍狐。

1Proxy0

作為實(shí)現(xiàn)zeppelin_os的一部分赠摇,zeppelin正致力于實(shí)現(xiàn)集中代理模式。目前已經(jīng)探索出來的有下面三個(gè):

  1. 繼承存儲模式 Inherited Storage
  2. 永久存儲模式 Eternal Storage
  3. 非結(jié)構(gòu)化存儲模式 Unstructured Storage

所有三種模式都依賴低階的delegatecall浅蚪。盡管solidity提供了一個(gè)delegatecall方法藕帜,但它只能返回true或者false來顯示調(diào)用是否成功,而不是允許你操作返回的數(shù)據(jù)惜傲。

在我們深入了解之前洽故,理解兩個(gè)關(guān)鍵的概念很重要:

  • 當(dāng)調(diào)用一個(gè)合約中并不支持的的方法時(shí),就會(huì)調(diào)用合約中的fallback方法盗誊。你可以自己寫一個(gè)fallback函數(shù)來處理這種場景时甚。代理合約就是用自定義的fallback方法將調(diào)用重定向到其他合約實(shí)現(xiàn)。
  • 每當(dāng)合約A授權(quán)對另一個(gè)合約B的調(diào)用時(shí)哈踱,它就會(huì)在合約A的上下文中執(zhí)行合約B的代碼荒适。這就意味著msg.valuemsg.sender的值會(huì)被保留。并且對存儲的修改將會(huì)作用在合約A的存儲上开镣。

zeppelin的代理合約刀诬,為了可以返回調(diào)用邏輯合約后的結(jié)果,實(shí)現(xiàn)了自己的delegatecall方法邪财,所有模式都是這樣舅列。如果你想要使用zeppelin的代理合約代碼,你就要理解代碼的每一個(gè)細(xì)節(jié)卧蜓。讓我們先來看看它是如何發(fā)揮作用的帐要,以及理解為了達(dá)到目的它所使用的assembly操作碼。

 assembly {
    let ptr := mload(0x40)
    calldatacopy(ptr, 0, calldatasize)
    let result := delegatecall(gas, _impl, ptr, calldatasize, 0, 0)
    let size := returndatasize
    returndatacopy(ptr, 0, size)

    switch result
    case 0 { revert(ptr, size) }
    default { return(ptr, size) }
 }

為了授權(quán)對另一個(gè)合約中方法的調(diào)用弥奸,我們需要把它賦值給proxy合約接收的msg.data榨惠。因?yàn)?code>msg.data是bytes類型的,是一個(gè)動(dòng)態(tài)的數(shù)據(jù)結(jié)構(gòu)盛霎,所以它在msg.data的第一個(gè)字(word赠橙,也就是32個(gè)字節(jié))中的存儲長度會(huì)不一樣。如果我們想要只取出真正的數(shù)據(jù)愤炸,我們需要跳過第一個(gè)字(word)期揪,從msg.data0x20(32個(gè)字節(jié))開始。然而规个,我們會(huì)用到兩個(gè)操作碼來實(shí)現(xiàn)此目的凤薛。我們會(huì)使用calldatasize來獲取msg.data的大小姓建,以及calldatacopy來把它復(fù)制到ptr所指向的位置。

注意到我們是如何初始化ptr變量的缤苫。在solidity中速兔,內(nèi)存槽中的0x40位置是和特殊的,因?yàn)樗鎯α酥赶蛳乱粋€(gè)可用自由內(nèi)存的指針活玲。每次當(dāng)你想往內(nèi)存里存儲一個(gè)變量時(shí)涣狗,你都要檢查存儲在0x40的值。這就是你變量即將存放的位置∈婧叮現(xiàn)在我們知道了我們要在哪兒存變量镀钓,我們就可以使用calldatacopy,把大小為calldatasize的calldata從0開始啊復(fù)制到ptr指向的那個(gè)位置了镀迂。

let ptr := mload(0x40)
calldatacopy(ptr, 0, calldatasize)

我們再看看下面的assembly代碼(使用delegatecall操作碼)

let result := delegatecall(gas, _impl,  ptr, calldatasize, 0, 0)

解釋一下上面的參數(shù):

  • gas:函數(shù)執(zhí)行所需的gas
  • _impl:我們調(diào)用的邏輯合約的地址
  • ptr:內(nèi)存指針(指向數(shù)據(jù)開始存儲的地方)
  • calldatasize:傳入的數(shù)據(jù)大小
  • 0:調(diào)用邏輯合約后的返回值掸宛。我們沒有使用這個(gè)參數(shù)因?yàn)槲覀冞€不知道返回值的大小,所以不能把它賦值給一個(gè)變量招拙。我們可以后面可以進(jìn)一步使用returndata操作碼來獲取這些信息唧瘾。
  • 0:返回值的大小。這個(gè)參數(shù)也沒有被使用因?yàn)槲覀儧]有機(jī)會(huì)創(chuàng)造一個(gè)臨時(shí)變量用來存儲返回值别凤。鑒于我們在調(diào)用其他合約之前無法知道它的大惺涡颉(所以就無法創(chuàng)造臨時(shí)變量呀)。我們稍后可以用returndatasize操作碼來得到這個(gè)值规哪。

下面一行代碼就是用returndatasize操作碼得到了返回?cái)?shù)據(jù)的大星笤ァ:

let size := returndatasize

我們使用這個(gè)返回值的大小,來把返回值復(fù)制到ptr指向的內(nèi)存诉稍,使用returndatacopy來達(dá)到這個(gè)目的:

returndatacopy(ptr, 0, size)

最后蝠嘉,switch語句要么返回 【返回值】,要么拋出錯(cuò)誤杯巨,如果發(fā)生錯(cuò)誤的話蚤告。

storage_proxy

很好,我們現(xiàn)在有了一個(gè)從邏輯合約中獲取正確結(jié)果的方法服爷。

現(xiàn)在杜恰,我們理解了代理合約是如何工作的。下面就讓我們正式學(xué)習(xí)三種模式:繼承存儲模式仍源、永久存儲模式和非結(jié)構(gòu)化存儲模式心褐。

這三種方法用不同的方法來解決同一個(gè)難點(diǎn):怎樣確保邏輯合約不會(huì)重寫/覆蓋代理中的狀態(tài)變量。

任何代理結(jié)構(gòu)模式的主要問題就是如何分配存儲笼踩。記住逗爹,既然我們使用一個(gè)合約來存儲,另一個(gè)合約來實(shí)現(xiàn)邏輯嚎于,它們中的任何一個(gè)都有可能重寫一個(gè)已經(jīng)使用的存儲槽掘而。這意味著如果代理合約有一個(gè)狀態(tài)變量在某個(gè)存儲槽中存儲著最新的邏輯合約地址挟冠,但是邏輯合約卻不知道的話,那么邏輯合約可能就會(huì)在那個(gè)槽中存一些其他數(shù)據(jù)镣屹,這樣就把代理合約中的重要信息覆蓋了圃郊。zeppelin的這三種方法代表了架構(gòu)合約系統(tǒng)的三種途徑价涝,實(shí)現(xiàn)通過代理模式升級合約的目的女蜈。

使用繼承存儲模式升級

繼承存儲方法要求邏輯合約內(nèi)部也實(shí)現(xiàn)代理合約內(nèi)的存儲結(jié)構(gòu)。代理合約和邏輯合約都要繼承完全一樣的存儲結(jié)構(gòu)色瘩,來確保二者都支持存儲必要的代理合約的狀態(tài)變量伪窖。

當(dāng)探索這種模式時(shí),我們有這樣一個(gè)想法居兆,我們想要有一個(gè)Registry合約來追蹤不同版本的邏輯合約覆山。 為了升級成新的邏輯合約,你需要為它在Registry里注冊一個(gè)新的版本泥栖,并且要求代理合約中也升級成這個(gè)最新版本的邏輯合約簇宽。注意到有一個(gè)Registry合約并不影響存儲機(jī)制,實(shí)際上吧享,它可以應(yīng)用到這篇文章中提到的任意一種存儲模式中魏割。

inherit_storage

如何初始化

  1. 部署Registry合約
  2. 部署一個(gè)邏輯合約的最初版本(V1),確保它繼承了Upgradeable合約
  3. Registry合約中注冊這個(gè)最初版本(V1)的地址
  4. 要求Registry合約創(chuàng)建一個(gè)UpgradeabilityProxy實(shí)例
  5. 調(diào)用你的UpgrageabilityProxy實(shí)例來升級到你最初版本(V1)

如何升級

  1. 部署一個(gè)繼承了你最初版本合約的新版本(V2)钢颂,==確保它保留了代理合約和最初版本邏輯合約中的存儲結(jié)構(gòu)==
  2. Registry中注冊合約的新版本
  3. 調(diào)用你的UpgradeabilityProxy實(shí)例來升級到最新注冊的版本

tips

我們可以在未來部署的邏輯合約中升級現(xiàn)有方法钞它、創(chuàng)造新的方法以及新的狀態(tài)變量,但仍然調(diào)用同一個(gè)UpgradeabilityProxy合約殊鞭。

使用永久存儲模式升級

在永久存儲模式中遭垛,存儲模式用一個(gè)獨(dú)立的合約(代理和邏輯合約都要繼承這個(gè)合約)來定義。這個(gè)存儲合約保留了所有邏輯合約需要的狀態(tài)變量操灿,因?yàn)榇砗霞s也會(huì)知道這些變量的存在(因?yàn)槔^承)锯仪,它就可以為升級定義自己的狀態(tài)變量,不用考慮覆蓋變量這些問題趾盐。注意到所有的版本的邏輯合約都不可以再定義任何額外的狀態(tài)變量卵酪。所有版本的邏輯合約都必須一直使用一開始就定義好的永久存儲架構(gòu)。

這種應(yīng)用在zeppelin labs項(xiàng)目中提供了實(shí)現(xiàn)谤碳,并且同時(shí)引入了代理所有權(quán)的概念溃卡。一個(gè)代理的所有者是唯一一個(gè)可以升級代理并指定一個(gè)新的邏輯合約的地址,也是唯一一個(gè)可以轉(zhuǎn)移所有權(quán)的地址蜒简。

eternal_storage

如何初始化

  1. 部署一個(gè)EternalStorageProxy實(shí)例
  2. 部署一個(gè)邏輯合約的最初版本(V1)
  3. 調(diào)用EternalStorageProxy實(shí)例來升級到這個(gè)最初版本合約的地址
  4. 如果你的邏輯合約依賴自己的構(gòu)造函數(shù)(constructor)來設(shè)置某個(gè)初始狀態(tài)瘸羡,那么在它和代理合約產(chǎn)生聯(lián)系之后,之前的這些狀態(tài)就要重新修改搓茬,因?yàn)榇砗霞s的存儲并不知道(邏輯合約里的)這些值犹赖。EternalStorageProxy有一個(gè)叫upgradeToAndCall的函數(shù)專門來調(diào)用一些邏輯合約中的方法队他,一旦代理合約升級到最新版本時(shí),就把連接到的那個(gè)邏輯合約里的初始設(shè)置重新設(shè)置一遍峻村。

如何升級

  1. 部署一個(gè)邏輯合約的最新版本(v2)麸折,確保它也包含永久存儲結(jié)構(gòu)。
  2. 調(diào)用EternalStorageProxy實(shí)例來升級到最新版本粘昨。

tips

這是沒有增加太多開銷同時(shí)很直觀的邏輯合約垢啼。 以后的邏輯合約可以升級現(xiàn)有的方法或者創(chuàng)造新的方法,但是不能引入新的狀態(tài)變量张肾。

使用非結(jié)構(gòu)化存儲升級

非結(jié)構(gòu)化存儲模式和繼承存儲類似芭析,但是不要求邏輯合約繼承任何和升級相關(guān)的狀態(tài)變量。這個(gè)模式使用代理合約中定義的非結(jié)構(gòu)化的存儲槽來保存升級所需的數(shù)據(jù)吞瞪。

在代理合約中馁启,我們定義了一個(gè)常量,每當(dāng)哈希的時(shí)候芍秆,就給出一個(gè)足夠隨機(jī)的存儲位置來存儲代理合約需要調(diào)用的邏輯合約的地址惯疙。

bytes32 private constant implementationPosition = 
                     keccak256("org.zeppelinos.proxy.implementation");

因?yàn)槌A?恒定)狀態(tài)變量并不占用存儲槽,所以并不用擔(dān)心implementationPosition會(huì)不小心被邏輯合約占用妖啥。鑒于solidity在存儲中放置狀態(tài)變量的方法霉颠,依然有非常非常非常小的概率可能發(fā)生要存儲新變量的存儲槽已經(jīng)被占用了。

通過使用這種模式迹栓,任何版本的邏輯合約都不需要知道代理合約的存儲結(jié)構(gòu)掉分,但是所有后一個(gè)版本的邏輯合約都必須繼承上一個(gè)版本的存儲變量。就像在繼承存儲模式中一樣,未來的邏輯合約可以更新現(xiàn)有的方法,也可以創(chuàng)建新的方法和新的狀態(tài)變量脯倒。

這個(gè)模式也使用了代理合約所有權(quán)的概念。只有代理合約的所有者可以更新邏輯合約的地址不从,也是唯一可以轉(zhuǎn)移所有權(quán)的地址。

unstructured_storage

如何初始化

  1. 部署OwnedUpgradeabilityProxy實(shí)例
  2. 部署邏輯合約的初始版本(V1)
  3. 調(diào)用OwnedUpgradeabilityProxy實(shí)例來更新到初始版本的邏輯合約
  4. 如果你的邏輯合約依賴自己的構(gòu)造函數(shù)(constructor)來設(shè)置某個(gè)初始狀態(tài)犁跪,那么在它和代理合約產(chǎn)生聯(lián)系之后椿息,之前的這些狀態(tài)就要重新修改,因?yàn)榇砗霞s的存儲并不知道(邏輯合約里的)這些值坷衍。OwnedUpgradeabilityProxy有一個(gè)upgradeToAndCall方法專門來調(diào)用一些邏輯合約中的方法寝优,一旦代理合約升級到最新版本時(shí),就把連接到的那個(gè)邏輯合約里的初始設(shè)置重新設(shè)置一遍枫耳。

如何升級

  1. 部署一個(gè)新版本的邏輯合約(V2)乏矾,確保它繼承了上一個(gè)版本里的狀態(tài)變量結(jié)構(gòu)。
  2. 調(diào)用ownedUpgradeabilityProxy實(shí)例來升級到新版本合約的地址。

tips

這個(gè)方法很棒钻心,因?yàn)樗恍枰壿嫼霞s知道它是整個(gè)代理系統(tǒng)的一部分凄硼。

關(guān)于升級

重要:如果你的邏輯合約依賴自己的構(gòu)造器來設(shè)置一些初始狀態(tài)的話,這個(gè)過程在新版本的邏輯合約注冊到代理中時(shí)需要重新做一遍捷沸。舉個(gè)例子摊沉,邏輯合約繼承Zeppelin中的Ownable合約,這很常見痒给。當(dāng)你的邏輯合約繼承Ownable说墨,它也就繼承了Ownable的構(gòu)造器,構(gòu)造器會(huì)在合約創(chuàng)建的時(shí)候就設(shè)置合約的所有者是誰侈玄。當(dāng)你讓代理合約來使用你的邏輯合約的時(shí)候婉刀,代理合約是不知道邏輯合約的所有者是誰的吟温。

升級代理合約的一種常見的模式就代理立即對邏輯合約調(diào)用一個(gè)初始化方法序仙。這個(gè)初始化方法應(yīng)該去模仿在構(gòu)造器中做的一些事情。同時(shí)你也想要一個(gè)標(biāo)識鲁豪,用來確保你不可以再次對同一個(gè)邏輯合約調(diào)用初始化方法潘悼。(只能調(diào)用一次)

你的邏輯合約看上去可能像下面這樣:

contract Token is Ownable {
   ...
   bool internal _initialized;
   
   function initialize(address owner) public {
      require(!_initialized);
      setOwner(owner);
      _initialized = true;
   }
   ...
}

當(dāng)然這取決于你的部署策略,你可以寫一個(gè)幫助部署的合約爬橡,或者你可以可以單獨(dú)部署代理合約和邏輯合約治唤。如果你單獨(dú)部署的話,你需要使用upgradeToAndCall把代理合約鏈接到邏輯合約上糙申,這看上去就會(huì)像下面這樣:

const initializeData = encodeCall('initialize', ['address'], [tokenOwner])
await proxy.upgradeToAndCall(logicContract.address, initializeData, { from: proxyOwner })

結(jié)論

代理模式的概念已經(jīng)出來有一段時(shí)間了宾添,但是由于太復(fù)雜了、害怕引入安全漏洞以及繞過了區(qū)塊鏈不可變的特性柜裸,它還沒有被廣泛接受缕陕。過去的解決方法在關(guān)于未來版本的邏輯合約可以添加和修改的東西上有嚴(yán)格的限制,這很不靈活疙挺。但是很顯然扛邑,開發(fā)者對于可升級合約的需求很迫切。zeppelin提供并且測試了三種模式铐然,他們致力于幫助開發(fā)者架構(gòu)自己的項(xiàng)目蔬崩,引入可升級特性。

盡管代理模式的概念出來也有段時(shí)間了搀暑,但是它的應(yīng)用依然處在非常早期沥阳。很開心看到越來越多的高級DApp架構(gòu)通過這種方式得以實(shí)現(xiàn)。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末自点,一起剝皮案震驚了整個(gè)濱河市桐罕,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖冈绊,帶你破解...
    沈念sama閱讀 217,406評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件侠鳄,死亡現(xiàn)場離奇詭異,居然都是意外死亡死宣,警方通過查閱死者的電腦和手機(jī)伟恶,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,732評論 3 393
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來毅该,“玉大人博秫,你說我怎么就攤上這事】粽疲” “怎么了挡育?”我有些...
    開封第一講書人閱讀 163,711評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長朴爬。 經(jīng)常有香客問我即寒,道長,這世上最難降的妖魔是什么召噩? 我笑而不...
    開封第一講書人閱讀 58,380評論 1 293
  • 正文 為了忘掉前任母赵,我火速辦了婚禮,結(jié)果婚禮上具滴,老公的妹妹穿的比我還像新娘凹嘲。我一直安慰自己,他們只是感情好构韵,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,432評論 6 392
  • 文/花漫 我一把揭開白布周蹭。 她就那樣靜靜地躺著,像睡著了一般疲恢。 火紅的嫁衣襯著肌膚如雪凶朗。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,301評論 1 301
  • 那天冈闭,我揣著相機(jī)與錄音俱尼,去河邊找鬼。 笑死萎攒,一個(gè)胖子當(dāng)著我的面吹牛遇八,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播耍休,決...
    沈念sama閱讀 40,145評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼刃永,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了羊精?” 一聲冷哼從身側(cè)響起斯够,我...
    開封第一講書人閱讀 39,008評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后读规,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體抓督,經(jīng)...
    沈念sama閱讀 45,443評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,649評論 3 334
  • 正文 我和宋清朗相戀三年束亏,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了铃在。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,795評論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡碍遍,死狀恐怖定铜,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情怕敬,我是刑警寧澤揣炕,帶...
    沈念sama閱讀 35,501評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站东跪,受9級特大地震影響畸陡,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜越庇,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,119評論 3 328
  • 文/蒙蒙 一罩锐、第九天 我趴在偏房一處隱蔽的房頂上張望奉狈。 院中可真熱鬧卤唉,春花似錦、人聲如沸仁期。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,731評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽跛蛋。三九已至熬的,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間赊级,已是汗流浹背押框。 一陣腳步聲響...
    開封第一講書人閱讀 32,865評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留理逊,地道東北人橡伞。 一個(gè)月前我還...
    沈念sama閱讀 47,899評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像晋被,于是被迫代替她去往敵國和親兑徘。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,724評論 2 354

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理羡洛,服務(wù)發(fā)現(xiàn)挂脑,斷路器,智...
    卡卡羅2017閱讀 134,654評論 18 139
  • 本文翻譯自:https://github.com/ConsenSys/smart-contract-best-pr...
    tolak閱讀 4,950評論 4 21
  • 盤空頂上山風(fēng)烈,獨(dú)立崖邊解愁眠崭闲。 窮目四顧茫茫然肋联,莫辨身處仙與俗。 拂袖可攬?jiān)浦性碌蠹螅げ街比肓柘龅睢?鶴唳聲聲如貫...
    牧笛少年閱讀 230評論 0 0
  • 我在橋上看風(fēng)景 手里舉著相機(jī) 橋上的人不知 爭著擺姿勢 殊不知 我的心里只有你 等待的只是你的到來 撐著遮陽傘 你...
    拾穗的時(shí)光閱讀 111評論 0 1
  • 雪怕太陽薄翅,草怕霜沙兰。 人怕沒錢,情怕傷翘魄! 花怕凋零鼎天,田怕荒。 心怕欺騙暑竟,友怕誆斋射! 你沒用了,遠(yuǎn)離你的人就多了但荤, 這就...
    王路柯閱讀 1,066評論 0 0