Skip to content

Commit

Permalink
Light History Store for full nodes
Browse files Browse the repository at this point in the history
Use the light history store for full node clients
Necessary changes to use the history interface
Several changes to usage of the history store
  • Loading branch information
viquezclaudio committed Apr 15, 2024
1 parent d77e6a3 commit 98709f7
Show file tree
Hide file tree
Showing 23 changed files with 515 additions and 204 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions blockchain/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ workspace = true
[dependencies]
futures = { package = "futures-util", version = "0.3" }
hex = "0.4"
itertools = "0.10.3"
log = { package = "tracing", version = "0.1", features = ["log"] }
parking_lot = "0.12"
prometheus-client = { version = "0.22.2", optional = true }
Expand Down
4 changes: 2 additions & 2 deletions blockchain/src/block_production/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ impl BlockProducer {

let (history_root, _) = blockchain
.history_store
.add_to_history(&mut txn, Policy::epoch_at(block_number), &hist_txs)
.add_to_history(&mut txn, block_number, &hist_txs)
.expect("Failed to compute history root during block production.");

// Not strictly necessary to drop the lock here, but sign as well as compress might be somewhat expensive
Expand Down Expand Up @@ -327,7 +327,7 @@ impl BlockProducer {

macro_block.header.history_root = blockchain
.history_store
.add_to_history(&mut txn, Policy::epoch_at(block_number), &hist_txs)
.add_to_history(&mut txn, block_number, &hist_txs)
.expect("Failed to compute history root during block production.")
.0;

Expand Down
43 changes: 4 additions & 39 deletions blockchain/src/blockchain/accounts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ use nimiq_primitives::{
trie::{error::IncompleteTrie, trie_diff::TrieDiff, trie_proof::TrieProof},
};
use nimiq_serde::Deserialize;
use nimiq_transaction::historic_transaction::HistoricTransaction;
use nimiq_trie::WriteTransactionProxy;

use crate::Blockchain;
Expand Down Expand Up @@ -58,18 +57,9 @@ impl Blockchain {
// as rebranching across this block is not possible.
self.chain_store.clear_revert_infos(txn.raw());

// Store the transactions and the inherents into the History tree.
let hist_txs = HistoricTransaction::from(
self.network_id,
macro_block.header.block_number,
macro_block.header.timestamp,
vec![],
inherents,
vec![],
);
let total_tx_size = self
.history_store
.add_to_history(txn.raw(), macro_block.epoch_number(), &hist_txs)
.add_block(txn.raw(), block, inherents)
.expect("Failed to store history")
.1;

Expand Down Expand Up @@ -136,21 +126,9 @@ impl Blockchain {
&revert_info,
);

// Store the transactions and the inherents into the History tree.
let hist_txs = HistoricTransaction::from(
self.network_id,
micro_block.header.block_number,
micro_block.header.timestamp,
body.transactions.clone(),
inherents,
body.equivocation_proofs
.iter()
.map(|proof| proof.locator())
.collect(),
);
let total_tx_size = self
.history_store
.add_to_history(txn.raw(), micro_block.epoch_number(), &hist_txs)
.add_block(txn.raw(), block, inherents)
.expect("Failed to store history")
.1;

Expand Down Expand Up @@ -221,22 +199,9 @@ impl Blockchain {
panic!("Failed to revert {block} - {e:?}");
}

// Remove the transactions from the History tree. For this you only need to calculate the
// number of transactions that you want to remove.
let num_txs = HistoricTransaction::count(
body.transactions.len(),
&inherents,
body.equivocation_proofs
.iter()
.map(|proof| proof.locator())
.collect(),
);
let (_, total_size) = self
.history_store
.remove_partial_history(txn.raw(), block.epoch_number(), num_txs)
.expect("Failed to remove partial history");
self.history_store.remove_block(txn.raw(), block, inherents);

Ok(total_size)
Ok(0)
}

/// Produces a Merkle proof of the inclusion of the given keys in the
Expand Down
34 changes: 26 additions & 8 deletions blockchain/src/blockchain/blockchain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ use tokio::sync::broadcast::{channel as broadcast, Sender as BroadcastSender};
use crate::chain_metrics::BlockchainMetrics;
use crate::{
blockchain_state::BlockchainState, chain_store::ChainStore, history::HistoryStore,
interface::HistoryInterface, reward::genesis_parameters,
interface::HistoryInterface, light_history_store::LightHistoryStore,
reward::genesis_parameters,
};

const BROADCAST_MAX_CAPACITY: usize = 256;
Expand Down Expand Up @@ -67,13 +68,17 @@ pub struct BlockchainConfig {
/// Maximum number of epochs (other than the current one) that the ChainStore will store fully.
/// Epochs older than this number will be pruned.
pub max_epochs_stored: u32,
/// The history store that is used by the full blockchain.
/// If this is set to true, the light history store is used.
pub light_history_store: bool,
}

impl Default for BlockchainConfig {
fn default() -> Self {
Self {
keep_history: true,
max_epochs_stored: Policy::MIN_EPOCHS_STORED,
light_history_store: false,
}
}
}
Expand Down Expand Up @@ -131,14 +136,12 @@ impl Blockchain {
}

let chain_store = ChainStore::new(env.clone());
let history_store = HistoryStore::new(env.clone());

Ok(match chain_store.get_head(None) {
Some(head_hash) => Blockchain::load(
env,
config,
chain_store,
history_store,
time,
network_id,
genesis_block,
Expand All @@ -148,7 +151,6 @@ impl Blockchain {
env,
config,
chain_store,
history_store,
time,
network_id,
genesis_block,
Expand All @@ -162,7 +164,7 @@ impl Blockchain {
env: DatabaseProxy,
config: BlockchainConfig,
chain_store: ChainStore,
history_store: HistoryStore,

time: Arc<OffsetTime>,
network_id: NetworkId,
genesis_block: Block,
Expand Down Expand Up @@ -262,6 +264,14 @@ impl Blockchain {
let (tx_fork, _rx_fork) = broadcast(BROADCAST_MAX_CAPACITY);
let (tx_log, _rx_log) = broadcast(BROADCAST_MAX_CAPACITY);

let history_store = if config.light_history_store {
Box::new(LightHistoryStore::new(env.clone(), network_id))
as Box<dyn HistoryInterface + Sync + Send>
} else {
Box::new(HistoryStore::new(env.clone(), network_id))
as Box<dyn HistoryInterface + Sync + Send>
};

Ok(Blockchain {
env,
config,
Expand All @@ -271,7 +281,7 @@ impl Blockchain {
fork_notifier: tx_fork,
log_notifier: tx_log,
chain_store,
history_store: Box::new(history_store) as Box<dyn HistoryInterface + Sync + Send>,
history_store,
state: BlockchainState {
accounts,
main_chain,
Expand All @@ -297,7 +307,7 @@ impl Blockchain {
env: DatabaseProxy,
config: BlockchainConfig,
chain_store: ChainStore,
history_store: HistoryStore,

time: Arc<OffsetTime>,
network_id: NetworkId,
genesis_block: Block,
Expand Down Expand Up @@ -327,6 +337,14 @@ impl Blockchain {
let (tx_fork, _rx_fork) = broadcast(BROADCAST_MAX_CAPACITY);
let (tx_log, _rx_log) = broadcast(BROADCAST_MAX_CAPACITY);

let history_store = if config.light_history_store {
Box::new(LightHistoryStore::new(env.clone(), network_id))
as Box<dyn HistoryInterface + Sync + Send>
} else {
Box::new(HistoryStore::new(env.clone(), network_id))
as Box<dyn HistoryInterface + Sync + Send>
};

Ok(Blockchain {
env,
config,
Expand All @@ -336,7 +354,7 @@ impl Blockchain {
fork_notifier: tx_fork,
log_notifier: tx_log,
chain_store,
history_store: Box::new(history_store) as Box<dyn HistoryInterface + Sync + Send>,
history_store,
state: BlockchainState {
accounts,
macro_info: main_chain.clone(),
Expand Down
42 changes: 30 additions & 12 deletions blockchain/src/blockchain/history_sync.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use std::error::Error;
use itertools::Itertools;
use std::{collections::HashMap, error::Error};

use nimiq_account::{BlockLogger, BlockState};
use nimiq_block::{Block, BlockError};
Expand Down Expand Up @@ -96,24 +97,41 @@ impl Blockchain {
/// Extends the current chain with transactions from the validity sync process
pub fn extend_validity_sync(
this: RwLockUpgradableReadGuard<Blockchain>,
epoch: u32,
history: &[HistoricTransaction],
) -> Option<Blake2bHash> {
let mut txn = this.write_transaction();

// Store the new historic transactions into the History tree.
if let Some((root, _)) = this.history_store.add_to_history(&mut txn, epoch, history) {
txn.commit();
let mut txns_per_block: HashMap<u32, Vec<HistoricTransaction>> = HashMap::new();

for txn in history {
let bn = txn.block_number;

if let Some(txns) = txns_per_block.get_mut(&bn) {
txns.push(txn.clone());
} else {
let new_txns = vec![txn.clone()];
txns_per_block.insert(bn, new_txns);
}
}

let mut root = Blake2bHash::default();
for bn in txns_per_block.keys().sorted() {
let hist_txs = txns_per_block.get(bn).unwrap();

root = this
.history_store
.add_to_history(&mut txn, *bn, hist_txs)
.expect("Pushing txns to the history store failed")
.0;

debug!(
num_transactions = history.len(),
num_transactions = hist_txs.len(),
block = bn,
"Pushed txns to the history store during validity sync"
);
Some(root)
} else {
txn.abort();
None
}
txn.commit();
Some(root)
}

/// Extends the current chain with a macro block (election or checkpoint) during history sync.
Expand Down Expand Up @@ -347,7 +365,7 @@ impl Blockchain {
// Store the new historic transactions into the History tree.
this.history_store.add_to_history(
&mut txn,
block.epoch_number(),
block.block_number(),
&history[first_new_hist_tx..],
);

Expand All @@ -370,7 +388,7 @@ impl Blockchain {

let wanted_history_root = this
.history_store
.get_history_tree_root(block.epoch_number(), Some(&txn))
.get_history_tree_root(block.block_number(), Some(&txn))
.ok_or(PushError::InvalidBlock(BlockError::InvalidHistoryRoot))?;

if *block.history_root() != wanted_history_root {
Expand Down
2 changes: 1 addition & 1 deletion blockchain/src/blockchain/verify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ impl Blockchain {
// Verify the history root when we have the full transaction history of the epoch.
let real_history_root = self
.history_store
.get_history_tree_root(block.epoch_number(), Some(txn))
.get_history_tree_root(block.block_number(), Some(txn))
.ok_or_else(|| {
error!(
%block,
Expand Down
5 changes: 3 additions & 2 deletions blockchain/src/blockchain/wrappers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ impl Blockchain {
.release_balance(account, transaction, reserved_balance, None)
}

/// Checks if we have seen some transaction with this hash inside the a validity window.
/// Checks if we have seen some transaction with this hash inside the validity window.
pub fn tx_in_validity_window(
&self,
tx_hash: &Blake2bHash,
Expand Down Expand Up @@ -228,7 +228,8 @@ impl Blockchain {
let max_block_number = self
.block_number()
.saturating_sub(Policy::transaction_validity_window_blocks());
self.tx_in_validity_window(tx_hash, max_block_number, txn_opt)
self.history_store
.tx_in_validity_window(tx_hash, max_block_number, txn_opt)
}

pub fn staking_contract_address(&self) -> Address {
Expand Down

0 comments on commit 98709f7

Please sign in to comment.