Ecoer Logo
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
market_balance
0.000STEEM
savings_balance
0.000STEEM
reward_steem_balance
0.000STEEM
STEEM POWER
Own SP
0.251SP
Delegated Out
0.000SP
Delegation In
0.000SP
Effective Power
0.251SP
Reward SP (pending)
0.000SP
SBD
sbd_balance
0.000SBD
sbd_conversions
0.000SBD
sbd_market_balance
0.000SBD
savings_sbd_balance
0.000SBD
reward_sbd_balance
0.000SBD
{
  "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

namecamphortree
id742587
rank1,667,300
reputation8198947
created2018-02-10T04:42:51
recovery_accountcnsteem
proxyNone
post_count4
comment_count0
lifetime_vote_count0
witnesses_voted_for0
last_post2018-08-15T01:23:24
last_root_post2018-08-14T08:30:18
last_vote_time1970-01-01T00:00:00
proxied_vsf_votes0, 0, 0, 0
can_vote1
voting_power10,000
delayed_votes0
balance0.000 STEEM
savings_balance0.000 STEEM
sbd_balance0.000 SBD
savings_sbd_balance0.000 SBD
vesting_shares409.012969 VESTS
delegated_vesting_shares0.000000 VESTS
received_vesting_shares0.000000 VESTS
reward_vesting_balance0.000000 VESTS
vesting_balance0.000 STEEM
vesting_withdraw_rate0.000000 VESTS
next_vesting_withdrawal1969-12-31T23:59:59
withdrawn0
to_withdraw0
withdraw_routes0
savings_withdraw_requests0
last_account_recovery1970-01-01T00:00:00
reset_accountnull
last_owner_update2018-03-31T01:36:48
last_account_update2018-03-31T01:36:48
minedNo
sbd_seconds0
sbd_last_interest_payment1970-01-01T00:00:00
savings_sbd_last_interest_payment1970-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

IncomingOutgoing
Empty
Empty
{
  "incoming": [],
  "outgoing": []
}
From Date
To Date
2020/02/10 05:19:15
authorsteemitboard
bodyCongratulations @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 authorcamphortree
parent permlinkeos-dpos
permlinksteemitboard-notify-camphortree-20200210t051914000z
title
Transaction InfoBlock #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
authorsteemitboard
bodyCongratulations @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 authorcamphortree
parent permlinkeos-dpos
permlinksteemitboard-notify-camphortree-20190210t054735000z
title
Transaction InfoBlock #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
}
2018/09/18 06:35:48
authorcamphortree
permlinkre-camphortree-eos-dpos-20180815t012324317z
voterzhoufk
weight10000 (100.00%)
Transaction InfoBlock #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-dpos
2018/08/19 06:26:24
authorcamphortree
permlinkeos-dpos
voterdaykeneu
weight10000 (100.00%)
Transaction InfoBlock #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
authorcamphortree
body简书版本,格式较这个规范:https://www.jianshu.com/p/f32e50499cdc
json metadata{"tags":["eos"],"links":["https://www.jianshu.com/p/f32e50499cdc"],"app":"steemit/0.1"}
parent authorcamphortree
parent permlinkeos-dpos
permlinkre-camphortree-eos-dpos-20180815t012324317z
title
Transaction InfoBlock #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-dpos
2018/08/14 09:55:12
authorcamphortree
permlinkeos-dpos
votersensation
weight10000 (100.00%)
Transaction InfoBlock #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-dpos
2018/08/14 08:30:18
authorcamphortree
bodyeos的出块流程大致如下: 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 permlinkeos
permlinkeos-dpos
titleeos源码解析(三):dpos共识源码
Transaction InfoBlock #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-bancor
2018/07/13 02:58:09
authorcamphortree
permlinkeos-bancor
voterax3
weight100 (1.00%)
Transaction InfoBlock #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-bancor
2018/07/13 02:58:00
authorcamphortree
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单位 ![CodeCogsEqn.gif](https://upload-images.jianshu.io/upload_images/7298875-f629b3e3b43e1bd3.gif?imageMogr2/auto-orient/strip) 对应变量: ``` 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 ![](https://cdn.steemitimages.com/DQmZ7a6QRj6SzFWEqwbrLq5huwSwUuDRDdMW1sih6n1eGMv/image.png) 对应变量: ``` //这是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函数: ![](https://cdn.steemitimages.com/DQmWnaJBoZKtsXV2p4V7MWX2z1k5QVGxGikPvL6EnTuHPLA/image.png) 对应变量: ``` supply.amount : 10^14 in.amount : 1482443 quote.balance.amount : 10^10 结果E = 7410567 ``` 第4步,调用conver_from_exchange 将RAMCORE换算成RAM ![CodeCogsEqn (3).gif](https://upload-images.jianshu.io/upload_images/7298875-d7b4372bb6c355b2.gif?imageMogr2/auto-orient/strip) 对应变量: ``` base.banalce.amount : 64G base.banalce.amount -= 10185760 ``` 所以,最后,我们想买10240000byte的ram,结果只买了10185760byte的ram,其余的,扣了手续费。 实际上,卖出ram时,过程和前两步一样,不过在最后收取0.005的手续费。 将上文中,前两步的公式整合,化简,可以发现: ![image.png](https://upload-images.jianshu.io/upload_images/7298875-213756a564a55629.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 后两步的公式整合,化简可得到: ![image.png](https://upload-images.jianshu.io/upload_images/7298875-92e9a793d70257e5.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 呵呵!并没有什么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 permlinkeos
permlinkeos-bancor
titleeos源码解析(二) : bancor算法
Transaction InfoBlock #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![CodeCogsEqn.gif](https://upload-images.jianshu.io/upload_images/7298875-f629b3e3b43e1bd3.gif?imageMogr2/auto-orient/strip)\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![](https://cdn.steemitimages.com/DQmZ7a6QRj6SzFWEqwbrLq5huwSwUuDRDdMW1sih6n1eGMv/image.png)\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![](https://cdn.steemitimages.com/DQmWnaJBoZKtsXV2p4V7MWX2z1k5QVGxGikPvL6EnTuHPLA/image.png)\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![CodeCogsEqn (3).gif](https://upload-images.jianshu.io/upload_images/7298875-d7b4372bb6c355b2.gif?imageMogr2/auto-orient/strip)\n对应变量:\n```\nbase.banalce.amount : 64G\nbase.banalce.amount -= 10185760\n```\n所以,最后,我们想买10240000byte的ram,结果只买了10185760byte的ram,其余的,扣了手续费。\n实际上,卖出ram时,过程和前两步一样,不过在最后收取0.005的手续费。\n将上文中,前两步的公式整合,化简,可以发现:\n\n![image.png](https://upload-images.jianshu.io/upload_images/7298875-213756a564a55629.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)\n\n后两步的公式整合,化简可得到:\n\n![image.png](https://upload-images.jianshu.io/upload_images/7298875-92e9a793d70257e5.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)\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
authorcamphortree
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 authornoaommerrr
parent permlinkcliwallet-can-t-connect-to-steemd-on-the-same-machine
permlinkre-noaommerrr-cliwallet-can-t-connect-to-steemd-on-the-same-machine-20180331t034649111z
title
Transaction InfoBlock #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
authorcamphortree
body0, 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 authornoaommerrr
parent permlinkcliwallet-can-t-connect-to-steemd-on-the-same-machine
permlinkre-noaommerrr-cliwallet-can-t-connect-to-steemd-on-the-same-machine-20180331t034649111z
title
Transaction InfoBlock #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
}
2018/03/31 02:48:00
idfollow
json["follow",{"follower":"camphortree","following":"abit","what":["blog"]}]
required auths[]
required posting auths["camphortree"]
Transaction InfoBlock #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 / steemit
2018/03/31 01:45:18
authorunveiling
permlinksteemit
votercamphortree
weight10000 (100.00%)
Transaction InfoBlock #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 properties
2018/03/31 01:36:48
accountcamphortree
active{"account_auths":[],"key_auths":[["STM6kLyQmiCPqhj9DfAinK3TYFjmWpRr5yV7Wao2YLbd4Ppu8WUAE",1]],"weight_threshold":1}
json metadata
memo keySTM6LXXSQkbBUdKeFXrmeLo4WX3Y2mhjnvLuyryb3Kmb31CyY98Td
owner{"account_auths":[],"key_auths":[["STM7pqGByFvQyjg4L4m9STH7MVbSa4GxvabUajyZ47deCjG9uMayD",1]],"weight_threshold":1}
posting{"account_auths":[],"key_auths":[["STM8TvjNvtXNjCfdPuZi1PxtkdSJ1Wc19yUog2WUb2sLd7qfm7tDd",1]],"weight_threshold":1}
Transaction InfoBlock #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
}
2018/03/31 01:07:24
authorrobinwen
permlinkbts-steem-eos
votercamphortree
weight10000 (100.00%)
Transaction InfoBlock #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 @camphortree
2018/02/19 09:00:30
delegateecamphortree
delegatorcnsteem
vesting shares0.000000 VESTS
Transaction InfoBlock #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 @camphortree
2018/02/10 12:00:15
delegateecamphortree
delegatorcnsteem
vesting shares4090.065710 VESTS
Transaction InfoBlock #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 / dpos
2018/02/10 06:50:36
authorlegendx
permlinkdpos
votercamphortree
weight10000 (100.00%)
Transaction InfoBlock #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: @camphortree
2018/02/10 04:42:51
active{"account_auths":[],"key_auths":[["STM6XYBebuWANQxqcRjgYMQ23AwACkknYZ3ta4pz2xjwKh1cCSCTK",1]],"weight_threshold":1}
creatorcnsteem
delegation28631.907938 VESTS
extensions[]
fee0.200 STEEM
json metadata
memo keySTM8KRH7g8PRsvzMDAQNFekgi24j14FoVMpUFFU7rgFWc3i3K8vfF
new account namecamphortree
owner{"account_auths":[],"key_auths":[["STM6a5ssrpoSQR8UxCve1ZuokRB4GZdP8HoaseokRRTshqFUXF3M3",1]],"weight_threshold":1}
posting{"account_auths":[],"key_auths":[["STM5LLLHEnARMcwHHkbm16L4LuE28Bx4BqT6hhUcYnzfHAdsqHWqZ",1]],"weight_threshold":1}
Transaction InfoBlock #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
}

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.
[]