VOTING POWER100.00%
DOWNVOTE POWER100.00%
RESOURCE CREDITS100.00%
REPUTATION PROGRESS37.28%
Net Worth
0.542USD
STEEM
0.000STEEM
SBD
0.183SBD
Own SP
7.833SP
Detailed Balance
| STEEM | ||
| balance | 0.000STEEM | STEEM |
| market_balance | 0.000STEEM | STEEM |
| savings_balance | 0.000STEEM | STEEM |
| reward_steem_balance | 0.000STEEM | STEEM |
| STEEM POWER | ||
| Own SP | 7.833SP | SP |
| Delegated Out | 0.000SP | SP |
| Delegation In | 0.000SP | SP |
| Effective Power | 7.833SP | SP |
| Reward SP (pending) | 0.000SP | SP |
| SBD | ||
| sbd_balance | 0.183SBD | SBD |
| sbd_conversions | 0.000SBD | SBD |
| sbd_market_balance | 0.000SBD | SBD |
| savings_sbd_balance | 0.000SBD | SBD |
| reward_sbd_balance | 0.000SBD | SBD |
{
"balance": "0.000 STEEM",
"savings_balance": "0.000 STEEM",
"reward_steem_balance": "0.000 STEEM",
"vesting_shares": "12739.259326 VESTS",
"delegated_vesting_shares": "0.000000 VESTS",
"received_vesting_shares": "0.000000 VESTS",
"sbd_balance": "0.183 SBD",
"savings_sbd_balance": "0.000 SBD",
"reward_sbd_balance": "0.000 SBD",
"conversions": []
}Account Info
| name | deviatefish |
| id | 34065 |
| rank | 149,198 |
| reputation | 3953494373 |
| created | 2016-07-25T01:00:30 |
| recovery_account | steem |
| proxy | None |
| post_count | 4 |
| comment_count | 0 |
| lifetime_vote_count | 0 |
| witnesses_voted_for | 0 |
| last_post | 2016-08-03T17:51:24 |
| last_root_post | 2016-08-02T18:58:03 |
| last_vote_time | 2016-08-18T01:07:18 |
| proxied_vsf_votes | 0, 0, 0, 0 |
| can_vote | 1 |
| voting_power | 9,949 |
| delayed_votes | 0 |
| balance | 0.000 STEEM |
| savings_balance | 0.000 STEEM |
| sbd_balance | 0.183 SBD |
| savings_sbd_balance | 0.000 SBD |
| vesting_shares | 12739.259326 VESTS |
| delegated_vesting_shares | 0.000000 VESTS |
| received_vesting_shares | 0.000000 VESTS |
| reward_vesting_balance | 0.000000 VESTS |
| vesting_balance | 0.000 STEEM |
| vesting_withdraw_rate | 0.000000 VESTS |
| next_vesting_withdrawal | 1969-12-31T23:59:59 |
| withdrawn | 0 |
| to_withdraw | 0 |
| withdraw_routes | 0 |
| savings_withdraw_requests | 0 |
| last_account_recovery | 1970-01-01T00:00:00 |
| reset_account | null |
| last_owner_update | 1970-01-01T00:00:00 |
| last_account_update | 1970-01-01T00:00:00 |
| mined | No |
| sbd_seconds | 0 |
| sbd_last_interest_payment | 1970-01-01T00:00:00 |
| savings_sbd_last_interest_payment | 1970-01-01T00:00:00 |
{
"id": 34065,
"name": "deviatefish",
"owner": {
"weight_threshold": 1,
"account_auths": [],
"key_auths": [
[
"STM7tv98jDJ1UBQmWPUY2P6NZ1cJqmfG2LMqyTvAN74ruoS3KPTv7",
1
]
]
},
"active": {
"weight_threshold": 1,
"account_auths": [],
"key_auths": [
[
"STM6E9BLj429ZT3DRf5VB449JoGoQvmZVnzH1W5YmjPvdsrEqTVXc",
1
]
]
},
"posting": {
"weight_threshold": 1,
"account_auths": [],
"key_auths": [
[
"STM5LsLpABdQo1P4dVneY2D3mt9LpoHGSEm6ZLwQemsVrrMc24r3W",
1
]
]
},
"memo_key": "STM87UyCtKKKk66U8h1gXmf6uohK2d4oMacvpkDj57huLKJZ3VLQC",
"json_metadata": "",
"posting_json_metadata": "",
"proxy": "",
"last_owner_update": "1970-01-01T00:00:00",
"last_account_update": "1970-01-01T00:00:00",
"created": "2016-07-25T01:00:30",
"mined": false,
"recovery_account": "steem",
"last_account_recovery": "1970-01-01T00:00:00",
"reset_account": "null",
"comment_count": 0,
"lifetime_vote_count": 0,
"post_count": 4,
"can_vote": true,
"voting_manabar": {
"current_mana": 9949,
"last_update_time": 1471482438
},
"downvote_manabar": {
"current_mana": 0,
"last_update_time": 1469408430
},
"voting_power": 9949,
"balance": "0.000 STEEM",
"savings_balance": "0.000 STEEM",
"sbd_balance": "0.183 SBD",
"sbd_seconds": "0",
"sbd_seconds_last_update": "2016-07-26T20:26:27",
"sbd_last_interest_payment": "1970-01-01T00:00:00",
"savings_sbd_balance": "0.000 SBD",
"savings_sbd_seconds": "0",
"savings_sbd_seconds_last_update": "1970-01-01T00:00:00",
"savings_sbd_last_interest_payment": "1970-01-01T00:00:00",
"savings_withdraw_requests": 0,
"reward_sbd_balance": "0.000 SBD",
"reward_steem_balance": "0.000 STEEM",
"reward_vesting_balance": "0.000000 VESTS",
"reward_vesting_steem": "0.000 STEEM",
"vesting_shares": "12739.259326 VESTS",
"delegated_vesting_shares": "0.000000 VESTS",
"received_vesting_shares": "0.000000 VESTS",
"vesting_withdraw_rate": "0.000000 VESTS",
"next_vesting_withdrawal": "1969-12-31T23:59:59",
"withdrawn": 0,
"to_withdraw": 0,
"withdraw_routes": 0,
"curation_rewards": 0,
"posting_rewards": 102,
"proxied_vsf_votes": [
0,
0,
0,
0
],
"witnesses_voted_for": 0,
"last_post": "2016-08-03T17:51:24",
"last_root_post": "2016-08-02T18:58:03",
"last_vote_time": "2016-08-18T01:07:18",
"post_bandwidth": 10000,
"pending_claimed_accounts": 0,
"vesting_balance": "0.000 STEEM",
"reputation": 3953494373,
"transfer_history": [],
"market_history": [],
"post_history": [],
"vote_history": [],
"other_history": [],
"witness_votes": [],
"tags_usage": [],
"guest_bloggers": [],
"rank": 149198
}Withdraw Routes
| Incoming | Outgoing |
|---|---|
Empty | Empty |
{
"incoming": [],
"outgoing": []
}From Date
To Date
2019/07/25 01:48:57
2019/07/25 01:48:57
| parent author | deviatefish |
| parent permlink | the-dao-a-contract-engineered-for-failure |
| author | steemitboard |
| permlink | steemitboard-notify-deviatefish-20190725t014857000z |
| title | |
| body | Congratulations @deviatefish! You received a personal award! <table><tr><td>https://steemitimages.com/70x70/http://steemitboard.com/@deviatefish/birthday3.png</td><td>Happy Birthday! - You are on the Steem blockchain for 3 years!</td></tr></table> <sub>_You can view [your badges on your Steem Board](https://steemitboard.com/@deviatefish) and compare to others on the [Steem Ranking](https://steemitboard.com/ranking/index.php?name=deviatefish)_</sub> ###### [Vote for @Steemitboard as a witness](https://v2.steemconnect.com/sign/account-witness-vote?witness=steemitboard&approve=1) to get one more award and increased upvotes! |
| json metadata | {"image":["https://steemitboard.com/img/notify.png"]} |
| Transaction Info | Block #34958327/Trx cd9f1dd79aab312a228c5ff82cef0b30af482202 |
View Raw JSON Data
{
"trx_id": "cd9f1dd79aab312a228c5ff82cef0b30af482202",
"block": 34958327,
"trx_in_block": 6,
"op_in_trx": 0,
"virtual_op": 0,
"timestamp": "2019-07-25T01:48:57",
"op": [
"comment",
{
"parent_author": "deviatefish",
"parent_permlink": "the-dao-a-contract-engineered-for-failure",
"author": "steemitboard",
"permlink": "steemitboard-notify-deviatefish-20190725t014857000z",
"title": "",
"body": "Congratulations @deviatefish! You received a personal award!\n\n<table><tr><td>https://steemitimages.com/70x70/http://steemitboard.com/@deviatefish/birthday3.png</td><td>Happy Birthday! - You are on the Steem blockchain for 3 years!</td></tr></table>\n\n<sub>_You can view [your badges on your Steem Board](https://steemitboard.com/@deviatefish) and compare to others on the [Steem Ranking](https://steemitboard.com/ranking/index.php?name=deviatefish)_</sub>\n\n\n###### [Vote for @Steemitboard as a witness](https://v2.steemconnect.com/sign/account-witness-vote?witness=steemitboard&approve=1) to get one more award and increased upvotes!",
"json_metadata": "{\"image\":[\"https://steemitboard.com/img/notify.png\"]}"
}
]
}zetachangupvoted (100.00%) @deviatefish / on-proof-of-work-and-consensus2017/09/23 12:36:33
zetachangupvoted (100.00%) @deviatefish / on-proof-of-work-and-consensus
2017/09/23 12:36:33
| voter | zetachang |
| author | deviatefish |
| permlink | on-proof-of-work-and-consensus |
| weight | 10000 (100.00%) |
| Transaction Info | Block #15718468/Trx 5fbb04339824d337ad350ecd85f01c0ab69fdd6b |
View Raw JSON Data
{
"trx_id": "5fbb04339824d337ad350ecd85f01c0ab69fdd6b",
"block": 15718468,
"trx_in_block": 1,
"op_in_trx": 0,
"virtual_op": 0,
"timestamp": "2017-09-23T12:36:33",
"op": [
"vote",
{
"voter": "zetachang",
"author": "deviatefish",
"permlink": "on-proof-of-work-and-consensus",
"weight": 10000
}
]
}johanhermanupvoted (100.00%) @deviatefish / on-proof-of-work-and-consensus
johanhermanupvoted (100.00%) @deviatefish / on-proof-of-work-and-consensus
| voter | johanherman |
| author | deviatefish |
| permlink | on-proof-of-work-and-consensus |
| weight | 10000 (100.00%) |
| Transaction Info | Block #4426379/Trx 4bc1c7665396ff542caa0df8c060ed439480892a |
View Raw JSON Data
{
"trx_id": "4bc1c7665396ff542caa0df8c060ed439480892a",
"block": 4426379,
"trx_in_block": 0,
"op_in_trx": 0,
"virtual_op": 0,
"timestamp": "2016-08-26T19:52:36",
"op": [
"vote",
{
"voter": "johanherman",
"author": "deviatefish",
"permlink": "on-proof-of-work-and-consensus",
"weight": 10000
}
]
}thechosenwhanupvoted (100.00%) @deviatefish / on-proof-of-work-and-consensus
thechosenwhanupvoted (100.00%) @deviatefish / on-proof-of-work-and-consensus
| voter | thechosenwhan |
| author | deviatefish |
| permlink | on-proof-of-work-and-consensus |
| weight | 10000 (100.00%) |
| Transaction Info | Block #4398248/Trx 8eb9e84463239168fe2890db258b5c8ce6d36ed4 |
View Raw JSON Data
{
"trx_id": "8eb9e84463239168fe2890db258b5c8ce6d36ed4",
"block": 4398248,
"trx_in_block": 0,
"op_in_trx": 0,
"virtual_op": 0,
"timestamp": "2016-08-25T20:19:27",
"op": [
"vote",
{
"voter": "thechosenwhan",
"author": "deviatefish",
"permlink": "on-proof-of-work-and-consensus",
"weight": 10000
}
]
}deviatefishflagged (-100.00%) @charleshosk / thoughts-on-an-ethereum-classic-roadmap
deviatefishflagged (-100.00%) @charleshosk / thoughts-on-an-ethereum-classic-roadmap
| voter | deviatefish |
| author | charleshosk |
| permlink | thoughts-on-an-ethereum-classic-roadmap |
| weight | -10000 (-100.00%) |
| Transaction Info | Block #4175683/Trx 4428625672458f8dbf83cf66837a35e88863e990 |
View Raw JSON Data
{
"trx_id": "4428625672458f8dbf83cf66837a35e88863e990",
"block": 4175683,
"trx_in_block": 1,
"op_in_trx": 0,
"virtual_op": 0,
"timestamp": "2016-08-18T01:07:18",
"op": [
"vote",
{
"voter": "deviatefish",
"author": "charleshosk",
"permlink": "thoughts-on-an-ethereum-classic-roadmap",
"weight": -10000
}
]
}dbrockupvoted (100.00%) @deviatefish / on-proof-of-work-and-consensus
dbrockupvoted (100.00%) @deviatefish / on-proof-of-work-and-consensus
| voter | dbrock |
| author | deviatefish |
| permlink | on-proof-of-work-and-consensus |
| weight | 10000 (100.00%) |
| Transaction Info | Block #4175459/Trx 7367c853d20784af4be83c443dbd414e534eac99 |
View Raw JSON Data
{
"trx_id": "7367c853d20784af4be83c443dbd414e534eac99",
"block": 4175459,
"trx_in_block": 2,
"op_in_trx": 0,
"virtual_op": 0,
"timestamp": "2016-08-18T00:56:03",
"op": [
"vote",
{
"voter": "dbrock",
"author": "deviatefish",
"permlink": "on-proof-of-work-and-consensus",
"weight": 10000
}
]
}| voter | poesis |
| author | deviatefish |
| permlink | re-cjentzsch-re-deviatefish-the-dao-a-contract-engineered-for-failure-20160803t175123680z |
| weight | 10000 (100.00%) |
| Transaction Info | Block #3961695/Trx a2a5fdefbeb81e098a257f40300a04d5656cf27a |
View Raw JSON Data
{
"trx_id": "a2a5fdefbeb81e098a257f40300a04d5656cf27a",
"block": 3961695,
"trx_in_block": 4,
"op_in_trx": 0,
"virtual_op": 0,
"timestamp": "2016-08-10T13:54:54",
"op": [
"vote",
{
"voter": "poesis",
"author": "deviatefish",
"permlink": "re-cjentzsch-re-deviatefish-the-dao-a-contract-engineered-for-failure-20160803t175123680z",
"weight": 10000
}
]
}veoxupvoted (100.00%) @deviatefish / the-dao-a-contract-engineered-for-failure
veoxupvoted (100.00%) @deviatefish / the-dao-a-contract-engineered-for-failure
| voter | veox |
| author | deviatefish |
| permlink | the-dao-a-contract-engineered-for-failure |
| weight | 10000 (100.00%) |
| Transaction Info | Block #3935085/Trx 868f7ae74a79d9b8b8194cc945dfca2b38cd89f0 |
View Raw JSON Data
{
"trx_id": "868f7ae74a79d9b8b8194cc945dfca2b38cd89f0",
"block": 3935085,
"trx_in_block": 1,
"op_in_trx": 0,
"virtual_op": 0,
"timestamp": "2016-08-09T15:37:24",
"op": [
"vote",
{
"voter": "veox",
"author": "deviatefish",
"permlink": "the-dao-a-contract-engineered-for-failure",
"weight": 10000
}
]
}poesisupvoted (100.00%) @deviatefish / the-dao-a-contract-engineered-for-failure
poesisupvoted (100.00%) @deviatefish / the-dao-a-contract-engineered-for-failure
| voter | poesis |
| author | deviatefish |
| permlink | the-dao-a-contract-engineered-for-failure |
| weight | 10000 (100.00%) |
| Transaction Info | Block #3803620/Trx b53f8bfb3bdede0c73ea3e37635a6b125ae0ea71 |
View Raw JSON Data
{
"trx_id": "b53f8bfb3bdede0c73ea3e37635a6b125ae0ea71",
"block": 3803620,
"trx_in_block": 2,
"op_in_trx": 0,
"virtual_op": 0,
"timestamp": "2016-08-05T01:32:09",
"op": [
"vote",
{
"voter": "poesis",
"author": "deviatefish",
"permlink": "the-dao-a-contract-engineered-for-failure",
"weight": 10000
}
]
}fran2kupvoted (100.00%) @deviatefish / the-dao-a-contract-engineered-for-failure
fran2kupvoted (100.00%) @deviatefish / the-dao-a-contract-engineered-for-failure
| voter | fran2k |
| author | deviatefish |
| permlink | the-dao-a-contract-engineered-for-failure |
| weight | 10000 (100.00%) |
| Transaction Info | Block #3779091/Trx 6c49205d89e7c910ed6d36c436a4e5f45a413535 |
View Raw JSON Data
{
"trx_id": "6c49205d89e7c910ed6d36c436a4e5f45a413535",
"block": 3779091,
"trx_in_block": 3,
"op_in_trx": 0,
"virtual_op": 0,
"timestamp": "2016-08-04T04:48:57",
"op": [
"vote",
{
"voter": "fran2k",
"author": "deviatefish",
"permlink": "the-dao-a-contract-engineered-for-failure",
"weight": 10000
}
]
}| parent author | cjentzsch |
| parent permlink | re-deviatefish-the-dao-a-contract-engineered-for-failure-20160803t102124726z |
| author | deviatefish |
| permlink | re-cjentzsch-re-deviatefish-the-dao-a-contract-engineered-for-failure-20160803t175123680z |
| title | |
| body | @@ -45,16 +45,156 @@ t out. +%5BE%5D Or... it won't let me. Apparently there's a 24-hour limit or something. That's nice, guess I won't be using this platform anymore...%0A%0A The rest |
| json metadata | {"tags":["ethereum"]} |
| Transaction Info | Block #3766482/Trx 8686ff567eea6d5d2806ffa5c7c0c926a7d951c2 |
View Raw JSON Data
{
"trx_id": "8686ff567eea6d5d2806ffa5c7c0c926a7d951c2",
"block": 3766482,
"trx_in_block": 0,
"op_in_trx": 0,
"virtual_op": 0,
"timestamp": "2016-08-03T18:16:51",
"op": [
"comment",
{
"parent_author": "cjentzsch",
"parent_permlink": "re-deviatefish-the-dao-a-contract-engineered-for-failure-20160803t102124726z",
"author": "deviatefish",
"permlink": "re-cjentzsch-re-deviatefish-the-dao-a-contract-engineered-for-failure-20160803t175123680z",
"title": "",
"body": "@@ -45,16 +45,156 @@\n t out. \n+%5BE%5D Or... it won't let me. Apparently there's a 24-hour limit or something. That's nice, guess I won't be using this platform anymore...%0A%0A\n The rest\n",
"json_metadata": "{\"tags\":[\"ethereum\"]}"
}
]
}| parent author | cjentzsch |
| parent permlink | re-deviatefish-the-dao-a-contract-engineered-for-failure-20160803t102124726z |
| author | deviatefish |
| permlink | re-cjentzsch-re-deviatefish-the-dao-a-contract-engineered-for-failure-20160803t175123680z |
| title | |
| body | Yeah... how to boolean logic. I'll edit that out. The rest still kind of holds true, though. As far as using `.call` over `.send`... That explanation doesn't really make a whole lot of sense to me. It was `.send` before, and was changed to `.call`. `.send` works with contracts, so I don't see why it's necessary, even to support generic contracts as recipients. The only difference between a `.send` and a `.call` with no data is that there's much less gas provided, preventing things like this from happening (when the gas amount is correct, that is). So again... why did `.send` need to be changed to `.call`? There are only places in the contract where `.send` and `.call` are used appropriately... But this one was specifically changed to be used inappropriately. Still doesn't answer the issue of the backdoor in the `refund` method, if the fueling goal isn't met. |
| json metadata | {"tags":["ethereum"]} |
| Transaction Info | Block #3765977/Trx 7051794fb9ef5f52b463d0155dd28c8a21079840 |
View Raw JSON Data
{
"trx_id": "7051794fb9ef5f52b463d0155dd28c8a21079840",
"block": 3765977,
"trx_in_block": 3,
"op_in_trx": 0,
"virtual_op": 0,
"timestamp": "2016-08-03T17:51:24",
"op": [
"comment",
{
"parent_author": "cjentzsch",
"parent_permlink": "re-deviatefish-the-dao-a-contract-engineered-for-failure-20160803t102124726z",
"author": "deviatefish",
"permlink": "re-cjentzsch-re-deviatefish-the-dao-a-contract-engineered-for-failure-20160803t175123680z",
"title": "",
"body": "Yeah... how to boolean logic.\n\nI'll edit that out. The rest still kind of holds true, though.\n\nAs far as using `.call` over `.send`... That explanation doesn't really make a whole lot of sense to me. It was `.send` before, and was changed to `.call`. `.send` works with contracts, so I don't see why it's necessary, even to support generic contracts as recipients.\n\nThe only difference between a `.send` and a `.call` with no data is that there's much less gas provided, preventing things like this from happening (when the gas amount is correct, that is).\n\nSo again... why did `.send` need to be changed to `.call`? There are only places in the contract where `.send` and `.call` are used appropriately... But this one was specifically changed to be used inappropriately.\n\nStill doesn't answer the issue of the backdoor in the `refund` method, if the fueling goal isn't met.",
"json_metadata": "{\"tags\":[\"ethereum\"]}"
}
]
}| parent author | deviatefish |
| parent permlink | the-dao-a-contract-engineered-for-failure |
| author | cjentzsch |
| permlink | re-deviatefish-the-dao-a-contract-engineered-for-failure-20160803t102124726z |
| title | |
| body | The statement "This means, if there are funds in either the rewardAccount or DAOrewardAccount, anyone can move them anywhere." is completely false. Only the owner of the funds (in this case the DAO itself through a proposal) is able to move the funds. This is ensured by this line: ``` if (msg.sender != owner || msg.value > 0 || (payOwnerOnly && _recipient != owner)) throw; ``` msg.sender needs to be the owner, which is the DAO. The reason why call was used instead of send, was to allow for generic contracts receiving the ether. Since this was the responsibility of the receiver himself, this is not a probem. Because of the rentry exploit it becomes a problem, and in this light, of course send should have been used. So all comes down to the reentry exploit. When the code for the DAO was written, we have not been aware of this exploit, therefore there are several places in the code were this can be exploited. So this type of bug exists in several places in the DAO code. But to say it was engineered for failing, because there is one type of bug in the contract, is absurd. |
| json metadata | {"tags":["ethereum"]} |
| Transaction Info | Block #3757048/Trx 497d8cb5dff2a1405aa540fd4d983e5c8a5b8568 |
View Raw JSON Data
{
"trx_id": "497d8cb5dff2a1405aa540fd4d983e5c8a5b8568",
"block": 3757048,
"trx_in_block": 5,
"op_in_trx": 0,
"virtual_op": 0,
"timestamp": "2016-08-03T10:21:24",
"op": [
"comment",
{
"parent_author": "deviatefish",
"parent_permlink": "the-dao-a-contract-engineered-for-failure",
"author": "cjentzsch",
"permlink": "re-deviatefish-the-dao-a-contract-engineered-for-failure-20160803t102124726z",
"title": "",
"body": "The statement \"This means, if there are funds in either the rewardAccount or DAOrewardAccount, anyone can move them anywhere.\" is completely false.\n\nOnly the owner of the funds (in this case the DAO itself through a proposal) is able to move the funds. This is ensured by this line: \n```\nif (msg.sender != owner || msg.value > 0 || (payOwnerOnly && _recipient != owner))\n throw;\n```\nmsg.sender needs to be the owner, which is the DAO.\n\nThe reason why call was used instead of send, was to allow for generic contracts receiving the ether. Since this was the responsibility of the receiver himself, this is not a probem. Because of the rentry exploit it becomes a problem, and in this light, of course send should have been used.\n\nSo all comes down to the reentry exploit.\nWhen the code for the DAO was written, we have not been aware of this exploit, therefore there are several places in the code were this can be exploited. So this type of bug exists in several places in the DAO code.\n\nBut to say it was engineered for failing, because there is one type of bug in the contract, is absurd.",
"json_metadata": "{\"tags\":[\"ethereum\"]}"
}
]
}siegocertioremupvoted (100.00%) @deviatefish / the-dao-a-contract-engineered-for-failure
siegocertioremupvoted (100.00%) @deviatefish / the-dao-a-contract-engineered-for-failure
| voter | siegocertiorem |
| author | deviatefish |
| permlink | the-dao-a-contract-engineered-for-failure |
| weight | 10000 (100.00%) |
| Transaction Info | Block #3754781/Trx a5bcbb9d99370c06ae5386feaf40b1b9fa26e294 |
View Raw JSON Data
{
"trx_id": "a5bcbb9d99370c06ae5386feaf40b1b9fa26e294",
"block": 3754781,
"trx_in_block": 2,
"op_in_trx": 0,
"virtual_op": 0,
"timestamp": "2016-08-03T08:27:45",
"op": [
"vote",
{
"voter": "siegocertiorem",
"author": "deviatefish",
"permlink": "the-dao-a-contract-engineered-for-failure",
"weight": 10000
}
]
}esmitupvoted (100.00%) @deviatefish / the-dao-a-contract-engineered-for-failure
esmitupvoted (100.00%) @deviatefish / the-dao-a-contract-engineered-for-failure
| voter | esmit |
| author | deviatefish |
| permlink | the-dao-a-contract-engineered-for-failure |
| weight | 10000 (100.00%) |
| Transaction Info | Block #3748924/Trx 4d543606c94b0370184953ddac694e4156e0b8ca |
View Raw JSON Data
{
"trx_id": "4d543606c94b0370184953ddac694e4156e0b8ca",
"block": 3748924,
"trx_in_block": 2,
"op_in_trx": 0,
"virtual_op": 0,
"timestamp": "2016-08-03T03:34:18",
"op": [
"vote",
{
"voter": "esmit",
"author": "deviatefish",
"permlink": "the-dao-a-contract-engineered-for-failure",
"weight": 10000
}
]
}| voter | deviatefish |
| author | logical |
| permlink | re-deviatefish-the-dao-a-contract-engineered-for-failure-20160803t014416579z |
| weight | 10000 (100.00%) |
| Transaction Info | Block #3747626/Trx 39f3b2038a26978963e731548e3d4dcc69310e18 |
View Raw JSON Data
{
"trx_id": "39f3b2038a26978963e731548e3d4dcc69310e18",
"block": 3747626,
"trx_in_block": 0,
"op_in_trx": 0,
"virtual_op": 0,
"timestamp": "2016-08-03T02:29:18",
"op": [
"vote",
{
"voter": "deviatefish",
"author": "logical",
"permlink": "re-deviatefish-the-dao-a-contract-engineered-for-failure-20160803t014416579z",
"weight": 10000
}
]
}liberigoupvoted (100.00%) @deviatefish / the-dao-a-contract-engineered-for-failure
liberigoupvoted (100.00%) @deviatefish / the-dao-a-contract-engineered-for-failure
| voter | liberigo |
| author | deviatefish |
| permlink | the-dao-a-contract-engineered-for-failure |
| weight | 10000 (100.00%) |
| Transaction Info | Block #3746775/Trx b50edd5054c8a46eff6dcac5554dfd3515a1638f |
View Raw JSON Data
{
"trx_id": "b50edd5054c8a46eff6dcac5554dfd3515a1638f",
"block": 3746775,
"trx_in_block": 1,
"op_in_trx": 0,
"virtual_op": 0,
"timestamp": "2016-08-03T01:46:39",
"op": [
"vote",
{
"voter": "liberigo",
"author": "deviatefish",
"permlink": "the-dao-a-contract-engineered-for-failure",
"weight": 10000
}
]
}| parent author | deviatefish |
| parent permlink | the-dao-a-contract-engineered-for-failure |
| author | logical |
| permlink | re-deviatefish-the-dao-a-contract-engineered-for-failure-20160803t014416579z |
| title | |
| body | Great article! I am looking forward to more of your forensic work here. That stock.it intentionally left back doors to rob the DAO is a major assertion, not yet proven but also far from seeming fantastical at this point. It's clear that they are sloppy, unprincipled liars. How though to find persuasive evidence that they did this intentionally, even if they did? |
| json metadata | {"tags":["ethereum"]} |
| Transaction Info | Block #3746727/Trx 039a9e23fc3e22c01816cd4362c0dba7f0ee3267 |
View Raw JSON Data
{
"trx_id": "039a9e23fc3e22c01816cd4362c0dba7f0ee3267",
"block": 3746727,
"trx_in_block": 2,
"op_in_trx": 0,
"virtual_op": 0,
"timestamp": "2016-08-03T01:44:15",
"op": [
"comment",
{
"parent_author": "deviatefish",
"parent_permlink": "the-dao-a-contract-engineered-for-failure",
"author": "logical",
"permlink": "re-deviatefish-the-dao-a-contract-engineered-for-failure-20160803t014416579z",
"title": "",
"body": "Great article! I am looking forward to more of your forensic work here. That stock.it intentionally left back doors to rob the DAO is a major assertion, not yet proven but also far from seeming fantastical at this point. It's clear that they are sloppy, unprincipled liars. How though to find persuasive evidence that they did this intentionally, even if they did?",
"json_metadata": "{\"tags\":[\"ethereum\"]}"
}
]
}logicalupvoted (100.00%) @deviatefish / the-dao-a-contract-engineered-for-failure
logicalupvoted (100.00%) @deviatefish / the-dao-a-contract-engineered-for-failure
| voter | logical |
| author | deviatefish |
| permlink | the-dao-a-contract-engineered-for-failure |
| weight | 10000 (100.00%) |
| Transaction Info | Block #3746675/Trx aa604c845f7c1c46b5590f1d2538d35ce8020dc9 |
View Raw JSON Data
{
"trx_id": "aa604c845f7c1c46b5590f1d2538d35ce8020dc9",
"block": 3746675,
"trx_in_block": 0,
"op_in_trx": 0,
"virtual_op": 0,
"timestamp": "2016-08-03T01:41:39",
"op": [
"vote",
{
"voter": "logical",
"author": "deviatefish",
"permlink": "the-dao-a-contract-engineered-for-failure",
"weight": 10000
}
]
}ranko-kflagged (-100.00%) @deviatefish / the-dao-a-contract-engineered-for-failure
ranko-kflagged (-100.00%) @deviatefish / the-dao-a-contract-engineered-for-failure
| voter | ranko-k |
| author | deviatefish |
| permlink | the-dao-a-contract-engineered-for-failure |
| weight | -10000 (-100.00%) |
| Transaction Info | Block #3745493/Trx 570f08f604b8c66b9e224551ecfb9147244f9671 |
View Raw JSON Data
{
"trx_id": "570f08f604b8c66b9e224551ecfb9147244f9671",
"block": 3745493,
"trx_in_block": 1,
"op_in_trx": 0,
"virtual_op": 0,
"timestamp": "2016-08-03T00:42:30",
"op": [
"vote",
{
"voter": "ranko-k",
"author": "deviatefish",
"permlink": "the-dao-a-contract-engineered-for-failure",
"weight": -10000
}
]
}| parent author | deviatefish |
| parent permlink | the-dao-a-contract-engineered-for-failure |
| author | solar |
| permlink | re-deviatefish-the-dao-a-contract-engineered-for-failure-20160803t001531274z |
| title | |
| body | I've wondered about this too. Thanks for doing this research, this whole situation continues to fuck up the community. There is still so much to learn from this event. |
| json metadata | {"tags":["ethereum"]} |
| Transaction Info | Block #3744953/Trx 4ee4e5e719c568cefbe69012a91c63bf472c3281 |
View Raw JSON Data
{
"trx_id": "4ee4e5e719c568cefbe69012a91c63bf472c3281",
"block": 3744953,
"trx_in_block": 4,
"op_in_trx": 0,
"virtual_op": 0,
"timestamp": "2016-08-03T00:15:30",
"op": [
"comment",
{
"parent_author": "deviatefish",
"parent_permlink": "the-dao-a-contract-engineered-for-failure",
"author": "solar",
"permlink": "re-deviatefish-the-dao-a-contract-engineered-for-failure-20160803t001531274z",
"title": "",
"body": "I've wondered about this too. Thanks for doing this research, this whole situation continues to fuck up the community. There is still so much to learn from this event.",
"json_metadata": "{\"tags\":[\"ethereum\"]}"
}
]
}solarupvoted (100.00%) @deviatefish / the-dao-a-contract-engineered-for-failure
solarupvoted (100.00%) @deviatefish / the-dao-a-contract-engineered-for-failure
| voter | solar |
| author | deviatefish |
| permlink | the-dao-a-contract-engineered-for-failure |
| weight | 10000 (100.00%) |
| Transaction Info | Block #3743575/Trx bad1845bec6271aeac0769e453c51c8134deacb1 |
View Raw JSON Data
{
"trx_id": "bad1845bec6271aeac0769e453c51c8134deacb1",
"block": 3743575,
"trx_in_block": 2,
"op_in_trx": 0,
"virtual_op": 0,
"timestamp": "2016-08-02T23:06:24",
"op": [
"vote",
{
"voter": "solar",
"author": "deviatefish",
"permlink": "the-dao-a-contract-engineered-for-failure",
"weight": 10000
}
]
}napsyupvoted (100.00%) @deviatefish / the-dao-a-contract-engineered-for-failure
napsyupvoted (100.00%) @deviatefish / the-dao-a-contract-engineered-for-failure
| voter | napsy |
| author | deviatefish |
| permlink | the-dao-a-contract-engineered-for-failure |
| weight | 10000 (100.00%) |
| Transaction Info | Block #3743540/Trx e0e64fcec79e05ce19354d6aa0b9a05afd8ef2bf |
View Raw JSON Data
{
"trx_id": "e0e64fcec79e05ce19354d6aa0b9a05afd8ef2bf",
"block": 3743540,
"trx_in_block": 2,
"op_in_trx": 0,
"virtual_op": 0,
"timestamp": "2016-08-02T23:04:39",
"op": [
"vote",
{
"voter": "napsy",
"author": "deviatefish",
"permlink": "the-dao-a-contract-engineered-for-failure",
"weight": 10000
}
]
}nixonnoxupvoted (100.00%) @deviatefish / the-dao-a-contract-engineered-for-failure
nixonnoxupvoted (100.00%) @deviatefish / the-dao-a-contract-engineered-for-failure
| voter | nixonnox |
| author | deviatefish |
| permlink | the-dao-a-contract-engineered-for-failure |
| weight | 10000 (100.00%) |
| Transaction Info | Block #3743460/Trx 2ed44a7e532ab16d797a091b42867deb081d58f6 |
View Raw JSON Data
{
"trx_id": "2ed44a7e532ab16d797a091b42867deb081d58f6",
"block": 3743460,
"trx_in_block": 1,
"op_in_trx": 0,
"virtual_op": 0,
"timestamp": "2016-08-02T23:00:39",
"op": [
"vote",
{
"voter": "nixonnox",
"author": "deviatefish",
"permlink": "the-dao-a-contract-engineered-for-failure",
"weight": 10000
}
]
}flowirinupvoted (100.00%) @deviatefish / the-dao-a-contract-engineered-for-failure
flowirinupvoted (100.00%) @deviatefish / the-dao-a-contract-engineered-for-failure
| voter | flowirin |
| author | deviatefish |
| permlink | the-dao-a-contract-engineered-for-failure |
| weight | 10000 (100.00%) |
| Transaction Info | Block #3742724/Trx 6a14d1ddfeda88cf5e4c47cc2fe8b13c2145cb31 |
View Raw JSON Data
{
"trx_id": "6a14d1ddfeda88cf5e4c47cc2fe8b13c2145cb31",
"block": 3742724,
"trx_in_block": 3,
"op_in_trx": 0,
"virtual_op": 0,
"timestamp": "2016-08-02T22:23:51",
"op": [
"vote",
{
"voter": "flowirin",
"author": "deviatefish",
"permlink": "the-dao-a-contract-engineered-for-failure",
"weight": 10000
}
]
}dbendorfupvoted (100.00%) @deviatefish / the-dao-a-contract-engineered-for-failure
dbendorfupvoted (100.00%) @deviatefish / the-dao-a-contract-engineered-for-failure
| voter | dbendorf |
| author | deviatefish |
| permlink | the-dao-a-contract-engineered-for-failure |
| weight | 10000 (100.00%) |
| Transaction Info | Block #3741201/Trx 40650151d2b9886d98760d136397c690aa2145b7 |
View Raw JSON Data
{
"trx_id": "40650151d2b9886d98760d136397c690aa2145b7",
"block": 3741201,
"trx_in_block": 6,
"op_in_trx": 0,
"virtual_op": 0,
"timestamp": "2016-08-02T21:07:36",
"op": [
"vote",
{
"voter": "dbendorf",
"author": "deviatefish",
"permlink": "the-dao-a-contract-engineered-for-failure",
"weight": 10000
}
]
}dskloetupvoted (100.00%) @deviatefish / the-dao-a-contract-engineered-for-failure
dskloetupvoted (100.00%) @deviatefish / the-dao-a-contract-engineered-for-failure
| voter | dskloet |
| author | deviatefish |
| permlink | the-dao-a-contract-engineered-for-failure |
| weight | 10000 (100.00%) |
| Transaction Info | Block #3740578/Trx 23226ad1485e215acac95d0d6f1cca2accc02156 |
View Raw JSON Data
{
"trx_id": "23226ad1485e215acac95d0d6f1cca2accc02156",
"block": 3740578,
"trx_in_block": 1,
"op_in_trx": 0,
"virtual_op": 0,
"timestamp": "2016-08-02T20:36:27",
"op": [
"vote",
{
"voter": "dskloet",
"author": "deviatefish",
"permlink": "the-dao-a-contract-engineered-for-failure",
"weight": 10000
}
]
}digitalnotvirupvoted (100.00%) @deviatefish / the-dao-a-contract-engineered-for-failure
digitalnotvirupvoted (100.00%) @deviatefish / the-dao-a-contract-engineered-for-failure
| voter | digitalnotvir |
| author | deviatefish |
| permlink | the-dao-a-contract-engineered-for-failure |
| weight | 10000 (100.00%) |
| Transaction Info | Block #3739777/Trx 6b999201db4c881bab605003f2bfcd897430ebf7 |
View Raw JSON Data
{
"trx_id": "6b999201db4c881bab605003f2bfcd897430ebf7",
"block": 3739777,
"trx_in_block": 5,
"op_in_trx": 0,
"virtual_op": 0,
"timestamp": "2016-08-02T19:56:15",
"op": [
"vote",
{
"voter": "digitalnotvir",
"author": "deviatefish",
"permlink": "the-dao-a-contract-engineered-for-failure",
"weight": 10000
}
]
}| parent author | deviatefish |
| parent permlink | the-dao-a-contract-engineered-for-failure |
| author | isaac.asimov |
| permlink | re-the-dao-a-contract-engineered-for-failure-20160802t185919 |
| title | Flesch Kincaid Grade Level |
| body | Hi! This post has a <a href="https://en.wikipedia.org/wiki/Flesch%E2%80%93Kincaid_readability_tests">Flesch-Kincaid</a> grade level of 9.9 and reading ease of 62%. This puts the writing level on par with Michael Crichton and Mitt Romney. |
| json metadata | |
| Transaction Info | Block #3738643/Trx 7a9ccbb6f75842b326d7cbd39877620a01dfa87b |
View Raw JSON Data
{
"trx_id": "7a9ccbb6f75842b326d7cbd39877620a01dfa87b",
"block": 3738643,
"trx_in_block": 1,
"op_in_trx": 0,
"virtual_op": 0,
"timestamp": "2016-08-02T18:59:21",
"op": [
"comment",
{
"parent_author": "deviatefish",
"parent_permlink": "the-dao-a-contract-engineered-for-failure",
"author": "isaac.asimov",
"permlink": "re-the-dao-a-contract-engineered-for-failure-20160802t185919",
"title": "Flesch Kincaid Grade Level",
"body": "Hi! This post has a <a href=\"https://en.wikipedia.org/wiki/Flesch%E2%80%93Kincaid_readability_tests\">Flesch-Kincaid</a> grade level of 9.9 and reading ease of 62%. This puts the writing level on par with Michael Crichton and Mitt Romney.",
"json_metadata": ""
}
]
}gaspotupvoted (0.10%) @deviatefish / the-dao-a-contract-engineered-for-failure
gaspotupvoted (0.10%) @deviatefish / the-dao-a-contract-engineered-for-failure
| voter | gaspot |
| author | deviatefish |
| permlink | the-dao-a-contract-engineered-for-failure |
| weight | 10 (0.10%) |
| Transaction Info | Block #3738629/Trx 3a7e4d94ed8d277ed82c5a4fcd9fc79e49a8aa3e |
View Raw JSON Data
{
"trx_id": "3a7e4d94ed8d277ed82c5a4fcd9fc79e49a8aa3e",
"block": 3738629,
"trx_in_block": 4,
"op_in_trx": 0,
"virtual_op": 0,
"timestamp": "2016-08-02T18:58:39",
"op": [
"vote",
{
"voter": "gaspot",
"author": "deviatefish",
"permlink": "the-dao-a-contract-engineered-for-failure",
"weight": 10
}
]
}| parent author | deviatefish |
| parent permlink | the-dao-a-contract-engineered-for-failure |
| author | gaspot |
| permlink | the-dao-a-contract-engineered-for-failure |
| title | |
| body | Nice @deviatefish Shot you an Upvote :) |
| json metadata | |
| Transaction Info | Block #3738629/Trx ced03ed205e7d5b66df2cc7141dde5bdfc8fea43 |
View Raw JSON Data
{
"trx_id": "ced03ed205e7d5b66df2cc7141dde5bdfc8fea43",
"block": 3738629,
"trx_in_block": 2,
"op_in_trx": 0,
"virtual_op": 0,
"timestamp": "2016-08-02T18:58:39",
"op": [
"comment",
{
"parent_author": "deviatefish",
"parent_permlink": "the-dao-a-contract-engineered-for-failure",
"author": "gaspot",
"permlink": "the-dao-a-contract-engineered-for-failure",
"title": "",
"body": "Nice @deviatefish \n Shot you an Upvote :)",
"json_metadata": ""
}
]
}deviatefishupvoted (100.00%) @deviatefish / the-dao-a-contract-engineered-for-failure
deviatefishupvoted (100.00%) @deviatefish / the-dao-a-contract-engineered-for-failure
| voter | deviatefish |
| author | deviatefish |
| permlink | the-dao-a-contract-engineered-for-failure |
| weight | 10000 (100.00%) |
| Transaction Info | Block #3738617/Trx 681bc2232609e05b24de8cda30dc2ff1f850b7df |
View Raw JSON Data
{
"trx_id": "681bc2232609e05b24de8cda30dc2ff1f850b7df",
"block": 3738617,
"trx_in_block": 1,
"op_in_trx": 0,
"virtual_op": 0,
"timestamp": "2016-08-02T18:58:03",
"op": [
"vote",
{
"voter": "deviatefish",
"author": "deviatefish",
"permlink": "the-dao-a-contract-engineered-for-failure",
"weight": 10000
}
]
}deviatefishpublished a new post: the-dao-a-contract-engineered-for-failure
deviatefishpublished a new post: the-dao-a-contract-engineered-for-failure
| parent author | |
| parent permlink | ethereum |
| author | deviatefish |
| permlink | the-dao-a-contract-engineered-for-failure |
| title | The DAO: A contract engineered for failure. |
| body | # The DAO: A contract engineered for failure. ## Forward So, I'll admit, when something fails as spectacularly as the DAO did, I can't help but watch. And then, after the dust has settled, I can't help but dive in and find out just what went wrong. My [previous post](https://steemit.com/ethereum/@deviatefish/on-proof-of-work-and-consensus) on Proof-of-Work and consensus was along the same lines: how was it possible for non-voting majority to lose to a strongly-motivated, voting minority? It raised some serious concerns (to me, anyway) about the security of PoW, especially as it pertains to the economic incentives that are supposed to add security to the network. After having successfully replicated the DAO attacker's attack, as well as an unrelated attack used to lock down splits, I decided to go a [thorough documentation pass](https://gist.github.com/DeviateFish/a2aff9181d69e4c24a9e42fbe47ca973) over the DAO's code, to see how these attacks fit together. In the process, I discovered a few more, as well as some very poor design decisions in other parts of the code. ## Engineered to Fail As it turns out, the DAO's code is written in such a way to allow for someone to exploit it in nearly every case: the "failed to meet funding" case, the "received rewards/DAOrewards" case, and finally, the split case. In fact, the only process in the DAO which could be said to operate smoothly and without exploit (as far as I can tell) is the execution of a proposal. This is... interesting, to say the least. So, let's break down all these failure modes, and look at the associated code. ## The `reward` and `DAOReward` exploit First, let's start with the [`ManagedAccount`](https://gist.github.com/DeviateFish/a2aff9181d69e4c24a9e42fbe47ca973#file-the-dao-annotated-sol-L160) contract. This is supposed to create an account that can be utilized by only a particular owner, unless, of course, a flag has been set that unrestricts access. Given that this is used for the [`extraBalance`](https://gist.github.com/DeviateFish/a2aff9181d69e4c24a9e42fbe47ca973#file-the-dao-annotated-sol-L271), [`rewardAccount`](https://gist.github.com/DeviateFish/a2aff9181d69e4c24a9e42fbe47ca973#file-the-dao-annotated-sol-L685), and [`DAOrewardAccount`](https://gist.github.com/DeviateFish/a2aff9181d69e4c24a9e42fbe47ca973#file-the-dao-annotated-sol-L686) subcontracts, it's a little interesting that two out of the three would be configured for unrestricted access. The most glaring problem here, of course, is the [`payOut`](https://gist.github.com/DeviateFish/a2aff9181d69e4c24a9e42fbe47ca973#file-the-dao-annotated-sol-L177) method. We'll drop that here (with my annotations) for easier reference: ``` // > Alright, here's where things get a little weird. This is a method // > for transferring value from this account to a specific recipient. // > However, it doesn't really have much in the way of protections against // > unauthorized people calling it. // > In fact, both the DAORewardAccount and rewardAccount `ManagedAccounts` // > deployed by the DAO specifically disable the "payOwnerOnly" flag on creation. // > This means anyone can call this function, with any amount, and any recipient. // > This is a pretty glaring security hole. // > Furthermore... there's the use of `call` over `send`. The point of this // > entire function is to simply transfer value from this account to a specified // > recipient. There's no need to `call` the recipient when a `send` will suffice. // > So not only do you have an unrestricted method for moving value from this account, // > you also have one that's intentionally invoking remote code. function payOut(address _recipient, uint _amount) returns (bool) { if (msg.sender != owner || msg.value > 0 || (payOwnerOnly && _recipient != owner)) throw; if (_recipient.call.value(_amount)()) { PayOut(_recipient, _amount); return true; } else { return false; } } ``` As noted, this function does a couple very suspect things. First, it uses `.call` when `.send` would be more appropriate. The idea is for it to move funds to a target account, and this is exactly what `.send` is supposed to do. Using `.call` here is quite interesting, especially since the authors [later](https://gist.github.com/DeviateFish/a2aff9181d69e4c24a9e42fbe47ca973#file-the-dao-annotated-sol-L874) [prove](https://gist.github.com/DeviateFish/a2aff9181d69e4c24a9e42fbe47ca973#file-the-dao-annotated-sol-L895) they know how and when to use `.send`, and when using `.call` is [actually appropriate](https://gist.github.com/DeviateFish/a2aff9181d69e4c24a9e42fbe47ca973#file-the-dao-annotated-sol-L906). Second, if `payOwnerOnly` is set to `false`, anyone can use this function. This means, if there are funds in either the `rewardAccount` or `DAOrewardAccount`, anyone can move them anywhere. This is a huge security hole that anyone reading the code should have been able to catch. Which, of course, just goes to show I never read the code all the way through, before. It's worth noting here that the former of these two flaws is something you'll want to keep in mind for later. Another interesting takeaway from this particular exploit is how the `extraBalance` account doesn't have this flaw--because it's properly restricted to allow only the owner to move funds. If that's the proper mode for this account, it begs the question: why make that a configurable flag at all? ## The `!isFueled` exploit Remember how I mentioned that the DAO could be exploited in the event that it failed to reach its funding goal? That even the failure mode of the funding phase in itself had failure modes? Let's take a look at the `TokenCreation` contract's [`refund` method](https://gist.github.com/DeviateFish/a2aff9181d69e4c24a9e42fbe47ca973#file-the-dao-annotated-sol-L295) ``` // > Here's another interesting function. This never came to be, // > but if the DAO didn't meet its funding goal, this would have also // > been vulnerable to re-entrancy. This, again, is due to the use // > of `.call` when `.send` would be the appropriate method to use. // > Like the split recursion attack, the balances of the sender // > aren't cleared until after moving the balances. function refund() noEther { if (now > closingTime && !isFueled) { // Get extraBalance - will only succeed when called for the first time if (extraBalance.balance >= extraBalance.accumulatedInput()) extraBalance.payOut(address(this), extraBalance.accumulatedInput()); // Execute refund if (msg.sender.call.value(weiGiven[msg.sender])()) { Refund(msg.sender, weiGiven[msg.sender]); totalSupply -= balances[msg.sender]; balances[msg.sender] = 0; weiGiven[msg.sender] = 0; } } } ``` Now, the attack is outlined in my annotations, but we'll just break it down here. This function only works if we pass the closing time of the funding phase, but don't meet the funding goal (meeting the funding goal is what causes `isFueled` [to be set](https://gist.github.com/DeviateFish/a2aff9181d69e4c24a9e42fbe47ca973#file-the-dao-annotated-sol-L286)). If we're past the closing time and we're not funded, this function moves the contents of the `extraBalance` account back to the account that implements this function (the DAO), and then sends the sender his contribution back. Then it proceeds to zero out his balances, both of his Ether contribution, and his acquired tokens. It also uses `.call` to send the Ether, rather than `.send`. It's worth noting here that even if it used send, it would still be vulnerable to draining. There's no failure mode if the `.call` (or the theoretical `.send`) fails... In fact, all a contract would need to do is return false from its fallback function to to prevent its balances and tokens from being reset. It could then call the refund function again and again until the balance was drained. The use of `.call` only exacerbates this, since it allows for this to be done multiple times per transaction, through re-entrancy. So where does this leave us? Well, the "acquired rewards" case is exploitable, and now the "failed to fund" case is exploitable. Let's see what else is exploitable. ## The `splitDAO` exploit. Everyone knows this one, so I'll just gloss over it. Basically, calling `splitDAO` calls `withdrawRewardFor`, which executes the aforementioned vulnerable `payOut` method, allowing re-entrancy and further providing the opportunity for an attack to transfer his tokens away before returning up the stack, so his tokens can also be re-used. However, I should stop here, and point out something about [`withdrawRewardFor`](https://gist.github.com/DeviateFish/a2aff9181d69e4c24a9e42fbe47ca973#file-the-dao-annotated-sol-L1076): ``` // > This one, again, is vulnerable to re-entrancy, because it calls the insecure // > `payOut` method. In fact, the above method can be used with a token transfer // > to enable an interesting token-tainting attack, that compounds the `paidOut` // > that gets transfered with tokens as they move from owner to owner. function withdrawRewardFor(address _account) noEther internal returns (bool _success) { if ((balanceOf(_account) * rewardAccount.accumulatedInput()) / totalSupply < paidOut[_account]) throw; uint reward = (balanceOf(_account) * rewardAccount.accumulatedInput()) / totalSupply - paidOut[_account]; if (!rewardAccount.payOut(_account, reward)) throw; paidOut[_account] += reward; return true; } ``` It's interesting to note that this method can actually fail. It can fail in the event that the account we're withdrawing on behalf of has already been paid out some rewards, and when the rewardAccount lacks the funds to cover... what they've already been paid out? Here's where things start really getting interesting. This function fails when the tokenholder's fraction of the *total* accumulated rewards is less than what they've already been paid. This seems like a strange condition on the surface--however, the next line indicates why this is being tested. This is to prevent integer rollover issues when subtracting out what's already been paid from what's owed. It still raises and interesting question, though... how and why could this fail? Token rewards are always paid out on the basis of their fractional proportion of the total of all rewards ever, and are transferred as fractions of the account holder's total token holdings. This means if I've been paid out 1000 ether and hold 100 tokens, if I give you 90 of them, you'll be considered to have been "paid out" 900 ether and I'll remain with 100. But my 1000 ether was just my 100 tokens' fraction of the total amount of tokens issued, meaning if 10,000 were issued, the total reward accumulated must be 100,000 ether. Makes sense. Except when you remember that `payOut` is broken. Which brings us to our next attack: ## The token-tainting exploit I was tipped off to this one by the actions of (what later was identified as) the "Robin Hood Group", just prior to their attempt to extract the ether mistakenly (?) deposited into the DAO after the fork. This one is particularly clever, as it leverages all the above exploits. Let's focus on the `transfer` function, as [overridden by the DAO itself](https://gist.github.com/DeviateFish/a2aff9181d69e4c24a9e42fbe47ca973#file-the-dao-annotated-sol-L1145): ``` // > Here's the other piece to the "token tainting" attack. // > Since the `paidOut` values are transferred in proportion to // > the tokens being moved, it means the fractional amount of `paidOut` // > moves with tokens forever. It can be diluted by mixing high-penalty // > and no-penalty tokens, but the `paidOut` penalty can only be removed by // > splitting from the DAO. // > It's worth noting that `paidOut` is only really used by `withdrawRewardFor` // > and is the only reason that function can fail. // > Interestingly, `withdrawRewardFor` is an integral part of `splitDAO`. // > Huh. There's that function again. function transferPaidOut( address _from, address _to, uint256 _value ) internal returns (bool success) { uint transferPaidOut = paidOut[_from] * _value / balanceOf(_from); if (transferPaidOut > paidOut[_from]) throw; paidOut[_from] -= transferPaidOut; paidOut[_to] += transferPaidOut; return true; } ``` This attack works by combining `withdrawRewardFor`, the open access to the `rewardAccount`, and the `transfer` function defined above. It works roughly like this: 1. Given some amount of Ether that we want to leverage, calculate the number of required tokens (between two accounts, we'll call them `parent` and `child`) to fully extract that amount of Ether from the reward account. This requires a bit of math, but it's only algebra, and not terribly hard. Either the `parent` or the child should accumulate some `paidOut` before starting this attack. 2. If `parent` contains enough tokens to execute this attack, send the chosen amount of Ether to the `rewardAccount`, and then call the exploit function on `child`. 3. `child` calls [`getMyReward`](https://gist.github.com/DeviateFish/a2aff9181d69e4c24a9e42fbe47ca973#file-the-dao-annotated-sol-L1070) on the DAO. 4. The DAO calculates how much Ether should be moved from the `rewardAccount` to `child`, and then calls `payOut` to direct the reward account to send that much Ether to `child`. 5. `child` contains a fallback function (of course). This fallback function calls `transfer` on the DAO, and sends 99.9999% (or some other very large fraction) of its tokens back to `parent`. Remember that this also sends back the equivalent fraction of the `child`'s `paidOut`, which has not yet had this new payout added to it. (The `child` can also send the newly-acquired Ether back to `parent`, here, too). 6. `payOut` returns, and the `child`'s `paidOut` gets that reward amount added to it. This leaves `parent` with 99.9999% of the original `paidOut` value of the tokens used (and the same quantity of tokens), and `child` with .0001% of the tokens, but 100.0001% of the `paidOut` value of the total tokens. It can now send these "tainted" tokens to another account to reset its `paidOut` and repeat the process. The end result is a small quantity of tokens that carry with them a very large `paidOut` debt. So let's bring this whole thing full circle. ## The Perfect Drain With all of the above exploits, a perfect drain of the DAO is made possible. In the "funded" scenario, the only Ether safe from direct theft is that which is contained in the `extraBalance` of the DAO--which was approximately 345k Ether when funding closed. This Ether could only be brought back into the DAO's balance after the DAO had spend more than the total `extraBalance` on proposals. Any balance in the `rewardAccount`, however: free for the taking. Any balance in the `DAOrewardAccount`: free for the taking. Any balance in the DAO itself... well, as we saw, that was pretty much free for the taking, too. In fact, it's likely that we didn't see the token tainting attack in that case because no one else participated in that split. Let's assume, for this exercise, that others had participated in the split. As a hedge against this, the attack could taint some tokens using the above attack--and then when the voting period ends, send small fractions of that tainted balance to every voter on the split proposal. This would prevent everyone who was capable of splitting from executing the `splitDAO` call unless they dumped the tainted tokens and acquired new ones. In fact, given the nature of the tainting, it's even possible to "poison the well", so to speak--by sending tainted tokens to exchanges, where they'll be disseminated to many users. This dilutes the effect, but even a small adjustment to the `paidOut` is enough to break `withdrawRewardFor` when there's nothing in `rewardAccount`. This gives rise to the "perfect split": a recursive `splitDAO` attack in which all no stalkers are capable of effectively executing `splitDAO` and obtaining tokens in the newly-created child DAO. This leaves the attacker with the sole ownership of tokens in the child DAO, giving him perfect control over it (and the ability to split into a DAO he controls, if he didn't do that in the first place). ## An Engineered exploit? There are a lot of red flags around the DAO v1.0 code. The authors clearly knew when to use `.send` vs when to use `.call`, but chose to use `.call` in a select few areas. The decision to make `rewardAccount` and `DAOrewardAccount` unrestricted... inexplicable. The fact that the token tainting attack and the `splitDAO` attack go together perfectly to ensure a flawless exit... And then there's that failed funding failure mode. Literally every mode had an escape hatch. Didn't make the funding target? Doesn't matter, you can still take all the funding for yourself. Made the funding target? Cool, now you can take everything in the DAO, and prevent others from doing the same. This is all further compounded by the constant claims from the Slock.it team that they had the "community" review the code--and yet no instances of the DAO's code can be found prior to it being referenced on the DAO's website, which only went up when the full marketing blitz was ready and waiting. The initial investments into the DAO were likely from insiders involved in its creation. After all, this is a classic marketing technique: have your insiders pump up an offering (likes, comments, reviews, contributions, etc) to make it appear to have more initial momentum than it really has. This draws in more people, due to various social factors, not the least of which is the "fear of missing out." The DAO was [deployed with](https://github.com/slockit/DAO/wiki/The-DAO-v1.0-Code#constructor-arguments) a minimum token allocation of `50000000000000000000000`, or 5,000,000 tokens (50,000 ether). Given the above opportunity to drain the DAO if it didn't reach its minimum, it would imply that the insiders didn't contribute more than 50,000 of their own ether, which is a fairly low bar. ## Conclusions So, we have a contract, written by people who clearly know how Solidity works, making a whole lot of mistakes in a lot of obvious places. We have associates of the authors claiming that the code was "reviewed by the community" prior to it being launched... but there's no evidence of this anywhere, and when I ask them for links to these posts, I'm ignored. Suspicious. Even so, it's all still very circumstantial. However, there does exist a public record of the development of the DAO's code: https://github.com/TheDAO/DAO-1.0 So, up next: a deep dive through the commit history of the affected parts. Let's try to put together the story of how this contract became so broken--intentionally or otherwise. Stay tuned. |
| json metadata | {"tags":["ethereum","the-dao","security","thedao"],"links":["https://gist.github.com/DeviateFish/a2aff9181d69e4c24a9e42fbe47ca973#file-the-dao-annotated-sol-L1145"]} |
| Transaction Info | Block #3738617/Trx 681bc2232609e05b24de8cda30dc2ff1f850b7df |
View Raw JSON Data
{
"trx_id": "681bc2232609e05b24de8cda30dc2ff1f850b7df",
"block": 3738617,
"trx_in_block": 1,
"op_in_trx": 0,
"virtual_op": 0,
"timestamp": "2016-08-02T18:58:03",
"op": [
"comment",
{
"parent_author": "",
"parent_permlink": "ethereum",
"author": "deviatefish",
"permlink": "the-dao-a-contract-engineered-for-failure",
"title": "The DAO: A contract engineered for failure.",
"body": "# The DAO: A contract engineered for failure.\n\n## Forward\n\nSo, I'll admit, when something fails as spectacularly as the DAO did, I can't help but watch. And then, after the dust has settled, I can't help but dive in and find out just what went wrong. My [previous post](https://steemit.com/ethereum/@deviatefish/on-proof-of-work-and-consensus) on Proof-of-Work and consensus was along the same lines: how was it possible for non-voting majority to lose to a strongly-motivated, voting minority? It raised some serious concerns (to me, anyway) about the security of PoW, especially as it pertains to the economic incentives that are supposed to add security to the network.\n\nAfter having successfully replicated the DAO attacker's attack, as well as an unrelated attack used to lock down splits, I decided to go a [thorough documentation pass](https://gist.github.com/DeviateFish/a2aff9181d69e4c24a9e42fbe47ca973) over the DAO's code, to see how these attacks fit together. In the process, I discovered a few more, as well as some very poor design decisions in other parts of the code.\n\n## Engineered to Fail\n\nAs it turns out, the DAO's code is written in such a way to allow for someone to exploit it in nearly every case: the \"failed to meet funding\" case, the \"received rewards/DAOrewards\" case, and finally, the split case. In fact, the only process in the DAO which could be said to operate smoothly and without exploit (as far as I can tell) is the execution of a proposal.\n\nThis is... interesting, to say the least. So, let's break down all these failure modes, and look at the associated code.\n\n## The `reward` and `DAOReward` exploit\n\nFirst, let's start with the [`ManagedAccount`](https://gist.github.com/DeviateFish/a2aff9181d69e4c24a9e42fbe47ca973#file-the-dao-annotated-sol-L160) contract. This is supposed to create an account that can be utilized by only a particular owner, unless, of course, a flag has been set that unrestricts access.\n\nGiven that this is used for the [`extraBalance`](https://gist.github.com/DeviateFish/a2aff9181d69e4c24a9e42fbe47ca973#file-the-dao-annotated-sol-L271), [`rewardAccount`](https://gist.github.com/DeviateFish/a2aff9181d69e4c24a9e42fbe47ca973#file-the-dao-annotated-sol-L685), and [`DAOrewardAccount`](https://gist.github.com/DeviateFish/a2aff9181d69e4c24a9e42fbe47ca973#file-the-dao-annotated-sol-L686) subcontracts, it's a little interesting that two out of the three would be configured for unrestricted access.\n\nThe most glaring problem here, of course, is the [`payOut`](https://gist.github.com/DeviateFish/a2aff9181d69e4c24a9e42fbe47ca973#file-the-dao-annotated-sol-L177) method.\n\nWe'll drop that here (with my annotations) for easier reference:\n\n```\n// > Alright, here's where things get a little weird. This is a method\n// > for transferring value from this account to a specific recipient.\n// > However, it doesn't really have much in the way of protections against\n// > unauthorized people calling it.\n// > In fact, both the DAORewardAccount and rewardAccount `ManagedAccounts`\n// > deployed by the DAO specifically disable the \"payOwnerOnly\" flag on creation.\n// > This means anyone can call this function, with any amount, and any recipient.\n// > This is a pretty glaring security hole.\n// > Furthermore... there's the use of `call` over `send`. The point of this\n// > entire function is to simply transfer value from this account to a specified\n// > recipient. There's no need to `call` the recipient when a `send` will suffice.\n// > So not only do you have an unrestricted method for moving value from this account,\n// > you also have one that's intentionally invoking remote code.\nfunction payOut(address _recipient, uint _amount) returns (bool) {\n if (msg.sender != owner || msg.value > 0 || (payOwnerOnly && _recipient != owner))\n throw;\n if (_recipient.call.value(_amount)()) {\n PayOut(_recipient, _amount);\n return true;\n } else {\n return false;\n }\n}\n```\n\nAs noted, this function does a couple very suspect things. \n\nFirst, it uses `.call` when `.send` would be more appropriate. The idea is for it to move funds to a target account, and this is exactly what `.send` is supposed to do. Using `.call` here is quite interesting, especially since the authors [later](https://gist.github.com/DeviateFish/a2aff9181d69e4c24a9e42fbe47ca973#file-the-dao-annotated-sol-L874) [prove](https://gist.github.com/DeviateFish/a2aff9181d69e4c24a9e42fbe47ca973#file-the-dao-annotated-sol-L895) they know how and when to use `.send`, and when using `.call` is [actually appropriate](https://gist.github.com/DeviateFish/a2aff9181d69e4c24a9e42fbe47ca973#file-the-dao-annotated-sol-L906).\n\nSecond, if `payOwnerOnly` is set to `false`, anyone can use this function. This means, if there are funds in either the `rewardAccount` or `DAOrewardAccount`, anyone can move them anywhere. This is a huge security hole that anyone reading the code should have been able to catch.\n\nWhich, of course, just goes to show I never read the code all the way through, before.\n\nIt's worth noting here that the former of these two flaws is something you'll want to keep in mind for later.\n\nAnother interesting takeaway from this particular exploit is how the `extraBalance` account doesn't have this flaw--because it's properly restricted to allow only the owner to move funds. If that's the proper mode for this account, it begs the question: why make that a configurable flag at all?\n\n## The `!isFueled` exploit\n\nRemember how I mentioned that the DAO could be exploited in the event that it failed to reach its funding goal? That even the failure mode of the funding phase in itself had failure modes?\n\nLet's take a look at the `TokenCreation` contract's [`refund` method](https://gist.github.com/DeviateFish/a2aff9181d69e4c24a9e42fbe47ca973#file-the-dao-annotated-sol-L295)\n\n```\n// > Here's another interesting function. This never came to be,\n// > but if the DAO didn't meet its funding goal, this would have also\n// > been vulnerable to re-entrancy. This, again, is due to the use\n// > of `.call` when `.send` would be the appropriate method to use.\n// > Like the split recursion attack, the balances of the sender\n// > aren't cleared until after moving the balances.\nfunction refund() noEther {\n if (now > closingTime && !isFueled) {\n // Get extraBalance - will only succeed when called for the first time\n if (extraBalance.balance >= extraBalance.accumulatedInput())\n extraBalance.payOut(address(this), extraBalance.accumulatedInput());\n\n // Execute refund\n if (msg.sender.call.value(weiGiven[msg.sender])()) {\n Refund(msg.sender, weiGiven[msg.sender]);\n totalSupply -= balances[msg.sender];\n balances[msg.sender] = 0;\n weiGiven[msg.sender] = 0;\n }\n }\n}\n```\n\nNow, the attack is outlined in my annotations, but we'll just break it down here.\n\nThis function only works if we pass the closing time of the funding phase, but don't meet the funding goal (meeting the funding goal is what causes `isFueled` [to be set](https://gist.github.com/DeviateFish/a2aff9181d69e4c24a9e42fbe47ca973#file-the-dao-annotated-sol-L286)). If we're past the closing time and we're not funded, this function moves the contents of the `extraBalance` account back to the account that implements this function (the DAO), and then sends the sender his contribution back. Then it proceeds to zero out his balances, both of his Ether contribution, and his acquired tokens.\n\nIt also uses `.call` to send the Ether, rather than `.send`.\n\nIt's worth noting here that even if it used send, it would still be vulnerable to draining. There's no failure mode if the `.call` (or the theoretical `.send`) fails... In fact, all a contract would need to do is return false from its fallback function to to prevent its balances and tokens from being reset. It could then call the refund function again and again until the balance was drained.\n\nThe use of `.call` only exacerbates this, since it allows for this to be done multiple times per transaction, through re-entrancy.\n\nSo where does this leave us? Well, the \"acquired rewards\" case is exploitable, and now the \"failed to fund\" case is exploitable. Let's see what else is exploitable.\n\n## The `splitDAO` exploit.\n\nEveryone knows this one, so I'll just gloss over it. Basically, calling `splitDAO` calls `withdrawRewardFor`, which executes the aforementioned vulnerable `payOut` method, allowing re-entrancy and further providing the opportunity for an attack to transfer his tokens away before returning up the stack, so his tokens can also be re-used.\n\nHowever, I should stop here, and point out something about [`withdrawRewardFor`](https://gist.github.com/DeviateFish/a2aff9181d69e4c24a9e42fbe47ca973#file-the-dao-annotated-sol-L1076):\n\n```\n// > This one, again, is vulnerable to re-entrancy, because it calls the insecure\n// > `payOut` method. In fact, the above method can be used with a token transfer\n// > to enable an interesting token-tainting attack, that compounds the `paidOut`\n// > that gets transfered with tokens as they move from owner to owner.\nfunction withdrawRewardFor(address _account) noEther internal returns (bool _success) {\n if ((balanceOf(_account) * rewardAccount.accumulatedInput()) / totalSupply < paidOut[_account])\n throw;\n\n uint reward =\n (balanceOf(_account) * rewardAccount.accumulatedInput()) / totalSupply - paidOut[_account];\n if (!rewardAccount.payOut(_account, reward))\n throw;\n paidOut[_account] += reward;\n return true;\n}\n```\n\nIt's interesting to note that this method can actually fail. It can fail in the event that the account we're withdrawing on behalf of has already been paid out some rewards, and when the rewardAccount lacks the funds to cover... what they've already been paid out?\n\nHere's where things start really getting interesting. This function fails when the tokenholder's fraction of the *total* accumulated rewards is less than what they've already been paid. This seems like a strange condition on the surface--however, the next line indicates why this is being tested. This is to prevent integer rollover issues when subtracting out what's already been paid from what's owed.\n\nIt still raises and interesting question, though... how and why could this fail? Token rewards are always paid out on the basis of their fractional proportion of the total of all rewards ever, and are transferred as fractions of the account holder's total token holdings. This means if I've been paid out 1000 ether and hold 100 tokens, if I give you 90 of them, you'll be considered to have been \"paid out\" 900 ether and I'll remain with 100. But my 1000 ether was just my 100 tokens' fraction of the total amount of tokens issued, meaning if 10,000 were issued, the total reward accumulated must be 100,000 ether.\n\nMakes sense.\n\nExcept when you remember that `payOut` is broken. Which brings us to our next attack:\n\n## The token-tainting exploit\n\nI was tipped off to this one by the actions of (what later was identified as) the \"Robin Hood Group\", just prior to their attempt to extract the ether mistakenly (?) deposited into the DAO after the fork.\n\nThis one is particularly clever, as it leverages all the above exploits. Let's focus on the `transfer` function, as [overridden by the DAO itself](https://gist.github.com/DeviateFish/a2aff9181d69e4c24a9e42fbe47ca973#file-the-dao-annotated-sol-L1145):\n\n```\n// > Here's the other piece to the \"token tainting\" attack.\n// > Since the `paidOut` values are transferred in proportion to\n// > the tokens being moved, it means the fractional amount of `paidOut`\n// > moves with tokens forever. It can be diluted by mixing high-penalty\n// > and no-penalty tokens, but the `paidOut` penalty can only be removed by\n// > splitting from the DAO.\n// > It's worth noting that `paidOut` is only really used by `withdrawRewardFor`\n// > and is the only reason that function can fail.\n// > Interestingly, `withdrawRewardFor` is an integral part of `splitDAO`.\n// > Huh. There's that function again.\nfunction transferPaidOut(\n address _from,\n address _to,\n uint256 _value\n) internal returns (bool success) {\n\n uint transferPaidOut = paidOut[_from] * _value / balanceOf(_from);\n if (transferPaidOut > paidOut[_from])\n throw;\n paidOut[_from] -= transferPaidOut;\n paidOut[_to] += transferPaidOut;\n return true;\n}\n```\n\nThis attack works by combining `withdrawRewardFor`, the open access to the `rewardAccount`, and the `transfer` function defined above. It works roughly like this:\n\n1. Given some amount of Ether that we want to leverage, calculate the number of required tokens (between two accounts, we'll call them `parent` and `child`) to fully extract that amount of Ether from the reward account. This requires a bit of math, but it's only algebra, and not terribly hard. Either the `parent` or the child should accumulate some `paidOut` before starting this attack.\n2. If `parent` contains enough tokens to execute this attack, send the chosen amount of Ether to the `rewardAccount`, and then call the exploit function on `child`.\n3. `child` calls [`getMyReward`](https://gist.github.com/DeviateFish/a2aff9181d69e4c24a9e42fbe47ca973#file-the-dao-annotated-sol-L1070) on the DAO.\n4. The DAO calculates how much Ether should be moved from the `rewardAccount` to `child`, and then calls `payOut` to direct the reward account to send that much Ether to `child`.\n5. `child` contains a fallback function (of course). This fallback function calls `transfer` on the DAO, and sends 99.9999% (or some other very large fraction) of its tokens back to `parent`. Remember that this also sends back the equivalent fraction of the `child`'s `paidOut`, which has not yet had this new payout added to it. (The `child` can also send the newly-acquired Ether back to `parent`, here, too).\n6. `payOut` returns, and the `child`'s `paidOut` gets that reward amount added to it.\n\nThis leaves `parent` with 99.9999% of the original `paidOut` value of the tokens used (and the same quantity of tokens), and `child` with .0001% of the tokens, but 100.0001% of the `paidOut` value of the total tokens. It can now send these \"tainted\" tokens to another account to reset its `paidOut` and repeat the process.\n\nThe end result is a small quantity of tokens that carry with them a very large `paidOut` debt.\n\nSo let's bring this whole thing full circle.\n\n## The Perfect Drain\n\nWith all of the above exploits, a perfect drain of the DAO is made possible. In the \"funded\" scenario, the only Ether safe from direct theft is that which is contained in the `extraBalance` of the DAO--which was approximately 345k Ether when funding closed. This Ether could only be brought back into the DAO's balance after the DAO had spend more than the total `extraBalance` on proposals.\n\nAny balance in the `rewardAccount`, however: free for the taking.\nAny balance in the `DAOrewardAccount`: free for the taking.\nAny balance in the DAO itself... well, as we saw, that was pretty much free for the taking, too. In fact, it's likely that we didn't see the token tainting attack in that case because no one else participated in that split.\n\nLet's assume, for this exercise, that others had participated in the split. As a hedge against this, the attack could taint some tokens using the above attack--and then when the voting period ends, send small fractions of that tainted balance to every voter on the split proposal. This would prevent everyone who was capable of splitting from executing the `splitDAO` call unless they dumped the tainted tokens and acquired new ones. In fact, given the nature of the tainting, it's even possible to \"poison the well\", so to speak--by sending tainted tokens to exchanges, where they'll be disseminated to many users. This dilutes the effect, but even a small adjustment to the `paidOut` is enough to break `withdrawRewardFor` when there's nothing in `rewardAccount`.\n\nThis gives rise to the \"perfect split\": a recursive `splitDAO` attack in which all no stalkers are capable of effectively executing `splitDAO` and obtaining tokens in the newly-created child DAO. This leaves the attacker with the sole ownership of tokens in the child DAO, giving him perfect control over it (and the ability to split into a DAO he controls, if he didn't do that in the first place).\n\n## An Engineered exploit?\n\nThere are a lot of red flags around the DAO v1.0 code. The authors clearly knew when to use `.send` vs when to use `.call`, but chose to use `.call` in a select few areas. The decision to make `rewardAccount` and `DAOrewardAccount` unrestricted... inexplicable. The fact that the token tainting attack and the `splitDAO` attack go together perfectly to ensure a flawless exit...\n\nAnd then there's that failed funding failure mode.\n\nLiterally every mode had an escape hatch. Didn't make the funding target? Doesn't matter, you can still take all the funding for yourself.\n\nMade the funding target? Cool, now you can take everything in the DAO, and prevent others from doing the same.\n\nThis is all further compounded by the constant claims from the Slock.it team that they had the \"community\" review the code--and yet no instances of the DAO's code can be found prior to it being referenced on the DAO's website, which only went up when the full marketing blitz was ready and waiting.\n\nThe initial investments into the DAO were likely from insiders involved in its creation. After all, this is a classic marketing technique: have your insiders pump up an offering (likes, comments, reviews, contributions, etc) to make it appear to have more initial momentum than it really has. This draws in more people, due to various social factors, not the least of which is the \"fear of missing out.\" The DAO was [deployed with](https://github.com/slockit/DAO/wiki/The-DAO-v1.0-Code#constructor-arguments) a minimum token allocation of `50000000000000000000000`, or 5,000,000 tokens (50,000 ether). Given the above opportunity to drain the DAO if it didn't reach its minimum, it would imply that the insiders didn't contribute more than 50,000 of their own ether, which is a fairly low bar.\n\n## Conclusions\n\nSo, we have a contract, written by people who clearly know how Solidity works, making a whole lot of mistakes in a lot of obvious places. We have associates of the authors claiming that the code was \"reviewed by the community\" prior to it being launched... but there's no evidence of this anywhere, and when I ask them for links to these posts, I'm ignored.\n\nSuspicious.\n\nEven so, it's all still very circumstantial. However, there does exist a public record of the development of the DAO's code: https://github.com/TheDAO/DAO-1.0\n\nSo, up next: a deep dive through the commit history of the affected parts. Let's try to put together the story of how this contract became so broken--intentionally or otherwise.\n\nStay tuned.",
"json_metadata": "{\"tags\":[\"ethereum\",\"the-dao\",\"security\",\"thedao\"],\"links\":[\"https://gist.github.com/DeviateFish/a2aff9181d69e4c24a9e42fbe47ca973#file-the-dao-annotated-sol-L1145\"]}"
}
]
}| voter | deviatefish |
| author | deviatefish |
| permlink | re-eeks-re-deviatefish-on-proof-of-work-and-consensus-20160727t035723607z |
| weight | 10000 (100.00%) |
| Transaction Info | Block #3569006/Trx ab8ef3f245295906fce827be1ccac50869a69e16 |
View Raw JSON Data
{
"trx_id": "ab8ef3f245295906fce827be1ccac50869a69e16",
"block": 3569006,
"trx_in_block": 1,
"op_in_trx": 0,
"virtual_op": 0,
"timestamp": "2016-07-27T20:50:30",
"op": [
"vote",
{
"voter": "deviatefish",
"author": "deviatefish",
"permlink": "re-eeks-re-deviatefish-on-proof-of-work-and-consensus-20160727t035723607z",
"weight": 10000
}
]
}deviatefishupvoted (100.00%) @eeks / re-deviatefish-on-proof-of-work-and-consensus-20160725t203745425z
deviatefishupvoted (100.00%) @eeks / re-deviatefish-on-proof-of-work-and-consensus-20160725t203745425z
| voter | deviatefish |
| author | eeks |
| permlink | re-deviatefish-on-proof-of-work-and-consensus-20160725t203745425z |
| weight | 10000 (100.00%) |
| Transaction Info | Block #3548992/Trx 2f8110c06ee016f6579a14336b486220c64ad33c |
View Raw JSON Data
{
"trx_id": "2f8110c06ee016f6579a14336b486220c64ad33c",
"block": 3548992,
"trx_in_block": 1,
"op_in_trx": 0,
"virtual_op": 0,
"timestamp": "2016-07-27T03:57:30",
"op": [
"vote",
{
"voter": "deviatefish",
"author": "eeks",
"permlink": "re-deviatefish-on-proof-of-work-and-consensus-20160725t203745425z",
"weight": 10000
}
]
}| parent author | eeks |
| parent permlink | re-deviatefish-on-proof-of-work-and-consensus-20160725t203745425z |
| author | deviatefish |
| permlink | re-eeks-re-deviatefish-on-proof-of-work-and-consensus-20160727t035723607z |
| title | |
| body | Thanks m8! Honestly, the overconfidence is a bit of a problem. It comes from turning a problem over and over a little obsessively, until I see how all the pieces (can) fit together. Then it becomes hard to see them fitting together any other way--until more pieces get added, that is. I like complicated systems like this. I like them less so when they get political like this, but regardless of whether or not I like them, they're utterly fascinating. I do hope someone will come along and tell me where I'm wrong in some of my assumptions, or perhaps point out some additional information that would force me to rethink the model I've come up with. I'd especially like to hear from someone involved in Ethereum's development, but I don't think they're too fond of me over in their subreddit these days :( C'est la vie. |
| json metadata | {"tags":["ethereum"]} |
| Transaction Info | Block #3548989/Trx 374ede4fa68f9d556af3c8463bca9bd77b4cef72 |
View Raw JSON Data
{
"trx_id": "374ede4fa68f9d556af3c8463bca9bd77b4cef72",
"block": 3548989,
"trx_in_block": 0,
"op_in_trx": 0,
"virtual_op": 0,
"timestamp": "2016-07-27T03:57:21",
"op": [
"comment",
{
"parent_author": "eeks",
"parent_permlink": "re-deviatefish-on-proof-of-work-and-consensus-20160725t203745425z",
"author": "deviatefish",
"permlink": "re-eeks-re-deviatefish-on-proof-of-work-and-consensus-20160727t035723607z",
"title": "",
"body": "Thanks m8!\n\nHonestly, the overconfidence is a bit of a problem. It comes from turning a problem over and over a little obsessively, until I see how all the pieces (can) fit together. Then it becomes hard to see them fitting together any other way--until more pieces get added, that is.\n\nI like complicated systems like this. I like them less so when they get political like this, but regardless of whether or not I like them, they're utterly fascinating.\n\nI do hope someone will come along and tell me where I'm wrong in some of my assumptions, or perhaps point out some additional information that would force me to rethink the model I've come up with. I'd especially like to hear from someone involved in Ethereum's development, but I don't think they're too fond of me over in their subreddit these days :(\n\nC'est la vie.",
"json_metadata": "{\"tags\":[\"ethereum\"]}"
}
]
}deviatefishreceived 0.183 SBD, 0.129 SP author reward for @deviatefish / on-proof-of-work-and-consensus
deviatefishreceived 0.183 SBD, 0.129 SP author reward for @deviatefish / on-proof-of-work-and-consensus
| author | deviatefish |
| permlink | on-proof-of-work-and-consensus |
| sbd payout | 0.183 SBD |
| steem payout | 0.000 STEEM |
| vesting payout | 209.479774 VESTS |
| Transaction Info | Block #3540014/Virtual Operation #3 |
View Raw JSON Data
{
"trx_id": "0000000000000000000000000000000000000000",
"block": 3540014,
"trx_in_block": 4294967295,
"op_in_trx": 0,
"virtual_op": 3,
"timestamp": "2016-07-26T20:26:27",
"op": [
"author_reward",
{
"author": "deviatefish",
"permlink": "on-proof-of-work-and-consensus",
"sbd_payout": "0.183 SBD",
"steem_payout": "0.000 STEEM",
"vesting_payout": "209.479774 VESTS"
}
]
}takerupvoted (100.00%) @deviatefish / on-proof-of-work-and-consensus
takerupvoted (100.00%) @deviatefish / on-proof-of-work-and-consensus
| voter | taker |
| author | deviatefish |
| permlink | on-proof-of-work-and-consensus |
| weight | 10000 (100.00%) |
| Transaction Info | Block #3512228/Trx aacef3e93e7c9386d7e30d59fc6ced7fb23e0918 |
View Raw JSON Data
{
"trx_id": "aacef3e93e7c9386d7e30d59fc6ced7fb23e0918",
"block": 3512228,
"trx_in_block": 1,
"op_in_trx": 0,
"virtual_op": 0,
"timestamp": "2016-07-25T21:02:33",
"op": [
"vote",
{
"voter": "taker",
"author": "deviatefish",
"permlink": "on-proof-of-work-and-consensus",
"weight": 10000
}
]
}| parent author | deviatefish |
| parent permlink | on-proof-of-work-and-consensus |
| author | eeks |
| permlink | re-deviatefish-on-proof-of-work-and-consensus-20160725t203745425z |
| title | |
| body | Great disclosure. ;). I tell people IANAT -- I am not a trader. This deserves more upvotes. Thanks for putting it together. > I'm fairly new to the whole crypto-currency space, so there are a lot of nuances and such that I'm sure I miss out on. My experience with cryptocurrencies is mostly limited to Bitcoin and Ethereum, with some dabbling in day-trading on the side (Full disclosure: I suck at it). > Given that I follow Ethereum pretty closely, I've actually become fairly involved in the whole hard-fork debate. I'm very strongly anti-fork, and am really not very happy about the fact that the hard-fork was not only accepted by the majority, but how that came about. Watching the debate evolve over the last few weeks--and looking back on the history of similar debates in the Bitcoin space--I started to come up with some musings about PoW and what we call "consensus". It's interesting to me that you are fairly new to the space but you are so confident in your posture on these issues. Maybe it's your style on reddit and I am misreading you, but in my experience, such overconfidence is a hindrance. At best it's just remarkably unprofitable. Food for thought. [I am running on empty 350 posts](https://steemit.com/steem/@eeks/200-comments-in-6-hours-so-far-someone-drew-me-i-inspired-trucker-fiction-and-more) but good to have you on Steem and I look forward to your musings as they evolve. |
| json metadata | {"tags":["ethereum"],"links":["https://steemit.com/steem/@eeks/200-comments-in-6-hours-so-far-someone-drew-me-i-inspired-trucker-fiction-and-more"]} |
| Transaction Info | Block #3511739/Trx ccd6a047304d0c82ba1da034367a18b3ea570c3b |
View Raw JSON Data
{
"trx_id": "ccd6a047304d0c82ba1da034367a18b3ea570c3b",
"block": 3511739,
"trx_in_block": 5,
"op_in_trx": 0,
"virtual_op": 0,
"timestamp": "2016-07-25T20:37:57",
"op": [
"comment",
{
"parent_author": "deviatefish",
"parent_permlink": "on-proof-of-work-and-consensus",
"author": "eeks",
"permlink": "re-deviatefish-on-proof-of-work-and-consensus-20160725t203745425z",
"title": "",
"body": "Great disclosure. ;). I tell people IANAT -- I am not a trader.\n\nThis deserves more upvotes. Thanks for putting it together.\n\n> I'm fairly new to the whole crypto-currency space, so there are a lot of nuances and such that I'm sure I miss out on. My experience with cryptocurrencies is mostly limited to Bitcoin and Ethereum, with some dabbling in day-trading on the side (Full disclosure: I suck at it).\n\n> Given that I follow Ethereum pretty closely, I've actually become fairly involved in the whole hard-fork debate. I'm very strongly anti-fork, and am really not very happy about the fact that the hard-fork was not only accepted by the majority, but how that came about. Watching the debate evolve over the last few weeks--and looking back on the history of similar debates in the Bitcoin space--I started to come up with some musings about PoW and what we call \"consensus\".\n\nIt's interesting to me that you are fairly new to the space but you are so confident in your posture on these issues. Maybe it's your style on reddit and I am misreading you, but in my experience, such overconfidence is a hindrance. At best it's just remarkably unprofitable. Food for thought. [I am running on empty 350 posts](https://steemit.com/steem/@eeks/200-comments-in-6-hours-so-far-someone-drew-me-i-inspired-trucker-fiction-and-more) but good to have you on Steem and I look forward to your musings as they evolve.",
"json_metadata": "{\"tags\":[\"ethereum\"],\"links\":[\"https://steemit.com/steem/@eeks/200-comments-in-6-hours-so-far-someone-drew-me-i-inspired-trucker-fiction-and-more\"]}"
}
]
}eeksupvoted (100.00%) @deviatefish / on-proof-of-work-and-consensus
eeksupvoted (100.00%) @deviatefish / on-proof-of-work-and-consensus
| voter | eeks |
| author | deviatefish |
| permlink | on-proof-of-work-and-consensus |
| weight | 10000 (100.00%) |
| Transaction Info | Block #3511619/Trx cff4cfef0459e3f5c292a1657ff6febda5cee671 |
View Raw JSON Data
{
"trx_id": "cff4cfef0459e3f5c292a1657ff6febda5cee671",
"block": 3511619,
"trx_in_block": 3,
"op_in_trx": 0,
"virtual_op": 0,
"timestamp": "2016-07-25T20:31:51",
"op": [
"vote",
{
"voter": "eeks",
"author": "deviatefish",
"permlink": "on-proof-of-work-and-consensus",
"weight": 10000
}
]
}politicsupvoted (100.00%) @deviatefish / on-proof-of-work-and-consensus
politicsupvoted (100.00%) @deviatefish / on-proof-of-work-and-consensus
| voter | politics |
| author | deviatefish |
| permlink | on-proof-of-work-and-consensus |
| weight | 10000 (100.00%) |
| Transaction Info | Block #3489009/Trx 45a203ee47c0be7b1d59d0000a62704ce19788a9 |
View Raw JSON Data
{
"trx_id": "45a203ee47c0be7b1d59d0000a62704ce19788a9",
"block": 3489009,
"trx_in_block": 2,
"op_in_trx": 0,
"virtual_op": 0,
"timestamp": "2016-07-25T01:29:48",
"op": [
"vote",
{
"voter": "politics",
"author": "deviatefish",
"permlink": "on-proof-of-work-and-consensus",
"weight": 10000
}
]
}crokupvoted (100.00%) @deviatefish / on-proof-of-work-and-consensus
crokupvoted (100.00%) @deviatefish / on-proof-of-work-and-consensus
| voter | crok |
| author | deviatefish |
| permlink | on-proof-of-work-and-consensus |
| weight | 10000 (100.00%) |
| Transaction Info | Block #3488989/Trx 706a52a57b56cbeb6034d34adfb17b5287c6b01f |
View Raw JSON Data
{
"trx_id": "706a52a57b56cbeb6034d34adfb17b5287c6b01f",
"block": 3488989,
"trx_in_block": 1,
"op_in_trx": 0,
"virtual_op": 0,
"timestamp": "2016-07-25T01:28:48",
"op": [
"vote",
{
"voter": "crok",
"author": "deviatefish",
"permlink": "on-proof-of-work-and-consensus",
"weight": 10000
}
]
}luisucv34upvoted (100.00%) @deviatefish / on-proof-of-work-and-consensus
luisucv34upvoted (100.00%) @deviatefish / on-proof-of-work-and-consensus
| voter | luisucv34 |
| author | deviatefish |
| permlink | on-proof-of-work-and-consensus |
| weight | 10000 (100.00%) |
| Transaction Info | Block #3488558/Trx 5633477aecdae0c52699bd61a4222b644f1934dc |
View Raw JSON Data
{
"trx_id": "5633477aecdae0c52699bd61a4222b644f1934dc",
"block": 3488558,
"trx_in_block": 1,
"op_in_trx": 0,
"virtual_op": 0,
"timestamp": "2016-07-25T01:07:06",
"op": [
"vote",
{
"voter": "luisucv34",
"author": "deviatefish",
"permlink": "on-proof-of-work-and-consensus",
"weight": 10000
}
]
}cire81upvoted (100.00%) @deviatefish / on-proof-of-work-and-consensus
cire81upvoted (100.00%) @deviatefish / on-proof-of-work-and-consensus
| voter | cire81 |
| author | deviatefish |
| permlink | on-proof-of-work-and-consensus |
| weight | 10000 (100.00%) |
| Transaction Info | Block #3488530/Trx 395ca1484562db18cad9e36d38ff9ac6a5d8089b |
View Raw JSON Data
{
"trx_id": "395ca1484562db18cad9e36d38ff9ac6a5d8089b",
"block": 3488530,
"trx_in_block": 4,
"op_in_trx": 0,
"virtual_op": 0,
"timestamp": "2016-07-25T01:05:42",
"op": [
"vote",
{
"voter": "cire81",
"author": "deviatefish",
"permlink": "on-proof-of-work-and-consensus",
"weight": 10000
}
]
}deviatefishupvoted (100.00%) @deviatefish / on-proof-of-work-and-consensus
deviatefishupvoted (100.00%) @deviatefish / on-proof-of-work-and-consensus
| voter | deviatefish |
| author | deviatefish |
| permlink | on-proof-of-work-and-consensus |
| weight | 10000 (100.00%) |
| Transaction Info | Block #3488502/Trx b9f25d13675460b515d077d857e5d813a2b2840c |
View Raw JSON Data
{
"trx_id": "b9f25d13675460b515d077d857e5d813a2b2840c",
"block": 3488502,
"trx_in_block": 5,
"op_in_trx": 0,
"virtual_op": 0,
"timestamp": "2016-07-25T01:04:15",
"op": [
"vote",
{
"voter": "deviatefish",
"author": "deviatefish",
"permlink": "on-proof-of-work-and-consensus",
"weight": 10000
}
]
}deviatefishpublished a new post: on-proof-of-work-and-consensus
deviatefishpublished a new post: on-proof-of-work-and-consensus
| parent author | |
| parent permlink | ethereum |
| author | deviatefish |
| permlink | on-proof-of-work-and-consensus |
| title | On Proof-of-Work and "Consensus" |
| body | # On Proof-of-Work Consensus Mechanisms ### Or: how "consensus" became more centralized. ## Forward So, just some context, because sometimes it needs to be said. I'm fairly new to the whole crypto-currency space, so there are a lot of nuances and such that I'm sure I miss out on. My experience with cryptocurrencies is mostly limited to Bitcoin and Ethereum, with some dabbling in day-trading on the side (Full disclosure: I suck at it). Given that I follow Ethereum pretty closely, I've actually become fairly involved in the whole hard-fork debate. I'm very strongly anti-fork, and am really not very happy about the fact that the hard-fork was not only accepted by the majority, but how that came about. Watching the debate evolve over the last few weeks--and looking back on the history of similar debates in the Bitcoin space--I started to come up with some musings about PoW and what we call "consensus". More importantly, this is my attempt at articulating why what we know as "consensus" isn't really consensus anymore, at all. ## On Proof-of-Work as Security The entire point of Proof-of-Work as a security mechanism hinges on its ability to adequately deter attempts to rewrite or control history. The illustrious "51% attack", as the term goes, refers to the concept of obtaining so much hashpower as to be able to (on average) control the writing of the blockchain. The amount of hashpower required to "rewrite history" goes up the further back in time you wish to rewrite. Thus, a "51% attack" merely refers to the minimum amount of hashpower required to, on average, control which blocks get added to the blockchain, and serves as a minimum threshold for anyone who wants to control the blockchain (and potentially rewrite its history). Proof-of-Work, in theory, is designed such that miners, acting in accordance with their own selfish interests, prevent these sorts of damaging attacks from being economically viable. In other words, it pays better to be honest. Even if many miners were to collude to control the chain, the idea is that it should cost them more to do so than they could make by doing so. Attacks such as these are further mitigated by the presence of nodes, which enforce the rules of consensus, refusing to relay blocks that don't follow the agreed-upon protocol. The simplest attack that this prevents is the attack where the miners collude to pay themselves more per block. If the nodes don't also agree to this change, the miners will be wasting their time mining blocks that are then rejected by the network. Again, it should pay more to be honest. Note that there are some assumptions at play here. The first is that miners are acting as individual actors, acting in their own individual best interests. This creates a large population of individual, (assumedly) rational actors, leading to an overall more secure consensus among them. The second is that miners are incentivized to maintain the health of *the* chain, meaning they won't act in such a way as to destroy the value of the chain they're mining. Another note: "consensus", as used within the context of PoW, refers to agreement among *all* miners, as individuals. This implies that miners who refuse or fail to act upon a vote that requires their input are implicit votes for the status quo. The idea behind the concept of "a non-vote is a vote for the status quo" is to impart inertia upon the network. If a change is desired by some miners, it needs to be compelling enough that it's in the economic best interest of the majority of them to make the change. If a majority of miners cannot be incentivized to vote, the vote should fail. This prevents a highly-incentivized minority from seizing control of the chain before the apathetic majority can act. In fact, the idea is that there is never such thing as a "no" vote--just votes for change, and votes for the status quo. This ensures that changes can only be made when there's sufficient incentive for the majority to vote *at all*. These concepts--that attacks should be economically infeasible, that miners act in defense of the chain through their own economic self-interest, and that highly-incentivized minorities cannot control the chain--are what govern the security of a PoW blockchain. ## On Mining Pools and Altcoins Since the original implementation of PoW in Bitcoin, many other coins have been created that leverage the same scheme for security. Most of them differ in terms of what algorithm is used to provide the proof of work, and most of the generally rely on the same assumptions and mechanics to guarantee the security of their own chains. Mining pools (as far as I can tell) originally started out as a measure of convenience, and a way to minimize the variability of getting paid. By joining a pool, the randomness of mining blocks can be smoothed across the entire pool, leading to more regular payouts for everyone in the pool. The pool operator is incentivized to run a pool by skimming a little bit off the top of every block mined, presumably to cover their own expenses. Altcoins come in many flavors, some of them created as scams, others to fill in perceived gaps in another coin's functionality. It's widely regarded that most are (ultimately) scams, but a few important ones have been created along the way. Ethereum, after all, is one of these altcoins. The creation of many altcoins, the advent of pools, and the fact that most altcoins can be mined on the same GPUs has led to changes that affect the assumptions of PoW. One of the more interesting ones is the advent of GPU mining. This allows anyone with a GPU to mine a coin, greatly expanding the set of miners. In the one-chain scenario, this would be a very good thing. However, since that same GPU can mine nearly any other altcoin, it represents a shift in the incentives for that miner. No longer are they only incentivized to see the value of the coin they're mining increase--they're incentivized to see the value of any mineable coin increase. If the one they're currently mining depreciates in value, they can easily switch to another, more profitable coin. If another coin comes along and surpasses the coin they're mining in value, they can easily switch to that one. This has led to the commoditization of mining power. People can rent their miners out to whomever is willing to pay them the most. People can configure their miners to automatically switch to whichever coin is the most profitable *right now*. By pointing these miners at mining pools with consistent payout schemes, their losses in the event of needing to switch coins are even futher reduced. Consistent payout schemes reduce the time a miner has to wait until their next "paycheck". This shift in incentive for miners leads to a very large population of "apathetic" miners--miners who are completely on auto-pilot, and often aren't even in control of which blockchain they're contributing their hashpower towards. All that matters to them is that they're getting paid the most they could be paid right now--regardless of which coin that is. This breaks the assumption that miners' economic self-interest is aligned with the economic interests of the coin they're mining. This also breaks the assumption that miners are interested in the long-term health of the chain they're mining--since it's now in their best interest to just mine whichever coin is worth most *right now.* In essense, miners see mining more like investing in an index fund rather than in an individual company. ## On Pool Operators With miners' investments in mining no longer tied to a single coin, but rather to a large set of coins, we end up in a scenario where the majority of hashpower is entirely apathetic to any single coin's current status. Combined with the fact that the vast majority of hashpower on any given chain belongs to a pool for that coin, the number of economic actors for any given coin has been reduced. Instead of consensus of miners being consensus within the population of all individual miners, it has now been reduced to consensus among pool operators for that coin. Many pools even service multiple coins, meaning their relative investment in any one coin is reduced. It also continues to be in a pool operators' best interests--economically, again--to align their hashpower with the will of the other pools. This further shrinks the number of individual actors controlling the hashpower to the few largest pools. Where they go, the smaller pools follow. ## On the Modern Hashpower "Consensus" All this leads to an outcome that mining was supposed to defend against: the centralization of the security of the blockchain among a small group of actors. The original concept of PoW security was partially predicated by an increase in the number of economic actors--the more there are, the harder it is for any subset of them to control the network. However, with the majority of hashpower for most coins belonging to 2-5 pools, the number of actors controlling the hashpower of the network has dramatically shrunk, not increased. This is further compounded by reducing the likelihood that any split be "contentious", at least from a hashpower perspective. Even when the users of a coin are strongly divided on its future, the outcome will be that the majority of miners are on one side or the other. Some would argue that this is good for the coin--but others argue that it reduces the mindshare of the community by distilling it to one group or another. The other problem is that this is largely divorced from the actual control over the blockchain. Pool operators, in general, don't care which side of a fork wins. They just care that they all agree, since being in agreement is what's economically rational for them to do. Less important is the how and why a particular side of an argument wins. This defeats one of the final assumptions that PoW rests upon: the inertia of miners. No longer does an apathetic majority of hashpower lend a dampening effect to the direction of a chain--now it can be used as leverage by pool operators to encourage the agreement of other operators. Again, given that pool operators will be largely unconcerned about *which* side of a contentious fork wins, this makes them especially susceptible to manipulation--both social and economic. ## On Nodes and Exchanges Which brings us back to nodes. The only mechanism still in place to prevent collusion among pool operators is the consensus of the nodes that relay their blocks. Among these nodes, however, are a few important actors: exchanges. Given that exchanges are the entrypoints into a coin (from another currency, usually), whichever side of a fork they pick will most likely be the one that retains its value. If a coin is no longer listed on an exchange, it becomes extremely difficult to gain more users, and thus more value. It's worth examining the incentives of exchanges at this point. Most will, like pool operators, be apathetic to the actual reasons for a fork. This is because their incentives mostly revolve around volume of trades, and less around the relative value of any given coin. Like an index fund, their profitability is "smoothed" across the value of all coins they offer. They can offer one side of the fork, or both, and suffer little in the way of losses. Like pool operators, that leaves them more susceptible to influence--again, both economic and social. If they can be convinced that it's in their economic best interests to support one side of the fork or the other, they'll support that side. It's also in their best interests to be aligned with the pools on which side of the fork they pick... They only risk losing out if they pick the opposite choice as the pools. The reverse holds true about the mining pools: they only risk losing money if they pick differently than the exchanges. So all things being equal, it's in both the exchanges' and pools' best interests to be aligned on the outcome of a fork. Which leaves us with the rest of the nodes. ## On Defaults With nodes, same as miners, on decisions that are unrelated to the protocol, the default vote is a vote for the status quo. Nodes that do not upgrade should continue to be on the same chain as nodes that do--unless there is a required change in the protocol. The design here is the same as with miners--the inertia of the implicit vote for the status quo protects the network from a highly-incentivized minority taking control. This is where the concept of defaults becomes a little tricky. For changes to the protocol like bug fixes, it should be assumed that the default would be to support the fix, with nodes that fail to upgrade becoming orphaned. For changes to the protocol along the lines of upgrades, the same story should apply. However, for forks involving things other than changes to the protocol, it's assumed that the default vote is a vote for the status quo. Again, the assumption is that this for the protection of the entire network, and that nodes should be apathetic to anything other than the nature of the protocol itself. Where this gets sticky is when the change is a matter of opinion. Here, whichever setting is chosen to be the default exerts extreme control over the outcome of the vote. This, again, is because it leverages the "apathetic" nodes to sway the vote. Instead of them being a "vote for the status quo", they can become an implicit vote for change. The control over the default is also in the hands of a very small number of actors, even when there are multiple implementations--like exchanges and pools, they're highly incentivized to align on the default. In fact, they're also highly incentivized to align with pools and exchanges, for the same reasons pools and exchanges are incentivized to align. ## On "Consensus" and "Trustless Networks." What this boils down to is this: in the event of a contentious hard-fork--one that is neither a protocol upgrade nor a bug fix--the outcome is likely not determined by individual nodes, individual miners, or individual users. Consensus among miners is likely impossible, due to the lack of involvement--and consensus among users or nodes is mostly predicated by the defaults set within the code they're using. This reduces "consensus" from "consensus among individual miners, users, and node operators" to "consensus among pools, exchanges, and developers." Rather than the network securing itself in a "trustless" (trust-minimized) fashion, it now depends on the opinions of a very small set of actors. ## Conclusion The security of Proof-of-Work is still sound in some ways, but it's based on outdated assumptions. The assumption that the number of individual actors mining the chain grows over time is no longer true, as that responsibility has been centralized among the pool operators, whose largest incentive is to be aligned, not pick a side. The assumption that miners' interests are aligned with the blockchain's goals is broken by the ability to switch coins based on current profitability, rather than future profitability. Which, of course, begs the question... What do we do about this? This should be a question on everyone's minds, especially in the Ethereum ecosystem. Ethereum, in particular, is at risk of forking again, this time over the upcoming "difficulty bomb." It's in pool operators' best interests to fork the difficulty bomb away, since it drastically reduces the profitability of mining Ethereum. This would lead to an exodus of mining hashpower, as Ethereum loses its place as the "most profitable coin to mine." This, in turn, means it's in the users' best interests to fork the bomb away, as well, since no one wants to be using a blockchain whose security is decreasing over time. Which leads to the biggest question of all: how does the switch to Proof-of-Stake play out in this world? It's decidedly against any miners' best interests to support a switch to Proof-of-Stake, so how do we prevent them from creating a fork in which Proof-of-Work continues to be supported? Exchanges, on the other hand, might be highly incentivized to see Proof-of-Stake become a reality--because they currently hold the largest reserves of Ethereum, and have the most to gain from staking some portion of it. The only way I see the switch happening is a clean break, with the nodes defaulting to the switch, and with the exchanges on board. But the "interest rate" of PoS is small, meaning miners could bribe exchanges into not supporting the PoS coin. This means the decision might ultimately fall to the users of Ethereum--the same users who just recently revealed they were fine with giving up on some of its core values under extenuating circumstances. This carries the potential of it dividing the community, yet again. With those who feel the mostly strongly about Ethereum's core values--those who opposed the hard-fork and are still on the minority chain--already having left the community, it will be interesting to see how it plays out. After all, when it comes to manipulating consensus, the cheapest way to do it is with politics. |
| json metadata | {"tags":["ethereum","bitcoin","proofowork","theorycrafting","consensus","pow","technology","blockchain"]} |
| Transaction Info | Block #3488502/Trx b9f25d13675460b515d077d857e5d813a2b2840c |
View Raw JSON Data
{
"trx_id": "b9f25d13675460b515d077d857e5d813a2b2840c",
"block": 3488502,
"trx_in_block": 5,
"op_in_trx": 0,
"virtual_op": 0,
"timestamp": "2016-07-25T01:04:15",
"op": [
"comment",
{
"parent_author": "",
"parent_permlink": "ethereum",
"author": "deviatefish",
"permlink": "on-proof-of-work-and-consensus",
"title": "On Proof-of-Work and \"Consensus\"",
"body": "# On Proof-of-Work Consensus Mechanisms\n### Or: how \"consensus\" became more centralized.\n\n## Forward\n\nSo, just some context, because sometimes it needs to be said.\n\nI'm fairly new to the whole crypto-currency space, so there are a lot of nuances and such that I'm sure I miss out on. My experience with cryptocurrencies is mostly limited to Bitcoin and Ethereum, with some dabbling in day-trading on the side (Full disclosure: I suck at it).\n\nGiven that I follow Ethereum pretty closely, I've actually become fairly involved in the whole hard-fork debate. I'm very strongly anti-fork, and am really not very happy about the fact that the hard-fork was not only accepted by the majority, but how that came about. Watching the debate evolve over the last few weeks--and looking back on the history of similar debates in the Bitcoin space--I started to come up with some musings about PoW and what we call \"consensus\".\n\nMore importantly, this is my attempt at articulating why what we know as \"consensus\" isn't really consensus anymore, at all.\n\n## On Proof-of-Work as Security\n\nThe entire point of Proof-of-Work as a security mechanism hinges on its ability to adequately deter attempts to rewrite or control history. The illustrious \"51% attack\", as the term goes, refers to the concept of obtaining so much hashpower as to be able to (on average) control the writing of the blockchain. The amount of hashpower required to \"rewrite history\" goes up the further back in time you wish to rewrite. Thus, a \"51% attack\" merely refers to the minimum amount of hashpower required to, on average, control which blocks get added to the blockchain, and serves as a minimum threshold for anyone who wants to control the blockchain (and potentially rewrite its history). Proof-of-Work, in theory, is designed such that miners, acting in accordance with their own selfish interests, prevent these sorts of damaging attacks from being economically viable.\n\nIn other words, it pays better to be honest.\n\nEven if many miners were to collude to control the chain, the idea is that it should cost them more to do so than they could make by doing so.\n\nAttacks such as these are further mitigated by the presence of nodes, which enforce the rules of consensus, refusing to relay blocks that don't follow the agreed-upon protocol. The simplest attack that this prevents is the attack where the miners collude to pay themselves more per block. If the nodes don't also agree to this change, the miners will be wasting their time mining blocks that are then rejected by the network.\n\nAgain, it should pay more to be honest.\n\nNote that there are some assumptions at play here. The first is that miners are acting as individual actors, acting in their own individual best interests. This creates a large population of individual, (assumedly) rational actors, leading to an overall more secure consensus among them. The second is that miners are incentivized to maintain the health of *the* chain, meaning they won't act in such a way as to destroy the value of the chain they're mining.\n\nAnother note: \"consensus\", as used within the context of PoW, refers to agreement among *all* miners, as individuals. This implies that miners who refuse or fail to act upon a vote that requires their input are implicit votes for the status quo. The idea behind the concept of \"a non-vote is a vote for the status quo\" is to impart inertia upon the network. If a change is desired by some miners, it needs to be compelling enough that it's in the economic best interest of the majority of them to make the change. If a majority of miners cannot be incentivized to vote, the vote should fail.\n\nThis prevents a highly-incentivized minority from seizing control of the chain before the apathetic majority can act. In fact, the idea is that there is never such thing as a \"no\" vote--just votes for change, and votes for the status quo. This ensures that changes can only be made when there's sufficient incentive for the majority to vote *at all*.\n\nThese concepts--that attacks should be economically infeasible, that miners act in defense of the chain through their own economic self-interest, and that highly-incentivized minorities cannot control the chain--are what govern the security of a PoW blockchain.\n\n## On Mining Pools and Altcoins\n\nSince the original implementation of PoW in Bitcoin, many other coins have been created that leverage the same scheme for security. Most of them differ in terms of what algorithm is used to provide the proof of work, and most of the generally rely on the same assumptions and mechanics to guarantee the security of their own chains.\n\nMining pools (as far as I can tell) originally started out as a measure of convenience, and a way to minimize the variability of getting paid. By joining a pool, the randomness of mining blocks can be smoothed across the entire pool, leading to more regular payouts for everyone in the pool. The pool operator is incentivized to run a pool by skimming a little bit off the top of every block mined, presumably to cover their own expenses.\n\nAltcoins come in many flavors, some of them created as scams, others to fill in perceived gaps in another coin's functionality. It's widely regarded that most are (ultimately) scams, but a few important ones have been created along the way. Ethereum, after all, is one of these altcoins.\n\nThe creation of many altcoins, the advent of pools, and the fact that most altcoins can be mined on the same GPUs has led to changes that affect the assumptions of PoW.\n\nOne of the more interesting ones is the advent of GPU mining. This allows anyone with a GPU to mine a coin, greatly expanding the set of miners. In the one-chain scenario, this would be a very good thing. However, since that same GPU can mine nearly any other altcoin, it represents a shift in the incentives for that miner. No longer are they only incentivized to see the value of the coin they're mining increase--they're incentivized to see the value of any mineable coin increase. If the one they're currently mining depreciates in value, they can easily switch to another, more profitable coin. If another coin comes along and surpasses the coin they're mining in value, they can easily switch to that one.\n\nThis has led to the commoditization of mining power. People can rent their miners out to whomever is willing to pay them the most. People can configure their miners to automatically switch to whichever coin is the most profitable *right now*. By pointing these miners at mining pools with consistent payout schemes, their losses in the event of needing to switch coins are even futher reduced. Consistent payout schemes reduce the time a miner has to wait until their next \"paycheck\".\n\nThis shift in incentive for miners leads to a very large population of \"apathetic\" miners--miners who are completely on auto-pilot, and often aren't even in control of which blockchain they're contributing their hashpower towards. All that matters to them is that they're getting paid the most they could be paid right now--regardless of which coin that is.\n\nThis breaks the assumption that miners' economic self-interest is aligned with the economic interests of the coin they're mining. This also breaks the assumption that miners are interested in the long-term health of the chain they're mining--since it's now in their best interest to just mine whichever coin is worth most *right now.*\n\nIn essense, miners see mining more like investing in an index fund rather than in an individual company.\n\n## On Pool Operators\n\nWith miners' investments in mining no longer tied to a single coin, but rather to a large set of coins, we end up in a scenario where the majority of hashpower is entirely apathetic to any single coin's current status. Combined with the fact that the vast majority of hashpower on any given chain belongs to a pool for that coin, the number of economic actors for any given coin has been reduced. Instead of consensus of miners being consensus within the population of all individual miners, it has now been reduced to consensus among pool operators for that coin.\n\nMany pools even service multiple coins, meaning their relative investment in any one coin is reduced.\n\nIt also continues to be in a pool operators' best interests--economically, again--to align their hashpower with the will of the other pools. This further shrinks the number of individual actors controlling the hashpower to the few largest pools. Where they go, the smaller pools follow.\n\n## On the Modern Hashpower \"Consensus\"\n\nAll this leads to an outcome that mining was supposed to defend against: the centralization of the security of the blockchain among a small group of actors. The original concept of PoW security was partially predicated by an increase in the number of economic actors--the more there are, the harder it is for any subset of them to control the network. However, with the majority of hashpower for most coins belonging to 2-5 pools, the number of actors controlling the hashpower of the network has dramatically shrunk, not increased.\n\nThis is further compounded by reducing the likelihood that any split be \"contentious\", at least from a hashpower perspective. Even when the users of a coin are strongly divided on its future, the outcome will be that the majority of miners are on one side or the other. Some would argue that this is good for the coin--but others argue that it reduces the mindshare of the community by distilling it to one group or another. The other problem is that this is largely divorced from the actual control over the blockchain.\n\nPool operators, in general, don't care which side of a fork wins. They just care that they all agree, since being in agreement is what's economically rational for them to do. Less important is the how and why a particular side of an argument wins.\n\nThis defeats one of the final assumptions that PoW rests upon: the inertia of miners. No longer does an apathetic majority of hashpower lend a dampening effect to the direction of a chain--now it can be used as leverage by pool operators to encourage the agreement of other operators.\n\nAgain, given that pool operators will be largely unconcerned about *which* side of a contentious fork wins, this makes them especially susceptible to manipulation--both social and economic.\n\n## On Nodes and Exchanges\n\nWhich brings us back to nodes. The only mechanism still in place to prevent collusion among pool operators is the consensus of the nodes that relay their blocks. Among these nodes, however, are a few important actors: exchanges. Given that exchanges are the entrypoints into a coin (from another currency, usually), whichever side of a fork they pick will most likely be the one that retains its value. If a coin is no longer listed on an exchange, it becomes extremely difficult to gain more users, and thus more value.\n\nIt's worth examining the incentives of exchanges at this point. Most will, like pool operators, be apathetic to the actual reasons for a fork. This is because their incentives mostly revolve around volume of trades, and less around the relative value of any given coin. Like an index fund, their profitability is \"smoothed\" across the value of all coins they offer. They can offer one side of the fork, or both, and suffer little in the way of losses.\n\nLike pool operators, that leaves them more susceptible to influence--again, both economic and social. If they can be convinced that it's in their economic best interests to support one side of the fork or the other, they'll support that side. It's also in their best interests to be aligned with the pools on which side of the fork they pick... They only risk losing out if they pick the opposite choice as the pools.\n\nThe reverse holds true about the mining pools: they only risk losing money if they pick differently than the exchanges.\n\nSo all things being equal, it's in both the exchanges' and pools' best interests to be aligned on the outcome of a fork.\n\nWhich leaves us with the rest of the nodes.\n\n## On Defaults\n\nWith nodes, same as miners, on decisions that are unrelated to the protocol, the default vote is a vote for the status quo. Nodes that do not upgrade should continue to be on the same chain as nodes that do--unless there is a required change in the protocol. The design here is the same as with miners--the inertia of the implicit vote for the status quo protects the network from a highly-incentivized minority taking control.\n\nThis is where the concept of defaults becomes a little tricky. For changes to the protocol like bug fixes, it should be assumed that the default would be to support the fix, with nodes that fail to upgrade becoming orphaned. For changes to the protocol along the lines of upgrades, the same story should apply.\n\nHowever, for forks involving things other than changes to the protocol, it's assumed that the default vote is a vote for the status quo. Again, the assumption is that this for the protection of the entire network, and that nodes should be apathetic to anything other than the nature of the protocol itself.\n\nWhere this gets sticky is when the change is a matter of opinion. Here, whichever setting is chosen to be the default exerts extreme control over the outcome of the vote. This, again, is because it leverages the \"apathetic\" nodes to sway the vote. Instead of them being a \"vote for the status quo\", they can become an implicit vote for change.\n\nThe control over the default is also in the hands of a very small number of actors, even when there are multiple implementations--like exchanges and pools, they're highly incentivized to align on the default. In fact, they're also highly incentivized to align with pools and exchanges, for the same reasons pools and exchanges are incentivized to align.\n\n## On \"Consensus\" and \"Trustless Networks.\"\n\nWhat this boils down to is this: in the event of a contentious hard-fork--one that is neither a protocol upgrade nor a bug fix--the outcome is likely not determined by individual nodes, individual miners, or individual users. Consensus among miners is likely impossible, due to the lack of involvement--and consensus among users or nodes is mostly predicated by the defaults set within the code they're using.\n\nThis reduces \"consensus\" from \"consensus among individual miners, users, and node operators\" to \"consensus among pools, exchanges, and developers.\"\n\nRather than the network securing itself in a \"trustless\" (trust-minimized) fashion, it now depends on the opinions of a very small set of actors.\n\n## Conclusion\n\nThe security of Proof-of-Work is still sound in some ways, but it's based on outdated assumptions. The assumption that the number of individual actors mining the chain grows over time is no longer true, as that responsibility has been centralized among the pool operators, whose largest incentive is to be aligned, not pick a side. The assumption that miners' interests are aligned with the blockchain's goals is broken by the ability to switch coins based on current profitability, rather than future profitability.\n\nWhich, of course, begs the question... What do we do about this? This should be a question on everyone's minds, especially in the Ethereum ecosystem. Ethereum, in particular, is at risk of forking again, this time over the upcoming \"difficulty bomb.\" It's in pool operators' best interests to fork the difficulty bomb away, since it drastically reduces the profitability of mining Ethereum. This would lead to an exodus of mining hashpower, as Ethereum loses its place as the \"most profitable coin to mine.\"\n\nThis, in turn, means it's in the users' best interests to fork the bomb away, as well, since no one wants to be using a blockchain whose security is decreasing over time.\n\nWhich leads to the biggest question of all: how does the switch to Proof-of-Stake play out in this world? It's decidedly against any miners' best interests to support a switch to Proof-of-Stake, so how do we prevent them from creating a fork in which Proof-of-Work continues to be supported? Exchanges, on the other hand, might be highly incentivized to see Proof-of-Stake become a reality--because they currently hold the largest reserves of Ethereum, and have the most to gain from staking some portion of it.\n\nThe only way I see the switch happening is a clean break, with the nodes defaulting to the switch, and with the exchanges on board.\n\nBut the \"interest rate\" of PoS is small, meaning miners could bribe exchanges into not supporting the PoS coin.\n\nThis means the decision might ultimately fall to the users of Ethereum--the same users who just recently revealed they were fine with giving up on some of its core values under extenuating circumstances. This carries the potential of it dividing the community, yet again. With those who feel the mostly strongly about Ethereum's core values--those who opposed the hard-fork and are still on the minority chain--already having left the community, it will be interesting to see how it plays out.\n\nAfter all, when it comes to manipulating consensus, the cheapest way to do it is with politics.",
"json_metadata": "{\"tags\":[\"ethereum\",\"bitcoin\",\"proofowork\",\"theorycrafting\",\"consensus\",\"pow\",\"technology\",\"blockchain\"]}"
}
]
}steemcreated a new account: @deviatefish
steemcreated a new account: @deviatefish
| fee | 3.000 STEEM |
| creator | steem |
| new account name | deviatefish |
| owner | {"weight_threshold":1,"account_auths":[],"key_auths":[["STM7tv98jDJ1UBQmWPUY2P6NZ1cJqmfG2LMqyTvAN74ruoS3KPTv7",1]]} |
| active | {"weight_threshold":1,"account_auths":[],"key_auths":[["STM6E9BLj429ZT3DRf5VB449JoGoQvmZVnzH1W5YmjPvdsrEqTVXc",1]]} |
| posting | {"weight_threshold":1,"account_auths":[],"key_auths":[["STM5LsLpABdQo1P4dVneY2D3mt9LpoHGSEm6ZLwQemsVrrMc24r3W",1]]} |
| memo key | STM87UyCtKKKk66U8h1gXmf6uohK2d4oMacvpkDj57huLKJZ3VLQC |
| json metadata | |
| Transaction Info | Block #3488427/Trx 6eade0c4b32b149223c73dc2211d3f606e97d7fd |
View Raw JSON Data
{
"trx_id": "6eade0c4b32b149223c73dc2211d3f606e97d7fd",
"block": 3488427,
"trx_in_block": 1,
"op_in_trx": 0,
"virtual_op": 0,
"timestamp": "2016-07-25T01:00:30",
"op": [
"account_create",
{
"fee": "3.000 STEEM",
"creator": "steem",
"new_account_name": "deviatefish",
"owner": {
"weight_threshold": 1,
"account_auths": [],
"key_auths": [
[
"STM7tv98jDJ1UBQmWPUY2P6NZ1cJqmfG2LMqyTvAN74ruoS3KPTv7",
1
]
]
},
"active": {
"weight_threshold": 1,
"account_auths": [],
"key_auths": [
[
"STM6E9BLj429ZT3DRf5VB449JoGoQvmZVnzH1W5YmjPvdsrEqTVXc",
1
]
]
},
"posting": {
"weight_threshold": 1,
"account_auths": [],
"key_auths": [
[
"STM5LsLpABdQo1P4dVneY2D3mt9LpoHGSEm6ZLwQemsVrrMc24r3W",
1
]
]
},
"memo_key": "STM87UyCtKKKk66U8h1gXmf6uohK2d4oMacvpkDj57huLKJZ3VLQC",
"json_metadata": ""
}
]
}Manabar
Voting Power100.00%
Downvote Power100.00%
Resource Credits100.00%
Reputation Progress37.28%
{
"voting_manabar": {
"current_mana": 9949,
"last_update_time": 1471482438
},
"downvote_manabar": {
"current_mana": 0,
"last_update_time": 1469408430
},
"rc_account": {
"account": "deviatefish",
"rc_manabar": {
"current_mana": "14760008299",
"last_update_time": 1537887600
},
"max_rc_creation_adjustment": {
"amount": "2020748973",
"precision": 6,
"nai": "@@000000037"
},
"max_rc": "14760008299"
}
}Account Metadata
| POSTING JSON METADATA | |
| None | |
| JSON METADATA | |
| None |
{
"posting_json_metadata": {},
"json_metadata": {}
}Auth Keys
Owner
Single Signature
Public Keys
STM7tv98jDJ1UBQmWPUY2P6NZ1cJqmfG2LMqyTvAN74ruoS3KPTv71/1
Active
Single Signature
Public Keys
STM6E9BLj429ZT3DRf5VB449JoGoQvmZVnzH1W5YmjPvdsrEqTVXc1/1
Posting
Single Signature
Public Keys
STM5LsLpABdQo1P4dVneY2D3mt9LpoHGSEm6ZLwQemsVrrMc24r3W1/1
Memo
STM87UyCtKKKk66U8h1gXmf6uohK2d4oMacvpkDj57huLKJZ3VLQC
{
"owner": {
"weight_threshold": 1,
"account_auths": [],
"key_auths": [
[
"STM7tv98jDJ1UBQmWPUY2P6NZ1cJqmfG2LMqyTvAN74ruoS3KPTv7",
1
]
]
},
"active": {
"weight_threshold": 1,
"account_auths": [],
"key_auths": [
[
"STM6E9BLj429ZT3DRf5VB449JoGoQvmZVnzH1W5YmjPvdsrEqTVXc",
1
]
]
},
"posting": {
"weight_threshold": 1,
"account_auths": [],
"key_auths": [
[
"STM5LsLpABdQo1P4dVneY2D3mt9LpoHGSEm6ZLwQemsVrrMc24r3W",
1
]
]
},
"memo": "STM87UyCtKKKk66U8h1gXmf6uohK2d4oMacvpkDj57huLKJZ3VLQC"
}Witness Votes
0 / 30
No active witness votes.
[]