Today Slock.it has published the Ethereum Hard Fork Specification in its blog.
Here's the post:
After evaluating a lot of feedback, discussing the options described here and finding support for a multisig that would send ether to withdraw contracts for the edge cases, the following hard fork specification is proposed as having an acceptable tradeoff between auditability and completeness:
Specification:
The DAO (0xbb9bc244d798123fde783fcc1c72d3bb8c189413), its extraBalance (0x807640a13483f8ac783c557fcdf27be11ea4ac7a), all children of the DAO creator (0x4a574510c7014e4ae985403536074abe582adfc8) and the extrabalance of each child are encoded into a list L
at block 1880000.
L
is a list of all DAO addresses and extraBalanceAccount objects at ~~~ embed:faf894be44ee82b7ea07fd032eaf69a8, or in a list form https://gist.github.com/gavofyork/af747a034fbee2920f862ed352d32347. It can also be generated via the following python script: gist metadata:Q0plbnR6c2NoL2ZhZjg5NGJlNDRlZTgyYjdlYTA3ZmQwMzJlYWY2OWE4LCBvciBpbiBhIGxpc3QgZm9ybSBodHRwczovL2dpc3QuZ2l0aHViLmNvbS9nYXZvZnlvcmsvYWY3NDdhMDM0ZmJlZTI5MjBmODYyZWQzNTJkMzIzNDcuIEl0IGNhbiBhbHNvIGJlIGdlbmVyYXRlZCB2aWEgdGhlIGZvbGxvd2luZyBweXRob24gc2NyaXB0Og== ~~~
from ethereum.utils import mk_contract_address
source = ‘0x4a574510c7014e4ae985403536074abe582adfc8’
L = [‘0xbb9bc244d798123fde783fcc1c72d3bb8c189413’,
‘0x807640a13483f8ac783c557fcdf27be11ea4ac7a’] + \
[‘0x’+mk_contract_address(source, i).encode(‘hex’)
for i in range(1,58)] + \
[‘0x’+mk_contract_address(mk_contract_address(source, i), 0).encode(‘hex’)
for i in range(1, 58)]
Or using this script:
At the beginning of block 1920000, all ether throughout all accounts in L
will be transferred to contract account C
, which is deployed at 0xbf4ed7b27f1d666546e30d74d50d173d20bca754. You can verify the solidity source code of C
at https://etherscan.io/address/0xbf4ed7b27f1d666546e30d74d50d173d20bca754#code.
The contract C
includes a withdraw function that allows DAO token holders to exchange their DAO tokens for ETH at a rate of 100:1 (NOTE: in base units, the exchange rate is 1:1 as ether has 10¹⁸ wei in 1 ETH whereas DAO has 10¹⁶ base units in 1 DAO). The trusteeWithdraw() function allows the curator multisig to withdraw the excess ether which cannot be withdrawn via the withdraw function. This allows the curator multisig to simultaneously:
- Rescue all childDAOs including “legitimate” splits that may contain attackers
- Allow the DAO token holders that sent ether to the extraBalance to withdraw that ether using an automated script as quickly as possible but still provide time for an appropriate security review.
- Cover all edge cases including the 30% ETH losses from DAO token holders who split after the attack
The totalSupply() is 11538165987024671407837618 (see here); this.balance would return the balance of the withdrawable contract (namely, 12072877.497524777 ETH — X where X
is the amount of ether already withdrawn, and 12072877.497524777 is the amount of ether in L
at the time of this writing), and mainDAO.balanceOf(this) is the amount of DAO tokens collected by the the contract itself, which is equal to the amount of ether that has already been withdrawn divided by 100 (note again the 1:1 base-unit correspondence).
Hence, for example, if 100,000 ETH has already been withdrawn, the trusteeWithdraw() function would withdraw 11972877 + 100000–11538165 = 534712 ETH, which is exactly the intended amount. If 200000 ETH has already been withdrawn, it would withdraw 11872877 + 200000–11538165 = 534712 ETH. If the function is called multiple times, the second time it would withdraw 0 ETH.
Withdrawing for main DAO token holders is a two-step process: first call The DAO approve() function to approve the withdraw contract to transfer your DAO tokens, and then call the withdraw contract’s withdraw() function. In the geth command line, this would look as follows:
dao = web3.eth.contract([{“constant”:true,”inputs”:[],”name”:”name”,”outputs”:[{“name”:””,”type”:”string”}],”type”:”function”},{“constant”:false,”inputs”:[{“name”:”_spender”,”type”:”address”},{“name”:”_amount”,”type”:”uint256"}],”name”:”approve”,”outputs”:[{“name”:”success”,”type”:”bool”}],”type”:”function”},{“constant”:true,”inputs”:[],”name”:”totalSupply”,”outputs”:[{“name”:””,”type”:”uint256"}],”type”:”function”},{“constant”:false,”inputs”:[{“name”:”_from”,”type”:”address”},{“name”:”_to”,”type”:”address”},{“name”:”_amount”,”type”:”uint256"}],”name”:”transferFrom”,”outputs”:[{“name”:”success”,”type”:”bool”}],”type”:”function”},{“constant”:true,”inputs”:[],”name”:”decimals”,”outputs”:[{“name”:””,”type”:”uint8"}],”type”:”function”},{“constant”:true,”inputs”:[],”name”:”standard”,”outputs”:[{“name”:””,”type”:”string”}],”type”:”function”},{“constant”:true,”inputs”:[{“name”:”_owner”,”type”:”address”}],”name”:”balanceOf”,”outputs”:[{“name”:”balance”,”type”:”uint256"}],”type”:”function”},{“constant”:true,”inputs”:[],”name”:”symbol”,”outputs”:[{“name”:””,”type”:”string”}],”type”:”function”},{“constant”:false,”inputs”:[{“name”:”_to”,”type”:”address”},{“name”:”_amount”,”type”:”uint256"}],”name”:”transfer”,”outputs”:[{“name”:”success”,”type”:”bool”}],”type”:”function”},{“constant”:true,”inputs”:[{“name”:”_owner”,”type”:”address”},{“name”:”_spender”,”type”:”address”}],”name”:”allowance”,”outputs”:[{“name”:”remaining”,”type”:”uint256"}],”type”:”function”},{“anonymous”:false,”inputs”:[{“indexed”:true,”name”:”_from”,”type”:”address”},{“indexed”:true,”name”:”_to”,”type”:”address”},{“indexed”:false,”name”:”_amount”,”type”:”uint256"}],”name”:”Transfer”,”type”:”event”},{“anonymous”:false,”inputs”:[{“indexed”:true,”name”:”_owner”,”type”:”address”},{“indexed”:true,”name”:”_spender”,”type”:”address”},{“indexed”:false,”name”:”_amount”,”type”:”uint256"}],”name”:”Approval”,”type”:”event”}]).at(‘0xbb9bc244d798123fde783fcc1c72d3bb8c189413’)
withdrawable = web3.eth.contract([{“constant”:false,”inputs”:[],”name”:”trusteeWithdraw”,”outputs”:[],”type”:”function”},{“constant”:false,”inputs”:[],”name”:”withdraw”,”outputs”:[],”type”:”function”},{“constant”:true,”inputs”:[],”name”:”mainDAO”,”outputs”:[{“name”:””,”type”:”address”}],”type”:”function”},{“constant”:true,”inputs”:[],”name”:”trustee”,”outputs”:[{“name”:””,”type”:”address”}],”type”:”function”}]).at(‘0xbf4ed7b27f1d666546e30d74d50d173d20bca754)
dao.approve(withdrawable.address, dao.balanceOf(eth.accounts[0]), {from: eth.accounts[0]})
withdrawable.withdraw({from: eth.accounts[0], gas: 500000})
This procedure has been tested. See this transaction:
https://etherscan.io/tx/0xa2d43ef6ec0b34ffe83628ccacc21df47858140e6a3fe5a33c078fede26ac109.
The extradata of blocks X to X+9 inclusive must be: 0x64616f2d686172642d666f726b (this is needed so that header only clients (fast sync, light client) can differentiate between peers on different chains). DAO-aware “anti-fork” clients can accept block headers between block X and X+9 only if their extradata is NOT the above.
Implementations:
Go-ethereum:
https://github.com/ethereum/go-ethereum/pull/2814
Parity:
https://github.com/ethcore/parity/pull/1483
Misc links:
Diff between (already-audited) Ethereum foundation multisig (left) and the Curator multisig (right):
https://www.diffchecker.com/lasyum7z
Here you have the official link to the post https://blog.slock.it/hard-fork-specification-24b889e70703#.99km4xnmy
What if , and I mean what if the DAO hacker was two or three steps ahead for instance:
"If they do this, I will do this, If they do that , I will do that"
Now that Ether has shown its "playbook" the hacker can act accordingly, but just a guesstimation.
You're right, that could happen :/