Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: block apply in the past #11767

Merged
merged 6 commits into from
Jul 12, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions chain/chain/src/runtime/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -745,6 +745,13 @@ impl RuntimeAdapter for NightshadeRuntime {
StorageDataSource::Db => {
self.tries.get_trie_for_shard(shard_uid, storage_config.state_root)
}
StorageDataSource::DbTrieOnly => {
// If there is no flat storage on disk, use trie but simulate costs with enabled
// flat storage by not charging gas for trie nodes.
Trisfald marked this conversation as resolved.
Show resolved Hide resolved
let mut trie = self.tries.get_trie_for_shard(shard_uid, storage_config.state_root);
trie.dont_charge_gas_for_trie_node_access();
trie
}
StorageDataSource::Recorded(storage) => Trie::from_recorded_storage(
storage,
storage_config.state_root,
Expand Down Expand Up @@ -958,6 +965,18 @@ impl RuntimeAdapter for NightshadeRuntime {
storage_config.state_root,
storage_config.use_flat_storage,
)?,
StorageDataSource::DbTrieOnly => {
// If there is no flat storage on disk, use trie but simulate costs with enabled
// flat storage by not charging gas for trie nodes.
let mut trie = self.get_trie_for_shard(
shard_id,
&block.prev_block_hash,
storage_config.state_root,
false,
)?;
trie.dont_charge_gas_for_trie_node_access();
trie
}
StorageDataSource::Recorded(storage) => Trie::from_recorded_storage(
storage,
storage_config.state_root,
Expand Down
4 changes: 4 additions & 0 deletions chain/chain/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,10 @@ impl ChainGenesis {
pub enum StorageDataSource {
/// Full state data is present in DB.
Db,
/// Trie is present in DB and flat storage is not.
/// Used to reply past blocks and simulate gas costs as if flat storage
/// was present.
DbTrieOnly,
/// State data is supplied from state witness, there is no state data
/// stored on disk.
Recorded(PartialStorage),
Expand Down
5 changes: 5 additions & 0 deletions core/store/src/trie/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -660,6 +660,11 @@ impl Trie {
}
}

/// Helper to simulate gas costs as if flat storage was present.
pub fn dont_charge_gas_for_trie_node_access(&mut self) {
self.charge_gas_for_trie_node_access = false;
}

/// Makes a new trie that has everything the same except that access
/// through that trie accumulates a state proof for all nodes accessed.
pub fn recording_reads(&self) -> Self {
Expand Down
27 changes: 23 additions & 4 deletions tools/state-viewer/src/apply_chain_range.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use near_chain::chain::collect_receipts_from_response;
use near_chain::migrations::check_if_block_is_first_with_chunk_of_version;
use near_chain::types::{
ApplyChunkBlockContext, ApplyChunkResult, ApplyChunkShardContext, RuntimeAdapter,
RuntimeStorageConfig,
RuntimeStorageConfig, StorageDataSource,
};
use near_chain::{ChainStore, ChainStoreAccess, ChainStoreUpdate};
use near_chain_configs::Genesis;
Expand Down Expand Up @@ -126,6 +126,7 @@ fn apply_block_from_range(
csv_file_mutex: &Mutex<Option<&mut File>>,
only_contracts: bool,
use_flat_storage: bool,
use_trie_for_free: bool,
Trisfald marked this conversation as resolved.
Show resolved Hide resolved
) {
// normally save_trie_changes depends on whether the node is
// archival, but here we don't care, and can just set it to false
Expand Down Expand Up @@ -232,9 +233,16 @@ fn apply_block_from_range(
return;
}
}

let mut storage =
RuntimeStorageConfig::new(*chunk_inner.prev_state_root(), use_flat_storage);
if use_trie_for_free {
storage.source = StorageDataSource::DbTrieOnly;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems like use flat storage and source should be exclusive options. Can you structure it that way in code?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have started in such a way, but then I realized you could potentially use both together.. for example use flat storage and still access trie for free.
No strong opinion if you prefer I'll make the options in CLI exclusive

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When would it be possible to use both?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think when flat storage is enabled we don't read trie at all (except for the non-inlined values).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm referring to this struct

pub struct RuntimeStorageConfig {
    pub state_root: StateRoot,
    pub use_flat_storage: bool,
    pub source: StorageDataSource,
    pub state_patch: SandboxStatePatch,
}

API-wise it gives the impression use-flat-storage is orthogonal from the StorageDataSource. You can use flat storage or not over a DB that charges gas costs or not.
Yes not charging gas costs is similar to using flat-storage in some contexts (replaying block)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah well then this struct is wrong too :) Given the source is already there the 'use_flat_storage' should be derivable from it. I would just remove it. If you don't want to go so far in this PR perhaps you can just add a new ctor? I just dislike this pattern of calling ctor and then immediately overwriting one of the fields, it seems very volatile.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree, I'll add another constructor!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have made some sweeping changes to make stuff more explicit. No more flags to enable flat storage or free tries, instead now there's an enum StorageSource with all different options


runtime_adapter
.apply_chunk(
RuntimeStorageConfig::new(*chunk_inner.prev_state_root(), use_flat_storage),
storage,
ApplyChunkReason::UpdateTrackedShard,
ApplyChunkShardContext {
shard_id,
Expand All @@ -258,9 +266,14 @@ fn apply_block_from_range(
chain_store.get_chunk_extra(block.header().prev_hash(), &shard_uid).unwrap();
prev_chunk_extra = Some(chunk_extra.clone());

let mut storage = RuntimeStorageConfig::new(*chunk_extra.state_root(), use_flat_storage);
if use_trie_for_free {
storage.source = StorageDataSource::DbTrieOnly;
}

runtime_adapter
.apply_chunk(
RuntimeStorageConfig::new(*chunk_extra.state_root(), use_flat_storage),
storage,
ApplyChunkReason::UpdateTrackedShard,
ApplyChunkShardContext {
shard_id,
Expand Down Expand Up @@ -377,6 +390,7 @@ pub fn apply_chain_range(
csv_file: Option<&mut File>,
only_contracts: bool,
use_flat_storage: bool,
use_trie_for_free: bool,
) {
let parent_span = tracing::debug_span!(
target: "state_viewer",
Expand All @@ -386,14 +400,16 @@ pub fn apply_chain_range(
?end_height,
%shard_id,
only_contracts,
use_flat_storage)
use_flat_storage,
use_trie_for_free)
.entered();
let chain_store = ChainStore::new(store.clone(), genesis.config.genesis_height, false);
let (start_height, end_height) = match mode {
ApplyRangeMode::Benchmarking => {
// Benchmarking mode requires flat storage and retrieves start and
// end heights from flat storage and chain.
assert!(use_flat_storage);
assert!(!use_trie_for_free);
assert!(start_height.is_none());
assert!(end_height.is_none());

Expand Down Expand Up @@ -458,6 +474,7 @@ pub fn apply_chain_range(
&csv_file_mutex,
only_contracts,
use_flat_storage,
use_trie_for_free,
);
};

Expand Down Expand Up @@ -642,6 +659,7 @@ mod test {
None,
false,
false,
false,
);
}

Expand Down Expand Up @@ -686,6 +704,7 @@ mod test {
Some(file.as_file_mut()),
false,
false,
false,
);
let mut csv = String::new();
file.as_file_mut().seek(SeekFrom::Start(0)).unwrap();
Expand Down
38 changes: 32 additions & 6 deletions tools/state-viewer/src/apply_chunk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use near_chain::chain::collect_receipts_from_response;
use near_chain::migrations::check_if_block_is_first_with_chunk_of_version;
use near_chain::types::{
ApplyChunkBlockContext, ApplyChunkResult, ApplyChunkShardContext, RuntimeAdapter,
RuntimeStorageConfig,
RuntimeStorageConfig, StorageDataSource,
};
use near_chain::{ChainStore, ChainStoreAccess};
use near_epoch_manager::{EpochManagerAdapter, EpochManagerHandle};
Expand Down Expand Up @@ -87,6 +87,7 @@ pub(crate) fn apply_chunk(
target_height: Option<u64>,
rng: Option<StdRng>,
use_flat_storage: bool,
use_trie_for_free: bool,
) -> anyhow::Result<(ApplyChunkResult, Gas)> {
let chunk = chain_store.get_chunk(&chunk_hash)?;
let chunk_header = chunk.cloned_header();
Expand Down Expand Up @@ -131,9 +132,14 @@ pub(crate) fn apply_chunk(
shard_id,
)?;

let mut storage = RuntimeStorageConfig::new(prev_state_root, use_flat_storage);
if use_trie_for_free {
storage.source = StorageDataSource::DbTrieOnly;
}

Ok((
runtime.apply_chunk(
RuntimeStorageConfig::new(prev_state_root, use_flat_storage),
storage,
ApplyChunkReason::UpdateTrackedShard,
ApplyChunkShardContext {
shard_id,
Expand Down Expand Up @@ -204,14 +210,15 @@ fn apply_tx_in_block(
tx_hash: &CryptoHash,
block_hash: CryptoHash,
use_flat_storage: bool,
use_trie_for_free: bool,
) -> anyhow::Result<ApplyChunkResult> {
match find_tx_or_receipt(tx_hash, &block_hash, epoch_manager, chain_store)? {
Some((hash_type, shard_id)) => {
match hash_type {
HashType::Tx => {
println!("Found tx in block {} shard {}. equivalent command:\nview_state apply --height {} --shard-id {}\n",
&block_hash, shard_id, chain_store.get_block_header(&block_hash)?.height(), shard_id);
let (block, apply_result) = crate::commands::apply_block(block_hash, shard_id, epoch_manager, runtime, chain_store, use_flat_storage);
let (block, apply_result) = crate::commands::apply_block(block_hash, shard_id, epoch_manager, runtime, chain_store, use_flat_storage, use_trie_for_free);
crate::commands::check_apply_block_result(&block, &apply_result, epoch_manager, chain_store, shard_id)?;
Ok(apply_result)
},
Expand All @@ -233,6 +240,7 @@ fn apply_tx_in_chunk(
chain_store: &mut ChainStore,
tx_hash: &CryptoHash,
use_flat_storage: bool,
use_trie_for_free: bool,
) -> anyhow::Result<Vec<ApplyChunkResult>> {
if chain_store.get_transaction(tx_hash)?.is_none() {
return Err(anyhow!("tx with hash {} not known", tx_hash));
Expand All @@ -241,6 +249,7 @@ fn apply_tx_in_chunk(
println!("Transaction is known but doesn't seem to have been applied. Searching in chunks that haven't been applied...");

let head = chain_store.head()?.height;
let protocol_version = chain_store.head_header()?.latest_protocol_version();
let mut chunk_hashes = vec![];

for item in store.iter(DBCol::ChunkHashesByHeight) {
Expand Down Expand Up @@ -284,10 +293,11 @@ fn apply_tx_in_chunk(
None,
None,
use_flat_storage,
use_trie_for_free,
)?;
println!(
"resulting chunk extra:\n{:?}",
crate::commands::resulting_chunk_extra(&apply_result, gas_limit)
crate::commands::resulting_chunk_extra(&apply_result, gas_limit, protocol_version)
);
results.push(apply_result);
}
Expand All @@ -301,6 +311,7 @@ pub(crate) fn apply_tx(
store: Store,
tx_hash: CryptoHash,
use_flat_storage: bool,
use_trie_for_free: bool,
) -> anyhow::Result<Vec<ApplyChunkResult>> {
let mut chain_store = ChainStore::new(store.clone(), genesis_height, false);
let outcomes = chain_store.get_outcomes_by_id(&tx_hash)?;
Expand All @@ -313,6 +324,7 @@ pub(crate) fn apply_tx(
&tx_hash,
outcome.block_hash,
use_flat_storage,
use_trie_for_free,
)?])
} else {
apply_tx_in_chunk(
Expand All @@ -322,6 +334,7 @@ pub(crate) fn apply_tx(
&mut chain_store,
&tx_hash,
use_flat_storage,
use_trie_for_free,
)
}
}
Expand All @@ -333,6 +346,7 @@ fn apply_receipt_in_block(
id: &CryptoHash,
block_hash: CryptoHash,
use_flat_storage: bool,
use_trie_for_free: bool,
) -> anyhow::Result<ApplyChunkResult> {
match find_tx_or_receipt(id, &block_hash, epoch_manager, chain_store)? {
Some((hash_type, shard_id)) => {
Expand All @@ -343,7 +357,7 @@ fn apply_receipt_in_block(
HashType::Receipt => {
println!("Found receipt in block {}. Receiver is in shard {}. equivalent command:\nview_state apply --height {} --shard-id {}\n",
&block_hash, shard_id, chain_store.get_block_header(&block_hash)?.height(), shard_id);
let (block, apply_result) = crate::commands::apply_block(block_hash, shard_id, epoch_manager, runtime, chain_store, use_flat_storage);
let (block, apply_result) = crate::commands::apply_block(block_hash, shard_id, epoch_manager, runtime, chain_store, use_flat_storage, use_trie_for_free);
crate::commands::check_apply_block_result(&block, &apply_result, epoch_manager, chain_store, shard_id)?;
Ok(apply_result)
},
Expand All @@ -363,6 +377,7 @@ fn apply_receipt_in_chunk(
chain_store: &mut ChainStore,
id: &CryptoHash,
use_flat_storage: bool,
use_trie_for_free: bool,
) -> anyhow::Result<Vec<ApplyChunkResult>> {
if chain_store.get_receipt(id)?.is_none() {
// TODO: handle local/delayed receipts
Expand All @@ -374,6 +389,7 @@ fn apply_receipt_in_chunk(
);

let head = chain_store.head()?.height;
let protocol_version = chain_store.head_header()?.latest_protocol_version();
let mut to_apply = HashSet::new();
let mut non_applied_chunks = HashMap::new();

Expand Down Expand Up @@ -441,8 +457,10 @@ fn apply_receipt_in_chunk(
None,
None,
use_flat_storage,
use_trie_for_free,
)?;
let chunk_extra = crate::commands::resulting_chunk_extra(&apply_result, gas_limit);
let chunk_extra =
crate::commands::resulting_chunk_extra(&apply_result, gas_limit, protocol_version);
println!("resulting chunk extra:\n{:?}", chunk_extra);
results.push(apply_result);
}
Expand All @@ -456,6 +474,7 @@ pub(crate) fn apply_receipt(
store: Store,
id: CryptoHash,
use_flat_storage: bool,
use_trie_for_free: bool,
) -> anyhow::Result<Vec<ApplyChunkResult>> {
let mut chain_store = ChainStore::new(store.clone(), genesis_height, false);
let outcomes = chain_store.get_outcomes_by_id(&id)?;
Expand All @@ -467,6 +486,7 @@ pub(crate) fn apply_receipt(
&id,
outcome.block_hash,
use_flat_storage,
use_trie_for_free,
)?])
} else {
apply_receipt_in_chunk(
Expand All @@ -476,6 +496,7 @@ pub(crate) fn apply_receipt(
&mut chain_store,
&id,
use_flat_storage,
use_trie_for_free,
)
}
}
Expand Down Expand Up @@ -593,6 +614,7 @@ mod test {
None,
Some(rng),
false,
false,
)
.unwrap();
assert_eq!(apply_result.new_root, new_root);
Expand Down Expand Up @@ -676,6 +698,7 @@ mod test {
store.clone(),
tx.get_hash(),
false,
false,
)
.unwrap();
assert_eq!(results.len(), 1);
Expand All @@ -695,6 +718,7 @@ mod test {
store.clone(),
receipt.get_hash(),
false,
false,
)
.unwrap();
assert_eq!(results.len(), 1);
Expand Down Expand Up @@ -725,6 +749,7 @@ mod test {
store.clone(),
tx.get_hash(),
false,
false,
)
.unwrap();
for result in results {
Expand All @@ -746,6 +771,7 @@ mod test {
store.clone(),
receipt.get_hash(),
false,
false,
)
.unwrap();
for result in results {
Expand Down
Loading
Loading