Skip to content

Commit

Permalink
Make max supported version dependent on network
Browse files Browse the repository at this point in the history
Implement upgrade test
Add block tests
Add accounts tree tests and fix block number
Test creation of inherent
  • Loading branch information
paberr committed Nov 19, 2024
1 parent 477cfbd commit 42076ca
Show file tree
Hide file tree
Showing 18 changed files with 709 additions and 52 deletions.
5 changes: 0 additions & 5 deletions blockchain/src/blockchain/inherents.rs
Original file line number Diff line number Diff line change
Expand Up @@ -326,11 +326,6 @@ impl Blockchain {
let mut inherents = vec![Inherent::FinalizeEpoch];
// Add version upgrade inherent if needed.
if version != self.state.current_version() {
assert_eq!(
version,
self.state.current_version(),
"Version should only be upgraded in steps of one."
);
inherents.push(Inherent::VersionUpgrade {
new_version: version,
})
Expand Down
116 changes: 116 additions & 0 deletions blockchain/tests/inherents.rs
Original file line number Diff line number Diff line change
Expand Up @@ -699,3 +699,119 @@ async fn create_fork_proof() {
// Verify that the fork proof was generated
assert!(fork_rx.next().await.is_some());
}

#[test]
fn it_can_create_version_upgrade_inherents() {
let time = Arc::new(OffsetTime::new());
let env = MdbxDatabase::new_volatile(Default::default()).unwrap();
let blockchain = Arc::new(
Blockchain::new(
env,
BlockchainConfig::default(),
NetworkId::UnitAlbatross,
time,
)
.unwrap(),
);

let block_number = Policy::election_block_after(Policy::genesis_block_number());

let staking_contract = blockchain.get_staking_contract();
let active_validators = staking_contract.active_validators.clone();
let next_batch_initial_punished_set = staking_contract
.punished_slots
.next_batch_initial_punished_set(block_number, &active_validators);

let mut macro_header = MacroHeader {
network: NetworkId::UnitAlbatross,
version: 2,
block_number,
round: 0,
timestamp: blockchain.state.election_head.header.timestamp + 20000,
parent_hash: Blake2bHash::default(),
parent_election_hash: Blake2bHash::default(),
interlink: None,
seed: VrfSeed::default(),
extra_data: vec![],
state_root: Blake2bHash::default(),
body_root: Blake2sHash::default(),
diff_root: Blake2bHash::default(),
history_root: Blake2bHash::default(),
validators: None,
next_batch_initial_punished_set,
..Default::default()
};

let reward_transactions =
blockchain.create_reward_transactions(&macro_header, &staking_contract);

let body = MacroBody {
transactions: reward_transactions,
};

let macro_block = MacroBlock {
header: macro_header.clone(),
body: Some(body.clone()),
justification: None,
};

// Simple case. Expect 1x FinalizeBatch, 1x FinalizeEpoch, 1x Reward to validator, 2x Version Upgrade
let inherents = blockchain.create_macro_block_inherents(&macro_block);
assert_eq!(inherents.len(), 4);

let mut got_reward = false;
let mut got_finalize_batch = false;
let mut got_finalize_epoch = false;
let mut got_version_upgrade = false;
for inherent in &inherents {
match inherent {
Inherent::Reward { value, .. } => {
got_reward = true;
}
Inherent::FinalizeBatch => {
got_finalize_batch = true;
}
Inherent::FinalizeEpoch => {
got_finalize_epoch = true;
}
Inherent::VersionUpgrade { new_version } => {
got_version_upgrade = true;
assert_eq!(*new_version, 2);
}
_ => panic!(),
}
}
assert!(got_reward && got_finalize_batch && got_finalize_epoch && got_version_upgrade);

// Downgrading version will remove version upgrade.
macro_header.version = 1;

let macro_block = MacroBlock {
header: macro_header.clone(),
body: Some(body),
justification: None,
};

// Simple case. Expect 1x FinalizeBatch, 1x FinalizeEpoch, 1x Reward to validator, 2x Version Upgrade
let inherents = blockchain.create_macro_block_inherents(&macro_block);
assert_eq!(inherents.len(), 3);

let mut got_reward = false;
let mut got_finalize_batch = false;
let mut got_finalize_epoch = false;
for inherent in &inherents {
match inherent {
Inherent::Reward { value, .. } => {
got_reward = true;
}
Inherent::FinalizeBatch => {
got_finalize_batch = true;
}
Inherent::FinalizeEpoch => {
got_finalize_epoch = true;
}
_ => panic!(),
}
}
assert!(got_reward && got_finalize_batch && got_finalize_epoch);
}
4 changes: 3 additions & 1 deletion blockchain/tests/push.rs
Original file line number Diff line number Diff line change
Expand Up @@ -283,7 +283,9 @@ fn it_validates_network() {
fn it_validates_version() {
expect_push_micro_block(
BlockConfig {
version: Some(Policy::MAX_SUPPORTED_VERSION - 1),
version: Some(Policy::max_supported_version(NetworkId::UnitAlbatross) + 1),
// We exclude election blocks here, because they might try to perform an upgrade.
test_election: false,
..Default::default()
},
Err(InvalidBlock(BlockError::UnsupportedVersion)),
Expand Down
2 changes: 1 addition & 1 deletion light-blockchain/tests/push.rs
Original file line number Diff line number Diff line change
Expand Up @@ -308,7 +308,7 @@ fn it_works_with_valid_blocks() {
fn it_validates_version() {
expect_push_micro_block(
BlockConfig {
version: Some(Policy::MAX_SUPPORTED_VERSION - 1),
version: Some(Policy::max_supported_version(NetworkId::UnitAlbatross) + 1),
..Default::default()
},
Err(InvalidBlock(BlockError::UnsupportedVersion)),
Expand Down
2 changes: 1 addition & 1 deletion primitives/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ coin = ["hex", "nimiq-serde", "regex", "thiserror"]
key-nibbles = ["hex", "nimiq-keys", "nimiq-database-value", "nimiq-database-value-derive", "nimiq-serde"]
networks = ["thiserror"]
parallel = ["rayon", "ark-ec/parallel"]
policy = ["nimiq-keys", "nimiq-utils", "parking_lot"]
policy = ["nimiq-keys", "nimiq-utils", "parking_lot", "networks"]
serde-derive = ["nimiq-serde", "serde", "serde_bytes", "serde_repr"]
slots = ["nimiq-bls", "nimiq-keys", "nimiq-serde", "nimiq-utils", "policy"]
tendermint = ["networks", "nimiq-bls", "serde-derive"]
Expand Down
25 changes: 21 additions & 4 deletions primitives/account/src/account/staking_contract/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ use std::collections::BTreeMap;

use nimiq_hash::Blake2bHash;
use nimiq_keys::Address;
#[cfg(feature = "interaction-traits")]
use nimiq_primitives::account::AccountError;
use nimiq_primitives::{
account::AccountError,
coin::Coin,
policy::Policy,
slots_allocation::{Validators, ValidatorsBuilder},
Expand All @@ -17,13 +18,14 @@ pub use store::StakingContractStore;
pub use store::StakingContractStoreWrite;
pub use validator::{Tombstone, Validator};

#[cfg(feature = "interaction-traits")]
use crate::TransactionLog;
use crate::{
account::staking_contract::{
punished_slots::PunishedSlots,
store::{StakingContractStoreRead, StakingContractStoreReadOps},
},
data_store_ops::{DataStoreIterOps, DataStoreReadOps},
TransactionLog,
};

pub mod punished_slots;
Expand Down Expand Up @@ -181,6 +183,11 @@ impl StakingContract {
slots_builder.build()
}

/// Returns the amount of active stake (i.e., total stake of active validators).
pub fn get_active_stake(&self) -> Coin {
self.active_validators.values().copied().sum()
}

/// Returns the total amount of coins that are supporting the upgrade and are active.
/// The support is determined by a function over the signal data.
/// IMPORTANT: This is a fairly expensive function, iterating over all validators.
Expand Down Expand Up @@ -235,7 +242,8 @@ impl StakingContract {
/// Deactivates validators that did not support the upgrade.
/// The support is determined by a function over the signal data.
/// IMPORTANT: This is a fairly expensive function, iterating over all validators.
pub fn deactivate_unsupporting_validators<F: Fn(Option<Blake2bHash>) -> bool>(
#[cfg(feature = "interaction-traits")]
pub(crate) fn deactivate_unsupporting_validators<F: Fn(Option<Blake2bHash>) -> bool>(
&mut self,
store: &mut StakingContractStoreWrite,
support_check: F,
Expand All @@ -253,7 +261,16 @@ impl StakingContract {

// Deactivate those validators.
for (validator_address, signer) in unsupporting_validators {
self.deactivate_validator(store, &validator_address, &signer, block_number, tx_logger)?;
// Since this function will deactivate the validator from the following election block,
// we will pass `block_number - 1` as the block number. This ensures the validator is
// inactive from this election block already.
self.deactivate_validator(
store,
&validator_address,
&signer,
block_number - 1,
tx_logger,
)?;
}

Ok(())
Expand Down
6 changes: 2 additions & 4 deletions primitives/account/src/account/staking_contract/validator.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
use nimiq_bls::CompressedPublicKey as BlsPublicKey;
use nimiq_hash::Blake2bHash;
use nimiq_keys::{Address, Ed25519PublicKey as SchnorrPublicKey};
use nimiq_primitives::coin::Coin;
#[cfg(feature = "interaction-traits")]
use nimiq_primitives::{account::AccountError, policy::Policy};
use nimiq_primitives::{account::AccountError, coin::Coin, policy::Policy};
use serde::{Deserialize, Serialize};

#[cfg(feature = "interaction-traits")]
Expand Down Expand Up @@ -86,7 +84,7 @@ pub struct Validator {
/// A flag indicating if the validator is retired.
pub retired: bool,
}
#[cfg(feature = "interaction-traits")]

impl Validator {
pub fn is_active(&self) -> bool {
self.inactive_from.is_none()
Expand Down
2 changes: 1 addition & 1 deletion primitives/account/src/data_store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ impl<'store, 'tree, 'txn, 'txni, 'env> DataStoreWrite<'store, 'tree, 'txn, 'txni
self.store
.tree
.iter_nodes(
&self.txn,
self.txn,
&(&self.store.prefix + start_key),
&(&self.store.prefix + end_key),
)
Expand Down
1 change: 0 additions & 1 deletion primitives/account/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
#[cfg(feature = "accounts")]
#[macro_use]
extern crate log;

Expand Down
Loading

0 comments on commit 42076ca

Please sign in to comment.