block - [mainnet]
This module defines a struct storing the metadata of the block and new block events.
use 0x1::account;use 0x1::error;use 0x1::event;use 0x1::features;use 0x1::option;use 0x1::randomness;use 0x1::reconfiguration;use 0x1::reconfiguration_with_dkg;use 0x1::stake;use 0x1::state_storage;use 0x1::system_addresses;use 0x1::table_with_length;use 0x1::timestamp;
Constants
const MAX_U64: u64 = 18446744073709551615;
An invalid proposer was provided. Expected the proposer to be the VM or an active validator.
const EINVALID_PROPOSER: u64 = 2;
The number of new block events does not equal the current block height.
const ENUM_NEW_BLOCK_EVENTS_DOES_NOT_MATCH_BLOCK_HEIGHT: u64 = 1;
Epoch interval cannot be 0.
const EZERO_EPOCH_INTERVAL: u64 = 3;
The maximum capacity of the commit history cannot be 0.
const EZERO_MAX_CAPACITY: u64 = 3;
Structs
NewBlockEvent
Should be in-sync with NewBlockEvent rust struct in new_block.rs
struct NewBlockEvent has copy, drop, store
Fields
UpdateEpochIntervalEvent
Event emitted when a proposal is created.
struct UpdateEpochIntervalEvent has drop, store
Fields
-
old_epoch_interval: u64
-
new_epoch_interval: u64
NewBlock
Should be in-sync with NewBlockEvent rust struct in new_block.rs
#[event]struct NewBlock has drop, store
Fields
UpdateEpochInterval
Event emitted when a proposal is created.
#[event]struct UpdateEpochInterval has drop, store
Fields
-
old_epoch_interval: u64
-
new_epoch_interval: u64
Resources
BlockResource
Should be in-sync with BlockResource rust struct in new_block.rs
struct BlockResource has key
Fields
-
height: u64
- Height of the current block
-
epoch_interval: u64
- Time period between epochs.
-
new_block_events: event::EventHandle<block::NewBlockEvent>
- Handle where events with the time of new blocks are emitted
-
update_epoch_interval_events: event::EventHandle<block::UpdateEpochIntervalEvent>
CommitHistory
Store new block events as a move resource, internally using a circular buffer.
struct CommitHistory has key
Fields
-
max_capacity: u32
-
next_idx: u32
-
table: table_with_length::TableWithLength<u32, block::NewBlockEvent>
Functions
initialize
This can only be called during Genesis.
public(friend) fun initialize(aptos_framework: &signer, epoch_interval_microsecs: u64)
Implementation
public(friend) fun initialize(aptos_framework: &signer, epoch_interval_microsecs: u64) { system_addresses::assert_aptos_framework(aptos_framework); assert!(epoch_interval_microsecs > 0, error::invalid_argument(EZERO_EPOCH_INTERVAL));
move_to<CommitHistory>(aptos_framework, CommitHistory { max_capacity: 2000, next_idx: 0, table: table_with_length::new(), });
move_to<BlockResource>( aptos_framework, BlockResource { height: 0, epoch_interval: epoch_interval_microsecs, new_block_events: account::new_event_handle<NewBlockEvent>(aptos_framework), update_epoch_interval_events: account::new_event_handle<UpdateEpochIntervalEvent>(aptos_framework), } );}
initialize_commit_history
Initialize the commit history resource if it’s not in genesis.
public fun initialize_commit_history(fx: &signer, max_capacity: u32)
Implementation
public fun initialize_commit_history(fx: &signer, max_capacity: u32) { assert!(max_capacity > 0, error::invalid_argument(EZERO_MAX_CAPACITY)); move_to<CommitHistory>(fx, CommitHistory { max_capacity, next_idx: 0, table: table_with_length::new(), });}
update_epoch_interval_microsecs
Update the epoch interval. Can only be called as part of the Aptos governance proposal process established by the AptosGovernance module.
public fun update_epoch_interval_microsecs(aptos_framework: &signer, new_epoch_interval: u64)
Implementation
public fun update_epoch_interval_microsecs( aptos_framework: &signer, new_epoch_interval: u64,) acquires BlockResource { system_addresses::assert_aptos_framework(aptos_framework); assert!(new_epoch_interval > 0, error::invalid_argument(EZERO_EPOCH_INTERVAL));
let block_resource = borrow_global_mut<BlockResource>(@aptos_framework); let old_epoch_interval = block_resource.epoch_interval; block_resource.epoch_interval = new_epoch_interval;
if (std::features::module_event_migration_enabled()) { event::emit( UpdateEpochInterval { old_epoch_interval, new_epoch_interval }, ); } else { event::emit_event<UpdateEpochIntervalEvent>( &mut block_resource.update_epoch_interval_events, UpdateEpochIntervalEvent { old_epoch_interval, new_epoch_interval }, ); };}
get_epoch_interval_secs
Return epoch interval in seconds.
#[view]public fun get_epoch_interval_secs(): u64
Implementation
public fun get_epoch_interval_secs(): u64 acquires BlockResource { borrow_global<BlockResource>(@aptos_framework).epoch_interval / 1000000}
block_prologue_common
fun block_prologue_common(vm: &signer, hash: address, epoch: u64, round: u64, proposer: address, failed_proposer_indices: vector<u64>, previous_block_votes_bitvec: vector<u8>, timestamp: u64): u64
Implementation
fun block_prologue_common( vm: &signer, hash: address, epoch: u64, round: u64, proposer: address, failed_proposer_indices: vector<u64>, previous_block_votes_bitvec: vector<u8>, timestamp: u64): u64 acquires BlockResource, CommitHistory { // Operational constraint: can only be invoked by the VM. system_addresses::assert_vm(vm);
// Blocks can only be produced by a valid proposer or by the VM itself for Nil blocks (no user txs). assert!( proposer == @vm_reserved || stake::is_current_epoch_validator(proposer), error::permission_denied(EINVALID_PROPOSER), );
let proposer_index = option::none(); if (proposer != @vm_reserved) { proposer_index = option::some(stake::get_validator_index(proposer)); };
let block_metadata_ref = borrow_global_mut<BlockResource>(@aptos_framework); block_metadata_ref.height = event::counter(&block_metadata_ref.new_block_events);
let new_block_event = NewBlockEvent { hash, epoch, round, height: block_metadata_ref.height, previous_block_votes_bitvec, proposer, failed_proposer_indices, time_microseconds: timestamp, }; emit_new_block_event(vm, &mut block_metadata_ref.new_block_events, new_block_event);
// Performance scores have to be updated before the epoch transition as the transaction that triggers the // transition is the last block in the previous epoch. stake::update_performance_statistics(proposer_index, failed_proposer_indices); state_storage::on_new_block(reconfiguration::current_epoch());
block_metadata_ref.epoch_interval}
block_prologue
Set the metadata for the current block. The runtime always runs this before executing the transactions in a block.
fun block_prologue(vm: signer, hash: address, epoch: u64, round: u64, proposer: address, failed_proposer_indices: vector<u64>, previous_block_votes_bitvec: vector<u8>, timestamp: u64)
Implementation
fun block_prologue( vm: signer, hash: address, epoch: u64, round: u64, proposer: address, failed_proposer_indices: vector<u64>, previous_block_votes_bitvec: vector<u8>, timestamp: u64) acquires BlockResource, CommitHistory { let epoch_interval = block_prologue_common(&vm, hash, epoch, round, proposer, failed_proposer_indices, previous_block_votes_bitvec, timestamp); randomness::on_new_block(&vm, epoch, round, option::none()); if (timestamp - reconfiguration::last_reconfiguration_time() >= epoch_interval) { reconfiguration::reconfigure(); };}
block_prologue_ext
block_prologue()
but trigger reconfiguration with DKG after epoch timed out.
fun block_prologue_ext(vm: signer, hash: address, epoch: u64, round: u64, proposer: address, failed_proposer_indices: vector<u64>, previous_block_votes_bitvec: vector<u8>, timestamp: u64, randomness_seed: option::Option<vector<u8>>)
Implementation
fun block_prologue_ext( vm: signer, hash: address, epoch: u64, round: u64, proposer: address, failed_proposer_indices: vector<u64>, previous_block_votes_bitvec: vector<u8>, timestamp: u64, randomness_seed: Option<vector<u8>>,) acquires BlockResource, CommitHistory { let epoch_interval = block_prologue_common( &vm, hash, epoch, round, proposer, failed_proposer_indices, previous_block_votes_bitvec, timestamp ); randomness::on_new_block(&vm, epoch, round, randomness_seed);
if (timestamp - reconfiguration::last_reconfiguration_time() >= epoch_interval) { reconfiguration_with_dkg::try_start(); };}
get_current_block_height
Get the current block height
#[view]public fun get_current_block_height(): u64
Implementation
public fun get_current_block_height(): u64 acquires BlockResource { borrow_global<BlockResource>(@aptos_framework).height}
emit_new_block_event
Emit the event and update height and global timestamp
fun emit_new_block_event(vm: &signer, event_handle: &mut event::EventHandle<block::NewBlockEvent>, new_block_event: block::NewBlockEvent)
Implementation
fun emit_new_block_event( vm: &signer, event_handle: &mut EventHandle<NewBlockEvent>, new_block_event: NewBlockEvent,) acquires CommitHistory { if (exists<CommitHistory>(@aptos_framework)) { let commit_history_ref = borrow_global_mut<CommitHistory>(@aptos_framework); let idx = commit_history_ref.next_idx; if (table_with_length::contains(&commit_history_ref.table, idx)) { table_with_length::remove(&mut commit_history_ref.table, idx); }; table_with_length::add(&mut commit_history_ref.table, idx, copy new_block_event); spec { assume idx + 1 <= MAX_U32; }; commit_history_ref.next_idx = (idx + 1) % commit_history_ref.max_capacity; }; timestamp::update_global_time(vm, new_block_event.proposer, new_block_event.time_microseconds); assert!( event::counter(event_handle) == new_block_event.height, error::invalid_argument(ENUM_NEW_BLOCK_EVENTS_DOES_NOT_MATCH_BLOCK_HEIGHT), ); event::emit_event<NewBlockEvent>(event_handle, new_block_event);}
emit_genesis_block_event
Emit a NewBlockEvent
event. This function will be invoked by genesis directly to generate the very first
reconfiguration event.
fun emit_genesis_block_event(vm: signer)
Implementation
fun emit_genesis_block_event(vm: signer) acquires BlockResource, CommitHistory { let block_metadata_ref = borrow_global_mut<BlockResource>(@aptos_framework); let genesis_id = @0x0; emit_new_block_event( &vm, &mut block_metadata_ref.new_block_events, NewBlockEvent { hash: genesis_id, epoch: 0, round: 0, height: 0, previous_block_votes_bitvec: vector::empty(), proposer: @vm_reserved, failed_proposer_indices: vector::empty(), time_microseconds: 0, }, );}
emit_writeset_block_event
Emit a NewBlockEvent
event. This function will be invoked by write set script directly to generate the
new block event for WriteSetPayload.
public fun emit_writeset_block_event(vm_signer: &signer, fake_block_hash: address)
Implementation
public fun emit_writeset_block_event(vm_signer: &signer, fake_block_hash: address) acquires BlockResource, CommitHistory { system_addresses::assert_vm(vm_signer); let block_metadata_ref = borrow_global_mut<BlockResource>(@aptos_framework); block_metadata_ref.height = event::counter(&block_metadata_ref.new_block_events);
emit_new_block_event( vm_signer, &mut block_metadata_ref.new_block_events, NewBlockEvent { hash: fake_block_hash, epoch: reconfiguration::current_epoch(), round: MAX_U64, height: block_metadata_ref.height, previous_block_votes_bitvec: vector::empty(), proposer: @vm_reserved, failed_proposer_indices: vector::empty(), time_microseconds: timestamp::now_microseconds(), }, );}
Specification
High-level Requirements
No. | Requirement | Criticality | Implementation | Enforcement |
---|---|---|---|---|
1 | During the module's initialization, it guarantees that the BlockResource resource moves under the Aptos framework account with initial values. | High | The initialize function is responsible for setting up the initial state of the module, ensuring that the following conditions are met (1) the BlockResource resource is created, indicating its existence within the module's context, and moved under the Aptos framework account, (2) the block height is set to zero during initialization, and (3) the epoch interval is greater than zero. | Formally Verified via Initialize. |
2 | Only the Aptos framework address may execute the following functionalities: (1) initialize BlockResource, and (2) update the epoch interval. | Critical | The initialize and update_epoch_interval_microsecs functions ensure that only aptos_framework can call them. | Formally Verified via Initialize and update_epoch_interval_microsecs. |
3 | When updating the epoch interval, its value must be greater than zero and BlockResource must exist. | High | The update_epoch_interval_microsecs function asserts that new_epoch_interval is greater than zero and updates BlockResource's state. | Formally verified via UpdateEpochIntervalMicrosecs and epoch_interval. |
4 | Only a valid proposer or the virtual machine is authorized to produce blocks. | Critical | During the execution of the block_prologue function, the validity of the proposer address is verified when setting the metadata for the current block. | Formally Verified via block_prologue. |
5 | While emitting a new block event, the number of them is equal to the current block height. | Medium | The emit_new_block_event function asserts that the number of new block events equals the current block height. | Formally Verified via emit_new_block_event. |
Module-level Specification
pragma verify = false;invariant [suspendable] chain_status::is_operating() ==> exists<BlockResource>(@aptos_framework);invariant [suspendable] chain_status::is_operating() ==> exists<CommitHistory>(@aptos_framework);
BlockResource
struct BlockResource has key
-
height: u64
- Height of the current block
-
epoch_interval: u64
- Time period between epochs.
-
new_block_events: event::EventHandle<block::NewBlockEvent>
- Handle where events with the time of new blocks are emitted
-
update_epoch_interval_events: event::EventHandle<block::UpdateEpochIntervalEvent>
// This enforces high-level requirement 3:invariant epoch_interval > 0;
CommitHistory
struct CommitHistory has key
-
max_capacity: u32
-
next_idx: u32
-
table: table_with_length::TableWithLength<u32, block::NewBlockEvent>
invariant max_capacity > 0;
initialize
public(friend) fun initialize(aptos_framework: &signer, epoch_interval_microsecs: u64)
The caller is aptos_framework. The new_epoch_interval must be greater than 0. The BlockResource is not under the caller before initializing. The Account is not under the caller until the BlockResource is created for the caller. Make sure The BlockResource under the caller existed after initializing. The number of new events created does not exceed MAX_U64.
// This enforces high-level requirement 1:include Initialize;include NewEventHandle;let addr = signer::address_of(aptos_framework);let account = global<account::Account>(addr);aborts_if account.guid_creation_num + 2 >= account::MAX_GUID_CREATION_NUM;
schema BlockRequirement { vm: signer; hash: address; epoch: u64; round: u64; proposer: address; failed_proposer_indices: vector<u64>; previous_block_votes_bitvec: vector<u8>; timestamp: u64; requires chain_status::is_operating(); requires system_addresses::is_vm(vm); // This enforces high-level requirement 4: requires proposer == @vm_reserved || stake::spec_is_current_epoch_validator(proposer); requires (proposer == @vm_reserved) ==> (timestamp::spec_now_microseconds() == timestamp); requires (proposer != @vm_reserved) ==> (timestamp::spec_now_microseconds() < timestamp); requires exists<CoinInfo<AptosCoin>>(@aptos_framework); include staking_config::StakingRewardsConfigRequirement;}
schema Initialize { aptos_framework: signer; epoch_interval_microsecs: u64; let addr = signer::address_of(aptos_framework); // This enforces high-level requirement 2: aborts_if addr != @aptos_framework; aborts_if epoch_interval_microsecs == 0; aborts_if exists<BlockResource>(addr); aborts_if exists<CommitHistory>(addr); ensures exists<BlockResource>(addr); ensures exists<CommitHistory>(addr); ensures global<BlockResource>(addr).height == 0;}
schema NewEventHandle { aptos_framework: signer; let addr = signer::address_of(aptos_framework); let account = global<account::Account>(addr); aborts_if !exists<account::Account>(addr); aborts_if account.guid_creation_num + 2 > MAX_U64;}
update_epoch_interval_microsecs
public fun update_epoch_interval_microsecs(aptos_framework: &signer, new_epoch_interval: u64)
The caller is @aptos_framework. The new_epoch_interval must be greater than 0. The BlockResource existed under the @aptos_framework.
// This enforces high-level requirement 3:include UpdateEpochIntervalMicrosecs;
schema UpdateEpochIntervalMicrosecs { aptos_framework: signer; new_epoch_interval: u64; let addr = signer::address_of(aptos_framework); // This enforces high-level requirement 2: aborts_if addr != @aptos_framework; aborts_if new_epoch_interval == 0; aborts_if !exists<BlockResource>(addr); let post block_resource = global<BlockResource>(addr); ensures block_resource.epoch_interval == new_epoch_interval;}
get_epoch_interval_secs
#[view]public fun get_epoch_interval_secs(): u64
aborts_if !exists<BlockResource>(@aptos_framework);
block_prologue_common
fun block_prologue_common(vm: &signer, hash: address, epoch: u64, round: u64, proposer: address, failed_proposer_indices: vector<u64>, previous_block_votes_bitvec: vector<u8>, timestamp: u64): u64
pragma verify_duration_estimate = 1000;include BlockRequirement;aborts_if false;
block_prologue
fun block_prologue(vm: signer, hash: address, epoch: u64, round: u64, proposer: address, failed_proposer_indices: vector<u64>, previous_block_votes_bitvec: vector<u8>, timestamp: u64)
pragma verify_duration_estimate = 1000;requires timestamp >= reconfiguration::last_reconfiguration_time();include BlockRequirement;aborts_if false;
block_prologue_ext
fun block_prologue_ext(vm: signer, hash: address, epoch: u64, round: u64, proposer: address, failed_proposer_indices: vector<u64>, previous_block_votes_bitvec: vector<u8>, timestamp: u64, randomness_seed: option::Option<vector<u8>>)
pragma verify_duration_estimate = 1000;requires timestamp >= reconfiguration::last_reconfiguration_time();include BlockRequirement;include stake::ResourceRequirement;include stake::GetReconfigStartTimeRequirement;aborts_if false;
get_current_block_height
#[view]public fun get_current_block_height(): u64
aborts_if !exists<BlockResource>(@aptos_framework);
emit_new_block_event
fun emit_new_block_event(vm: &signer, event_handle: &mut event::EventHandle<block::NewBlockEvent>, new_block_event: block::NewBlockEvent)
let proposer = new_block_event.proposer;let timestamp = new_block_event.time_microseconds;requires chain_status::is_operating();requires system_addresses::is_vm(vm);requires (proposer == @vm_reserved) ==> (timestamp::spec_now_microseconds() == timestamp);requires (proposer != @vm_reserved) ==> (timestamp::spec_now_microseconds() < timestamp);// This enforces high-level requirement 5:requires event::counter(event_handle) == new_block_event.height;aborts_if false;
emit_genesis_block_event
fun emit_genesis_block_event(vm: signer)
requires chain_status::is_operating();requires system_addresses::is_vm(vm);requires event::counter(global<BlockResource>(@aptos_framework).new_block_events) == 0;requires (timestamp::spec_now_microseconds() == 0);aborts_if false;
emit_writeset_block_event
public fun emit_writeset_block_event(vm_signer: &signer, fake_block_hash: address)
The caller is @vm_reserved. The BlockResource existed under the @aptos_framework. The Configuration existed under the @aptos_framework. The CurrentTimeMicroseconds existed under the @aptos_framework.
requires chain_status::is_operating();include EmitWritesetBlockEvent;
schema EmitWritesetBlockEvent { vm_signer: signer; let addr = signer::address_of(vm_signer); aborts_if addr != @vm_reserved; aborts_if !exists<BlockResource>(@aptos_framework); aborts_if !exists<reconfiguration::Configuration>(@aptos_framework); aborts_if !exists<timestamp::CurrentTimeMicroseconds>(@aptos_framework);}