From 5ab90214e9e2bdaeb76e8ae165e058c4328a52f6 Mon Sep 17 00:00:00 2001 From: Vladimir Motylenko Date: Wed, 1 Dec 2021 21:16:57 +0200 Subject: [PATCH] Chore: merge gc with archive node. --- Cargo.lock | 1 + client/src/rpc_request.rs | 2 +- core/src/evm_rpc_impl/mod.rs | 2 +- core/src/evm_services/block_recorder.rs | 2 +- core/src/evm_services/state_recorder.rs | 7 +- core/src/validator.rs | 42 +++-- core/tests/snapshots.rs | 5 + evm-utils/evm-rpc/src/lib.rs | 1 - evm-utils/evm-state/Cargo.toml | 4 +- evm-utils/evm-state/benches/bench_storage.rs | 4 +- evm-utils/evm-state/src/state.rs | 17 +- evm-utils/evm-state/src/storage/inspectors.rs | 50 +++--- evm-utils/evm-state/src/storage/mod.rs | 29 +++- evm-utils/evm-state/src/storage/walker.rs | 2 +- ledger-tool/Cargo.toml | 2 +- ledger-tool/src/evm_state.rs | 2 +- local-cluster/src/validator_configs.rs | 1 - runtime/src/serde_snapshot.rs | 159 +++++++++++++----- runtime/src/serde_snapshot/tests.rs | 7 + runtime/src/snapshot_utils.rs | 92 +++------- validator/src/main.rs | 20 ++- 21 files changed, 271 insertions(+), 180 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index aa71f738ca..815114107f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7804,6 +7804,7 @@ dependencies = [ [[package]] name = "triedb" version = "0.5.0" +source = "git+https://github.com/velas/triedb?branch=feat/gc-simple#bed9cfa924655ac4e7e6982466d30f7c868d04c0" dependencies = [ "anyhow", "dashmap", diff --git a/client/src/rpc_request.rs b/client/src/rpc_request.rs index f2c60fe19c..7bbdb81a20 100644 --- a/client/src/rpc_request.rs +++ b/client/src/rpc_request.rs @@ -281,7 +281,7 @@ pub enum TokenAccountsFilter { mod tests { use super::*; use crate::rpc_config::RpcTokenAccountsFilter; - use solana_sdk::commitment_config::{CommitmentConfig, CommitmentLevel}; + use solana_sdk::commitment_config::CommitmentConfig; #[test] fn test_build_request_json() { diff --git a/core/src/evm_rpc_impl/mod.rs b/core/src/evm_rpc_impl/mod.rs index fbf1cf3789..c600c36c26 100644 --- a/core/src/evm_rpc_impl/mod.rs +++ b/core/src/evm_rpc_impl/mod.rs @@ -1,7 +1,7 @@ use std::str::FromStr; use sha3::{Digest, Keccak256}; -use solana_sdk::commitment_config::{CommitmentConfig, CommitmentLevel}; +use solana_sdk::commitment_config::CommitmentConfig; use solana_sdk::keyed_account::KeyedAccount; use crate::rpc::JsonRpcRequestProcessor; diff --git a/core/src/evm_services/block_recorder.rs b/core/src/evm_services/block_recorder.rs index 3a3e0c8b45..92111ba6e8 100644 --- a/core/src/evm_services/block_recorder.rs +++ b/core/src/evm_services/block_recorder.rs @@ -9,7 +9,7 @@ use std::{ time::Duration, }; -use evm_state::{Block, ChangedState}; +use evm_state::Block; pub type EvmRecorderReceiver = Receiver; pub type EvmRecorderSender = Sender; diff --git a/core/src/evm_services/state_recorder.rs b/core/src/evm_services/state_recorder.rs index 0042f111da..b2215aeee4 100644 --- a/core/src/evm_services/state_recorder.rs +++ b/core/src/evm_services/state_recorder.rs @@ -1,5 +1,4 @@ use crossbeam_channel::{Receiver, RecvTimeoutError, Sender}; -use solana_ledger::blockstore::Blockstore; use std::{ sync::{ atomic::{AtomicBool, Ordering}, @@ -9,7 +8,7 @@ use std::{ time::Duration, }; -use evm_state::{Block, ChangedState, Storage}; +use evm_state::{ChangedState, Storage, H256}; pub type EvmStateRecorderReceiver = Receiver<(H256, ChangedState)>; pub type EvmStateRecorderSender = Sender<(H256, ChangedState)>; @@ -22,8 +21,8 @@ impl EvmStateRecorderService { #[allow(clippy::new_ret_no_self)] pub fn new( evm_recorder_receiver: EvmStateRecorderReceiver, - exit: &Arc, archive: Storage, + exit: &Arc, ) -> Self { let exit = exit.clone(); let thread_hdl = Builder::new() @@ -33,7 +32,7 @@ impl EvmStateRecorderService { break; } if let Err(RecvTimeoutError::Disconnected) = - write_evm_record(&archive, &evm_recorder_receiver) + Self::write_evm_record(&archive, &evm_recorder_receiver) { break; } diff --git a/core/src/validator.rs b/core/src/validator.rs index a3406ca0d8..adcd6d2ea8 100644 --- a/core/src/validator.rs +++ b/core/src/validator.rs @@ -137,7 +137,6 @@ pub struct ValidatorConfig { pub validator_exit: Arc>, pub no_wait_for_vote_to_start_leader: bool, pub verify_evm_state: bool, - pub enable_evm_archive: bool, } impl Default for ValidatorConfig { @@ -195,7 +194,6 @@ impl Default for ValidatorConfig { validator_exit: Arc::new(RwLock::new(ValidatorExit::default())), no_wait_for_vote_to_start_leader: true, verify_evm_state: false, - enable_evm_archive: false, } } } @@ -424,6 +422,7 @@ impl Validator { config.enforce_ulimit_nofile, &start_progress, config.no_poh_speed_test, + evm_state_archive.clone(), ); *start_progress.write().unwrap() = ValidatorStartProgress::StartingServices; @@ -900,6 +899,12 @@ impl Validator { .expect("evm_block_recorder_service"); } + if let Some(evm_state_recorder_service) = self.evm_state_recorder_service { + evm_state_recorder_service + .join() + .expect("evm_state_recorder_service"); + } + if let Some(s) = self.snapshot_packager_service { s.join().expect("snapshot_packager_service"); } @@ -1059,6 +1064,7 @@ fn new_banks_from_ledger( enforce_ulimit_nofile: bool, start_progress: &Arc>, no_poh_speed_test: bool, + evm_archive: Option, ) -> ( GenesisConfig, BankForks, @@ -1154,7 +1160,7 @@ fn new_banks_from_ledger( blockstore.clone(), exit, config.rpc_config.enable_cpi_and_log_storage, - config.enable_evm_archive, + evm_archive, ) } else { TransactionHistoryServices::default() @@ -1342,7 +1348,7 @@ fn initialize_rpc_transaction_history_services( blockstore: Arc, exit: &Arc, enable_cpi_and_log_storage: bool, - archive_evm_state: bool, + archive_evm_state: Option, ) -> TransactionHistoryServices { let max_complete_transaction_status_slot = Arc::new(AtomicU64::new(blockstore.max_root())); let (transaction_status_sender, transaction_status_receiver) = unbounded(); @@ -1381,19 +1387,21 @@ fn initialize_rpc_transaction_history_services( exit, )); - let (evm_state_recorder_service, evm_state_recorder_sender) = if archive_evm_state { - let (evm_state_recorder_sender, evm_state_recorder_receiver) = unbounded(); - let evm_state_recorder_sender = Some(evm_state_recorder_sender); - ( - Some(EvmStateRecorderService::new( - evm_state_recorder_receiver, - exit, - )), - evm_state_recorder_sender, - ) - } else { - (None, None) - }; + let (evm_state_recorder_service, evm_state_recorder_sender) = + if let Some(archive) = archive_evm_state { + let (evm_state_recorder_sender, evm_state_recorder_receiver) = unbounded(); + let evm_state_recorder_sender = Some(evm_state_recorder_sender); + ( + Some(EvmStateRecorderService::new( + evm_state_recorder_receiver, + archive, + exit, + )), + evm_state_recorder_sender, + ) + } else { + (None, None) + }; TransactionHistoryServices { transaction_status_sender, diff --git a/core/tests/snapshots.rs b/core/tests/snapshots.rs index 4f9b118ba5..4eb79eabf9 100644 --- a/core/tests/snapshots.rs +++ b/core/tests/snapshots.rs @@ -81,6 +81,11 @@ mod tests { DEFINE_SNAPSHOT_VERSION_PARAMETERIZED_TEST_FUNCTIONS!(V1_4_0, Testnet, V1_4_0_Testnet); DEFINE_SNAPSHOT_VERSION_PARAMETERIZED_TEST_FUNCTIONS!(V1_4_0, MainnetBeta, V1_4_0_MainnetBeta); + DEFINE_SNAPSHOT_VERSION_PARAMETERIZED_TEST_FUNCTIONS!(V1_5_0, Development, V1_5_0_Development); + DEFINE_SNAPSHOT_VERSION_PARAMETERIZED_TEST_FUNCTIONS!(V1_5_0, Devnet, V1_5_0_Devnet); + DEFINE_SNAPSHOT_VERSION_PARAMETERIZED_TEST_FUNCTIONS!(V1_5_0, Testnet, V1_5_0_Testnet); + DEFINE_SNAPSHOT_VERSION_PARAMETERIZED_TEST_FUNCTIONS!(V1_5_0, MainnetBeta, V1_5_0_MainnetBeta); + struct SnapshotTestConfig { accounts_dir: TempDir, snapshot_dir: TempDir, diff --git a/evm-utils/evm-rpc/src/lib.rs b/evm-utils/evm-rpc/src/lib.rs index 94befc0f3a..16110c10d5 100644 --- a/evm-utils/evm-rpc/src/lib.rs +++ b/evm-utils/evm-rpc/src/lib.rs @@ -54,7 +54,6 @@ pub struct RPCLogFilter { pub topics: Option>>, } - #[derive(Serialize, Deserialize, Debug, Clone)] #[serde(rename_all = "camelCase")] pub struct RPCLog { diff --git a/evm-utils/evm-state/Cargo.toml b/evm-utils/evm-state/Cargo.toml index aacb02492d..ff457405bd 100644 --- a/evm-utils/evm-state/Cargo.toml +++ b/evm-utils/evm-state/Cargo.toml @@ -11,8 +11,8 @@ secp256k1 = { version = "0.19.0", features = ["recovery", "global-context"] } rand2 = { version = "=0.6.1", package = "rand" } rocksdb = { git = "https://github.com/velas/rust-rocksdb", branch = "transaction", version = "0.17.0", default-features = false } # rocksdb = { version = "0.16.0", default-features = false } -# triedb = { git = "https://github.com/velas/triedb", branch = "fix/remove-before-add", features = ["rocksdb"] } -triedb = { path = "../../../triedb", features = ["rocksdb"] } +triedb = { git = "https://github.com/velas/triedb", branch = "feat/gc-simple", features = ["rocksdb"] } +# triedb = { path = "../../../triedb", features = ["rocksdb"] } primitive-types = "0.8.0" keccak-hash = "0.6" diff --git a/evm-utils/evm-state/benches/bench_storage.rs b/evm-utils/evm-state/benches/bench_storage.rs index 02e349b8ca..cd3450fe4e 100644 --- a/evm-utils/evm-state/benches/bench_storage.rs +++ b/evm-utils/evm-state/benches/bench_storage.rs @@ -155,7 +155,7 @@ fn fill_new_db_then_backup(c: &mut Criterion) { |b, _params| { b.iter_batched( || { - let evm_state = EvmState::load_from(&dir, persist_state.clone()) + let evm_state = EvmState::load_from(&dir, persist_state.clone(), false) .expect("Unable to create new EVM state in temporary directory"); let empty_dir = tempdir().unwrap(); @@ -229,7 +229,7 @@ fn fill_new_db_then_backup_and_then_backup_again(c: &mut Criterion) { |b, _params| { b.iter_batched( || { - let evm_state = EvmState::load_from(&dir, persist_state.clone()) + let evm_state = EvmState::load_from(&dir, persist_state.clone(), false) .expect("Unable to create new EVM state in temporary directory"); let _ = evm_state.make_backup().unwrap(); let mut state = match evm_state { diff --git a/evm-utils/evm-state/src/state.rs b/evm-utils/evm-state/src/state.rs index 1ab1fdbeab..52b68e475a 100644 --- a/evm-utils/evm-state/src/state.rs +++ b/evm-utils/evm-state/src/state.rs @@ -514,6 +514,15 @@ pub enum EvmPersistState { Committed(Committed), Incomming(Incomming), // Usually bank will never try to freeze banks with persist state. } +impl EvmPersistState { + pub fn last_root(&self) -> H256 { + match self { + EvmPersistState::Committed(c) => c.block.state_root, + EvmPersistState::Incomming(i) => i.state_root, + } + } +} + impl Default for EvmPersistState { fn default() -> Self { Self::Incomming(Incomming::default()) @@ -529,7 +538,7 @@ impl EvmState { fs::create_dir(&evm_state)?; } - Self::load_from(evm_state, Incomming::default()) + Self::load_from(evm_state, Incomming::default(), false) } pub fn new_from_genesis( @@ -556,6 +565,7 @@ impl EvmState { Self::load_from( evm_state, Incomming::new(1, root_hash, H256::zero(), timestamp, version), + false, ) } @@ -584,12 +594,11 @@ impl EvmState { pub fn load_from>( path: P, evm_persist_feilds: impl Into, + gc_enabled: bool, ) -> Result { info!("Open EVM storage {}", path.as_ref().display()); - let kvs = KVS::open_persistent( - path, true, // enable gc - )?; + let kvs = KVS::open_persistent(path, gc_enabled)?; Ok(match evm_persist_feilds.into() { EvmPersistState::Incomming(i) => EvmBackend::new(i, kvs).into(), diff --git a/evm-utils/evm-state/src/storage/inspectors.rs b/evm-utils/evm-state/src/storage/inspectors.rs index 196620533c..c1c01b69cb 100644 --- a/evm-utils/evm-state/src/storage/inspectors.rs +++ b/evm-utils/evm-state/src/storage/inspectors.rs @@ -179,61 +179,61 @@ pub mod memorizer { pub mod streamer { use super::*; - pub struct AccountsStreamer { + pub struct AccountsStreamer<'a> { pub source: Storage, - pub destination: Storage, + pub destinations: &'a [Storage], } - impl TrieInspector for AccountsStreamer { + impl<'a> TrieInspector for AccountsStreamer<'a> { fn inspect_node>(&self, trie_key: H256, node: Data) -> Result { - let destination = self.destination.db(); - destination.put(trie_key, node)?; + for destination in self.destinations { + destination.db().put(trie_key, node.as_ref())?; + } Ok(false) } } - impl DataInspector for AccountsStreamer { + impl<'a> DataInspector for AccountsStreamer<'a> { fn inspect_data(&self, _key: H256, account: Account) -> Result<()> { let source = self.source.borrow(); - let destination = self.destination.borrow(); // - Account Storage let walker = Walker::new_raw( source, - StoragesKeysStreamer::new(destination), + StoragesKeysStreamer::new(self.destinations), NoopInspector, ); walker.traverse(account.storage_root)?; - // - Account Code - let code_hash = account.code_hash; - if let Some(code_data) = self.source.get::(code_hash) { - self.destination.set::(code_hash, code_data); - } else { - assert_eq!(code_hash, Code::empty().hash()); + for destination in self.destinations { + // - Account Code + let code_hash = account.code_hash; + if let Some(code_data) = self.source.get::(code_hash) { + destination.set::(code_hash, code_data); + } else { + assert_eq!(code_hash, Code::empty().hash()); + } } Ok(()) } } - pub struct StoragesKeysStreamer { - destination: Destination, + pub struct StoragesKeysStreamer<'a> { + destinations: &'a [Storage], } - impl StoragesKeysStreamer { - fn new(destination: Destination) -> Self { - Self { destination } + impl<'a> StoragesKeysStreamer<'a> { + fn new(destinations: &'a [Storage]) -> Self { + Self { destinations } } } - impl TrieInspector for StoragesKeysStreamer - where - Destination: Borrow, - { + impl<'a> TrieInspector for StoragesKeysStreamer<'a> { fn inspect_node>(&self, trie_key: H256, node: Data) -> Result { - let destination = self.destination.borrow(); - destination.put(trie_key, node)?; + for destination in self.destinations { + destination.db().put(trie_key, node.as_ref())?; + } Ok(true) } } diff --git a/evm-utils/evm-state/src/storage/mod.rs b/evm-utils/evm-state/src/storage/mod.rs index 5f625950a8..cb8fc10061 100644 --- a/evm-utils/evm-state/src/storage/mod.rs +++ b/evm-utils/evm-state/src/storage/mod.rs @@ -35,7 +35,7 @@ use triedb::{ pub mod inspectors; pub mod walker; -pub type Result = std::result::Result; +pub type Result = std::result::Result; pub use rocksdb; // avoid mess with dependencies for another crates type BincodeOpts = WithOtherEndian; @@ -105,6 +105,10 @@ impl Storage { Self::open(Location::Temporary(Arc::new(TempDir::new()?)), true) } + pub fn gc_enabled(&self) -> bool { + self.gc_enabled + } + // without gc_enabled fn open(location: Location, gc_enabled: bool) -> Result { let db_opts = default_db_opts()?; @@ -559,14 +563,14 @@ pub mod cleaner { pub fn cleanup(self) -> Result<()> where - DB: Borrow, + DB: Borrow, { let db = self.db.borrow(); let trie_nodes = self.trie_nodes.as_ref(); // Cleanup unused trie keys in default column family { - let mut batch = rocksdb::WriteBatch::default(); + let mut batch = rocksdb::WriteBatchWithTransaction::::default(); for (key, _data) in db.iterator(rocksdb::IteratorMode::Start) { let key = @@ -589,7 +593,7 @@ pub mod cleaner { let codes_cf = db .cf_handle(column_name) .ok_or_else(|| anyhow!("Codes Column Family '{}' not found", column_name))?; - let mut batch = rocksdb::WriteBatch::default(); + let mut batch = rocksdb::WriteBatchWithTransaction::::default(); for (key, _data) in db.iterator_cf(codes_cf, rocksdb::IteratorMode::Start) { let code_hash = rlp::decode(&key)?; // NOTE: keep in sync with ::storage mod @@ -610,6 +614,23 @@ pub mod cleaner { } } +pub fn copy_and_purge<'a>( + src: Storage, + destinations: &'a [Storage], + root: H256, +) -> Result<(), anyhow::Error> { + anyhow::ensure!(src.check_root_exist(root), "Root does not exist"); + + let source = src.clone(); + let streamer = inspectors::streamer::AccountsStreamer { + source, + destinations, + }; + let walker = walker::Walker::new_shared(src, streamer); + walker.traverse(root)?; + Ok(()) +} + pub fn reference_counter_opts() -> Options { let mut opts = Options::default(); opts.set_merge_operator_associative("inc_counter", triedb::rocksdb::merge_counter); diff --git a/evm-utils/evm-state/src/storage/walker.rs b/evm-utils/evm-state/src/storage/walker.rs index 04f2a16f8c..2b804d8b77 100644 --- a/evm-utils/evm-state/src/storage/walker.rs +++ b/evm-utils/evm-state/src/storage/walker.rs @@ -42,7 +42,7 @@ impl Walker { impl Walker where - DB: Borrow + Sync + Send, + DB: Borrow + Sync + Send, TI: TrieInspector + Sync + Send, DI: TrieDataInsectorRaw + Sync + Send, { diff --git a/ledger-tool/Cargo.toml b/ledger-tool/Cargo.toml index bd840349c6..d8680967e6 100644 --- a/ledger-tool/Cargo.toml +++ b/ledger-tool/Cargo.toml @@ -41,7 +41,7 @@ tokio = { version = "1", features = ["full"] } evm-state = { path = "../evm-utils/evm-state" } evm-rpc = { path = "../evm-utils/evm-rpc" } -triedb = { path = "../../triedb", features = ["rocksdb"] } +triedb = { git = "https://github.com/velas/triedb", branch = "feat/gc-simple", features = ["rocksdb"] } rlp = "0.5.0" anyhow = "1.0.43" rayon = "1.5.0" diff --git a/ledger-tool/src/evm_state.rs b/ledger-tool/src/evm_state.rs index 72bb55d979..6a15b7709d 100644 --- a/ledger-tool/src/evm_state.rs +++ b/ledger-tool/src/evm_state.rs @@ -148,7 +148,7 @@ pub fn process_evm_state_command(ledger_path: &Path, matches: &ArgMatches<'_>) - let source = storage.clone(); let streamer = inspectors::streamer::AccountsStreamer { source, - destination, + destinations: &[destination], }; let walker = Walker::new_shared(storage, streamer); walker.traverse(root)?; diff --git a/local-cluster/src/validator_configs.rs b/local-cluster/src/validator_configs.rs index 1a5d2efee1..e8958bd0f7 100644 --- a/local-cluster/src/validator_configs.rs +++ b/local-cluster/src/validator_configs.rs @@ -56,7 +56,6 @@ pub fn safe_clone_config(config: &ValidatorConfig) -> ValidatorConfig { poh_hashes_per_batch: config.poh_hashes_per_batch, no_wait_for_vote_to_start_leader: config.no_wait_for_vote_to_start_leader, verify_evm_state: config.verify_evm_state, - enable_evm_archive: config.enable_evm_archive, } } diff --git a/runtime/src/serde_snapshot.rs b/runtime/src/serde_snapshot.rs index cc10d21747..8ace5603d9 100644 --- a/runtime/src/serde_snapshot.rs +++ b/runtime/src/serde_snapshot.rs @@ -16,7 +16,7 @@ use { bincode, bincode::{config::Options, Error}, log::*, - serde::{de::DeserializeOwned, Deserialize, Serialize}, + serde::{de::DeserializeOwned, de::Error as _, Deserialize, Serialize}, solana_sdk::{ clock::{Epoch, Slot, UnixTimestamp}, epoch_schedule::EpochSchedule, @@ -47,6 +47,7 @@ mod tests; mod utils; use future::Context as TypeContextFuture; +use solana_measure::measure::Measure; #[allow(unused_imports)] use utils::{serialize_iter_as_map, serialize_iter_as_seq, serialize_iter_as_tuple}; @@ -59,9 +60,19 @@ pub(crate) use crate::accounts_db::{SnapshotStorage, SnapshotStorages}; // NOTE(velas): // - old enum `SerdeStyle` was removed as single variant enum // - this enum should be treated as new, EVM only related enum without any previous history -#[derive(Copy, Clone, Eq, PartialEq)] +#[derive(Copy, Clone, Eq, PartialEq, Debug)] pub(crate) enum EvmStateVersion { V1_4_0, + V1_5_0, +} + +impl EvmStateVersion { + pub fn support_gc(&self) -> bool { + match self { + Self::V1_4_0 => false, // old snapshot support only archive mode + Self::V1_5_0 => true, + } + } } const MAX_STREAM_SIZE: u64 = 32 * 1024 * 1024 * 1024; @@ -133,33 +144,30 @@ pub(crate) fn bank_from_stream( additional_builtins: Option<&Builtins>, account_indexes: AccountSecondaryIndexes, caching_enabled: bool, + evm_state_backup_path: &Path, + skip_purge_verify: bool, ) -> std::result::Result where R: Read, { - macro_rules! INTO { - ($x:ident) => {{ - let (bank_fields, accounts_db_fields) = $x::deserialize_bank_fields(stream)?; - - let bank = reconstruct_bank_from_fields( - bank_fields, - accounts_db_fields, - genesis_config, - frozen_account_pubkeys, - evm_state_path, - account_paths, - unpacked_append_vec_map, - debug_keys, - additional_builtins, - account_indexes, - caching_enabled, - )?; - Ok(bank) - }}; - } - match evm_state_version { - EvmStateVersion::V1_4_0 => INTO!(TypeContextFuture), - } + let (bank_fields, accounts_db_fields) = TypeContextFuture::deserialize_bank_fields(stream)?; + reconstruct_bank_from_fields( + bank_fields, + accounts_db_fields, + genesis_config, + frozen_account_pubkeys, + evm_state_path, + account_paths, + unpacked_append_vec_map, + debug_keys, + additional_builtins, + account_indexes, + caching_enabled, + evm_state_backup_path, + evm_state_version.support_gc(), + skip_purge_verify, + true, // enable gc + ) .map_err(|err| { warn!("bankrc_from_stream error: {:?}", err); err @@ -175,21 +183,21 @@ pub(crate) fn bank_to_stream( where W: Write, { - macro_rules! INTO { - ($x:ident) => { - bincode::serialize_into( - stream, - &SerializableBankAndStorage::<$x> { - bank, - snapshot_storages, - phantom: std::marker::PhantomData::default(), - }, - ) - }; - } - match evm_version { - EvmStateVersion::V1_4_0 => INTO!(TypeContextFuture), + let gc_enabled = bank.evm_state.read().unwrap().kvs().gc_enabled(); + if evm_version.support_gc() != gc_enabled { + return Err(Error::custom(format!( + "Snapshot gc config is different from config in storage storage_gc={}, version={:?}", + gc_enabled, evm_version + ))); } + bincode::serialize_into( + stream, + &SerializableBankAndStorage:: { + bank, + snapshot_storages, + phantom: std::marker::PhantomData::default(), + }, + ) .map_err(|err| { warn!("bankrc_to_stream error: {:?}", err); err @@ -243,6 +251,12 @@ fn reconstruct_bank_from_fields( additional_builtins: Option<&Builtins>, account_indexes: AccountSecondaryIndexes, caching_enabled: bool, + evm_state_backup_path: &Path, + // true if we restoring from full backup, or from gc + load_full_backup: bool, + skip_purge_verify: bool, + // true if gc should be enabled + enable_gc: bool, ) -> Result where E: SerializableStorage, @@ -258,9 +272,70 @@ where accounts_db.freeze_accounts(&bank_fields.ancestors, frozen_account_pubkeys); let bank_rc = BankRc::new(Accounts::new_empty(accounts_db), bank_fields.slot); - let evm_state = - evm_state::EvmState::load_from(evm_state_path, bank_fields.evm_persist_feilds.clone()) - .expect("Unable to open EVM state storage"); + // EVM State load + if evm_state_path.exists() { + warn!( + "deleting existing evm state folder {}", + evm_state_path.display() + ); + std::fs::remove_dir_all(&evm_state_path)?; + } + + info!( + "Restoring evm-state snapshot, for root = {}, skip_purge_verify = {}, snapshot_gc = {}, our_state_gc = {}.", + bank_fields.evm_persist_feilds.last_root(), + skip_purge_verify, + load_full_backup, + enable_gc + ); + + // if we force verify, or our gc settings is not equal to settings in snapshot + if !skip_purge_verify || enable_gc != load_full_backup { + let mut tmp_evm_state_path_parent = evm_state_path.to_path_buf(); + tmp_evm_state_path_parent.pop(); + let tmp_dir = tempfile::TempDir::new_in(tmp_evm_state_path_parent)?; + let mut measure = Measure::start("EVM tmp state database restore"); + evm_state::Storage::restore_from(evm_state_backup_path, &tmp_dir.path()).map_err(|e| { + Error::custom(format!("Unable to restore tmp evm backup storage {}", e)) + })?; + measure.stop(); + info!("{}", measure); + let src = + evm_state::Storage::open_persistent(tmp_dir.path(), load_full_backup).map_err(|e| { + Error::custom(format!("Unable to restore tmp evm backup storage {}", e)) + })?; + + let destination = evm_state::Storage::open_persistent(evm_state_path, enable_gc) + .map_err(|e| Error::custom(format!("Unable to open destination evm-state {}", e)))?; + + let mut measure = Measure::start("EVM snapshot purging"); + evm_state::storage::copy_and_purge( + src, + &[destination], // TODO: Add archive storage + bank_fields.evm_persist_feilds.last_root(), + ) + .map_err(|e| Error::custom(format!("Unable to copy_and_purge storage {}", e)))?; + measure.stop(); + info!("{}", measure); + } else { + let mut measure = Measure::start("EVM state database restore"); + evm_state::Storage::restore_from(evm_state_backup_path, &evm_state_path) + .map_err(|e| Error::custom(format!("Unable to restore evm backup storage {}", e)))?; + measure.stop(); + info!("{}", measure); + } + + let evm_state = evm_state::EvmState::load_from( + evm_state_path, + bank_fields.evm_persist_feilds.clone(), + enable_gc, + ) + .map_err(|e| Error::custom(format!("Unable to open EVM state storage {}", e)))?; + + evm_state + .kvs() + .register_slot(bank_fields.slot, bank_fields.evm_persist_feilds.last_root()) + .map_err(|e| Error::custom(format!("Unable to register slot for evm root {}", e)))?; let bank = Bank::new_from_fields( evm_state, bank_rc, diff --git a/runtime/src/serde_snapshot/tests.rs b/runtime/src/serde_snapshot/tests.rs index 90de7abc44..5e78afa4bb 100644 --- a/runtime/src/serde_snapshot/tests.rs +++ b/runtime/src/serde_snapshot/tests.rs @@ -195,6 +195,7 @@ fn test_bank_serialize_style(evm_version: EvmStateVersion) { let mut reader = std::io::BufReader::new(&buf[rdr.position() as usize..]); let evm_state_dir = TempDir::new().unwrap(); + let evm_backup_state_path = TempDir::new().unwrap(); // Create a new set of directories for this bank's accounts let (_accounts_dir, dbank_paths) = get_temp_accounts_paths(4).unwrap(); let ref_sc = StatusCacheRc::default(); @@ -215,6 +216,8 @@ fn test_bank_serialize_style(evm_version: EvmStateVersion) { None, AccountSecondaryIndexes::default(), false, + evm_backup_state_path.path(), + true, ) .unwrap(); dbank.src = ref_sc; @@ -263,6 +266,10 @@ fn test_bank_serialize_newer() { test_bank_serialize_style(EvmStateVersion::V1_4_0) } +#[test] +fn test_bank_serialize_newer() { + test_bank_serialize_style(EvmStateVersion::V1_5_0) +} #[cfg(all(test, RUSTC_WITH_SPECIALIZATION))] mod test_bank_serialize { use super::*; diff --git a/runtime/src/snapshot_utils.rs b/runtime/src/snapshot_utils.rs index d111c795e4..7f372397e3 100644 --- a/runtime/src/snapshot_utils.rs +++ b/runtime/src/snapshot_utils.rs @@ -43,12 +43,14 @@ pub const MAX_SNAPSHOTS: usize = 8; // Save some snapshots but not too many const EVM_STATE_DIR: &str = "evm-state"; const MAX_SNAPSHOT_DATA_FILE_SIZE: u64 = 32 * 1024 * 1024 * 1024; // 32 GiB const VERSION_STRING_V1_4_0: &str = "1.4.0"; -const DEFAULT_SNAPSHOT_VERSION: SnapshotVersion = SnapshotVersion::V1_4_0; +const VERSION_STRING_V1_5_0: &str = "1.5.0"; +const DEFAULT_SNAPSHOT_VERSION: SnapshotVersion = SnapshotVersion::V1_5_0; const TMP_SNAPSHOT_PREFIX: &str = "tmp-snapshot-"; #[derive(Copy, Clone, Eq, PartialEq, Debug)] pub enum SnapshotVersion { V1_4_0, + V1_5_0, } impl Default for SnapshotVersion { @@ -67,6 +69,7 @@ impl From for &'static str { fn from(snapshot_version: SnapshotVersion) -> &'static str { match snapshot_version { SnapshotVersion::V1_4_0 => VERSION_STRING_V1_4_0, + SnapshotVersion::V1_5_0 => VERSION_STRING_V1_5_0, } } } @@ -86,6 +89,7 @@ impl FromStr for SnapshotVersion { }; match version_string { VERSION_STRING_V1_4_0 => Ok(SnapshotVersion::V1_4_0), + VERSION_STRING_V1_5_0 => Ok(SnapshotVersion::V1_5_0), _ => Err("unsupported snapshot version"), } } @@ -555,6 +559,7 @@ pub fn add_snapshot>( let bank_snapshot_serializer = move |stream: &mut BufWriter| -> Result<()> { let evm_version = match snapshot_version { SnapshotVersion::V1_4_0 => EvmStateVersion::V1_4_0, + SnapshotVersion::V1_5_0 => EvmStateVersion::V1_5_0, }; bank_to_stream(evm_version, stream.by_ref(), bank, snapshot_storages)?; Ok(()) @@ -833,78 +838,31 @@ fn rebuild_bank_from_snapshots( .pop() .ok_or_else(|| get_io_error("No snapshots found in snapshots directory"))?; - // EVM State load - if evm_state_path.exists() { - warn!( - "deleting existing evm state folder {}", - evm_state_path.display() - ); - fs::remove_dir_all(&evm_state_path)?; - } - let mut measure = Measure::start("EVM state database restore"); - evm_state::Storage::restore_from(root_paths.evm_state_backup_path, &evm_state_path) - .expect("Unable to restore EVM state underlying database from storage backup"); - measure.stop(); - info!("{}", measure); - info!( "Loading bank from {}", &root_paths.snapshot_file_path.display() ); let bank = deserialize_snapshot_data_file(&root_paths.snapshot_file_path, |mut stream| { - Ok(match snapshot_version_enum { - SnapshotVersion::V1_4_0 => bank_from_stream( - evm_state_path, - EvmStateVersion::V1_4_0, - &mut stream, - account_paths, - unpacked_append_vec_map, - genesis_config, - frozen_account_pubkeys, - debug_keys, - additional_builtins, - account_indexes, - accounts_db_caching_enabled, - ), - }?) - })?; - - if verify_evm_state { - use evm_state::storage::{ - inspectors::verifier::{AccountsVerifier, HashVerifier}, - inspectors::NoopInspector, - walker::Walker, + let evm_version = match snapshot_version_enum { + SnapshotVersion::V1_4_0 => EvmStateVersion::V1_4_0, + SnapshotVersion::V1_5_0 => EvmStateVersion::V1_5_0, }; - use rayon::prelude::*; - - let evm_state = bank.evm_state.read().unwrap(); - let storage = evm_state.kvs().clone(); - let last_root = evm_state.last_root(); - - info!("Verifying EVM state root {:?} ...", last_root); - - let accounts_verifier = AccountsVerifier::new(storage.clone()); - let walker = Walker::new_sec_encoding(storage.db(), HashVerifier, accounts_verifier); - let mut measure = Measure::start("verifying accounts"); - walker - .traverse(last_root) - .map(|_| measure.stop()) - .map_err(SnapshotError::EvmStateError)?; - info!("{}", measure); - - let mut measure = Measure::start("verifying storages"); - walker - .data_inspector - .inner - .storage_roots - .into_par_iter() - .try_for_each(|storage_root| { - Walker::new_raw(storage.db(), HashVerifier, NoopInspector).traverse(storage_root) - }) - .map(|_| measure.stop()) - .map_err(SnapshotError::EvmStateError)?; - info!("{}", measure); - } + Ok(bank_from_stream( + evm_state_path, + evm_version, + &mut stream, + account_paths, + unpacked_append_vec_map, + genesis_config, + frozen_account_pubkeys, + debug_keys, + additional_builtins, + account_indexes, + accounts_db_caching_enabled, + &root_paths.evm_state_backup_path, + !verify_evm_state, + )?) + })?; let status_cache_path = unpacked_snapshots_dir.join(SNAPSHOT_STATUS_CACHE_FILE_NAME); let slot_deltas = deserialize_snapshot_data_file(&status_cache_path, |stream| { diff --git a/validator/src/main.rs b/validator/src/main.rs index e37e49fe48..fab9948565 100644 --- a/validator/src/main.rs +++ b/validator/src/main.rs @@ -1109,7 +1109,6 @@ pub fn main() { .takes_value(true) .help("Use DIR as evm-state archive location"), ) - .arg( Arg::with_name("entrypoint") .short("n") @@ -2037,14 +2036,19 @@ pub fn main() { let admin_client = admin_rpc_service::connect(&ledger_path); admin_rpc_service::runtime() - .block_on(async move { admin_client.await?.merge_evm_state(evm_state_path, restore_backup).await }) + .block_on(async move { + admin_client + .await? + .merge_evm_state(evm_state_path, restore_backup) + .await + }) .unwrap_or_else(|err| { println!("set log filter failed: {}", err); exit(1); - }); + }); return; } - + _ => unreachable!(), }; @@ -2337,6 +2341,13 @@ pub fn main() { exit(1) }) }); + match snapshot_version { + SnapshotVersion::V1_5_0 => {} + v => { + eprintln!("This snapshot version is not valid, version: {:?}", v); + exit(1) + } + } validator_config.snapshot_config = Some(SnapshotConfig { snapshot_interval_slots: if snapshot_interval_slots > 0 { snapshot_interval_slots @@ -2434,7 +2445,6 @@ pub fn main() { let evm_state_archive = matches.value_of("evm_state_archive_path").map(|path| { info!("Opening evm archive storage"); - validator_config.enable_evm_archive = true; evm_state::Storage::open_persistent( path, false, // gc disabled )