Below, `parent` refers to the `block_state` of the parent block from which a new block is being constructed. ## dpos data currently in controller.cpp, we have the `building_block` whose members are: ```c++ struct building_block { pending_block_header_state _pending_block_header_state; // IF: Remove from building_block. See below for replacements. std::optional<producer_authority_schedule> _new_pending_producer_schedule; // IF: Replaced by new_proposal_policy. vector<digest_type> _new_protocol_feature_activations; // IF: Comes from building_block_input::new_protocol_feature_activations size_t _num_new_protocol_features_that_have_activated = 0; // Stays only in building_block deque<transaction_metadata_ptr> _pending_trx_metas; // Moved from building_block to assembled_block deque<transaction_receipt> _pending_trx_receipts; // Moved from building_block to the transactions in the constructed block std::variant<checksum256_type, digests_t> _trx_mroot_or_receipt_digests; // IF: Extract computed trx mroot to assembled_block_input::transaction_mroot digests_t _action_receipt_digests; // IF: Extract computed action mroot to assembled_block_input::action_mroot }; ``` the `assembled_block`: ```c++ struct assembled_block { block_id_type _id; // Cache of _unsigned_block->calculate_id(). pending_block_header_state _pending_block_header_state; // IF: Remove from assembled_block. See below for replacements. deque<transaction_metadata_ptr> _trx_metas; // Comes from building_block::_pending_trx_metas // Carried over to put into block_state (optimization for fork reorgs) signed_block_ptr _unsigned_block; // IF: keep same member // if the _unsigned_block pre-dates block-signing authorities this may be present. std::optional<producer_authority_schedule> _new_producer_authority_cache; // IF: Remove from assembled_block // pending_producers() not needed in IF. proposed_proposers() sufficient. }; ``` and the `pending_block_header_state`: ```c++ struct block_header_state_legacy_common { uint32_t block_num = 0; // IF: block_header::num_from_id(parent_id) + 1 uint32_t dpos_proposed_irreversible_blocknum = 0; // Unneeded for IF uint32_t dpos_irreversible_blocknum = 0; // Unneeded during the building block stage for IF producer_authority_schedule active_schedule; // IF: Replaced by active_proposer_policy stored in building_block. incremental_merkle blockroot_merkle; // Unneeded during the building block stage for IF flat_map<account_name,uint32_t> producer_to_last_produced; // Unneeded for IF flat_map<account_name,uint32_t> producer_to_last_implied_irb; // Unneeded for IF block_signing_authority valid_block_signing_authority; // IF: Get from within active_proposer_policy for building_block.producer. vector<uint8_t> confirm_count; // Unneeded for IF }; struct pending_block_header_state : public detail::block_header_state_legacy_common { protocol_feature_activation_set_ptr prev_activated_protocol_features; // IF: building_block.prev_activated_protocol_features detail::schedule_info prev_pending_schedule; // Unneeded for IF bool was_pending_promoted = false; // Unneeded for IF block_id_type previous; // Not needed but present anyway at building_block.parent_id account_name producer; // IF: building_block.producer block_timestamp_type timestamp; // IF: building_block.timestamp uint32_t active_schedule_version = 0; // Unneeded for IF uint16_t confirmed = 1; // Unneeded for IF }; ``` and all this lives in `pending_state` which I believe can stay unchanged. ## IF data The new storage for IF is: ```c++ struct block_header_state_core { uint32_t last_final_block_num = 0; // last irreversible (final) block. std::optional<uint32_t> final_on_strong_qc_block_num; // will become final if this header achives a strong QC. std::optional<uint32_t> last_qc_block_num; // uint32_t finalizer_policy_generation; block_header_state_core next(uint32_t last_qc_block_num, bool is_last_qc_strong) const; }; struct quorum_certificate { uint32_t block_num; valid_quorum_certificate qc; }; struct block_header_state { block_header header; protocol_feature_activation_set_ptr activated_protocol_features; block_header_state_core core; incremental_merkle_tree proposal_mtree; incremental_merkle_tree finality_mtree; finalizer_policy_ptr active_finalizer_policy; // finalizer set + threshold + generation, supports `digest()` proposer_policy_ptr active_proposer_policy; // producer authority schedule, supports `digest()` flat_map<block_timestamp_type, proposer_policy_ptr> proposer_policies; flat_map<uint32_t, finalizer_policy_ptr> finalizer_policies; digest_type compute_finalizer_digest() const; proposer_policy_ptr get_next_active_proposer_policy(block_timestamp_type next_timestamp) const { // Find latest proposer policy within proposer_policies that has an active_time <= next_timestamp. // If found, return the proposer policy that was found. // Otherwise, return active_proposer_policy. } block_timestamp_type timestamp() const { return header.timestamp; } account_name producer() const { return header.producer; } block_id_type previous() const { return header.previous; } uint32_t block_num() const { return block_header::num_from_id(previous()) + 1; } // block descending from this need the provided qc in the block extension bool is_needed(const quorum_certificate& qc) const { return !core.last_qc_block_num || qc.block_num > *core.last_qc_block_num; } block_header_state next(const block_header_state_input& data) const; }; struct block_state { const block_header_state bhs; const signed_block_ptr block; const block_id_type id; // cache of bhs.header.calculate_id() (indexed on this field) const digest_type finalizer_digest; // cache of bhs.compute_finalizer_digest() std::optional<pending_quorum_certificate> pending_qc; std::optional<valid_quorum_certificate> valid_qc; std::optional<quorum_certificate> get_best_qc() const { // If pending_qc does not have a valid QC, return valid_qc. // Otherwise, extract the valid QC from *pending_qc. // Compare that to valid_qc to determine which is better: Strong beats Weak. Break tie with highest accumulated weight. // Return the better one. } uint64_t block_num() const { return block_header::num_from_id(id); } }; ``` In addition, in IF `pending_state._block_stage` will still contain the three stages: `building_block`, `assembled_block`, and `completed_block`. 1. `building_block`: ```c++ struct building_block { const block_id_type parent_id; // Comes from building_block_input::parent_id const block_timestamp_type timestamp; // Comes from building_block_input::timestamp const account_name producer; // Comes from building_block_input::producer const vector<digest_type> new_protocol_feature_activations; // Comes from building_block_input::new_protocol_feature_activations const protocol_feature_activation_set_ptr prev_activated_protocol_features; // Cached: parent.bhs.activated_protocol_features const proposer_policy_ptr active_proposer_policy; // Cached: parent.bhs.get_next_active_proposer_policy(timestamp) // Members below start from initial state and are mutated as the block is built. size_t num_new_protocol_features_that_have_activated = 0; std::optional<proposer_policy> new_proposer_policy; std::optional<finalizer_policy> new_finalizer_policy; deque<transaction_metadata_ptr> pending_trx_metas; deque<transaction_receipt> pending_trx_receipts; std::variant<checksum256_type, digests_t> trx_mroot_or_receipt_digests; digests_t action_receipt_digests; }; ``` ``` struct building_block { pending_block_header_state _pending_block_header_state; // IF: Remove from building_block. See below for replacements. std::optional<producer_authority_schedule> _new_pending_producer_schedule; // IF: Replaced by new_proposal_policy. vector<digest_type> _new_protocol_feature_activations; // IF: Comes from building_block_input::new_protocol_feature_activations size_t _num_new_protocol_features_that_have_activated = 0; // Stays only in building_block deque<transaction_metadata_ptr> _pending_trx_metas; // Moved from building_block to assembled_block deque<transaction_receipt> _pending_trx_receipts; // Moved from building_block to the transactions in the constructed block std::variant<checksum256_type, digests_t> _trx_mroot_or_receipt_digests; // IF: Extract computed trx mroot to assembled_block_input::transaction_mroot digests_t _action_receipt_digests; // IF: Extract computed action mroot to assembled_block_input::action_mroot }; ``` which is constructed from: ```c++ struct building_block_input { block_id_type parent_id; block_timestamp_type timestamp; account_name producer; vector<digest_type> new_protocol_feature_activations; }; ``` When done with building the block, from `building_block` we can extract: ```c++ struct block_header_state_input : public building_block_input { digest_type transaction_mroot; // Comes from std::get<checksum256_type>(building_block::trx_mroot_or_receipt_digests) digest_type action_mroot; // Compute root from building_block::action_receipt_digests std::optional<proposer_policy> new_proposer_policy; // Comes from building_block::new_proposer_policy std::optional<finalizer_policy> new_finalizer_policy; // Comes from building_block::new_finalizer_policy std::optional<quorum_certificate> qc; // Comes from traversing branch from parent and calling get_best_qc() // assert(qc->block_num <= num_from_id(previous)); // ... ? }; ``` which is the input needed to `block_header_state::next` to compute the new block header state. 2. `assembled_block`: ```c++ struct assembled_block { block_header_state new_block_header_state; deque<transaction_metadata_ptr> trx_metas; // Comes from building_block::pending_trx_metas // Carried over to put into block_state (optimization for fork reorgs) deque<transaction_receipt> trx_receipts; // Comes from building_block::pending_trx_receipts std::optional<quorum_certificate> qc; // QC to add as block extension to new block }; ``` which is constructed from `building_block` and `parent.bhs`. 3. `completed_block`: ```c++ struct completed_block { block_state_ptr block_state; }; ``` which is constructed from `assembled_block` and a block header signature provider.