如何在硬分叉後有條件地將乙太幣發送到另一個帳戶以保護自己免受重放攻擊
DAO 合約於 2016 年 6 月 17 日遭到攻擊,攻擊者利用DAO 合約程式碼中的遞歸呼叫漏洞耗盡了價值約5000 萬美元的乙太幣。
攻擊者將被盜的乙太幣轉移到幾個子 DAO 中,而羅賓漢集團將剩餘的資金轉移到其他子 DAO 中。
乙太坊區塊鏈於2016 年 7 月 20 日在#1,920,000區塊中硬分叉,以將乙太幣從 The DAO 合約及其子 DAO 轉移到WithdrawDAO 合約中。
雖然大多數乙太坊節點客戶端執行的是硬分叉區塊鏈(ETH),但少數乙太坊節點客戶端執行的是非硬分叉經典區塊鏈(ETC)。
乙太坊節點客戶端可以配置為使用硬分叉區塊鍊或非硬分叉區塊鏈。一些節點已配置為將交易從硬分叉區塊鏈中繼到非硬分叉區塊鏈,反之亦然。在 ETH 區塊鏈上執行的交易極有可能在 ETC 區塊鏈上重播,而 ETC 區塊鏈上的交易極有可能在 ETH 區塊鏈上重播。
在硬分叉和非硬分叉的區塊鏈上,多個拆分合約已部署在同一合約地址。
您如何使用這些拆分合約有條件地將乙太幣發送到硬分叉的 ETH 鍊或非硬分叉的 ETC Classic 鏈中的另一個賬戶?
DAO 退款
2017 年 6 月 2 日更新
來自警告:不要使用 SafeConditionalHFTransfer!或正確使用:
SafeConditionalHFTransfer避免了在 DAO 硬分叉後許多乙太幣被錯誤地移動到錯誤的鏈上。到目前為止,已經有 20549 個 txns + 16022 個 internalTxns 通過位於0x1e143b2588705dfea63a17f2032ca123df995ce0的SafeConditionalHFTransfer。作者今天早上聯繫我,約 67,317.257581981046981598 ETH ~ USD 14,892,596.89 (@ $221.23/ETH) 錯誤地發送到合約。
使用此合約時,您必須呼叫
classicTransfer(...)
或transfer(...)
函式將您的 ETH 或 ETC 引導到預期鏈。如果您將 ETH(或 ETC)直接發送到合約地址,您的 ETH(或 ETC)將不會被重定向到目標鏈上的目標地址,而是永遠被困在該合約中。由於 ETH 和 ETC 鏈上的最新客戶端都內置了EIP155 重放保護,您不必再使用它
SafeConditionalHFTransfer
了。只要確保您使用的是最新的客戶端,EIP155!以下是實現 EIP155 的主要客戶端和版本:
geth
Go Ethereum - 自Let There Be Light (v1.5.0) 2016 年11 月 16 日以來的 EIP155。最新發布的帽子戲法 (v1.6.5)。- Parity - 自Civility (v1.4) 2016 年11 月 7 日以來的 EIP155。最新版本來自https://parity.io/parity.html>或<https://github.com/paritytech/parity/releases
- MyEtherWallet(記得使用正確的 URL https://www.myetherwallet.com/)具有 EIP155 重放保護。
這個警告也被放在瞭如何在硬分叉後有條件地將乙太幣發送到另一個帳戶以保護自己免受重放攻擊的答案的頂部。
2016 年11 月 26 日更新- 請參閱 @eth 對上述問題的回答,因為
geth
1.5.3 已實施重放攻擊保護。概括
如果您想通過合約將以
geth
太幣轉移到硬分叉或非硬分叉經典鏈,請使用下面的方法 1。SafeConditionalHFTransfer
如果您想通過合約將以
geth
太幣轉移到硬分叉和非硬分叉經典鏈,請使用下面的方法 2。ReplaySafeSplitV2
如果您想使用乙太坊錢包通過合約將乙太幣轉移到硬分叉或非硬分叉的經典鏈,請使用下面的方法3
SafeConditionalHFTransfer
。如果您想使用乙太坊錢包通過合約將乙太幣轉移到硬分叉和非硬分叉的經典鏈,請使用下面的方法 4
ReplaySafeSplitV2
。任何一個賬戶都可以是您的來源賬戶,以便將資金發送回您在其中一個鏈上的賬戶。上述方法依賴於一些乙太坊節點將交易從硬分叉鏈中繼到非硬分叉鏈,反之亦然。如果交易停止中繼到其他網路,則交易將僅在您的乙太坊節點傳輸的鏈上執行。
警告
- 先轉入少量測試
- 如果您希望您的交易在兩條鏈上執行,請僅轉移不超過硬分叉前賬戶餘額的金額。
問題
- 如果您在其中一條鏈上轉移的乙太幣金額超過您的賬戶餘額,則該交易將在該鏈上被拒絕,但在另一條鏈上執行。這將導致您的交易隨機數在兩條鏈中不同步,並阻止您在兩條鏈上自動執行交易。請參閱退出非硬分叉乙太坊後的問題。
- 如果您的交易未在兩條鏈中執行,則您的交易隨機數可能在兩條鏈中不同步。如果您仍想在 OTHER 鏈上執行交易,則必須將您的乙太坊節點客戶端同步到該 OTHER 鏈。
如果您想將預硬分叉的 ETC 發送到 Exchange ETC 賬戶
- 您可以使用您的
support-hard-fork
或oppose-hard-fork
乙太坊節點客戶端來執行此操作。- 在交易所網站,獲取ETC充值地址。
- 使用乙太坊錢包
classicTransfer
中的geth
或僅在非硬分叉經典鏈上轉賬的方法。- 首先轉移少量測試量。在區塊鏈瀏覽器(例如 http://etherscan.io/ )上檢查您的 ETH 賬戶,您應該注意到只有一小部分從您的賬戶中扣除了用於
SafeConditionalHFTransfer
退回您的 ETH 的合約的 gas 成本。- 在 ETC 交易出現在您的交易所 ETC 存款地址之前,您將不得不等待一小段時間,因為中繼會為出現在 ETC 鏈上的交易增加短暫的延遲。
2016 年 8 月 25 日更新
乙太坊錢包和 Mist Beta 0.8.2現在具有重放預防功能:
重放預防
我們添加了一項高級功能,以防止您的交易在其他鏈上重放,例如 ethereum classic。這使您可以完全阻止經典轉賬發生,或者使用該交易將相同的金額發送到不同的合約,例如新創建的賬戶或交易所。如果您想完全分離您的所有交易,我們建議您創建兩個新賬戶,一個用於乙太坊,另一個用於經典,然後將您的所有資金轉移到它們中(記住您需要乙太幣來轉移代幣),確保每個帳戶在另一條鏈上的乙太幣為 0 - 這樣做一次會阻止任何未來的交易被重播。要使用此功能,請使用發送頁面上的“更多選項”按鈕。
此功能還支持拆分令牌,但它是非常實驗性的,不適用於所有令牌。由於所有這些都是使用合約完成的,因此首先您需要通過點擊“批准代幣轉移”來允許該合約代表您移動代幣。
與往常一樣,這些功能是實驗性的,應先進行少量測試。儘管大多數交易都在兩條鏈上重放,但有些交易可能不會出於多種原因。此外,一些交易所在從合約地址接收乙太幣時遇到問題 - 如果是這種情況,請聯繫交易所。
我們還從 Mist 應用程序中刪除了所有 Fork 程式碼,因此如果您想使用 Ether Classic,您必須直接從他們的儲存庫下載 Classic Mist 或使用您自己的節點作為您錢包的後端(乙太坊錢包並且 Mist 可以連接到任何節點),就像您對專用網路所做的那樣。
重放保護合約可以在0x1ca4a86bba124426507d1ef67ad271cc5a02820a找到。
方法 1 - 使用
geth
和SafeConditionalHFTransfer
契約確保您執行的是 geth 版本 1.4.10或更高版本。並使用 –support-dao-fork 選項執行您的 geth 命令,以便您位於硬分叉區塊鏈上。使用
transfer(...)
或classicTransfer(...)
函式進行傳輸:user@Kumquat:~$ geth console // Allow chain to sync var fromAccount = "{from account}"; var toAccount = "{to account}"; var amount = web3.toWei(1.123, "ether"); personal.unlockAccount(fromAccount, "{password}") var safeConditionalHFTransferAddress = "0x1e143b2588705dfea63a17f2032ca123df995ce0"; var safeConditionalHFTransferABI = [{"constant":false,"inputs":[{"name":"to","type":"address"}],"name":"transfer","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"to","type":"address"}],"name":"classicTransfer","outputs":[],"type":"function"},{"inputs":[],"type":"constructor"}]; var safeConditionalHFTransfer = eth.contract(safeConditionalHFTransferABI).at(safeConditionalHFTransferAddress); // WARNING - Run the next statement to transfer ETH on the hard-forked chain // This will only cost some gas on the non-hard-forked ETC Classic chain // Test with a small amount first var transfer = safeConditionalHFTransfer.transfer(toAccount, {from: fromAccount, value: amount}); console.log(transfer); // WARNING - Run the next statement to transfer ETC on the non-hard-forked Classic chain // This will only cost some gas on the hard-forked ETH chain // Test with a small amount first var classicTransfer = safeConditionalHFTransfer.classicTransfer(toAccount, {from: fromAccount, value: amount}); console.log(classicTransfer);
方法 2 - 使用
geth
和ReplaySafeSplitV2
契約確保您執行的是 geth 版本 1.4.10或更高版本。並使用 –support-dao-fork 選項執行您的 geth 命令,以便您位於硬分叉區塊鏈上。使用
split(...)
功能轉移:user@Kumquat:~$ geth console // Allow chain to sync var fromAccount = "{from account}"; var toAccountFork = "{to account on forked chain}"; var toAccountNoFork = "{to account on non-forked chain}"; var amount = web3.toWei(1.123, "ether"); personal.unlockAccount(fromAccount, "{password}") var replaySafeSplitV2Address = "0xaBbb6bEbFA05aA13e908EaA492Bd7a8343760477"; var replaySafeSplitV2ABI = [{"constant":false,"inputs":[{"name":"targetFork","type":"address"},{"name":"targetNoFork","type":"address"}],"name":"split","outputs":[{"name":"","type":"bool"}],"type":"function"}]; var replaySafeSplitV2 = eth.contract(replaySafeSplitV2ABI).at(replaySafeSplitV2Address); var transfer = replaySafeSplitV2.split(toAccountFork, toAccountNoFork, {from: fromAccount, value: amount}); console.log(transfer);
方法 3 - 使用乙太坊錢包和
SafeConditionalHFTransfer
合約請注意,以下
ReplaySafeSplitV2
合約內置了更多安全功能。確保您已下載Ethereum Wallet 0.8.1或更高版本。
第一次啟動乙太坊錢包 0.8.1 時,對問題“你想啟動與漏洞相關的資金恢復到可以由 The DAO 代幣持有者提取的契約的鏈嗎?”選擇“是”。 . 您現在已經選擇使用硬分叉的乙太坊鏈。
在乙太坊錢包中,選擇頂部菜單中的契約頁面。點擊觀看契約。
- 輸入契約名稱
SafeConditionalHFTransfer
- 輸入契約地址
0x1e143b2588705dfea63a17f2032ca123df995ce0
- 在 JSON INTERFACE 中輸入以下文本
[{"constant":false,"inputs":[{"name":"to","type":"address"}],"name":"transfer","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"to","type":"address"}],"name":"classicTransfer","outputs":[],"type":"function"},{"inputs":[],"type":"constructor"}]
- 點擊確定。您的 Watch 合約應如下所示:
僅在硬分叉鏈上轉移
這會將你的 ETH 轉移到硬分叉鏈上的目標賬戶,並且只會在非硬分叉的 Classic 鏈上花費你的 gas。
在頂部菜單中選擇契約頁面。點擊安全條件高頻傳輸。在頁面右側的 WRITE TO CONTRACT 下,選擇 function
Transfer
。在To - address欄位中輸入您在硬分叉鏈上的目標地址。在執行自中,選擇您的帳戶。在發送 ETHER欄位下輸入 ETH 數量。點擊執行按鈕,輸入您的密碼並確認。這是螢幕圖像:僅在非硬分叉經典鏈上轉移
這會將您的 ETH 轉移到非硬分叉 Classic 鏈上的目標賬戶,並且只會在硬分叉鏈上花費您的 gas。
在頂部菜單中選擇契約頁面。點擊安全條件高頻傳輸。在頁面右側的 WRITE TO CONTRACT 下,選擇 function
Classic Transfer
。在To-address欄位中輸入您在非硬分叉鏈上的目標地址。在執行自中,選擇您的帳戶。在發送 ETHER欄位下輸入 ETH (ETC) 的數量。點擊執行按鈕,輸入您的密碼並確認。這是螢幕圖像:方法 4 - 使用乙太坊錢包和
ReplaySafeSplitV2
合約2016 年 9 月 4 日更新 22:27這是@chevdor 在thedao.slack.com/messages/general在更安全的 ReplaySafeSplit 智能合約版本
ReplaySafeSplit
中討論的新版本。這個新版本在將您的乙太幣發送到地址之前檢查您指定的地址不是 0x0000…0000。此部分已使用新的契約詳細資訊進行了更新。確保您已下載Ethereum Wallet 0.8.1或更高版本。
第一次啟動乙太坊錢包 0.8.1 時,對問題“你想啟動與漏洞相關的資金恢復到可以由 The DAO 代幣持有者提取的契約的鏈嗎?”選擇“是”。 . 您現在已經選擇使用硬分叉的乙太坊鏈。
在乙太坊錢包中,選擇頂部菜單中的契約頁面。點擊觀看契約。
- 輸入契約名稱
ReplaySafeSplitV2
- 輸入一個CONTRACT ADDRESS
0x8201...
(這個舊地址只在ETH鏈上。如果你使用了這個合約,請刪除你的Watch Contract,並用下面的新地址重新創建Watch Contract。)- 輸入契約地址
0xaBbb6bEbFA05aA13e908EaA492Bd7a8343760477
- 在 JSON INTERFACE 中輸入以下文本
[{"constant":false,"inputs":[{"name":"targetFork","type":"address"},{"name":"targetNoFork","type":"address"}],"name":"split","outputs":[{"name":"","type":"bool"}],"type":"function"}]
- 點擊確定。您的 Watch 合約應如下所示:
轉移
這會將您的 ETH 轉移到兩個賬戶,第一個是硬分叉鏈上的目標賬戶,第二個是非硬分叉經典鏈上的目標賬戶。
在頂部菜單中選擇契約頁面。點擊 REPLAYSAFESPLITV2。在頁面右側的 WRITE TO CONTRACT 下,選擇 function
Split
。在Target fork - address欄位中輸入硬分叉鏈上的目標地址。在Target no fork - address欄位中輸入您在非硬分叉 Classic鏈上的目標地址。在執行自中,選擇您的帳戶。在發送乙太幣欄位下輸入乙太幣數量。點擊執行按鈕,輸入您的密碼並確認。這是螢幕圖像:
SafeConditionalHFTransfer
契約請注意,以下
ReplaySafeSplitV2
合約內置了更多安全功能。以下是SafeConditionalHFTransfer 合約的原始碼(建議於 2016 年 7 月 26 日
@shoraibit
)。該合約不依賴於未來餘額超過 1,000,000 乙太幣的 WithdrawDAO 合約。contract ClassicCheck { function isClassic() constant returns (bool isClassic); } contract SafeConditionalHFTransfer { bool classic; function SafeConditionalHFTransfer() { classic = ClassicCheck(0x882fb4240f9a11e197923d0507de9a983ed69239).isClassic(); } function classicTransfer(address to) { if (!classic) msg.sender.send(msg.value); else to.send(msg.value); } function transfer(address to) { if (classic) msg.sender.send(msg.value); else to.send(msg.value); } }
該合約依賴於ClassicCheck 合約:
contract ClassicCheck { bool public classic; function ClassicCheck() { if (address(0xbf4ed7b27f1d666546e30d74d50d173d20bca754).balance > 1000000 ether) classic = false; else classic = true; } function isClassic() constant returns (bool isClassic) { return classic; } }
部署合約時
SafeConditionalHFTransfer
,它使用ClassicCheck
合約來確定程式碼是部署到硬分叉鏈還是非硬分叉鏈。這個檢查是在WithdrawDAO的餘額超過 1,000,000 時完成的。SafeConditionalHFTransfer
應該始終有效,因為它不再需要檢查 WithdrawDAO 餘額的餘額。
ReplaySafeSplitV2
契約2016 年 9 月 4 日更新 22:27這是@chevdor 在thedao.slack.com/messages/general在更安全的 ReplaySafeSplit 智能合約版本
ReplaySafeSplit
中討論的新版本。這個新版本會檢查您指定的地址是否不是在將您的乙太幣發送到地址之前。0x0000...0000
以下是ReplaySafeSplitV2 合約的原始碼:
contract RequiringFunds { modifier NeedEth () { if (msg.value <= 0 ) throw; _ } } contract AmIOnTheFork { function forked() constant returns(bool); } contract ReplaySafeSplit is RequiringFunds { // address private constant oracleAddress = 0x8128B12cABc6043d94BD3C4d9B9455077Eb18807; // testnet address private constant oracleAddress = 0x2bd2326c993dfaef84f696526064ff22eba5b362; // mainnet // Fork oracle to use AmIOnTheFork amIOnTheFork = AmIOnTheFork(oracleAddress); // Splits the funds into 2 addresses function split(address targetFork, address targetNoFork) NeedEth returns(bool) { // The 2 checks are to ensure that users provide BOTH addresses // and prevent funds to be sent to 0x0 on one fork or the other. if (targetFork == 0) throw; if (targetNoFork == 0) throw; if (amIOnTheFork.forked() // if we are on the fork && targetFork.send(msg.value)) { // send the ETH to the targetFork address return true; } else if (!amIOnTheFork.forked() // if we are NOT on the fork && targetNoFork.send(msg.value)) { // send the ETH to the targetNoFork address return true; } throw; // don't accept value transfer, otherwise it would be trapped. } // Reject value transfers. function() { throw; } }
以下是來自非硬分叉經典區塊鏈的 VM 程式碼,它與經過驗證的 VM 程式碼 +硬分叉鏈上的原始碼相匹配。
user@PussyWillow:~$ geth -exec 'eth.getCode("0xaBbb6bEbFA05aA13e908EaA492Bd7a8343760477")' attach "0x6060604052361561001f5760e060020a60003504630f2c93298114610028575b6100005b610002565b6100406004356024356000348190116100e157610002565b60408051918252519081900360200190f35b80547f16c72721000000000000000000000000000000000000000000000000000000006060908152600160a060020a0391909116906316c727219060649060209060048187876161da5a03f11561000257505060405151905080156100d25750604051600160a060020a038416908290349082818181858883f193505050505b1561010f575060015b92915050565b82600160a060020a0316600014156100f857610002565b81600160a060020a03166000141561005257610002565b600060009054906101000a9004600160a060020a0316600160a060020a03166316c727216040518160e060020a0281526004018090506020604051808303816000876161da5a03f11561000257505060405151159050801561018c5750604051600160a060020a038316908290349082818181858883f193505050505b15610023575060016100db56"
該合約依賴於AmIONTheFork 合約:
contract AmIOnTheFork { bool public forked = false; address constant darkDAO = 0x304a554a310c7e546dfe434669c62820b7d83490; // Check the fork condition during creation of the contract. // This function should be called between block 1920000 and 1921200. // Approximately between 2016-07-20 12:00:00 UTC and 2016-07-20 17:00:00 UTC. // After that the status will be locked in. function update() { if (block.number >= 1920000 && block.number <= 1921200) { forked = darkDAO.balance < 3600000 ether; } } function() { throw; } }