VOTING POWER100.00%
DOWNVOTE POWER100.00%
RESOURCE CREDITS100.00%
REPUTATION PROGRESS0.00%
Net Worth
0.015USD
STEEM
0.000STEEM
SBD
0.000SBD
Own SP
0.251SP
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 | 0.251SP | SP |
| Delegated Out | 0.000SP | SP |
| Delegation In | 0.000SP | SP |
| Effective Power | 0.251SP | SP |
| Reward SP (pending) | 0.000SP | SP |
| SBD | ||
| sbd_balance | 0.000SBD | 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": "409.012969 VESTS",
"delegated_vesting_shares": "0.000000 VESTS",
"received_vesting_shares": "0.000000 VESTS",
"sbd_balance": "0.000 SBD",
"savings_sbd_balance": "0.000 SBD",
"reward_sbd_balance": "0.000 SBD",
"conversions": []
}Account Info
| name | camphortree |
| id | 742587 |
| rank | 1,667,300 |
| reputation | 8198947 |
| created | 2018-02-10T04:42:51 |
| recovery_account | cnsteem |
| proxy | None |
| post_count | 4 |
| comment_count | 0 |
| lifetime_vote_count | 0 |
| witnesses_voted_for | 0 |
| last_post | 2018-08-15T01:23:24 |
| last_root_post | 2018-08-14T08:30:18 |
| last_vote_time | 1970-01-01T00:00:00 |
| proxied_vsf_votes | 0, 0, 0, 0 |
| can_vote | 1 |
| voting_power | 10,000 |
| delayed_votes | 0 |
| balance | 0.000 STEEM |
| savings_balance | 0.000 STEEM |
| sbd_balance | 0.000 SBD |
| savings_sbd_balance | 0.000 SBD |
| vesting_shares | 409.012969 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 | 2018-03-31T01:36:48 |
| last_account_update | 2018-03-31T01:36:48 |
| 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": 742587,
"name": "camphortree",
"owner": {
"weight_threshold": 1,
"account_auths": [],
"key_auths": [
[
"STM7pqGByFvQyjg4L4m9STH7MVbSa4GxvabUajyZ47deCjG9uMayD",
1
]
]
},
"active": {
"weight_threshold": 1,
"account_auths": [],
"key_auths": [
[
"STM6kLyQmiCPqhj9DfAinK3TYFjmWpRr5yV7Wao2YLbd4Ppu8WUAE",
1
]
]
},
"posting": {
"weight_threshold": 1,
"account_auths": [],
"key_auths": [
[
"STM8TvjNvtXNjCfdPuZi1PxtkdSJ1Wc19yUog2WUb2sLd7qfm7tDd",
1
]
]
},
"memo_key": "STM6LXXSQkbBUdKeFXrmeLo4WX3Y2mhjnvLuyryb3Kmb31CyY98Td",
"json_metadata": "",
"posting_json_metadata": "",
"proxy": "",
"last_owner_update": "2018-03-31T01:36:48",
"last_account_update": "2018-03-31T01:36:48",
"created": "2018-02-10T04:42:51",
"mined": false,
"recovery_account": "cnsteem",
"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": 10000,
"last_update_time": 1518237771
},
"downvote_manabar": {
"current_mana": 0,
"last_update_time": 1518237771
},
"voting_power": 10000,
"balance": "0.000 STEEM",
"savings_balance": "0.000 STEEM",
"sbd_balance": "0.000 SBD",
"sbd_seconds": "0",
"sbd_seconds_last_update": "1970-01-01T00:00:00",
"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": "409.012969 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": 0,
"proxied_vsf_votes": [
0,
0,
0,
0
],
"witnesses_voted_for": 0,
"last_post": "2018-08-15T01:23:24",
"last_root_post": "2018-08-14T08:30:18",
"last_vote_time": "1970-01-01T00:00:00",
"post_bandwidth": 0,
"pending_claimed_accounts": 0,
"vesting_balance": "0.000 STEEM",
"reputation": 8198947,
"transfer_history": [],
"market_history": [],
"post_history": [],
"vote_history": [],
"other_history": [],
"witness_votes": [],
"tags_usage": [],
"guest_bloggers": [],
"rank": 1667300
}Withdraw Routes
| Incoming | Outgoing |
|---|---|
Empty | Empty |
{
"incoming": [],
"outgoing": []
}From Date
To Date
2020/02/10 05:19:15
2020/02/10 05:19:15
| author | steemitboard |
| body | Congratulations @camphortree! You received a personal award! <table><tr><td>https://steemitimages.com/70x70/http://steemitboard.com/@camphortree/birthday2.png</td><td>Happy Birthday! - You are on the Steem blockchain for 2 years!</td></tr></table> <sub>_You can view [your badges on your Steem Board](https://steemitboard.com/@camphortree) and compare to others on the [Steem Ranking](https://steemitboard.com/ranking/index.php?name=camphortree)_</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"]} |
| parent author | camphortree |
| parent permlink | eos-dpos |
| permlink | steemitboard-notify-camphortree-20200210t051914000z |
| title | |
| Transaction Info | Block #40688395/Trx e6b5933789a4474974c444b9bc7269e2b6368ee3 |
View Raw JSON Data
{
"block": 40688395,
"op": [
"comment",
{
"author": "steemitboard",
"body": "Congratulations @camphortree! You received a personal award!\n\n<table><tr><td>https://steemitimages.com/70x70/http://steemitboard.com/@camphortree/birthday2.png</td><td>Happy Birthday! - You are on the Steem blockchain for 2 years!</td></tr></table>\n\n<sub>_You can view [your badges on your Steem Board](https://steemitboard.com/@camphortree) and compare to others on the [Steem Ranking](https://steemitboard.com/ranking/index.php?name=camphortree)_</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\"]}",
"parent_author": "camphortree",
"parent_permlink": "eos-dpos",
"permlink": "steemitboard-notify-camphortree-20200210t051914000z",
"title": ""
}
],
"op_in_trx": 0,
"timestamp": "2020-02-10T05:19:15",
"trx_id": "e6b5933789a4474974c444b9bc7269e2b6368ee3",
"trx_in_block": 3,
"virtual_op": 0
}2019/02/10 05:47:36
2019/02/10 05:47:36
| author | steemitboard |
| body | Congratulations @camphortree! You received a personal award! <table><tr><td>https://steemitimages.com/70x70/http://steemitboard.com/@camphortree/birthday1.png</td><td>Happy Birthday! - You are on the Steem blockchain for 1 year!</td></tr></table> <sub>_[Click here to view your Board](https://steemitboard.com/@camphortree)_</sub> > Support [SteemitBoard's project](https://steemit.com/@steemitboard)! **[Vote for its witness](https://v2.steemconnect.com/sign/account-witness-vote?witness=steemitboard&approve=1)** and **get one more award**! |
| json metadata | {"image":["https://steemitboard.com/img/notify.png"]} |
| parent author | camphortree |
| parent permlink | eos-dpos |
| permlink | steemitboard-notify-camphortree-20190210t054735000z |
| title | |
| Transaction Info | Block #30217013/Trx be8578e346269feb33c3114c88e8eee4357df0cd |
View Raw JSON Data
{
"block": 30217013,
"op": [
"comment",
{
"author": "steemitboard",
"body": "Congratulations @camphortree! You received a personal award!\n\n<table><tr><td>https://steemitimages.com/70x70/http://steemitboard.com/@camphortree/birthday1.png</td><td>Happy Birthday! - You are on the Steem blockchain for 1 year!</td></tr></table>\n\n<sub>_[Click here to view your Board](https://steemitboard.com/@camphortree)_</sub>\n\n\n> Support [SteemitBoard's project](https://steemit.com/@steemitboard)! **[Vote for its witness](https://v2.steemconnect.com/sign/account-witness-vote?witness=steemitboard&approve=1)** and **get one more award**!",
"json_metadata": "{\"image\":[\"https://steemitboard.com/img/notify.png\"]}",
"parent_author": "camphortree",
"parent_permlink": "eos-dpos",
"permlink": "steemitboard-notify-camphortree-20190210t054735000z",
"title": ""
}
],
"op_in_trx": 0,
"timestamp": "2019-02-10T05:47:36",
"trx_id": "be8578e346269feb33c3114c88e8eee4357df0cd",
"trx_in_block": 4,
"virtual_op": 0
}zhoufkupvoted (100.00%) @camphortree / re-camphortree-eos-dpos-20180815t012324317z2018/09/18 06:35:48
zhoufkupvoted (100.00%) @camphortree / re-camphortree-eos-dpos-20180815t012324317z
2018/09/18 06:35:48
| author | camphortree |
| permlink | re-camphortree-eos-dpos-20180815t012324317z |
| voter | zhoufk |
| weight | 10000 (100.00%) |
| Transaction Info | Block #26045530/Trx 9c9d59243858c57ec9939a2e1eddacc4bd090bdf |
View Raw JSON Data
{
"block": 26045530,
"op": [
"vote",
{
"author": "camphortree",
"permlink": "re-camphortree-eos-dpos-20180815t012324317z",
"voter": "zhoufk",
"weight": 10000
}
],
"op_in_trx": 0,
"timestamp": "2018-09-18T06:35:48",
"trx_id": "9c9d59243858c57ec9939a2e1eddacc4bd090bdf",
"trx_in_block": 17,
"virtual_op": 0
}daykeneuupvoted (100.00%) @camphortree / eos-dpos2018/08/19 06:26:24
daykeneuupvoted (100.00%) @camphortree / eos-dpos
2018/08/19 06:26:24
| author | camphortree |
| permlink | eos-dpos |
| voter | daykeneu |
| weight | 10000 (100.00%) |
| Transaction Info | Block #25197000/Trx 6ffe015d976a9734006ea11a905515b887e23e25 |
View Raw JSON Data
{
"block": 25197000,
"op": [
"vote",
{
"author": "camphortree",
"permlink": "eos-dpos",
"voter": "daykeneu",
"weight": 10000
}
],
"op_in_trx": 0,
"timestamp": "2018-08-19T06:26:24",
"trx_id": "6ffe015d976a9734006ea11a905515b887e23e25",
"trx_in_block": 7,
"virtual_op": 0
}2018/08/15 01:23:24
2018/08/15 01:23:24
| author | camphortree |
| body | 简书版本,格式较这个规范:https://www.jianshu.com/p/f32e50499cdc |
| json metadata | {"tags":["eos"],"links":["https://www.jianshu.com/p/f32e50499cdc"],"app":"steemit/0.1"} |
| parent author | camphortree |
| parent permlink | eos-dpos |
| permlink | re-camphortree-eos-dpos-20180815t012324317z |
| title | |
| Transaction Info | Block #25075782/Trx 0f884844b0fd8b9ff780a63e347c842dd60ccf3e |
View Raw JSON Data
{
"block": 25075782,
"op": [
"comment",
{
"author": "camphortree",
"body": "简书版本,格式较这个规范:https://www.jianshu.com/p/f32e50499cdc",
"json_metadata": "{\"tags\":[\"eos\"],\"links\":[\"https://www.jianshu.com/p/f32e50499cdc\"],\"app\":\"steemit/0.1\"}",
"parent_author": "camphortree",
"parent_permlink": "eos-dpos",
"permlink": "re-camphortree-eos-dpos-20180815t012324317z",
"title": ""
}
],
"op_in_trx": 0,
"timestamp": "2018-08-15T01:23:24",
"trx_id": "0f884844b0fd8b9ff780a63e347c842dd60ccf3e",
"trx_in_block": 41,
"virtual_op": 0
}sensationupvoted (100.00%) @camphortree / eos-dpos2018/08/14 09:55:12
sensationupvoted (100.00%) @camphortree / eos-dpos
2018/08/14 09:55:12
| author | camphortree |
| permlink | eos-dpos |
| voter | sensation |
| weight | 10000 (100.00%) |
| Transaction Info | Block #25057224/Trx c72826e8841aaec33a94c210fd37c33ab23aaeac |
View Raw JSON Data
{
"block": 25057224,
"op": [
"vote",
{
"author": "camphortree",
"permlink": "eos-dpos",
"voter": "sensation",
"weight": 10000
}
],
"op_in_trx": 0,
"timestamp": "2018-08-14T09:55:12",
"trx_id": "c72826e8841aaec33a94c210fd37c33ab23aaeac",
"trx_in_block": 7,
"virtual_op": 0
}camphortreepublished a new post: eos-dpos2018/08/14 08:30:18
camphortreepublished a new post: eos-dpos
2018/08/14 08:30:18
| author | camphortree |
| body | eos的出块流程大致如下: Plain Text ........ //启动生产插件 producer_plugin::plugin_startup(); ........ //出块循环 my->schedule_production_loop(); ......... //出块 auto result = start_block(); .......... //签名和提交 auto res = self->maybe_produce_block(); .......... 以上步骤的核心在于:auto result = start_block(); 这个函数更跟下去会发现,重点在于: Plain Text chain.start_block(block_time, blocks_to_confirm); 这个函数才是dops的关键 C++ void start_block( block_timestamp_type when, uint16_t confirm_block_count, controller::block_status s ) { FC_ASSERT( !pending ); FC_ASSERT( db.revision() == head->block_num, "", ("db.revision()", db.revision())("controller_head_block", head->block_num)("fork_db_head_block", fork_db.head()->block_num) ); auto guard_pending = fc::make_scoped_exit([this](){ pending.reset(); }); pending = db.start_undo_session(true); pending->_block_status = s; //由当前区块头创建一个新的block_state ---->>>>> 11111 pending->_pending_block_state = std::make_shared<block_state>( *head, when ); // promotes pending schedule (if any) to active pending->_pending_block_state->in_current_chain = true; //确认链上的区块 ----->>>>> 22222 pending->_pending_block_state->set_confirmed(confirm_block_count); //更新激活的生产者 ----->>>> 33333 auto was_pending_promoted = pending->_pending_block_state->maybe_promote_pending(); const auto& gpo = db.get<global_property_object>(); if( gpo.proposed_schedule_block_num.valid() && // if there is a proposed schedule that was proposed in a block ... ( *gpo.proposed_schedule_block_num <= pending->_pending_block_state->dpos_irreversible_blocknum ) && // ... that has now become irreversible ... pending->_pending_block_state->pending_schedule.producers.size() == 0 && // ... and there is room for a new pending schedule ... !was_pending_promoted // ... and not just because it was promoted to active at the start of this block, then: ) { // Promote proposed schedule to pending schedule. if( !replaying ) { ilog( "promoting proposed schedule (set in block ${proposed_num}) to pending; current block: ${n} lib: ${lib} schedule: ${schedule} ", ("proposed_num", *gpo.proposed_schedule_block_num)("n", pending->_pending_block_state->block_num) ("lib", pending->_pending_block_state->dpos_irreversible_blocknum) ("schedule", static_cast<producer_schedule_type>(gpo.proposed_schedule) ) ); } pending->_pending_block_state->set_new_producers( gpo.proposed_schedule ); db.modify( gpo, [&]( auto& gp ) { gp.proposed_schedule_block_num = optional<block_num_type>(); gp.proposed_schedule.clear(); }); std::cout<<"\n========555==========\n"; std::cout<< fc::json::to_string(*pending->_pending_block_state)<<"\n"; std::cout<<"==========555========\n"; } 11111,22222,33333这三步涉及到的数据结构是block_state JSON { "id": "0000058af1b33d45e4ec927e52b74e8568422f45245dc0399c144bdff6dc16d8", "block_num": 1418, "header": { "timestamp": "2018-08-04T07:31:47.500", "producer": "accountnum33", "confirmed": 0, "previous": "0000058980ac0382075af4216ddbd7be1eae1690a278d7cc04a61570f307b39d", "transaction_mroot": "0000000000000000000000000000000000000000000000000000000000000000", "action_mroot": "eaadc508183ba966551e48dba3a44505a95e6a42d7a53f435db39c3dec3584c9", "schedule_version": 1, "header_extensions": [ ], "producer_signature": "SIG_K1_KVyKdpLpEXPQUvRR5jZL4FMYvFDNic6tyvX6icLXFKhoriskCg21esduAceX2cZDv2GwcB12TxdneeKMwfzWB2TckZpZvQ" }, "dpos_proposed_irreversible_blocknum": 1370, "dpos_irreversible_blocknum": 1370, "bft_irreversible_blocknum": 0, "pending_schedule_lib_num": 1369, "pending_schedule_hash": "b4ea72a3e20628a54028e681258cd1e6a46b20ae07210836f29087f9f8f37346", "pending_schedule": { "version": 1, "producers": [ ] }, "active_schedule": { "version": 1, "producers": [ { "producer_name": "accountnum11", "block_signing_key": "EOS8mUftJXepGzdQ2TaCduNuSPAfXJHf22uex4u41ab1EVv9EAhWt" }, { "producer_name": "accountnum22", "block_signing_key": "EOS8mUftJXepGzdQ2TaCduNuSPAfXJHf22uex4u41ab1EVv9EAhWt" }, { "producer_name": "accountnum33", "block_signing_key": "EOS8mUftJXepGzdQ2TaCduNuSPAfXJHf22uex4u41ab1EVv9EAhWt" } ] }, "blockroot_merkle": { "_active_nodes": [ "0000058980ac0382075af4216ddbd7be1eae1690a278d7cc04a61570f307b39d", "ab53f4807abffafbcf15fa6edd6ffe72cf73d18689403e389127015b4fd1e544", "0275481b63dcb217603558835292d4057d39f9a5d41c9d5302230d379a992b18", "af944001e02b80671b6ea50e5f83969fc2db5e56671ff83700a9018336d0fc58", "e1ed7b7a82f5507110497fd41f2098a269ffbe8202de0f57393b4aeb27792434", "5a36ca2325d5f7b2de22b98de7cf70c0a06cf391fc17f1d242b964d16146dee9" ], "_node_count": 1417 }, "producer_to_last_produced": [ [ "accountnum11", 1394 ], [ "accountnum22", 1406 ], [ "accountnum33", 1418 ], [ "eosio", 1370 ] ], "producer_to_last_implied_irb": [ [ "accountnum11", 1370 ], [ "accountnum22", 1370 ], [ "accountnum33", 1370 ] ], "block_signing_key": "EOS8mUftJXepGzdQ2TaCduNuSPAfXJHf22uex4u41ab1EVv9EAhWt", "confirm_count": [ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 ], "confirmations": [ ], "block": { "timestamp": "2018-08-04T07:31:47.500", "producer": "accountnum33", "confirmed": 0, "previous": "0000058980ac0382075af4216ddbd7be1eae1690a278d7cc04a61570f307b39d", "transaction_mroot": "0000000000000000000000000000000000000000000000000000000000000000", "action_mroot": "eaadc508183ba966551e48dba3a44505a95e6a42d7a53f435db39c3dec3584c9", "schedule_version": 1, "header_extensions": [ ], "producer_signature": "SIG_K1_KVyKdpLpEXPQUvRR5jZL4FMYvFDNic6tyvX6icLXFKhoriskCg21esduAceX2cZDv2GwcB12TxdneeKMwfzWB2TckZpZvQ", "transactions": [ ], "block_extensions": [ ] }, "validated": true, "in_current_chain": true } 第一步:从当前块头出发,生成一个新的block_state。 C++ ........ result.producer_to_last_implied_irb[prokey.producer_name] = result.dpos_proposed_irreversible_blocknum; result.dpos_irreversible_blocknum = result.calc_dpos_last_irreversible(); /// grow the confirmed count static_assert(std::numeric_limits<uint8_t>::max() >= (config::max_producers * 2 / 3) + 1, "8bit confirmations may not be able to hold all of the needed confirmations"); // This uses the previous block active_schedule because thats the "schedule" that signs and therefore confirms _this_ block auto num_active_producers = active_schedule.producers.size(); uint32_t required_confs = (uint32_t)(num_active_producers * 2 / 3) + 1; if( confirm_count.size() < config::maximum_tracked_dpos_confirmations ) { result.confirm_count.reserve( confirm_count.size() + 1 ); result.confirm_count = confirm_count; result.confirm_count.resize( confirm_count.size() + 1 ); result.confirm_count.back() = (uint8_t)required_confs; } else { result.confirm_count.resize( confirm_count.size() ); memcpy( &result.confirm_count[0], &confirm_count[1], confirm_count.size() - 1 ); result.confirm_count.back() = (uint8_t)required_confs; } ....... 当前3个生产节点,required_confs为3,所以11111结束后,confirm_count中会压入3,即当前出产块所需的确认个数。 下面直接给出几个解释: confirm_count:区块所需确认个数表,从当前出产块往前推,最后的元素为当前出产块所需的确认个数 producer_to_last_implied_irb:由生产者确定的不可逆区块候选名单 dpos_irreversible_blocknum:不可逆区块 dpos_proposed_irreversible_blocknum:候选不可逆区块 active_schedule:已激活的生产者列表 有了对这几个定义的解释,代码变得很清晰 C++ //计算不可逆区块,将不可逆区块候选名单中从小到大排,选出1/3处的区块号作为不可逆区块 uint32_t block_header_state::calc_dpos_last_irreversible()const { vector<uint32_t> blocknums; blocknums.reserve( producer_to_last_implied_irb.size() ); for( auto& i : producer_to_last_implied_irb ) { blocknums.push_back(i.second); } /// 2/3 must be greater, so if I go 1/3 into the list sorted from low to high, then 2/3 are greater if( blocknums.size() == 0 ) return 0; /// TODO: update to nth_element std::sort( blocknums.begin(), blocknums.end() ); return blocknums[ (blocknums.size()-1) / 3 ]; } 上面的程序中出现两次3分之几的算法,第一次是required_confs,没毛病,一个区块要得到2/3个生产者的确认。但是得到2/3的确认与不可逆区块的联系不仅仅是这个,严格来说,不可逆区块是从不可逆区块候选名单中选择出来的。 第11111步之后的json JSON { "id": "0000000000000000000000000000000000000000000000000000000000000000", "block_num": 1419, "header": { "timestamp": "2018-08-04T07:31:48.000", "producer": "accountnum11", "confirmed": 1, "previous": "0000058af1b33d45e4ec927e52b74e8568422f45245dc0399c144bdff6dc16d8", "transaction_mroot": "0000000000000000000000000000000000000000000000000000000000000000", "action_mroot": "0000000000000000000000000000000000000000000000000000000000000000", "schedule_version": 1, "header_extensions": [ ], "producer_signature": "SIG_K1_111111111111111111111111111111111111111111111111111111111111111116uk5ne" }, "dpos_proposed_irreversible_blocknum": 1370, "dpos_irreversible_blocknum": 1370, "bft_irreversible_blocknum": 0, "pending_schedule_lib_num": 1369, "pending_schedule_hash": "b4ea72a3e20628a54028e681258cd1e6a46b20ae07210836f29087f9f8f37346", "pending_schedule": { "version": 1, "producers": [ ] }, "active_schedule": { "version": 1, "producers": [ { "producer_name": "accountnum11", "block_signing_key": "EOS8mUftJXepGzdQ2TaCduNuSPAfXJHf22uex4u41ab1EVv9EAhWt" }, { "producer_name": "accountnum22", "block_signing_key": "EOS8mUftJXepGzdQ2TaCduNuSPAfXJHf22uex4u41ab1EVv9EAhWt" }, { "producer_name": "accountnum33", "block_signing_key": "EOS8mUftJXepGzdQ2TaCduNuSPAfXJHf22uex4u41ab1EVv9EAhWt" } ] }, "blockroot_merkle": { "_active_nodes": [ "f8a83438724a996caaa42231c39b757bec3d1b6fb0bd2c982812eb2f7fadc377", "ab53f4807abffafbcf15fa6edd6ffe72cf73d18689403e389127015b4fd1e544", "0275481b63dcb217603558835292d4057d39f9a5d41c9d5302230d379a992b18", "af944001e02b80671b6ea50e5f83969fc2db5e56671ff83700a9018336d0fc58", "e1ed7b7a82f5507110497fd41f2098a269ffbe8202de0f57393b4aeb27792434", "2553b9e4b8225b0f311c2a382fccd027e90f909319616b75933b56f19d20c440" ], "_node_count": 1418 }, "producer_to_last_produced": [ [ "accountnum11", 1419 ], [ "accountnum22", 1406 ], [ "accountnum33", 1418 ], [ "eosio", 1370 ] ], "producer_to_last_implied_irb": [ [ "accountnum11", 1370 ], [ "accountnum22", 1370 ], [ "accountnum33", 1370 ] ], "block_signing_key": "EOS8mUftJXepGzdQ2TaCduNuSPAfXJHf22uex4u41ab1EVv9EAhWt", "confirm_count": [ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3 ], "confirmations": [ ], "block": { "timestamp": "2018-08-04T07:31:48.000", "producer": "accountnum11", "confirmed": 1, "previous": "0000058af1b33d45e4ec927e52b74e8568422f45245dc0399c144bdff6dc16d8", "transaction_mroot": "0000000000000000000000000000000000000000000000000000000000000000", "action_mroot": "0000000000000000000000000000000000000000000000000000000000000000", "schedule_version": 1, "header_extensions": [ ], "producer_signature": "SIG_K1_111111111111111111111111111111111111111111111111111111111111111116uk5ne", "transactions": [ ], "block_extensions": [ ] }, "validated": false, "in_current_chain": false } 到第22222步,这个函数确定了候选不可逆区与确认列表的关系 num_prev_blocks是指当前节点最后一次出块到现在的间隔,本文使用3个节点,故间隔为24 比如出块顺序为accountnum33--》accountnum11--》accountnum22--》accountnum33 accountnum33会把自己的块确认一次 accountnum11的时候会把33出过的块和自己确认一次。。。。 确认就是将confirmed数组的相应元素减去1,第一步得到的json中: "confirm_count":[2,2,2,2,2,2,2,2,2,2,2,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,3] 此时num_prev_blocks = 24 所以以下程序的工作是先将confirm_count元素从尾到头减去1,最多遍历num_prev_blocks和confirm_count大小中最小的。 "confirm_count":[2,2,2,2,2,2,2,2,2,2,2,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,2] 由于出现了“0”元素,将0元素的区块号(1406)记录在dpos_proposed_irreversible_blocknum中,然后在更新数组,将0之前的元素去掉。 "confirm_count":[1,1,1,1,1,1,1,1,1,1,1,1,2] 所以这样看来,dpos_proposed_irreversible_blocknum就是本生产节点在本块确定的候选不可逆区块。而这个参数会在下一次出块时打包进producer_to_last_implied_irb。 C++ void block_header_state::set_confirmed( uint16_t num_prev_blocks ) { /* idump((num_prev_blocks)(confirm_count.size())); for( uint32_t i = 0; i < confirm_count.size(); ++i ) { std::cerr << "confirm_count["<<i<<"] = " << int(confirm_count[i]) << "\n"; } */ header.confirmed = num_prev_blocks; int32_t i = (int32_t)(confirm_count.size() - 1); uint32_t blocks_to_confirm = num_prev_blocks + 1; /// confirm the head block too while( i >= 0 && blocks_to_confirm ) { --confirm_count[i]; //idump((confirm_count[i])); if( confirm_count[i] == 0 ) { uint32_t block_num_for_i = block_num - (uint32_t)(confirm_count.size() - 1 - i); dpos_proposed_irreversible_blocknum = block_num_for_i; //idump((dpos2_lib)(block_num)(dpos_irreversible_blocknum)); if (i == confirm_count.size() - 1) { confirm_count.resize(0); } else { memmove( &confirm_count[0], &confirm_count[i + 1], confirm_count.size() - i - 1); confirm_count.resize( confirm_count.size() - i - 1 ); } return; } --i; --blocks_to_confirm; } } 第22222步之后的输出json为: JSON { "id": "0000000000000000000000000000000000000000000000000000000000000000", "block_num": 1419, "header": { "timestamp": "2018-08-04T07:31:48.000", "producer": "accountnum11", "confirmed": 24, "previous": "0000058af1b33d45e4ec927e52b74e8568422f45245dc0399c144bdff6dc16d8", "transaction_mroot": "0000000000000000000000000000000000000000000000000000000000000000", "action_mroot": "0000000000000000000000000000000000000000000000000000000000000000", "schedule_version": 1, "header_extensions": [ ], "producer_signature": "SIG_K1_111111111111111111111111111111111111111111111111111111111111111116uk5ne" }, "dpos_proposed_irreversible_blocknum": 1406, "dpos_irreversible_blocknum": 1370, "bft_irreversible_blocknum": 0, "pending_schedule_lib_num": 1369, "pending_schedule_hash": "b4ea72a3e20628a54028e681258cd1e6a46b20ae07210836f29087f9f8f37346", "pending_schedule": { "version": 1, "producers": [ ] }, "active_schedule": { "version": 1, "producers": [ { "producer_name": "accountnum11", "block_signing_key": "EOS8mUftJXepGzdQ2TaCduNuSPAfXJHf22uex4u41ab1EVv9EAhWt" }, { "producer_name": "accountnum22", "block_signing_key": "EOS8mUftJXepGzdQ2TaCduNuSPAfXJHf22uex4u41ab1EVv9EAhWt" }, { "producer_name": "accountnum33", "block_signing_key": "EOS8mUftJXepGzdQ2TaCduNuSPAfXJHf22uex4u41ab1EVv9EAhWt" } ] }, "blockroot_merkle": { "_active_nodes": [ "f8a83438724a996caaa42231c39b757bec3d1b6fb0bd2c982812eb2f7fadc377", "ab53f4807abffafbcf15fa6edd6ffe72cf73d18689403e389127015b4fd1e544", "0275481b63dcb217603558835292d4057d39f9a5d41c9d5302230d379a992b18", "af944001e02b80671b6ea50e5f83969fc2db5e56671ff83700a9018336d0fc58", "e1ed7b7a82f5507110497fd41f2098a269ffbe8202de0f57393b4aeb27792434", "2553b9e4b8225b0f311c2a382fccd027e90f909319616b75933b56f19d20c440" ], "_node_count": 1418 }, "producer_to_last_produced": [ [ "accountnum11", 1419 ], [ "accountnum22", 1406 ], [ "accountnum33", 1418 ], [ "eosio", 1370 ] ], "producer_to_last_implied_irb": [ [ "accountnum11", 1370 ], [ "accountnum22", 1370 ], [ "accountnum33", 1370 ] ], "block_signing_key": "EOS8mUftJXepGzdQ2TaCduNuSPAfXJHf22uex4u41ab1EVv9EAhWt", "confirm_count": [ 1, 1, 1, 1, eos的出块流程大致如下: Plain Text ........ //启动生产插件 producer_plugin::plugin_startup(); ........ //出块循环 my->schedule_production_loop(); ......... //出块 auto result = start_block(); .......... //签名和提交 auto res = self->maybe_produce_block(); .......... 以上步骤的核心在于:auto result = start_block(); 这个函数更跟下去会发现,重点在于: Plain Text chain.start_block(block_time, blocks_to_confirm); 这个函数才是dops的关键 C++ void start_block( block_timestamp_type when, uint16_t confirm_block_count, controller::block_status s ) { FC_ASSERT( !pending ); FC_ASSERT( db.revision() == head->block_num, "", ("db.revision()", db.revision())("controller_head_block", head->block_num)("fork_db_head_block", fork_db.head()->block_num) ); auto guard_pending = fc::make_scoped_exit([this](){ pending.reset(); }); pending = db.start_undo_session(true); pending->_block_status = s; //由当前区块头创建一个新的block_state ---->>>>> 11111 pending->_pending_block_state = std::make_shared<block_state>( *head, when ); // promotes pending schedule (if any) to active pending->_pending_block_state->in_current_chain = true; //确认链上的区块 ----->>>>> 22222 pending->_pending_block_state->set_confirmed(confirm_block_count); //更新激活的生产者 ----->>>> 33333 auto was_pending_promoted = pending->_pending_block_state->maybe_promote_pending(); const auto& gpo = db.get<global_property_object>(); if( gpo.proposed_schedule_block_num.valid() && // if there is a proposed schedule that was proposed in a block ... ( *gpo.proposed_schedule_block_num <= pending->_pending_block_state->dpos_irreversible_blocknum ) && // ... that has now become irreversible ... pending->_pending_block_state->pending_schedule.producers.size() == 0 && // ... and there is room for a new pending schedule ... !was_pending_promoted // ... and not just because it was promoted to active at the start of this block, then: ) { // Promote proposed schedule to pending schedule. if( !replaying ) { ilog( "promoting proposed schedule (set in block ${proposed_num}) to pending; current block: ${n} lib: ${lib} schedule: ${schedule} ", ("proposed_num", *gpo.proposed_schedule_block_num)("n", pending->_pending_block_state->block_num) ("lib", pending->_pending_block_state->dpos_irreversible_blocknum) ("schedule", static_cast<producer_schedule_type>(gpo.proposed_schedule) ) ); } pending->_pending_block_state->set_new_producers( gpo.proposed_schedule ); db.modify( gpo, [&]( auto& gp ) { gp.proposed_schedule_block_num = optional<block_num_type>(); gp.proposed_schedule.clear(); }); std::cout<<"\n========555==========\n"; std::cout<< fc::json::to_string(*pending->_pending_block_state)<<"\n"; std::cout<<"==========555========\n"; } 11111,22222,33333这三步涉及到的数据结构是block_state JSON { "id": "0000058af1b33d45e4ec927e52b74e8568422f45245dc0399c144bdff6dc16d8", "block_num": 1418, "header": { "timestamp": "2018-08-04T07:31:47.500", "producer": "accountnum33", "confirmed": 0, "previous": "0000058980ac0382075af4216ddbd7be1eae1690a278d7cc04a61570f307b39d", "transaction_mroot": "0000000000000000000000000000000000000000000000000000000000000000", "action_mroot": "eaadc508183ba966551e48dba3a44505a95e6a42d7a53f435db39c3dec3584c9", "schedule_version": 1, "header_extensions": [ ], "producer_signature": "SIG_K1_KVyKdpLpEXPQUvRR5jZL4FMYvFDNic6tyvX6icLXFKhoriskCg21esduAceX2cZDv2GwcB12TxdneeKMwfzWB2TckZpZvQ" }, "dpos_proposed_irreversible_blocknum": 1370, "dpos_irreversible_blocknum": 1370, "bft_irreversible_blocknum": 0, "pending_schedule_lib_num": 1369, "pending_schedule_hash": "b4ea72a3e20628a54028e681258cd1e6a46b20ae07210836f29087f9f8f37346", "pending_schedule": { "version": 1, "producers": [ ] }, "active_schedule": { "version": 1, "producers": [ { "producer_name": "accountnum11", "block_signing_key": "EOS8mUftJXepGzdQ2TaCduNuSPAfXJHf22uex4u41ab1EVv9EAhWt" }, { "producer_name": "accountnum22", "block_signing_key": "EOS8mUftJXepGzdQ2TaCduNuSPAfXJHf22uex4u41ab1EVv9EAhWt" }, { "producer_name": "accountnum33", "block_signing_key": "EOS8mUftJXepGzdQ2TaCduNuSPAfXJHf22uex4u41ab1EVv9EAhWt" } ] }, "blockroot_merkle": { "_active_nodes": [ "0000058980ac0382075af4216ddbd7be1eae1690a278d7cc04a61570f307b39d", "ab53f4807abffafbcf15fa6edd6ffe72cf73d18689403e389127015b4fd1e544", "0275481b63dcb217603558835292d4057d39f9a5d41c9d5302230d379a992b18", "af944001e02b80671b6ea50e5f83969fc2db5e56671ff83700a9018336d0fc58", "e1ed7b7a82f5507110497fd41f2098a269ffbe8202de0f57393b4aeb27792434", "5a36ca2325d5f7b2de22b98de7cf70c0a06cf391fc17f1d242b964d16146dee9" ], "_node_count": 1417 }, "producer_to_last_produced": [ [ "accountnum11", 1394 ], [ "accountnum22", 1406 ], [ "accountnum33", 1418 ], [ "eosio", 1370 ] ], "producer_to_last_implied_irb": [ [ "accountnum11", 1370 ], [ "accountnum22", 1370 ], [ "accountnum33", 1370 ] ], "block_signing_key": "EOS8mUftJXepGzdQ2TaCduNuSPAfXJHf22uex4u41ab1EVv9EAhWt", "confirm_count": [ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 ], "confirmations": [ ], "block": { "timestamp": "2018-08-04T07:31:47.500", "producer": "accountnum33", "confirmed": 0, "previous": "0000058980ac0382075af4216ddbd7be1eae1690a278d7cc04a61570f307b39d", "transaction_mroot": "0000000000000000000000000000000000000000000000000000000000000000", "action_mroot": "eaadc508183ba966551e48dba3a44505a95e6a42d7a53f435db39c3dec3584c9", "schedule_version": 1, "header_extensions": [ ], "producer_signature": "SIG_K1_KVyKdpLpEXPQUvRR5jZL4FMYvFDNic6tyvX6icLXFKhoriskCg21esduAceX2cZDv2GwcB12TxdneeKMwfzWB2TckZpZvQ", "transactions": [ ], "block_extensions": [ ] }, "validated": true, "in_current_chain": true } 第一步:从当前块头出发,生成一个新的block_state。 C++ ........ result.producer_to_last_implied_irb[prokey.producer_name] = result.dpos_proposed_irreversible_blocknum; result.dpos_irreversible_blocknum = result.calc_dpos_last_irreversible(); /// grow the confirmed count static_assert(std::numeric_limits<uint8_t>::max() >= (config::max_producers * 2 / 3) + 1, "8bit confirmations may not be able to hold all of the needed confirmations"); // This uses the previous block active_schedule because thats the "schedule" that signs and therefore confirms _this_ block auto num_active_producers = active_schedule.producers.size(); uint32_t required_confs = (uint32_t)(num_active_producers * 2 / 3) + 1; if( confirm_count.size() < config::maximum_tracked_dpos_confirmations ) { result.confirm_count.reserve( confirm_count.size() + 1 ); result.confirm_count = confirm_count; result.confirm_count.resize( confirm_count.size() + 1 ); result.confirm_count.back() = (uint8_t)required_confs; } else { result.confirm_count.resize( confirm_count.size() ); memcpy( &result.confirm_count[0], &confirm_count[1], confirm_count.size() - 1 ); result.confirm_count.back() = (uint8_t)required_confs; } ....... 当前3个生产节点,required_confs为3,所以11111结束后,confirm_count中会压入3,即当前出产块所需的确认个数。 下面直接给出几个解释: confirm_count:区块所需确认个数表,从当前出产块往前推,最后的元素为当前出产块所需的确认个数 producer_to_last_implied_irb:由生产者确定的不可逆区块候选名单 dpos_irreversible_blocknum:不可逆区块 dpos_proposed_irreversible_blocknum:候选不可逆区块 active_schedule:已激活的生产者列表 有了对这几个定义的解释,代码变得很清晰 C++ //计算不可逆区块,将不可逆区块候选名单中从小到大排,选出1/3处的区块号作为不可逆区块 uint32_t block_header_state::calc_dpos_last_irreversible()const { vector<uint32_t> blocknums; blocknums.reserve( producer_to_last_implied_irb.size() ); for( auto& i : producer_to_last_implied_irb ) { blocknums.push_back(i.second); } /// 2/3 must be greater, so if I go 1/3 into the list sorted from low to high, then 2/3 are greater if( blocknums.size() == 0 ) return 0; /// TODO: update to nth_element std::sort( blocknums.begin(), blocknums.end() ); return blocknums[ (blocknums.size()-1) / 3 ]; } 上面的程序中出现两次3分之几的算法,第一次是required_confs,没毛病,一个区块要得到2/3个生产者的确认。但是得到2/3的确认与不可逆区块的联系不仅仅是这个,严格来说,不可逆区块是从不可逆区块候选名单中选择出来的。 第11111步之后的json JSON { "id": "0000000000000000000000000000000000000000000000000000000000000000", "block_num": 1419, "header": { "timestamp": "2018-08-04T07:31:48.000", "producer": "accountnum11", "confirmed": 1, "previous": "0000058af1b33d45e4ec927e52b74e8568422f45245dc0399c144bdff6dc16d8", "transaction_mroot": "0000000000000000000000000000000000000000000000000000000000000000", "action_mroot": "0000000000000000000000000000000000000000000000000000000000000000", "schedule_version": 1, "header_extensions": [ ], "producer_signature": "SIG_K1_111111111111111111111111111111111111111111111111111111111111111116uk5ne" }, "dpos_proposed_irreversible_blocknum": 1370, "dpos_irreversible_blocknum": 1370, "bft_irreversible_blocknum": 0, "pending_schedule_lib_num": 1369, "pending_schedule_hash": "b4ea72a3e20628a54028e681258cd1e6a46b20ae07210836f29087f9f8f37346", "pending_schedule": { "version": 1, "producers": [ ] }, "active_schedule": { "version": 1, "producers": [ { "producer_name": "accountnum11", "block_signing_key": "EOS8mUftJXepGzdQ2TaCduNuSPAfXJHf22uex4u41ab1EVv9EAhWt" }, { "producer_name": "accountnum22", "block_signing_key": "EOS8mUftJXepGzdQ2TaCduNuSPAfXJHf22uex4u41ab1EVv9EAhWt" }, { "producer_name": "accountnum33", "block_signing_key": "EOS8mUftJXepGzdQ2TaCduNuSPAfXJHf22uex4u41ab1EVv9EAhWt" } ] }, "blockroot_merkle": { "_active_nodes": [ "f8a83438724a996caaa42231c39b757bec3d1b6fb0bd2c982812eb2f7fadc377", "ab53f4807abffafbcf15fa6edd6ffe72cf73d18689403e389127015b4fd1e544", "0275481b63dcb217603558835292d4057d39f9a5d41c9d5302230d379a992b18", "af944001e02b80671b6ea50e5f83969fc2db5e56671ff83700a9018336d0fc58", "e1ed7b7a82f5507110497fd41f2098a269ffbe8202de0f57393b4aeb27792434", "2553b9e4b8225b0f311c2a382fccd027e90f909319616b75933b56f19d20c440" ], "_node_count": 1418 }, "producer_to_last_produced": [ [ "accountnum11", 1419 ], [ "accountnum22", 1406 ], [ "accountnum33", 1418 ], [ "eosio", 1370 ] ], "producer_to_last_implied_irb": [ [ "accountnum11", 1370 ], [ "accountnum22", 1370 ], [ "accountnum33", 1370 ] ], "block_signing_key": "EOS8mUftJXepGzdQ2TaCduNuSPAfXJHf22uex4u41ab1EVv9EAhWt", "confirm_count": [ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3 ], "confirmations": [ ], "block": { "timestamp": "2018-08-04T07:31:48.000", "producer": "accountnum11", "confirmed": 1, "previous": "0000058af1b33d45e4ec927e52b74e8568422f45245dc0399c144bdff6dc16d8", "transaction_mroot": "0000000000000000000000000000000000000000000000000000000000000000", "action_mroot": "0000000000000000000000000000000000000000000000000000000000000000", "schedule_version": 1, "header_extensions": [ ], "producer_signature": "SIG_K1_111111111111111111111111111111111111111111111111111111111111111116uk5ne", "transactions": [ ], "block_extensions": [ ] }, "validated": false, "in_current_chain": false } 到第22222步,这个函数确定了候选不可逆区与确认列表的关系 num_prev_blocks是指当前节点最后一次出块到现在的间隔,本文使用3个节点,故间隔为24 比如出块顺序为accountnum33--》accountnum11--》accountnum22--》accountnum33 accountnum33会把自己的块确认一次 accountnum11的时候会把33出过的块和自己确认一次。。。。 确认就是将confirmed数组的相应元素减去1,第一步得到的json中: "confirm_count":[2,2,2,2,2,2,2,2,2,2,2,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,3] 此时num_prev_blocks = 24 所以以下程序的工作是先将confirm_count元素从尾到头减去1,最多遍历num_prev_blocks和confirm_count大小中最小的。 "confirm_count":[2,2,2,2,2,2,2,2,2,2,2,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,2] 由于出现了“0”元素,将0元素的区块号(1406)记录在dpos_proposed_irreversible_blocknum中,然后在更新数组,将0之前的元素去掉。 "confirm_count":[1,1,1,1,1,1,1,1,1,1,1,1,2] 所以这样看来,dpos_proposed_irreversible_blocknum就是本生产节点在本块确定的候选不可逆区块。而这个参数会在下一次出块时打包进producer_to_last_implied_irb。 C++ void block_header_state::set_confirmed( uint16_t num_prev_blocks ) { /* idump((num_prev_blocks)(confirm_count.size())); for( uint32_t i = 0; i < confirm_count.size(); ++i ) { std::cerr << "confirm_count["<<i<<"] = " << int(confirm_count[i]) << "\n"; } */ header.confirmed = num_prev_blocks; int32_t i = (int32_t)(confirm_count.size() - 1); uint32_t blocks_to_confirm = num_prev_blocks + 1; /// confirm the head block too while( i >= 0 && blocks_to_confirm ) { --confirm_count[i]; //idump((confirm_count[i])); if( confirm_count[i] == 0 ) { uint32_t block_num_for_i = block_num - (uint32_t)(confirm_count.size() - 1 - i); dpos_proposed_irreversible_blocknum = block_num_for_i; //idump((dpos2_lib)(block_num)(dpos_irreversible_blocknum)); if (i == confirm_count.size() - 1) { confirm_count.resize(0); } else { memmove( &confirm_count[0], &confirm_count[i + 1], confirm_count.size() - i - 1); confirm_count.resize( confirm_count.size() - i - 1 ); } return; } --i; --blocks_to_confirm; } } 第22222步之后的输出json为: JSON { "id": "0000000000000000000000000000000000000000000000000000000000000000", "block_num": 1419, "header": { "timestamp": "2018-08-04T07:31:48.000", "producer": "accountnum11", "confirmed": 24, "previous": "0000058af1b33d45e4ec927e52b74e8568422f45245dc0399c144bdff6dc16d8", "transaction_mroot": "0000000000000000000000000000000000000000000000000000000000000000", "action_mroot": "0000000000000000000000000000000000000000000000000000000000000000", "schedule_version": 1, "header_extensions": [ ], "producer_signature": "SIG_K1_111111111111111111111111111111111111111111111111111111111111111116uk5ne" }, "dpos_proposed_irreversible_blocknum": 1406, "dpos_irreversible_blocknum": 1370, "bft_irreversible_blocknum": 0, "pending_schedule_lib_num": 1369, "pending_schedule_hash": "b4ea72a3e20628a54028e681258cd1e6a46b20ae07210836f29087f9f8f37346", "pending_schedule": { "version": 1, "producers": [ ] }, "active_schedule": { "version": 1, "producers": [ { "producer_name": "accountnum11", "block_signing_key": "EOS8mUftJXepGzdQ2TaCduNuSPAfXJHf22uex4u41ab1EVv9EAhWt" }, { 1, 1, 1, 1, 1, 1, 1, 1, 2 ], "confirmations": [ ], "block": { "timestamp": "2018-08-04T07:31:48.000", "producer": "accountnum11", "confirmed": 1, "previous": "0000058af1b33d45e4ec927e52b74e8568422f45245dc0399c144bdff6dc16d8", "transaction_mroot": "0000000000000000000000000000000000000000000000000000000000000000", "action_mroot": "0000000000000000000000000000000000000000000000000000000000000000", "schedule_version": 1, "header_extensions": [ ], "producer_signature": "SIG_K1_111111111111111111111111111111111111111111111111111111111111111116uk5ne", "transactions": [ ], "block_extensions": [ ] }, "validated": false, "in_current_chain": true } 综上,实际上dops在代码里面使用是分作两步的。 1,2/3共识,符合2/3认可的最高区块可以进入”由生产者确定的不可逆区块候选名单”。由于出块轮转,每个生产者所能见证的高度是不一样的。 2,1/3共识,从“由生产者确定的不可逆区块候选名单”中选择1/3高度的地方得到不可逆区块。 仔细一想,2还是加强了不可逆区块的共识。 |
| json metadata | {"tags":["eos","dpos","btf","sourcecode"],"app":"steemit/0.1","format":"markdown"} |
| parent author | |
| parent permlink | eos |
| permlink | eos-dpos |
| title | eos源码解析(三):dpos共识源码 |
| Transaction Info | Block #25055526/Trx e96bd60b02eaa11a054c64e6d495c39ac891e819 |
View Raw JSON Data
{
"block": 25055526,
"op": [
"comment",
{
"author": "camphortree",
"body": "eos的出块流程大致如下:\nPlain Text\n........\n//启动生产插件\nproducer_plugin::plugin_startup();\n........\n//出块循环\nmy->schedule_production_loop();\n.........\n//出块\nauto result = start_block();\n..........\n//签名和提交\nauto res = self->maybe_produce_block();\n..........\n以上步骤的核心在于:auto result = start_block();\n这个函数更跟下去会发现,重点在于:\nPlain Text\nchain.start_block(block_time, blocks_to_confirm);\n这个函数才是dops的关键\nC++\nvoid start_block( block_timestamp_type when, uint16_t confirm_block_count, controller::block_status s ) {\n FC_ASSERT( !pending );\n\n FC_ASSERT( db.revision() == head->block_num, \"\",\n (\"db.revision()\", db.revision())(\"controller_head_block\", head->block_num)(\"fork_db_head_block\", fork_db.head()->block_num) );\n\n auto guard_pending = fc::make_scoped_exit([this](){\n pending.reset();\n });\n\n pending = db.start_undo_session(true);\n\n pending->_block_status = s;\n //由当前区块头创建一个新的block_state ---->>>>> 11111\n pending->_pending_block_state = std::make_shared<block_state>( *head, when ); // promotes pending schedule (if any) to active\n pending->_pending_block_state->in_current_chain = true;\n //确认链上的区块 ----->>>>> 22222\n pending->_pending_block_state->set_confirmed(confirm_block_count);\n //更新激活的生产者 ----->>>> 33333\n auto was_pending_promoted = pending->_pending_block_state->maybe_promote_pending();\n const auto& gpo = db.get<global_property_object>();\n if( gpo.proposed_schedule_block_num.valid() && // if there is a proposed schedule that was proposed in a block ...\n ( *gpo.proposed_schedule_block_num <= pending->_pending_block_state->dpos_irreversible_blocknum ) && // ... that has now become irreversible ...\n pending->_pending_block_state->pending_schedule.producers.size() == 0 && // ... and there is room for a new pending schedule ...\n !was_pending_promoted // ... and not just because it was promoted to active at the start of this block, then:\n )\n {\n // Promote proposed schedule to pending schedule.\n if( !replaying ) {\n ilog( \"promoting proposed schedule (set in block ${proposed_num}) to pending; current block: ${n} lib: ${lib} schedule: ${schedule} \",\n (\"proposed_num\", *gpo.proposed_schedule_block_num)(\"n\", pending->_pending_block_state->block_num)\n (\"lib\", pending->_pending_block_state->dpos_irreversible_blocknum)\n (\"schedule\", static_cast<producer_schedule_type>(gpo.proposed_schedule) ) );\n }\n pending->_pending_block_state->set_new_producers( gpo.proposed_schedule );\n db.modify( gpo, [&]( auto& gp ) {\n gp.proposed_schedule_block_num = optional<block_num_type>();\n gp.proposed_schedule.clear();\n });\n\n std::cout<<\"\\n========555==========\\n\";\n std::cout<< fc::json::to_string(*pending->_pending_block_state)<<\"\\n\";\n std::cout<<\"==========555========\\n\";\n }\n11111,22222,33333这三步涉及到的数据结构是block_state\nJSON\n{\n \"id\": \"0000058af1b33d45e4ec927e52b74e8568422f45245dc0399c144bdff6dc16d8\", \n \"block_num\": 1418, \n \"header\": {\n \"timestamp\": \"2018-08-04T07:31:47.500\", \n \"producer\": \"accountnum33\", \n \"confirmed\": 0, \n \"previous\": \"0000058980ac0382075af4216ddbd7be1eae1690a278d7cc04a61570f307b39d\", \n \"transaction_mroot\": \"0000000000000000000000000000000000000000000000000000000000000000\", \n \"action_mroot\": \"eaadc508183ba966551e48dba3a44505a95e6a42d7a53f435db39c3dec3584c9\", \n \"schedule_version\": 1, \n \"header_extensions\": [ ], \n \"producer_signature\": \"SIG_K1_KVyKdpLpEXPQUvRR5jZL4FMYvFDNic6tyvX6icLXFKhoriskCg21esduAceX2cZDv2GwcB12TxdneeKMwfzWB2TckZpZvQ\"\n }, \n \"dpos_proposed_irreversible_blocknum\": 1370, \n \"dpos_irreversible_blocknum\": 1370, \n \"bft_irreversible_blocknum\": 0, \n \"pending_schedule_lib_num\": 1369, \n \"pending_schedule_hash\": \"b4ea72a3e20628a54028e681258cd1e6a46b20ae07210836f29087f9f8f37346\", \n \"pending_schedule\": {\n \"version\": 1, \n \"producers\": [ ]\n }, \n \"active_schedule\": {\n \"version\": 1, \n \"producers\": [\n {\n \"producer_name\": \"accountnum11\", \n \"block_signing_key\": \"EOS8mUftJXepGzdQ2TaCduNuSPAfXJHf22uex4u41ab1EVv9EAhWt\"\n }, \n {\n \"producer_name\": \"accountnum22\", \n \"block_signing_key\": \"EOS8mUftJXepGzdQ2TaCduNuSPAfXJHf22uex4u41ab1EVv9EAhWt\"\n }, \n {\n \"producer_name\": \"accountnum33\", \n \"block_signing_key\": \"EOS8mUftJXepGzdQ2TaCduNuSPAfXJHf22uex4u41ab1EVv9EAhWt\"\n }\n ]\n }, \n \"blockroot_merkle\": {\n \"_active_nodes\": [\n \"0000058980ac0382075af4216ddbd7be1eae1690a278d7cc04a61570f307b39d\", \n \"ab53f4807abffafbcf15fa6edd6ffe72cf73d18689403e389127015b4fd1e544\", \n \"0275481b63dcb217603558835292d4057d39f9a5d41c9d5302230d379a992b18\", \n \"af944001e02b80671b6ea50e5f83969fc2db5e56671ff83700a9018336d0fc58\", \n \"e1ed7b7a82f5507110497fd41f2098a269ffbe8202de0f57393b4aeb27792434\", \n \"5a36ca2325d5f7b2de22b98de7cf70c0a06cf391fc17f1d242b964d16146dee9\"\n ], \n \"_node_count\": 1417\n }, \n \"producer_to_last_produced\": [\n [\n \"accountnum11\", \n 1394\n ], \n [\n \"accountnum22\", \n 1406\n ], \n [\n \"accountnum33\", \n 1418\n ], \n [\n \"eosio\", \n 1370\n ]\n ], \n \"producer_to_last_implied_irb\": [\n [\n \"accountnum11\", \n 1370\n ], \n [\n \"accountnum22\", \n 1370\n ], \n [\n \"accountnum33\", \n 1370\n ]\n ], \n \"block_signing_key\": \"EOS8mUftJXepGzdQ2TaCduNuSPAfXJHf22uex4u41ab1EVv9EAhWt\", \n \"confirm_count\": [\n 2, \n 2, \n 2, \n 2, \n 2, \n 2, \n 2, \n 2, \n 2, \n 2, \n 2, \n 2, \n 1, \n 1, \n 1, \n 1, \n 1, \n 1, \n 1, \n 1, \n 1, \n 1, \n 1, \n 1, \n 1, \n 1, \n 1, \n 1, \n 1, \n 1, \n 1, \n 1, \n 1, \n 1, \n 1, \n 1, \n 2, \n 2, \n 2, \n 2, \n 2, \n 2, \n 2, \n 2, \n 2, \n 2, \n 2, \n 2\n ], \n \"confirmations\": [ ], \n \"block\": {\n \"timestamp\": \"2018-08-04T07:31:47.500\", \n \"producer\": \"accountnum33\", \n \"confirmed\": 0, \n \"previous\": \"0000058980ac0382075af4216ddbd7be1eae1690a278d7cc04a61570f307b39d\", \n \"transaction_mroot\": \"0000000000000000000000000000000000000000000000000000000000000000\", \n \"action_mroot\": \"eaadc508183ba966551e48dba3a44505a95e6a42d7a53f435db39c3dec3584c9\", \n \"schedule_version\": 1, \n \"header_extensions\": [ ], \n \"producer_signature\": \"SIG_K1_KVyKdpLpEXPQUvRR5jZL4FMYvFDNic6tyvX6icLXFKhoriskCg21esduAceX2cZDv2GwcB12TxdneeKMwfzWB2TckZpZvQ\", \n \"transactions\": [ ], \n \"block_extensions\": [ ]\n }, \n \"validated\": true, \n \"in_current_chain\": true\n}\n第一步:从当前块头出发,生成一个新的block_state。\nC++\n........\nresult.producer_to_last_implied_irb[prokey.producer_name] = result.dpos_proposed_irreversible_blocknum;\nresult.dpos_irreversible_blocknum = result.calc_dpos_last_irreversible(); \n\n/// grow the confirmed count\nstatic_assert(std::numeric_limits<uint8_t>::max() >= (config::max_producers * 2 / 3) + 1, \"8bit confirmations may not be able to hold all of the needed confirmations\");\n\n// This uses the previous block active_schedule because thats the \"schedule\" that signs and therefore confirms _this_ block\nauto num_active_producers = active_schedule.producers.size();\nuint32_t required_confs = (uint32_t)(num_active_producers * 2 / 3) + 1;\n\nif( confirm_count.size() < config::maximum_tracked_dpos_confirmations ) {\n result.confirm_count.reserve( confirm_count.size() + 1 );\n result.confirm_count = confirm_count;\n result.confirm_count.resize( confirm_count.size() + 1 );\n result.confirm_count.back() = (uint8_t)required_confs;\n} else {\n result.confirm_count.resize( confirm_count.size() );\n memcpy( &result.confirm_count[0], &confirm_count[1], confirm_count.size() - 1 );\n result.confirm_count.back() = (uint8_t)required_confs;\n}\n.......\n当前3个生产节点,required_confs为3,所以11111结束后,confirm_count中会压入3,即当前出产块所需的确认个数。\n下面直接给出几个解释:\nconfirm_count:区块所需确认个数表,从当前出产块往前推,最后的元素为当前出产块所需的确认个数\nproducer_to_last_implied_irb:由生产者确定的不可逆区块候选名单\ndpos_irreversible_blocknum:不可逆区块\ndpos_proposed_irreversible_blocknum:候选不可逆区块\nactive_schedule:已激活的生产者列表\n有了对这几个定义的解释,代码变得很清晰\nC++\n//计算不可逆区块,将不可逆区块候选名单中从小到大排,选出1/3处的区块号作为不可逆区块\nuint32_t block_header_state::calc_dpos_last_irreversible()const {\n vector<uint32_t> blocknums; blocknums.reserve( producer_to_last_implied_irb.size() );\n for( auto& i : producer_to_last_implied_irb ) {\n blocknums.push_back(i.second);\n }\n /// 2/3 must be greater, so if I go 1/3 into the list sorted from low to high, then 2/3 are greater\n\n if( blocknums.size() == 0 ) return 0;\n /// TODO: update to nth_element \n std::sort( blocknums.begin(), blocknums.end() );\n return blocknums[ (blocknums.size()-1) / 3 ];\n}\n上面的程序中出现两次3分之几的算法,第一次是required_confs,没毛病,一个区块要得到2/3个生产者的确认。但是得到2/3的确认与不可逆区块的联系不仅仅是这个,严格来说,不可逆区块是从不可逆区块候选名单中选择出来的。\n第11111步之后的json\nJSON\n{\n \"id\": \"0000000000000000000000000000000000000000000000000000000000000000\", \n \"block_num\": 1419, \n \"header\": {\n \"timestamp\": \"2018-08-04T07:31:48.000\", \n \"producer\": \"accountnum11\", \n \"confirmed\": 1, \n \"previous\": \"0000058af1b33d45e4ec927e52b74e8568422f45245dc0399c144bdff6dc16d8\", \n \"transaction_mroot\": \"0000000000000000000000000000000000000000000000000000000000000000\", \n \"action_mroot\": \"0000000000000000000000000000000000000000000000000000000000000000\", \n \"schedule_version\": 1, \n \"header_extensions\": [ ], \n \"producer_signature\": \"SIG_K1_111111111111111111111111111111111111111111111111111111111111111116uk5ne\"\n }, \n \"dpos_proposed_irreversible_blocknum\": 1370, \n \"dpos_irreversible_blocknum\": 1370, \n \"bft_irreversible_blocknum\": 0, \n \"pending_schedule_lib_num\": 1369, \n \"pending_schedule_hash\": \"b4ea72a3e20628a54028e681258cd1e6a46b20ae07210836f29087f9f8f37346\", \n \"pending_schedule\": {\n \"version\": 1, \n \"producers\": [ ]\n }, \n \"active_schedule\": {\n \"version\": 1, \n \"producers\": [\n {\n \"producer_name\": \"accountnum11\", \n \"block_signing_key\": \"EOS8mUftJXepGzdQ2TaCduNuSPAfXJHf22uex4u41ab1EVv9EAhWt\"\n }, \n {\n \"producer_name\": \"accountnum22\", \n \"block_signing_key\": \"EOS8mUftJXepGzdQ2TaCduNuSPAfXJHf22uex4u41ab1EVv9EAhWt\"\n }, \n {\n \"producer_name\": \"accountnum33\", \n \"block_signing_key\": \"EOS8mUftJXepGzdQ2TaCduNuSPAfXJHf22uex4u41ab1EVv9EAhWt\"\n }\n ]\n }, \n \"blockroot_merkle\": {\n \"_active_nodes\": [\n \"f8a83438724a996caaa42231c39b757bec3d1b6fb0bd2c982812eb2f7fadc377\", \n \"ab53f4807abffafbcf15fa6edd6ffe72cf73d18689403e389127015b4fd1e544\", \n \"0275481b63dcb217603558835292d4057d39f9a5d41c9d5302230d379a992b18\", \n \"af944001e02b80671b6ea50e5f83969fc2db5e56671ff83700a9018336d0fc58\", \n \"e1ed7b7a82f5507110497fd41f2098a269ffbe8202de0f57393b4aeb27792434\", \n \"2553b9e4b8225b0f311c2a382fccd027e90f909319616b75933b56f19d20c440\"\n ], \n \"_node_count\": 1418\n }, \n \"producer_to_last_produced\": [\n [\n \"accountnum11\", \n 1419\n ], \n [\n \"accountnum22\", \n 1406\n ], \n [\n \"accountnum33\", \n 1418\n ], \n [\n \"eosio\", \n 1370\n ]\n ], \n \"producer_to_last_implied_irb\": [\n [\n \"accountnum11\", \n 1370\n ], \n [\n \"accountnum22\", \n 1370\n ], \n [\n \"accountnum33\", \n 1370\n ]\n ], \n \"block_signing_key\": \"EOS8mUftJXepGzdQ2TaCduNuSPAfXJHf22uex4u41ab1EVv9EAhWt\", \n \"confirm_count\": [\n 2, \n 2, \n 2, \n 2, \n 2, \n 2, \n 2, \n 2, \n 2, \n 2, \n 2, \n 2, \n 1, \n 1, \n 1, \n 1, \n 1, \n 1, \n 1, \n 1, \n 1, \n 1, \n 1, \n 1, \n 1, \n 1, \n 1, \n 1, \n 1, \n 1, \n 1, \n 1, \n 1, \n 1, \n 1, \n 1, \n 2, \n 2, \n 2, \n 2, \n 2, \n 2, \n 2, \n 2, \n 2, \n 2, \n 2, \n 2, \n 3 \n ], \n \"confirmations\": [ ], \n \"block\": {\n \"timestamp\": \"2018-08-04T07:31:48.000\", \n \"producer\": \"accountnum11\", \n \"confirmed\": 1, \n \"previous\": \"0000058af1b33d45e4ec927e52b74e8568422f45245dc0399c144bdff6dc16d8\", \n \"transaction_mroot\": \"0000000000000000000000000000000000000000000000000000000000000000\", \n \"action_mroot\": \"0000000000000000000000000000000000000000000000000000000000000000\", \n \"schedule_version\": 1, \n \"header_extensions\": [ ], \n \"producer_signature\": \"SIG_K1_111111111111111111111111111111111111111111111111111111111111111116uk5ne\", \n \"transactions\": [ ], \n \"block_extensions\": [ ]\n }, \n \"validated\": false, \n \"in_current_chain\": false\n}\n到第22222步,这个函数确定了候选不可逆区与确认列表的关系\nnum_prev_blocks是指当前节点最后一次出块到现在的间隔,本文使用3个节点,故间隔为24\n比如出块顺序为accountnum33--》accountnum11--》accountnum22--》accountnum33\naccountnum33会把自己的块确认一次\naccountnum11的时候会把33出过的块和自己确认一次。。。。\n确认就是将confirmed数组的相应元素减去1,第一步得到的json中:\n\"confirm_count\":[2,2,2,2,2,2,2,2,2,2,2,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,3]\n此时num_prev_blocks = 24\n所以以下程序的工作是先将confirm_count元素从尾到头减去1,最多遍历num_prev_blocks和confirm_count大小中最小的。\n\"confirm_count\":[2,2,2,2,2,2,2,2,2,2,2,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,2]\n由于出现了“0”元素,将0元素的区块号(1406)记录在dpos_proposed_irreversible_blocknum中,然后在更新数组,将0之前的元素去掉。\n\"confirm_count\":[1,1,1,1,1,1,1,1,1,1,1,1,2]\n所以这样看来,dpos_proposed_irreversible_blocknum就是本生产节点在本块确定的候选不可逆区块。而这个参数会在下一次出块时打包进producer_to_last_implied_irb。\nC++\nvoid block_header_state::set_confirmed( uint16_t num_prev_blocks ) {\n /*\n idump((num_prev_blocks)(confirm_count.size()));\n\n for( uint32_t i = 0; i < confirm_count.size(); ++i ) {\n std::cerr << \"confirm_count[\"<<i<<\"] = \" << int(confirm_count[i]) << \"\\n\";\n }\n */\n header.confirmed = num_prev_blocks;\n\n int32_t i = (int32_t)(confirm_count.size() - 1);\n uint32_t blocks_to_confirm = num_prev_blocks + 1; /// confirm the head block too\n while( i >= 0 && blocks_to_confirm ) {\n --confirm_count[i];\n //idump((confirm_count[i]));\n if( confirm_count[i] == 0 )\n {\n uint32_t block_num_for_i = block_num - (uint32_t)(confirm_count.size() - 1 - i);\n dpos_proposed_irreversible_blocknum = block_num_for_i;\n //idump((dpos2_lib)(block_num)(dpos_irreversible_blocknum));\n\n if (i == confirm_count.size() - 1) {\n confirm_count.resize(0);\n } else {\n memmove( &confirm_count[0], &confirm_count[i + 1], confirm_count.size() - i - 1);\n confirm_count.resize( confirm_count.size() - i - 1 );\n }\n\n return;\n }\n --i;\n --blocks_to_confirm;\n }\n}\n第22222步之后的输出json为:\nJSON\n{\n \"id\": \"0000000000000000000000000000000000000000000000000000000000000000\", \n \"block_num\": 1419, \n \"header\": {\n \"timestamp\": \"2018-08-04T07:31:48.000\", \n \"producer\": \"accountnum11\", \n \"confirmed\": 24, \n \"previous\": \"0000058af1b33d45e4ec927e52b74e8568422f45245dc0399c144bdff6dc16d8\", \n \"transaction_mroot\": \"0000000000000000000000000000000000000000000000000000000000000000\", \n \"action_mroot\": \"0000000000000000000000000000000000000000000000000000000000000000\", \n \"schedule_version\": 1, \n \"header_extensions\": [ ], \n \"producer_signature\": \"SIG_K1_111111111111111111111111111111111111111111111111111111111111111116uk5ne\"\n }, \n \"dpos_proposed_irreversible_blocknum\": 1406, \n \"dpos_irreversible_blocknum\": 1370, \n \"bft_irreversible_blocknum\": 0, \n \"pending_schedule_lib_num\": 1369, \n \"pending_schedule_hash\": \"b4ea72a3e20628a54028e681258cd1e6a46b20ae07210836f29087f9f8f37346\", \n \"pending_schedule\": {\n \"version\": 1, \n \"producers\": [ ]\n }, \n \"active_schedule\": {\n \"version\": 1, \n \"producers\": [\n {\n \"producer_name\": \"accountnum11\", \n \"block_signing_key\": \"EOS8mUftJXepGzdQ2TaCduNuSPAfXJHf22uex4u41ab1EVv9EAhWt\"\n }, \n {\n \"producer_name\": \"accountnum22\", \n \"block_signing_key\": \"EOS8mUftJXepGzdQ2TaCduNuSPAfXJHf22uex4u41ab1EVv9EAhWt\"\n }, \n {\n \"producer_name\": \"accountnum33\", \n \"block_signing_key\": \"EOS8mUftJXepGzdQ2TaCduNuSPAfXJHf22uex4u41ab1EVv9EAhWt\"\n }\n ]\n }, \n \"blockroot_merkle\": {\n \"_active_nodes\": [\n \"f8a83438724a996caaa42231c39b757bec3d1b6fb0bd2c982812eb2f7fadc377\", \n \"ab53f4807abffafbcf15fa6edd6ffe72cf73d18689403e389127015b4fd1e544\", \n \"0275481b63dcb217603558835292d4057d39f9a5d41c9d5302230d379a992b18\", \n \"af944001e02b80671b6ea50e5f83969fc2db5e56671ff83700a9018336d0fc58\", \n \"e1ed7b7a82f5507110497fd41f2098a269ffbe8202de0f57393b4aeb27792434\", \n \"2553b9e4b8225b0f311c2a382fccd027e90f909319616b75933b56f19d20c440\"\n ], \n \"_node_count\": 1418\n }, \n \"producer_to_last_produced\": [\n [\n \"accountnum11\", \n 1419\n ], \n [\n \"accountnum22\", \n 1406\n ], \n [\n \"accountnum33\", \n 1418\n ], \n [\n \"eosio\", \n 1370\n ]\n ], \n \"producer_to_last_implied_irb\": [\n [\n \"accountnum11\", \n 1370\n ], \n [\n \"accountnum22\", \n 1370\n ], \n [\n \"accountnum33\", \n 1370\n ]\n ], \n \"block_signing_key\": \"EOS8mUftJXepGzdQ2TaCduNuSPAfXJHf22uex4u41ab1EVv9EAhWt\", \n \"confirm_count\": [\n 1, \n 1, \n 1, \n 1, eos的出块流程大致如下:\nPlain Text\n........\n//启动生产插件\nproducer_plugin::plugin_startup();\n........\n//出块循环\nmy->schedule_production_loop();\n.........\n//出块\nauto result = start_block();\n..........\n//签名和提交\nauto res = self->maybe_produce_block();\n..........\n以上步骤的核心在于:auto result = start_block();\n这个函数更跟下去会发现,重点在于:\nPlain Text\nchain.start_block(block_time, blocks_to_confirm);\n这个函数才是dops的关键\nC++\nvoid start_block( block_timestamp_type when, uint16_t confirm_block_count, controller::block_status s ) {\n FC_ASSERT( !pending );\n\n FC_ASSERT( db.revision() == head->block_num, \"\",\n (\"db.revision()\", db.revision())(\"controller_head_block\", head->block_num)(\"fork_db_head_block\", fork_db.head()->block_num) );\n\n auto guard_pending = fc::make_scoped_exit([this](){\n pending.reset();\n });\n\n pending = db.start_undo_session(true);\n\n pending->_block_status = s;\n //由当前区块头创建一个新的block_state ---->>>>> 11111\n pending->_pending_block_state = std::make_shared<block_state>( *head, when ); // promotes pending schedule (if any) to active\n pending->_pending_block_state->in_current_chain = true;\n //确认链上的区块 ----->>>>> 22222\n pending->_pending_block_state->set_confirmed(confirm_block_count);\n //更新激活的生产者 ----->>>> 33333\n auto was_pending_promoted = pending->_pending_block_state->maybe_promote_pending();\n const auto& gpo = db.get<global_property_object>();\n if( gpo.proposed_schedule_block_num.valid() && // if there is a proposed schedule that was proposed in a block ...\n ( *gpo.proposed_schedule_block_num <= pending->_pending_block_state->dpos_irreversible_blocknum ) && // ... that has now become irreversible ...\n pending->_pending_block_state->pending_schedule.producers.size() == 0 && // ... and there is room for a new pending schedule ...\n !was_pending_promoted // ... and not just because it was promoted to active at the start of this block, then:\n )\n {\n // Promote proposed schedule to pending schedule.\n if( !replaying ) {\n ilog( \"promoting proposed schedule (set in block ${proposed_num}) to pending; current block: ${n} lib: ${lib} schedule: ${schedule} \",\n (\"proposed_num\", *gpo.proposed_schedule_block_num)(\"n\", pending->_pending_block_state->block_num)\n (\"lib\", pending->_pending_block_state->dpos_irreversible_blocknum)\n (\"schedule\", static_cast<producer_schedule_type>(gpo.proposed_schedule) ) );\n }\n pending->_pending_block_state->set_new_producers( gpo.proposed_schedule );\n db.modify( gpo, [&]( auto& gp ) {\n gp.proposed_schedule_block_num = optional<block_num_type>();\n gp.proposed_schedule.clear();\n });\n\n std::cout<<\"\\n========555==========\\n\";\n std::cout<< fc::json::to_string(*pending->_pending_block_state)<<\"\\n\";\n std::cout<<\"==========555========\\n\";\n }\n11111,22222,33333这三步涉及到的数据结构是block_state\nJSON\n{\n \"id\": \"0000058af1b33d45e4ec927e52b74e8568422f45245dc0399c144bdff6dc16d8\", \n \"block_num\": 1418, \n \"header\": {\n \"timestamp\": \"2018-08-04T07:31:47.500\", \n \"producer\": \"accountnum33\", \n \"confirmed\": 0, \n \"previous\": \"0000058980ac0382075af4216ddbd7be1eae1690a278d7cc04a61570f307b39d\", \n \"transaction_mroot\": \"0000000000000000000000000000000000000000000000000000000000000000\", \n \"action_mroot\": \"eaadc508183ba966551e48dba3a44505a95e6a42d7a53f435db39c3dec3584c9\", \n \"schedule_version\": 1, \n \"header_extensions\": [ ], \n \"producer_signature\": \"SIG_K1_KVyKdpLpEXPQUvRR5jZL4FMYvFDNic6tyvX6icLXFKhoriskCg21esduAceX2cZDv2GwcB12TxdneeKMwfzWB2TckZpZvQ\"\n }, \n \"dpos_proposed_irreversible_blocknum\": 1370, \n \"dpos_irreversible_blocknum\": 1370, \n \"bft_irreversible_blocknum\": 0, \n \"pending_schedule_lib_num\": 1369, \n \"pending_schedule_hash\": \"b4ea72a3e20628a54028e681258cd1e6a46b20ae07210836f29087f9f8f37346\", \n \"pending_schedule\": {\n \"version\": 1, \n \"producers\": [ ]\n }, \n \"active_schedule\": {\n \"version\": 1, \n \"producers\": [\n {\n \"producer_name\": \"accountnum11\", \n \"block_signing_key\": \"EOS8mUftJXepGzdQ2TaCduNuSPAfXJHf22uex4u41ab1EVv9EAhWt\"\n }, \n {\n \"producer_name\": \"accountnum22\", \n \"block_signing_key\": \"EOS8mUftJXepGzdQ2TaCduNuSPAfXJHf22uex4u41ab1EVv9EAhWt\"\n }, \n {\n \"producer_name\": \"accountnum33\", \n \"block_signing_key\": \"EOS8mUftJXepGzdQ2TaCduNuSPAfXJHf22uex4u41ab1EVv9EAhWt\"\n }\n ]\n }, \n \"blockroot_merkle\": {\n \"_active_nodes\": [\n \"0000058980ac0382075af4216ddbd7be1eae1690a278d7cc04a61570f307b39d\", \n \"ab53f4807abffafbcf15fa6edd6ffe72cf73d18689403e389127015b4fd1e544\", \n \"0275481b63dcb217603558835292d4057d39f9a5d41c9d5302230d379a992b18\", \n \"af944001e02b80671b6ea50e5f83969fc2db5e56671ff83700a9018336d0fc58\", \n \"e1ed7b7a82f5507110497fd41f2098a269ffbe8202de0f57393b4aeb27792434\", \n \"5a36ca2325d5f7b2de22b98de7cf70c0a06cf391fc17f1d242b964d16146dee9\"\n ], \n \"_node_count\": 1417\n }, \n \"producer_to_last_produced\": [\n [\n \"accountnum11\", \n 1394\n ], \n [\n \"accountnum22\", \n 1406\n ], \n [\n \"accountnum33\", \n 1418\n ], \n [\n \"eosio\", \n 1370\n ]\n ], \n \"producer_to_last_implied_irb\": [\n [\n \"accountnum11\", \n 1370\n ], \n [\n \"accountnum22\", \n 1370\n ], \n [\n \"accountnum33\", \n 1370\n ]\n ], \n \"block_signing_key\": \"EOS8mUftJXepGzdQ2TaCduNuSPAfXJHf22uex4u41ab1EVv9EAhWt\", \n \"confirm_count\": [\n 2, \n 2, \n 2, \n 2, \n 2, \n 2, \n 2, \n 2, \n 2, \n 2, \n 2, \n 2, \n 1, \n 1, \n 1, \n 1, \n 1, \n 1, \n 1, \n 1, \n 1, \n 1, \n 1, \n 1, \n 1, \n 1, \n 1, \n 1, \n 1, \n 1, \n 1, \n 1, \n 1, \n 1, \n 1, \n 1, \n 2, \n 2, \n 2, \n 2, \n 2, \n 2, \n 2, \n 2, \n 2, \n 2, \n 2, \n 2\n ], \n \"confirmations\": [ ], \n \"block\": {\n \"timestamp\": \"2018-08-04T07:31:47.500\", \n \"producer\": \"accountnum33\", \n \"confirmed\": 0, \n \"previous\": \"0000058980ac0382075af4216ddbd7be1eae1690a278d7cc04a61570f307b39d\", \n \"transaction_mroot\": \"0000000000000000000000000000000000000000000000000000000000000000\", \n \"action_mroot\": \"eaadc508183ba966551e48dba3a44505a95e6a42d7a53f435db39c3dec3584c9\", \n \"schedule_version\": 1, \n \"header_extensions\": [ ], \n \"producer_signature\": \"SIG_K1_KVyKdpLpEXPQUvRR5jZL4FMYvFDNic6tyvX6icLXFKhoriskCg21esduAceX2cZDv2GwcB12TxdneeKMwfzWB2TckZpZvQ\", \n \"transactions\": [ ], \n \"block_extensions\": [ ]\n }, \n \"validated\": true, \n \"in_current_chain\": true\n}\n第一步:从当前块头出发,生成一个新的block_state。\nC++\n........\nresult.producer_to_last_implied_irb[prokey.producer_name] = result.dpos_proposed_irreversible_blocknum;\nresult.dpos_irreversible_blocknum = result.calc_dpos_last_irreversible(); \n\n/// grow the confirmed count\nstatic_assert(std::numeric_limits<uint8_t>::max() >= (config::max_producers * 2 / 3) + 1, \"8bit confirmations may not be able to hold all of the needed confirmations\");\n\n// This uses the previous block active_schedule because thats the \"schedule\" that signs and therefore confirms _this_ block\nauto num_active_producers = active_schedule.producers.size();\nuint32_t required_confs = (uint32_t)(num_active_producers * 2 / 3) + 1;\n\nif( confirm_count.size() < config::maximum_tracked_dpos_confirmations ) {\n result.confirm_count.reserve( confirm_count.size() + 1 );\n result.confirm_count = confirm_count;\n result.confirm_count.resize( confirm_count.size() + 1 );\n result.confirm_count.back() = (uint8_t)required_confs;\n} else {\n result.confirm_count.resize( confirm_count.size() );\n memcpy( &result.confirm_count[0], &confirm_count[1], confirm_count.size() - 1 );\n result.confirm_count.back() = (uint8_t)required_confs;\n}\n.......\n当前3个生产节点,required_confs为3,所以11111结束后,confirm_count中会压入3,即当前出产块所需的确认个数。\n下面直接给出几个解释:\nconfirm_count:区块所需确认个数表,从当前出产块往前推,最后的元素为当前出产块所需的确认个数\nproducer_to_last_implied_irb:由生产者确定的不可逆区块候选名单\ndpos_irreversible_blocknum:不可逆区块\ndpos_proposed_irreversible_blocknum:候选不可逆区块\nactive_schedule:已激活的生产者列表\n有了对这几个定义的解释,代码变得很清晰\nC++\n//计算不可逆区块,将不可逆区块候选名单中从小到大排,选出1/3处的区块号作为不可逆区块\nuint32_t block_header_state::calc_dpos_last_irreversible()const {\n vector<uint32_t> blocknums; blocknums.reserve( producer_to_last_implied_irb.size() );\n for( auto& i : producer_to_last_implied_irb ) {\n blocknums.push_back(i.second);\n }\n /// 2/3 must be greater, so if I go 1/3 into the list sorted from low to high, then 2/3 are greater\n\n if( blocknums.size() == 0 ) return 0;\n /// TODO: update to nth_element \n std::sort( blocknums.begin(), blocknums.end() );\n return blocknums[ (blocknums.size()-1) / 3 ];\n}\n上面的程序中出现两次3分之几的算法,第一次是required_confs,没毛病,一个区块要得到2/3个生产者的确认。但是得到2/3的确认与不可逆区块的联系不仅仅是这个,严格来说,不可逆区块是从不可逆区块候选名单中选择出来的。\n第11111步之后的json\nJSON\n{\n \"id\": \"0000000000000000000000000000000000000000000000000000000000000000\", \n \"block_num\": 1419, \n \"header\": {\n \"timestamp\": \"2018-08-04T07:31:48.000\", \n \"producer\": \"accountnum11\", \n \"confirmed\": 1, \n \"previous\": \"0000058af1b33d45e4ec927e52b74e8568422f45245dc0399c144bdff6dc16d8\", \n \"transaction_mroot\": \"0000000000000000000000000000000000000000000000000000000000000000\", \n \"action_mroot\": \"0000000000000000000000000000000000000000000000000000000000000000\", \n \"schedule_version\": 1, \n \"header_extensions\": [ ], \n \"producer_signature\": \"SIG_K1_111111111111111111111111111111111111111111111111111111111111111116uk5ne\"\n }, \n \"dpos_proposed_irreversible_blocknum\": 1370, \n \"dpos_irreversible_blocknum\": 1370, \n \"bft_irreversible_blocknum\": 0, \n \"pending_schedule_lib_num\": 1369, \n \"pending_schedule_hash\": \"b4ea72a3e20628a54028e681258cd1e6a46b20ae07210836f29087f9f8f37346\", \n \"pending_schedule\": {\n \"version\": 1, \n \"producers\": [ ]\n }, \n \"active_schedule\": {\n \"version\": 1, \n \"producers\": [\n {\n \"producer_name\": \"accountnum11\", \n \"block_signing_key\": \"EOS8mUftJXepGzdQ2TaCduNuSPAfXJHf22uex4u41ab1EVv9EAhWt\"\n }, \n {\n \"producer_name\": \"accountnum22\", \n \"block_signing_key\": \"EOS8mUftJXepGzdQ2TaCduNuSPAfXJHf22uex4u41ab1EVv9EAhWt\"\n }, \n {\n \"producer_name\": \"accountnum33\", \n \"block_signing_key\": \"EOS8mUftJXepGzdQ2TaCduNuSPAfXJHf22uex4u41ab1EVv9EAhWt\"\n }\n ]\n }, \n \"blockroot_merkle\": {\n \"_active_nodes\": [\n \"f8a83438724a996caaa42231c39b757bec3d1b6fb0bd2c982812eb2f7fadc377\", \n \"ab53f4807abffafbcf15fa6edd6ffe72cf73d18689403e389127015b4fd1e544\", \n \"0275481b63dcb217603558835292d4057d39f9a5d41c9d5302230d379a992b18\", \n \"af944001e02b80671b6ea50e5f83969fc2db5e56671ff83700a9018336d0fc58\", \n \"e1ed7b7a82f5507110497fd41f2098a269ffbe8202de0f57393b4aeb27792434\", \n \"2553b9e4b8225b0f311c2a382fccd027e90f909319616b75933b56f19d20c440\"\n ], \n \"_node_count\": 1418\n }, \n \"producer_to_last_produced\": [\n [\n \"accountnum11\", \n 1419\n ], \n [\n \"accountnum22\", \n 1406\n ], \n [\n \"accountnum33\", \n 1418\n ], \n [\n \"eosio\", \n 1370\n ]\n ], \n \"producer_to_last_implied_irb\": [\n [\n \"accountnum11\", \n 1370\n ], \n [\n \"accountnum22\", \n 1370\n ], \n [\n \"accountnum33\", \n 1370\n ]\n ], \n \"block_signing_key\": \"EOS8mUftJXepGzdQ2TaCduNuSPAfXJHf22uex4u41ab1EVv9EAhWt\", \n \"confirm_count\": [\n 2, \n 2, \n 2, \n 2, \n 2, \n 2, \n 2, \n 2, \n 2, \n 2, \n 2, \n 2, \n 1, \n 1, \n 1, \n 1, \n 1, \n 1, \n 1, \n 1, \n 1, \n 1, \n 1, \n 1, \n 1, \n 1, \n 1, \n 1, \n 1, \n 1, \n 1, \n 1, \n 1, \n 1, \n 1, \n 1, \n 2, \n 2, \n 2, \n 2, \n 2, \n 2, \n 2, \n 2, \n 2, \n 2, \n 2, \n 2, \n 3 \n ], \n \"confirmations\": [ ], \n \"block\": {\n \"timestamp\": \"2018-08-04T07:31:48.000\", \n \"producer\": \"accountnum11\", \n \"confirmed\": 1, \n \"previous\": \"0000058af1b33d45e4ec927e52b74e8568422f45245dc0399c144bdff6dc16d8\", \n \"transaction_mroot\": \"0000000000000000000000000000000000000000000000000000000000000000\", \n \"action_mroot\": \"0000000000000000000000000000000000000000000000000000000000000000\", \n \"schedule_version\": 1, \n \"header_extensions\": [ ], \n \"producer_signature\": \"SIG_K1_111111111111111111111111111111111111111111111111111111111111111116uk5ne\", \n \"transactions\": [ ], \n \"block_extensions\": [ ]\n }, \n \"validated\": false, \n \"in_current_chain\": false\n}\n到第22222步,这个函数确定了候选不可逆区与确认列表的关系\nnum_prev_blocks是指当前节点最后一次出块到现在的间隔,本文使用3个节点,故间隔为24\n比如出块顺序为accountnum33--》accountnum11--》accountnum22--》accountnum33\naccountnum33会把自己的块确认一次\naccountnum11的时候会把33出过的块和自己确认一次。。。。\n确认就是将confirmed数组的相应元素减去1,第一步得到的json中:\n\"confirm_count\":[2,2,2,2,2,2,2,2,2,2,2,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,3]\n此时num_prev_blocks = 24\n所以以下程序的工作是先将confirm_count元素从尾到头减去1,最多遍历num_prev_blocks和confirm_count大小中最小的。\n\"confirm_count\":[2,2,2,2,2,2,2,2,2,2,2,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,2]\n由于出现了“0”元素,将0元素的区块号(1406)记录在dpos_proposed_irreversible_blocknum中,然后在更新数组,将0之前的元素去掉。\n\"confirm_count\":[1,1,1,1,1,1,1,1,1,1,1,1,2]\n所以这样看来,dpos_proposed_irreversible_blocknum就是本生产节点在本块确定的候选不可逆区块。而这个参数会在下一次出块时打包进producer_to_last_implied_irb。\nC++\nvoid block_header_state::set_confirmed( uint16_t num_prev_blocks ) {\n /*\n idump((num_prev_blocks)(confirm_count.size()));\n\n for( uint32_t i = 0; i < confirm_count.size(); ++i ) {\n std::cerr << \"confirm_count[\"<<i<<\"] = \" << int(confirm_count[i]) << \"\\n\";\n }\n */\n header.confirmed = num_prev_blocks;\n\n int32_t i = (int32_t)(confirm_count.size() - 1);\n uint32_t blocks_to_confirm = num_prev_blocks + 1; /// confirm the head block too\n while( i >= 0 && blocks_to_confirm ) {\n --confirm_count[i];\n //idump((confirm_count[i]));\n if( confirm_count[i] == 0 )\n {\n uint32_t block_num_for_i = block_num - (uint32_t)(confirm_count.size() - 1 - i);\n dpos_proposed_irreversible_blocknum = block_num_for_i;\n //idump((dpos2_lib)(block_num)(dpos_irreversible_blocknum));\n\n if (i == confirm_count.size() - 1) {\n confirm_count.resize(0);\n } else {\n memmove( &confirm_count[0], &confirm_count[i + 1], confirm_count.size() - i - 1);\n confirm_count.resize( confirm_count.size() - i - 1 );\n }\n\n return;\n }\n --i;\n --blocks_to_confirm;\n }\n}\n第22222步之后的输出json为:\nJSON\n{\n \"id\": \"0000000000000000000000000000000000000000000000000000000000000000\", \n \"block_num\": 1419, \n \"header\": {\n \"timestamp\": \"2018-08-04T07:31:48.000\", \n \"producer\": \"accountnum11\", \n \"confirmed\": 24, \n \"previous\": \"0000058af1b33d45e4ec927e52b74e8568422f45245dc0399c144bdff6dc16d8\", \n \"transaction_mroot\": \"0000000000000000000000000000000000000000000000000000000000000000\", \n \"action_mroot\": \"0000000000000000000000000000000000000000000000000000000000000000\", \n \"schedule_version\": 1, \n \"header_extensions\": [ ], \n \"producer_signature\": \"SIG_K1_111111111111111111111111111111111111111111111111111111111111111116uk5ne\"\n }, \n \"dpos_proposed_irreversible_blocknum\": 1406, \n \"dpos_irreversible_blocknum\": 1370, \n \"bft_irreversible_blocknum\": 0, \n \"pending_schedule_lib_num\": 1369, \n \"pending_schedule_hash\": \"b4ea72a3e20628a54028e681258cd1e6a46b20ae07210836f29087f9f8f37346\", \n \"pending_schedule\": {\n \"version\": 1, \n \"producers\": [ ]\n }, \n \"active_schedule\": {\n \"version\": 1, \n \"producers\": [\n {\n \"producer_name\": \"accountnum11\", \n \"block_signing_key\": \"EOS8mUftJXepGzdQ2TaCduNuSPAfXJHf22uex4u41ab1EVv9EAhWt\"\n }, \n {\n 1, \n 1, \n 1, \n 1, \n 1, \n 1, \n 1, \n 1, \n 2\n ], \n \"confirmations\": [ ], \n \"block\": {\n \"timestamp\": \"2018-08-04T07:31:48.000\", \n \"producer\": \"accountnum11\", \n \"confirmed\": 1, \n \"previous\": \"0000058af1b33d45e4ec927e52b74e8568422f45245dc0399c144bdff6dc16d8\", \n \"transaction_mroot\": \"0000000000000000000000000000000000000000000000000000000000000000\", \n \"action_mroot\": \"0000000000000000000000000000000000000000000000000000000000000000\", \n \"schedule_version\": 1, \n \"header_extensions\": [ ], \n \"producer_signature\": \"SIG_K1_111111111111111111111111111111111111111111111111111111111111111116uk5ne\", \n \"transactions\": [ ], \n \"block_extensions\": [ ]\n }, \n \"validated\": false, \n \"in_current_chain\": true\n}\n综上,实际上dops在代码里面使用是分作两步的。\n1,2/3共识,符合2/3认可的最高区块可以进入”由生产者确定的不可逆区块候选名单”。由于出块轮转,每个生产者所能见证的高度是不一样的。\n2,1/3共识,从“由生产者确定的不可逆区块候选名单”中选择1/3高度的地方得到不可逆区块。\n仔细一想,2还是加强了不可逆区块的共识。",
"json_metadata": "{\"tags\":[\"eos\",\"dpos\",\"btf\",\"sourcecode\"],\"app\":\"steemit/0.1\",\"format\":\"markdown\"}",
"parent_author": "",
"parent_permlink": "eos",
"permlink": "eos-dpos",
"title": "eos源码解析(三):dpos共识源码"
}
],
"op_in_trx": 0,
"timestamp": "2018-08-14T08:30:18",
"trx_id": "e96bd60b02eaa11a054c64e6d495c39ac891e819",
"trx_in_block": 0,
"virtual_op": 0
}ax3upvoted (1.00%) @camphortree / eos-bancor2018/07/13 02:58:09
ax3upvoted (1.00%) @camphortree / eos-bancor
2018/07/13 02:58:09
| author | camphortree |
| permlink | eos-bancor |
| voter | ax3 |
| weight | 100 (1.00%) |
| Transaction Info | Block #24128204/Trx a44347a6ed631921f93d58f6ddc808b61c62fbac |
View Raw JSON Data
{
"block": 24128204,
"op": [
"vote",
{
"author": "camphortree",
"permlink": "eos-bancor",
"voter": "ax3",
"weight": 100
}
],
"op_in_trx": 0,
"timestamp": "2018-07-13T02:58:09",
"trx_id": "a44347a6ed631921f93d58f6ddc808b61c62fbac",
"trx_in_block": 21,
"virtual_op": 0
}camphortreepublished a new post: eos-bancor2018/07/13 02:58:00
camphortreepublished a new post: eos-bancor
2018/07/13 02:58:00
| author | camphortree |
| body | 近来ram经历了大涨大跌,ram的核心是bancor算法,网上有很多对bancor的介绍,这里就不谈了,eos少数派报告的星主Anima对此有深刻的分析。本文直接从源码解析eos的bancor算法。 最开始,,,我新建了一条链,对,是一条新链。然后在完整的64G内存上购买了10000k 内存。下面的命令是新建一个账户并购买10000k的内存。 !!!!直接要结果的,跳过源码,后文自取,不谢不谢!!!! ``` cleos system newaccount eosio --transfer accountnum11 EOS8mUftJXepGzdQ2TaCduNuSPAfXJHf22uex4u41ab1EVv9EAhWt --stake-net "100000.0000 SYS" --stake-cpu "100000.0000 SYS" --buy-ram-kbytes 10000 ``` 在eos的内部,这条命令触发的函数是这样的: ``` //payer:eosio receiver:accountnum11 bytes:10000 void system_contract::buyrambytes( account_name payer, account_name receiver, uint32_t bytes ) { auto itr = _rammarket.find(S(4,RAMCORE)); auto tmp = *itr; auto eosout = tmp.convert( asset(bytes,S(0,RAM)), CORE_SYMBOL ); buyram( payer, receiver, eosout ); } ``` 先调用convert,convert涉及到很多函数,这是bancor真正实现,一并列在下面,我加了打印,后面会放打印结果。。。。 ``` namespace eosiosystem { asset exchange_state::convert_to_exchange( connector& c, asset in ) { real_type R(supply.amount); real_type C(c.balance.amount+in.amount); real_type F(c.weight/1000.0); real_type T(in.amount); real_type ONE(1.0); print("\nconvert_to_exchange \n"); print( "supply.amount ", supply.amount , "\n" ); print( "c.balance.amount ", c.balance.amount, "\n" ); print( "c.weight: ", c.weight, "\n" ); print( "in.amount: ", in.amount, "\n\n" ); real_type E = -R * (ONE - std::pow( ONE + T / C, F) ); print( "E: ", E, "\n"); int64_t issued = int64_t(E); supply.amount += issued; c.balance.amount += in.amount; return asset( issued, supply.symbol ); } asset exchange_state::convert_from_exchange( connector& c, asset in ) { eosio_assert( in.symbol== supply.symbol, "unexpected asset symbol input" ); real_type R(supply.amount - in.amount); real_type C(c.balance.amount); real_type F(1000.0/c.weight); real_type E(in.amount); real_type ONE(1.0); print("\nconvert_from_exchange \n"); print( "supply.amount ", supply.amount , "\n" ); print( "c.balance.amount ", c.balance.amount, "\n" ); print( "c.weight: ", c.weight, "\n" ); print( "in.amount: ", in.amount, "\n\n" ); // potentially more accurate: // The functions std::expm1 and std::log1p are useful for financial calculations, for example, // when calculating small daily interest rates: (1+x)n // -1 can be expressed as std::expm1(n * std::log1p(x)). // real_type T = C * std::expm1( F * std::log1p(E/R) ); real_type T = C * (std::pow( ONE + E/R, F) - ONE); print( "T: ", T, "\n"); int64_t out = int64_t(T); supply.amount -= in.amount; c.balance.amount -= out; return asset( out, c.balance.symbol ); } asset exchange_state::convert( asset from, symbol_type to ) { auto sell_symbol = from.symbol; auto ex_symbol = supply.symbol; auto base_symbol = base.balance.symbol; auto quote_symbol = quote.balance.symbol; print( "From: ", from, " TO ", asset( 0,to), "\n" ); print( "base: ", base_symbol, "\n" ); print("base_amount: ", base.balance.amount , "\n"); print( "quote: ", quote_symbol, "\n" ); print( "quote_amount: ", quote.balance.amount, "\n" ); print( "ex: ", supply.symbol, "\n" ); print( "ex_amuont: ", supply.amount, "\n" ); if( sell_symbol != ex_symbol ) { if( sell_symbol == base_symbol ) { from = convert_to_exchange( base, from ); } else if( sell_symbol == quote_symbol ) { from = convert_to_exchange( quote, from ); } else { eosio_assert( false, "invalid sell" ); } } else { if( to == base_symbol ) { from = convert_from_exchange( base, from ); } else if( to == quote_symbol ) { from = convert_from_exchange( quote, from ); } else { eosio_assert( false, "invalid conversion" ); } } if( to != from.symbol ) return convert( from, to ); return from; } } /// namespace eosiosystem ``` 然后调用buyram ``` void system_contract::buyram( account_name payer, account_name receiver, asset quant ) { require_auth( payer ); eosio_assert( quant.amount > 0, "must purchase a positive amount" ); auto fee = quant; fee.amount = ( fee.amount + 199 ) / 200; /// .5% fee (round up) // fee.amount cannot be 0 since that is only possible if quant.amount is 0 which is not allowed by the assert above. // If quant.amount == 1, then fee.amount == 1, // otherwise if quant.amount > 1, then 0 < fee.amount < quant.amount. auto quant_after_fee = quant; quant_after_fee.amount -= fee.amount; // quant_after_fee.amount should be > 0 if quant.amount > 1. // If quant.amount == 1, then quant_after_fee.amount == 0 and the next inline transfer will fail causing the buyram action to fail. INLINE_ACTION_SENDER(eosio::token, transfer)( N(eosio.token), {payer,N(active)}, { payer, N(eosio.ram), quant_after_fee, std::string("buy ram") } ); if( fee.amount > 0 ) { INLINE_ACTION_SENDER(eosio::token, transfer)( N(eosio.token), {payer,N(active)}, { payer, N(eosio.ramfee), fee, std::string("ram fee") } ); } int64_t bytes_out; const auto& market = _rammarket.get(S(4,RAMCORE), "ram market does not exist"); _rammarket.modify( market, 0, [&]( auto& es ) { bytes_out = es.convert( quant_after_fee, S(0,RAM) ).amount; }); eosio_assert( bytes_out > 0, "must reserve a positive amount" ); _gstate.total_ram_bytes_reserved += uint64_t(bytes_out); _gstate.total_ram_stake += quant_after_fee.amount; user_resources_table userres( _self, receiver ); auto res_itr = userres.find( receiver ); if( res_itr == userres.end() ) { res_itr = userres.emplace( receiver, [&]( auto& res ) { res.owner = receiver; res.ram_bytes = bytes_out; }); } else { userres.modify( res_itr, receiver, [&]( auto& res ) { res.ram_bytes += bytes_out; }); } set_resource_limits( res_itr->owner, res_itr->ram_bytes, res_itr->net_weight.amount, res_itr->cpu_weight.amount ); } ``` 直接,打印输出: ``` 1786293ms thread-0 apply_context.cpp:28 print_debug ] [(eosio,buyrambytes)->eosio]: CONSOLE OUTPUT BEGIN ===================== From: 10240000. RAM TO 0.0000 SYS base: 0,RAM quote: 4,SYS ex: 4,RAMCORE convert_to_exchange supply.amount 100000000000000 c.balance.amount 68719476736 c.weight: 5.000000000000000e-01 in.amount: 10240000 E: 7.448915928520706e+06 From: 744.8915 RAMCORE TO 0.0000 SYS base: 0,RAM quote: 4,SYS ex: 4,RAMCORE convert_from_exchange supply.amount 100000007448915 c.balance.amount 100 0000 0000 c.weight: 5.000000000000000e-01 in.amount: 7448915 T: 1.489893921873264e+06 From: 148.2443 SYS TO 0. RAM base: 0,RAM quote: 4,SYS ex: 4,RAMCORE convert_to_exchange supply.amount 100 0000 0000 0000 c.balance.amount 100 0000 , 0000 c.weight: 5.000000000000000e-01 in.amount: 1482443 E: 7.410567426369141e+06 From: 741.0567 RAMCORE TO 0. RAM base: 0,RAM quote: 4,SYS ex: 4,RAMCORE convert_from_exchange supply.amount 100000007410567 c.balance.amount 68719476736 c.weight: 5.000000000000000e-01 in.amount: 7410567 T: 1.018576016383362e+07 [(eosio,buyrambytes)->eosio]: CONSOLE OUTPUT END ===================== ``` 这三个在后文中常见: 1, base:RAM 2, quote:SYS 3, ex:RAMCORE 我们根据结果倒推算法。 第1步,调用conver_to_exchange将RAM单位换算成RAMCORE单位  对应变量: ``` supply.amoumt : 10^14 base.balance.amount : 68719476736 即64G in.amount : 10240000 结果 E = 74485915 supply.amount += 74485915 base.balance.amount += 10240000 ``` 第2步,conver_frome_exchange函数将RAMCORE换算成SYS  对应变量: ``` //这是ram的初始资金池容量,100万,之所以是10次方,是因为在eos内部,为取整方便,SYS的单位是“分”,不是“元”。 quote.balance.amount : 10^10 supply.amount -= 7448915 quote.balance.amount -= 1489893 结果T=1489893 ``` 上面的分析是从ram-->sys ,意思是要购买10000k的ram,大概需要1489893分钱的SYS即149个EOS。但是像“supply.amount -= ” “quote.balance.amount -=”并不会对supply做真正的修改,只是修改的镜像。在调用buyram时使用“_rammarket.modify”才能修改系统的amount。 以下的两步是在buyram函数中进行的。 下面是大家比较关心的手续费问题:之所以要加上199,是要确保1分钱的交易也是需要手续费的,真是雁过拔毛呀!!!除以200 就是乘以0.005。没毛病。。。话说我算术不好。。。1/200 = 0.005 。 ``` fee.amount = ( fee.amount + 199 ) / 200; ``` 第3步,将SYS转换成RAMCORE,调用conver_to_exchange函数:  对应变量: ``` supply.amount : 10^14 in.amount : 1482443 quote.balance.amount : 10^10 结果E = 7410567 ``` 第4步,调用conver_from_exchange 将RAMCORE换算成RAM  对应变量: ``` base.banalce.amount : 64G base.banalce.amount -= 10185760 ``` 所以,最后,我们想买10240000byte的ram,结果只买了10185760byte的ram,其余的,扣了手续费。 实际上,卖出ram时,过程和前两步一样,不过在最后收取0.005的手续费。 将上文中,前两步的公式整合,化简,可以发现:  后两步的公式整合,化简可得到:  呵呵!并没有什么0.5/1000之类的幂,也没了什么RAMCORE。 不知道为啥BM一定要两步转换,强行引入RAMCORE,难道是为了确保RAMCORE的总量不变吗?希望知道的童靴答疑。 |
| json metadata | {"tags":["eos","eosram","bancor"],"image":["https://upload-images.jianshu.io/upload_images/7298875-f629b3e3b43e1bd3.gif?imageMogr2/auto-orient/strip","https://cdn.steemitimages.com/DQmZ7a6QRj6SzFWEqwbrLq5huwSwUuDRDdMW1sih6n1eGMv/image.png","https://cdn.steemitimages.com/DQmWnaJBoZKtsXV2p4V7MWX2z1k5QVGxGikPvL6EnTuHPLA/image.png","https://upload-images.jianshu.io/upload_images/7298875-d7b4372bb6c355b2.gif?imageMogr2/auto-orient/strip","https://upload-images.jianshu.io/upload_images/7298875-213756a564a55629.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240","https://upload-images.jianshu.io/upload_images/7298875-92e9a793d70257e5.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240"],"app":"steemit/0.1","format":"markdown"} |
| parent author | |
| parent permlink | eos |
| permlink | eos-bancor |
| title | eos源码解析(二) : bancor算法 |
| Transaction Info | Block #24128201/Trx 2a3b3c818e7c49828f2601f4b0016ed0e5177cce |
View Raw JSON Data
{
"block": 24128201,
"op": [
"comment",
{
"author": "camphortree",
"body": "近来ram经历了大涨大跌,ram的核心是bancor算法,网上有很多对bancor的介绍,这里就不谈了,eos少数派报告的星主Anima对此有深刻的分析。本文直接从源码解析eos的bancor算法。\n最开始,,,我新建了一条链,对,是一条新链。然后在完整的64G内存上购买了10000k 内存。下面的命令是新建一个账户并购买10000k的内存。\n!!!!直接要结果的,跳过源码,后文自取,不谢不谢!!!!\n\n```\ncleos system newaccount eosio --transfer accountnum11 EOS8mUftJXepGzdQ2TaCduNuSPAfXJHf22uex4u41ab1EVv9EAhWt --stake-net \"100000.0000 SYS\" --stake-cpu \"100000.0000 SYS\" --buy-ram-kbytes 10000\n\n```\n在eos的内部,这条命令触发的函数是这样的:\n\n```\n//payer:eosio receiver:accountnum11 bytes:10000\n void system_contract::buyrambytes( account_name payer, account_name receiver, uint32_t bytes ) {\n auto itr = _rammarket.find(S(4,RAMCORE));\n auto tmp = *itr;\n auto eosout = tmp.convert( asset(bytes,S(0,RAM)), CORE_SYMBOL );\n\n buyram( payer, receiver, eosout );\n }\n```\n先调用convert,convert涉及到很多函数,这是bancor真正实现,一并列在下面,我加了打印,后面会放打印结果。。。。\n\n```\nnamespace eosiosystem {\n asset exchange_state::convert_to_exchange( connector& c, asset in ) {\n\n real_type R(supply.amount);\n real_type C(c.balance.amount+in.amount);\n real_type F(c.weight/1000.0);\n real_type T(in.amount);\n real_type ONE(1.0);\n print(\"\\nconvert_to_exchange \\n\");\n print( \"supply.amount \", supply.amount , \"\\n\" );\n print( \"c.balance.amount \", c.balance.amount, \"\\n\" );\n print( \"c.weight: \", c.weight, \"\\n\" );\n print( \"in.amount: \", in.amount, \"\\n\\n\" );\n\n real_type E = -R * (ONE - std::pow( ONE + T / C, F) );\n print( \"E: \", E, \"\\n\");\n int64_t issued = int64_t(E);\n\n supply.amount += issued;\n c.balance.amount += in.amount;\n\n return asset( issued, supply.symbol );\n }\n\n asset exchange_state::convert_from_exchange( connector& c, asset in ) {\n eosio_assert( in.symbol== supply.symbol, \"unexpected asset symbol input\" );\n\n real_type R(supply.amount - in.amount);\n real_type C(c.balance.amount);\n real_type F(1000.0/c.weight);\n real_type E(in.amount);\n real_type ONE(1.0);\n\n print(\"\\nconvert_from_exchange \\n\");\n print( \"supply.amount \", supply.amount , \"\\n\" );\n print( \"c.balance.amount \", c.balance.amount, \"\\n\" );\n print( \"c.weight: \", c.weight, \"\\n\" );\n print( \"in.amount: \", in.amount, \"\\n\\n\" );\n\n // potentially more accurate: \n // The functions std::expm1 and std::log1p are useful for financial calculations, for example, \n // when calculating small daily interest rates: (1+x)n\n // -1 can be expressed as std::expm1(n * std::log1p(x)). \n // real_type T = C * std::expm1( F * std::log1p(E/R) );\n \n real_type T = C * (std::pow( ONE + E/R, F) - ONE);\n print( \"T: \", T, \"\\n\");\n int64_t out = int64_t(T);\n\n supply.amount -= in.amount;\n c.balance.amount -= out;\n\n return asset( out, c.balance.symbol );\n }\n\n asset exchange_state::convert( asset from, symbol_type to ) {\n auto sell_symbol = from.symbol;\n auto ex_symbol = supply.symbol;\n auto base_symbol = base.balance.symbol;\n auto quote_symbol = quote.balance.symbol;\n\n print( \"From: \", from, \" TO \", asset( 0,to), \"\\n\" );\n print( \"base: \", base_symbol, \"\\n\" );\n print(\"base_amount: \", base.balance.amount , \"\\n\");\n print( \"quote: \", quote_symbol, \"\\n\" );\n print( \"quote_amount: \", quote.balance.amount, \"\\n\" );\n print( \"ex: \", supply.symbol, \"\\n\" );\n print( \"ex_amuont: \", supply.amount, \"\\n\" );\n\n\n if( sell_symbol != ex_symbol ) {\n if( sell_symbol == base_symbol ) {\n from = convert_to_exchange( base, from );\n } else if( sell_symbol == quote_symbol ) {\n from = convert_to_exchange( quote, from );\n } else { \n eosio_assert( false, \"invalid sell\" );\n }\n } else {\n if( to == base_symbol ) {\n from = convert_from_exchange( base, from ); \n } else if( to == quote_symbol ) {\n from = convert_from_exchange( quote, from ); \n } else {\n eosio_assert( false, \"invalid conversion\" );\n }\n }\n\n if( to != from.symbol )\n return convert( from, to );\n\n return from;\n }\n} /// namespace eosiosystem\n```\n然后调用buyram\n```\n void system_contract::buyram( account_name payer, account_name receiver, asset quant )\n {\n require_auth( payer );\n eosio_assert( quant.amount > 0, \"must purchase a positive amount\" );\n\n auto fee = quant;\n fee.amount = ( fee.amount + 199 ) / 200; /// .5% fee (round up)\n // fee.amount cannot be 0 since that is only possible if quant.amount is 0 which is not allowed by the assert above.\n // If quant.amount == 1, then fee.amount == 1,\n // otherwise if quant.amount > 1, then 0 < fee.amount < quant.amount.\n auto quant_after_fee = quant;\n quant_after_fee.amount -= fee.amount;\n // quant_after_fee.amount should be > 0 if quant.amount > 1.\n // If quant.amount == 1, then quant_after_fee.amount == 0 and the next inline transfer will fail causing the buyram action to fail.\n\n INLINE_ACTION_SENDER(eosio::token, transfer)( N(eosio.token), {payer,N(active)},\n { payer, N(eosio.ram), quant_after_fee, std::string(\"buy ram\") } );\n\n if( fee.amount > 0 ) {\n INLINE_ACTION_SENDER(eosio::token, transfer)( N(eosio.token), {payer,N(active)},\n { payer, N(eosio.ramfee), fee, std::string(\"ram fee\") } );\n }\n\n int64_t bytes_out;\n\n const auto& market = _rammarket.get(S(4,RAMCORE), \"ram market does not exist\");\n _rammarket.modify( market, 0, [&]( auto& es ) {\n bytes_out = es.convert( quant_after_fee, S(0,RAM) ).amount;\n });\n\n eosio_assert( bytes_out > 0, \"must reserve a positive amount\" );\n\n _gstate.total_ram_bytes_reserved += uint64_t(bytes_out);\n _gstate.total_ram_stake += quant_after_fee.amount;\n\n user_resources_table userres( _self, receiver );\n auto res_itr = userres.find( receiver );\n if( res_itr == userres.end() ) {\n res_itr = userres.emplace( receiver, [&]( auto& res ) {\n res.owner = receiver;\n res.ram_bytes = bytes_out;\n });\n } else {\n userres.modify( res_itr, receiver, [&]( auto& res ) {\n res.ram_bytes += bytes_out;\n });\n }\n set_resource_limits( res_itr->owner, res_itr->ram_bytes, res_itr->net_weight.amount, res_itr->cpu_weight.amount );\n }\n```\n直接,打印输出:\n\n```\n1786293ms thread-0 apply_context.cpp:28 print_debug ] \n[(eosio,buyrambytes)->eosio]: CONSOLE OUTPUT BEGIN =====================\nFrom: 10240000. RAM TO 0.0000 SYS\nbase: 0,RAM\nquote: 4,SYS\nex: 4,RAMCORE\n\nconvert_to_exchange \nsupply.amount 100000000000000\nc.balance.amount 68719476736\nc.weight: 5.000000000000000e-01\nin.amount: 10240000\nE: 7.448915928520706e+06\n\nFrom: 744.8915 RAMCORE TO 0.0000 SYS\nbase: 0,RAM\nquote: 4,SYS\nex: 4,RAMCORE\n\nconvert_from_exchange \nsupply.amount 100000007448915\nc.balance.amount 100 0000 0000\nc.weight: 5.000000000000000e-01\nin.amount: 7448915\nT: 1.489893921873264e+06\n\nFrom: 148.2443 SYS TO 0. RAM\nbase: 0,RAM\nquote: 4,SYS\nex: 4,RAMCORE\n\nconvert_to_exchange \nsupply.amount 100 0000 0000 0000\nc.balance.amount 100 0000 , 0000\nc.weight: 5.000000000000000e-01\nin.amount: 1482443\nE: 7.410567426369141e+06\n\n\nFrom: 741.0567 RAMCORE TO 0. RAM\nbase: 0,RAM\nquote: 4,SYS\nex: 4,RAMCORE\n\nconvert_from_exchange \nsupply.amount 100000007410567\nc.balance.amount 68719476736\nc.weight: 5.000000000000000e-01\nin.amount: 7410567\nT: 1.018576016383362e+07\n\n\n[(eosio,buyrambytes)->eosio]: CONSOLE OUTPUT END =====================\n\n```\n这三个在后文中常见:\n1, base:RAM\n2, quote:SYS\n3, ex:RAMCORE\n\n我们根据结果倒推算法。\n第1步,调用conver_to_exchange将RAM单位换算成RAMCORE单位\n\n\n对应变量:\n```\nsupply.amoumt : 10^14\nbase.balance.amount : 68719476736 即64G\nin.amount : 10240000\n结果 E = 74485915\nsupply.amount += 74485915\nbase.balance.amount += 10240000\n```\n第2步,conver_frome_exchange函数将RAMCORE换算成SYS\n\n\n\n对应变量:\n```\n//这是ram的初始资金池容量,100万,之所以是10次方,是因为在eos内部,为取整方便,SYS的单位是“分”,不是“元”。 \nquote.balance.amount : 10^10 \nsupply.amount -= 7448915\nquote.balance.amount -= 1489893\n结果T=1489893\n```\n上面的分析是从ram-->sys ,意思是要购买10000k的ram,大概需要1489893分钱的SYS即149个EOS。但是像“supply.amount -= ” “quote.balance.amount -=”并不会对supply做真正的修改,只是修改的镜像。在调用buyram时使用“_rammarket.modify”才能修改系统的amount。\n\n以下的两步是在buyram函数中进行的。\n下面是大家比较关心的手续费问题:之所以要加上199,是要确保1分钱的交易也是需要手续费的,真是雁过拔毛呀!!!除以200 就是乘以0.005。没毛病。。。话说我算术不好。。。1/200 = 0.005 。\n```\n fee.amount = ( fee.amount + 199 ) / 200;\n```\n第3步,将SYS转换成RAMCORE,调用conver_to_exchange函数:\n\n对应变量:\n```\nsupply.amount : 10^14\nin.amount : 1482443\nquote.balance.amount : 10^10\n结果E = 7410567\n```\n第4步,调用conver_from_exchange 将RAMCORE换算成RAM\n\n对应变量:\n```\nbase.banalce.amount : 64G\nbase.banalce.amount -= 10185760\n```\n所以,最后,我们想买10240000byte的ram,结果只买了10185760byte的ram,其余的,扣了手续费。\n实际上,卖出ram时,过程和前两步一样,不过在最后收取0.005的手续费。\n将上文中,前两步的公式整合,化简,可以发现:\n\n\n\n后两步的公式整合,化简可得到:\n\n\n\n呵呵!并没有什么0.5/1000之类的幂,也没了什么RAMCORE。\n不知道为啥BM一定要两步转换,强行引入RAMCORE,难道是为了确保RAMCORE的总量不变吗?希望知道的童靴答疑。",
"json_metadata": "{\"tags\":[\"eos\",\"eosram\",\"bancor\"],\"image\":[\"https://upload-images.jianshu.io/upload_images/7298875-f629b3e3b43e1bd3.gif?imageMogr2/auto-orient/strip\",\"https://cdn.steemitimages.com/DQmZ7a6QRj6SzFWEqwbrLq5huwSwUuDRDdMW1sih6n1eGMv/image.png\",\"https://cdn.steemitimages.com/DQmWnaJBoZKtsXV2p4V7MWX2z1k5QVGxGikPvL6EnTuHPLA/image.png\",\"https://upload-images.jianshu.io/upload_images/7298875-d7b4372bb6c355b2.gif?imageMogr2/auto-orient/strip\",\"https://upload-images.jianshu.io/upload_images/7298875-213756a564a55629.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240\",\"https://upload-images.jianshu.io/upload_images/7298875-92e9a793d70257e5.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240\"],\"app\":\"steemit/0.1\",\"format\":\"markdown\"}",
"parent_author": "",
"parent_permlink": "eos",
"permlink": "eos-bancor",
"title": "eos源码解析(二) : bancor算法"
}
],
"op_in_trx": 0,
"timestamp": "2018-07-13T02:58:00",
"trx_id": "2a3b3c818e7c49828f2601f4b0016ed0e5177cce",
"trx_in_block": 20,
"virtual_op": 0
}2018/03/31 03:48:09
2018/03/31 03:48:09
| author | camphortree |
| body | @@ -52,16 +52,47 @@ .ini%0A2, + find %22rpc-endpoint%22 and set %22 rpc-endp @@ -112,16 +112,36 @@ 0.1:8090 +%22 then save the file %0A3, run |
| json metadata | {"tags":["witness-category"],"app":"steemit/0.1"} |
| parent author | noaommerrr |
| parent permlink | cliwallet-can-t-connect-to-steemd-on-the-same-machine |
| permlink | re-noaommerrr-cliwallet-can-t-connect-to-steemd-on-the-same-machine-20180331t034649111z |
| title | |
| Transaction Info | Block #21146148/Trx 7698a5a84916f7ca412f0fd2c1e379fe41d01047 |
View Raw JSON Data
{
"block": 21146148,
"op": [
"comment",
{
"author": "camphortree",
"body": "@@ -52,16 +52,47 @@\n .ini%0A2, \n+ find %22rpc-endpoint%22 and set %22\n rpc-endp\n@@ -112,16 +112,36 @@\n 0.1:8090\n+%22 then save the file\n %0A3, run\n",
"json_metadata": "{\"tags\":[\"witness-category\"],\"app\":\"steemit/0.1\"}",
"parent_author": "noaommerrr",
"parent_permlink": "cliwallet-can-t-connect-to-steemd-on-the-same-machine",
"permlink": "re-noaommerrr-cliwallet-can-t-connect-to-steemd-on-the-same-machine-20180331t034649111z",
"title": ""
}
],
"op_in_trx": 0,
"timestamp": "2018-03-31T03:48:09",
"trx_id": "7698a5a84916f7ca412f0fd2c1e379fe41d01047",
"trx_in_block": 54,
"virtual_op": 0
}2018/03/31 03:46:51
2018/03/31 03:46:51
| author | camphortree |
| body | 0, close steemd 1, vim witness_node_data_dir/config.ini 2, rpc-endpoint = 127.0.0.1:8090 3, run steemd |
| json metadata | {"tags":["witness-category"],"app":"steemit/0.1"} |
| parent author | noaommerrr |
| parent permlink | cliwallet-can-t-connect-to-steemd-on-the-same-machine |
| permlink | re-noaommerrr-cliwallet-can-t-connect-to-steemd-on-the-same-machine-20180331t034649111z |
| title | |
| Transaction Info | Block #21146122/Trx 83d42d73980b4a5887e644c2cde54fb076e5f321 |
View Raw JSON Data
{
"block": 21146122,
"op": [
"comment",
{
"author": "camphortree",
"body": "0, close steemd\n1, vim witness_node_data_dir/config.ini\n2, rpc-endpoint = 127.0.0.1:8090\n3, run steemd",
"json_metadata": "{\"tags\":[\"witness-category\"],\"app\":\"steemit/0.1\"}",
"parent_author": "noaommerrr",
"parent_permlink": "cliwallet-can-t-connect-to-steemd-on-the-same-machine",
"permlink": "re-noaommerrr-cliwallet-can-t-connect-to-steemd-on-the-same-machine-20180331t034649111z",
"title": ""
}
],
"op_in_trx": 0,
"timestamp": "2018-03-31T03:46:51",
"trx_id": "83d42d73980b4a5887e644c2cde54fb076e5f321",
"trx_in_block": 5,
"virtual_op": 0
}camphortreefollowed @abit2018/03/31 02:48:00
camphortreefollowed @abit
2018/03/31 02:48:00
| id | follow |
| json | ["follow",{"follower":"camphortree","following":"abit","what":["blog"]}] |
| required auths | [] |
| required posting auths | ["camphortree"] |
| Transaction Info | Block #21144946/Trx 7b99bd8f97fdba6d24bb8965d22ac0f4fdaf1331 |
View Raw JSON Data
{
"block": 21144946,
"op": [
"custom_json",
{
"id": "follow",
"json": "[\"follow\",{\"follower\":\"camphortree\",\"following\":\"abit\",\"what\":[\"blog\"]}]",
"required_auths": [],
"required_posting_auths": [
"camphortree"
]
}
],
"op_in_trx": 0,
"timestamp": "2018-03-31T02:48:00",
"trx_id": "7b99bd8f97fdba6d24bb8965d22ac0f4fdaf1331",
"trx_in_block": 13,
"virtual_op": 0
}camphortreeupvoted (100.00%) @unveiling / steemit2018/03/31 01:45:18
camphortreeupvoted (100.00%) @unveiling / steemit
2018/03/31 01:45:18
| author | unveiling |
| permlink | steemit |
| voter | camphortree |
| weight | 10000 (100.00%) |
| Transaction Info | Block #21143692/Trx 41c063b134e0598d64f744d75143d99c8d226b43 |
View Raw JSON Data
{
"block": 21143692,
"op": [
"vote",
{
"author": "unveiling",
"permlink": "steemit",
"voter": "camphortree",
"weight": 10000
}
],
"op_in_trx": 0,
"timestamp": "2018-03-31T01:45:18",
"trx_id": "41c063b134e0598d64f744d75143d99c8d226b43",
"trx_in_block": 59,
"virtual_op": 0
}camphortreeupdated their account properties2018/03/31 01:36:48
camphortreeupdated their account properties
2018/03/31 01:36:48
| account | camphortree |
| active | {"account_auths":[],"key_auths":[["STM6kLyQmiCPqhj9DfAinK3TYFjmWpRr5yV7Wao2YLbd4Ppu8WUAE",1]],"weight_threshold":1} |
| json metadata | |
| memo key | STM6LXXSQkbBUdKeFXrmeLo4WX3Y2mhjnvLuyryb3Kmb31CyY98Td |
| owner | {"account_auths":[],"key_auths":[["STM7pqGByFvQyjg4L4m9STH7MVbSa4GxvabUajyZ47deCjG9uMayD",1]],"weight_threshold":1} |
| posting | {"account_auths":[],"key_auths":[["STM8TvjNvtXNjCfdPuZi1PxtkdSJ1Wc19yUog2WUb2sLd7qfm7tDd",1]],"weight_threshold":1} |
| Transaction Info | Block #21143522/Trx 33f82cc6e13893dc44ce0ba94751770a56981d1e |
View Raw JSON Data
{
"block": 21143522,
"op": [
"account_update",
{
"account": "camphortree",
"active": {
"account_auths": [],
"key_auths": [
[
"STM6kLyQmiCPqhj9DfAinK3TYFjmWpRr5yV7Wao2YLbd4Ppu8WUAE",
1
]
],
"weight_threshold": 1
},
"json_metadata": "",
"memo_key": "STM6LXXSQkbBUdKeFXrmeLo4WX3Y2mhjnvLuyryb3Kmb31CyY98Td",
"owner": {
"account_auths": [],
"key_auths": [
[
"STM7pqGByFvQyjg4L4m9STH7MVbSa4GxvabUajyZ47deCjG9uMayD",
1
]
],
"weight_threshold": 1
},
"posting": {
"account_auths": [],
"key_auths": [
[
"STM8TvjNvtXNjCfdPuZi1PxtkdSJ1Wc19yUog2WUb2sLd7qfm7tDd",
1
]
],
"weight_threshold": 1
}
}
],
"op_in_trx": 0,
"timestamp": "2018-03-31T01:36:48",
"trx_id": "33f82cc6e13893dc44ce0ba94751770a56981d1e",
"trx_in_block": 70,
"virtual_op": 0
}camphortreeupvoted (100.00%) @robinwen / bts-steem-eos2018/03/31 01:07:24
camphortreeupvoted (100.00%) @robinwen / bts-steem-eos
2018/03/31 01:07:24
| author | robinwen |
| permlink | bts-steem-eos |
| voter | camphortree |
| weight | 10000 (100.00%) |
| Transaction Info | Block #21142934/Trx 73bd8c1abe47c12974eb4eb3924df67aff042728 |
View Raw JSON Data
{
"block": 21142934,
"op": [
"vote",
{
"author": "robinwen",
"permlink": "bts-steem-eos",
"voter": "camphortree",
"weight": 10000
}
],
"op_in_trx": 0,
"timestamp": "2018-03-31T01:07:24",
"trx_id": "73bd8c1abe47c12974eb4eb3924df67aff042728",
"trx_in_block": 27,
"virtual_op": 0
}cnsteemdelegated 0.000 SP to @camphortree2018/02/19 09:00:30
cnsteemdelegated 0.000 SP to @camphortree
2018/02/19 09:00:30
| delegatee | camphortree |
| delegator | cnsteem |
| vesting shares | 0.000000 VESTS |
| Transaction Info | Block #20002073/Trx 1db6af24b095605056c4482ca8d0a7d7188be5c1 |
View Raw JSON Data
{
"block": 20002073,
"op": [
"delegate_vesting_shares",
{
"delegatee": "camphortree",
"delegator": "cnsteem",
"vesting_shares": "0.000000 VESTS"
}
],
"op_in_trx": 0,
"timestamp": "2018-02-19T09:00:30",
"trx_id": "1db6af24b095605056c4482ca8d0a7d7188be5c1",
"trx_in_block": 6,
"virtual_op": 0
}cnsteemdelegated 2.515 SP to @camphortree2018/02/10 12:00:15
cnsteemdelegated 2.515 SP to @camphortree
2018/02/10 12:00:15
| delegatee | camphortree |
| delegator | cnsteem |
| vesting shares | 4090.065710 VESTS |
| Transaction Info | Block #19746653/Trx 330b5928c219a8210e5bac4f40b3654091f19cb1 |
View Raw JSON Data
{
"block": 19746653,
"op": [
"delegate_vesting_shares",
{
"delegatee": "camphortree",
"delegator": "cnsteem",
"vesting_shares": "4090.065710 VESTS"
}
],
"op_in_trx": 0,
"timestamp": "2018-02-10T12:00:15",
"trx_id": "330b5928c219a8210e5bac4f40b3654091f19cb1",
"trx_in_block": 16,
"virtual_op": 0
}camphortreeupvoted (100.00%) @legendx / dpos2018/02/10 06:50:36
camphortreeupvoted (100.00%) @legendx / dpos
2018/02/10 06:50:36
| author | legendx |
| permlink | dpos |
| voter | camphortree |
| weight | 10000 (100.00%) |
| Transaction Info | Block #19740463/Trx 7f7326351a2576df5413d5baf7f8a0bec0aee02d |
View Raw JSON Data
{
"block": 19740463,
"op": [
"vote",
{
"author": "legendx",
"permlink": "dpos",
"voter": "camphortree",
"weight": 10000
}
],
"op_in_trx": 0,
"timestamp": "2018-02-10T06:50:36",
"trx_id": "7f7326351a2576df5413d5baf7f8a0bec0aee02d",
"trx_in_block": 63,
"virtual_op": 0
}cnsteemcreated a new account: @camphortree2018/02/10 04:42:51
cnsteemcreated a new account: @camphortree
2018/02/10 04:42:51
| active | {"account_auths":[],"key_auths":[["STM6XYBebuWANQxqcRjgYMQ23AwACkknYZ3ta4pz2xjwKh1cCSCTK",1]],"weight_threshold":1} |
| creator | cnsteem |
| delegation | 28631.907938 VESTS |
| extensions | [] |
| fee | 0.200 STEEM |
| json metadata | |
| memo key | STM8KRH7g8PRsvzMDAQNFekgi24j14FoVMpUFFU7rgFWc3i3K8vfF |
| new account name | camphortree |
| owner | {"account_auths":[],"key_auths":[["STM6a5ssrpoSQR8UxCve1ZuokRB4GZdP8HoaseokRRTshqFUXF3M3",1]],"weight_threshold":1} |
| posting | {"account_auths":[],"key_auths":[["STM5LLLHEnARMcwHHkbm16L4LuE28Bx4BqT6hhUcYnzfHAdsqHWqZ",1]],"weight_threshold":1} |
| Transaction Info | Block #19737908/Trx faafc196680e74cd10ad483d5812ef8c6a757a82 |
View Raw JSON Data
{
"block": 19737908,
"op": [
"account_create_with_delegation",
{
"active": {
"account_auths": [],
"key_auths": [
[
"STM6XYBebuWANQxqcRjgYMQ23AwACkknYZ3ta4pz2xjwKh1cCSCTK",
1
]
],
"weight_threshold": 1
},
"creator": "cnsteem",
"delegation": "28631.907938 VESTS",
"extensions": [],
"fee": "0.200 STEEM",
"json_metadata": "",
"memo_key": "STM8KRH7g8PRsvzMDAQNFekgi24j14FoVMpUFFU7rgFWc3i3K8vfF",
"new_account_name": "camphortree",
"owner": {
"account_auths": [],
"key_auths": [
[
"STM6a5ssrpoSQR8UxCve1ZuokRB4GZdP8HoaseokRRTshqFUXF3M3",
1
]
],
"weight_threshold": 1
},
"posting": {
"account_auths": [],
"key_auths": [
[
"STM5LLLHEnARMcwHHkbm16L4LuE28Bx4BqT6hhUcYnzfHAdsqHWqZ",
1
]
],
"weight_threshold": 1
}
}
],
"op_in_trx": 0,
"timestamp": "2018-02-10T04:42:51",
"trx_id": "faafc196680e74cd10ad483d5812ef8c6a757a82",
"trx_in_block": 28,
"virtual_op": 0
}Manabar
Voting Power100.00%
Downvote Power100.00%
Resource Credits100.00%
Reputation Progress0.00%
{
"voting_manabar": {
"current_mana": 10000,
"last_update_time": 1518237771
},
"downvote_manabar": {
"current_mana": 0,
"last_update_time": 1518237771
},
"rc_account": {
"account": "camphortree",
"rc_manabar": {
"current_mana": 2429761942,
"last_update_time": 1537887600
},
"max_rc_creation_adjustment": {
"amount": "2020748973",
"precision": 6,
"nai": "@@000000037"
},
"max_rc": 2429761942
}
}Account Metadata
| POSTING JSON METADATA | |
| None | |
| JSON METADATA | |
| None |
{
"posting_json_metadata": {},
"json_metadata": {}
}Auth Keys
Owner
Single Signature
Public Keys
STM7pqGByFvQyjg4L4m9STH7MVbSa4GxvabUajyZ47deCjG9uMayD1/1
Active
Single Signature
Public Keys
STM6kLyQmiCPqhj9DfAinK3TYFjmWpRr5yV7Wao2YLbd4Ppu8WUAE1/1
Posting
Single Signature
Public Keys
STM8TvjNvtXNjCfdPuZi1PxtkdSJ1Wc19yUog2WUb2sLd7qfm7tDd1/1
Memo
STM6LXXSQkbBUdKeFXrmeLo4WX3Y2mhjnvLuyryb3Kmb31CyY98Td
{
"owner": {
"weight_threshold": 1,
"account_auths": [],
"key_auths": [
[
"STM7pqGByFvQyjg4L4m9STH7MVbSa4GxvabUajyZ47deCjG9uMayD",
1
]
]
},
"active": {
"weight_threshold": 1,
"account_auths": [],
"key_auths": [
[
"STM6kLyQmiCPqhj9DfAinK3TYFjmWpRr5yV7Wao2YLbd4Ppu8WUAE",
1
]
]
},
"posting": {
"weight_threshold": 1,
"account_auths": [],
"key_auths": [
[
"STM8TvjNvtXNjCfdPuZi1PxtkdSJ1Wc19yUog2WUb2sLd7qfm7tDd",
1
]
]
},
"memo": "STM6LXXSQkbBUdKeFXrmeLo4WX3Y2mhjnvLuyryb3Kmb31CyY98Td"
}Witness Votes
0 / 30
No active witness votes.
[]