diff --git a/CHANGELOG.md b/CHANGELOG.md index 259e9880f..68ffbebc7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ * *chain-abci* [1100](https://github.com/crypto-com/chain/pull/1100): account nonce increased after reward distribution * *chain-abci* [1292](https://github.com/crypto-com/chain/pull/1292): implements new punishment and staking state transition specification and new nonce logic * *chain-storage* [1424](https://github.com/crypto-com/chain/pull/1424): add two columnes to the database +* *chain-storage* [1428](https://github.com/crypto-com/chain/pull/1428): change merkle tree of staking states to jellyfish-merkle * *chain-core* [1162](https://github.com/crypto-com/chain/pull/1162): transaction witness contains BIP-340-compatible Schnorr signatures (instead of the previous WIP scheme) * *chain-core* [1325](https://github.com/crypto-com/chain/pull/1325): blake3 for message digest (txid) + tree hashing * *chain-core* [1222](https://github.com/crypto-com/chain/pull/1258): Add common outer type for public(non-enclave) tx, tx serialization is changed. diff --git a/Cargo.lock b/Cargo.lock index 6611218ed..1d5192b94 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -488,7 +488,6 @@ dependencies = [ "integer-encoding", "kvdb", "kvdb-memorydb", - "kvdb-rocksdb", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "parity-scale-codec", "protobuf", @@ -501,7 +500,6 @@ dependencies = [ "sgx_types", "sgx_urts", "sha3", - "starling", "structopt", "test-common", "thiserror", @@ -547,7 +545,6 @@ dependencies = [ "kvdb-rocksdb", "parity-scale-codec", "quickcheck", - "starling", ] [[package]] @@ -4047,12 +4044,6 @@ version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8" -[[package]] -name = "starling" -version = "3.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e31f22b013888d2337d4a7f4443e13d67b0fcc87d6f1461b5c437089f60a3cce" - [[package]] name = "static_assertions" version = "0.3.4" diff --git a/chain-abci/Cargo.toml b/chain-abci/Cargo.toml index cdb6f144f..26496b8a6 100644 --- a/chain-abci/Cargo.toml +++ b/chain-abci/Cargo.toml @@ -22,10 +22,6 @@ enclave-protocol = { path = "../enclave-protocol" } log = "0.4.8" env_logger = "0.7.1" bit-vec = { version = "0.6.1", features = ["serde_no_std"] } -kvdb = "0.5" -kvdb-rocksdb = "0.7" -kvdb-memorydb = "0.5" -starling = "3.1.4" byteorder = "1.3.4" serde = "1.0" serde_derive = "1.0" @@ -56,6 +52,8 @@ quickcheck = "0.9" digest = "0.8" sha3 = "0.8" base64 = "0.11" +kvdb = "0.5" +kvdb-memorydb = "0.5" test-common = { path = "../test-common" } # TODO: currently not maintained benchmarks diff --git a/chain-abci/src/app/app_init.rs b/chain-abci/src/app/app_init.rs index ea847df44..ea411a992 100644 --- a/chain-abci/src/app/app_init.rs +++ b/chain-abci/src/app/app_init.rs @@ -21,17 +21,15 @@ use chain_core::init::coin::Coin; use chain_core::init::config::InitConfig; use chain_core::init::config::NetworkParameters; use chain_core::state::account::StakedStateDestination; -use chain_core::state::account::{CouncilNode, StakedState, StakedStateAddress}; +use chain_core::state::account::{CouncilNode, StakedStateAddress}; use chain_core::state::tendermint::{BlockHeight, TendermintVotePower}; use chain_core::state::{ChainState, RewardsPoolState}; use chain_core::tx::TxAux; use chain_core::ChainInfo; -use chain_storage::account::AccountWrapper; -use chain_storage::account::StarlingFixedKey; -use chain_storage::account::{pure_account_storage, AccountStorage}; use chain_storage::buffer::{ - flush_storage, GetStaking, KVBuffer, StakingBuffer, StakingGetter, StoreKV, StoreStaking, + flush_storage, GetStaking, KVBuffer, StakingBuffer, StoreKV, StoreStaking, }; +use chain_storage::jellyfish::{compute_staking_root, StakingGetter, Version}; use chain_storage::{Storage, StoredChainState}; /// ABCI app state snapshot @@ -50,6 +48,8 @@ pub struct ChainNodeState { pub staking_table: StakingTable, /// genesis time pub genesis_time: Timespec, + /// Version number of staking merkle tree + pub staking_version: Version, /// The parts of states which involved in computing app_hash pub top_level: ChainState, @@ -73,7 +73,7 @@ impl ChainNodeState { pub fn genesis( genesis_apphash: H256, genesis_time: Timespec, - account_root: StarlingFixedKey, + account_root: H256, rewards_pool: RewardsPoolState, network_params: NetworkParameters, staking_table: StakingTable, @@ -85,6 +85,7 @@ impl ChainNodeState { block_height: BlockHeight::genesis(), staking_table, genesis_time, + staking_version: 0, top_level: ChainState { account_root, rewards_pool, @@ -104,8 +105,6 @@ pub enum BufferType { pub struct ChainNodeApp { /// the underlying key-value storage (+ possibly some info in the future) pub storage: Storage, - /// account trie storage - pub accounts: AccountStorage, /// valid transactions after DeliverTx before EndBlock/Commit pub delivered_txs: Vec, /// a reference to genesis (used when there is no committed state) @@ -207,17 +206,6 @@ fn get_voting_power( } } -pub fn compute_accounts_root( - account_storage: &mut AccountStorage, - accounts: &[StakedState], -) -> H256 { - let mut keys: Vec<_> = accounts.iter().map(StakedState::key).collect(); - let wrapped: Vec<_> = accounts.iter().cloned().map(AccountWrapper).collect(); - account_storage - .insert(None, &mut keys, &wrapped) - .expect("insert failed") -} - pub fn init_app_hash(conf: &InitConfig, genesis_time: Timespec) -> H256 { let (accounts, rp, _nodes) = conf .validate_config_get_genesis(genesis_time) @@ -225,7 +213,7 @@ pub fn init_app_hash(conf: &InitConfig, genesis_time: Timespec) -> H256 { compute_app_hash( &MerkleTree::empty(), - &compute_accounts_root(&mut pure_account_storage(20).unwrap(), &accounts), + &compute_staking_root(&accounts), &rp, &NetworkParameters::Genesis(conf.network_params.clone()), ) @@ -238,7 +226,6 @@ impl ChainNodeApp { genesis_app_hash: [u8; HASH_SIZE_256], chain_id: &str, storage: Storage, - accounts: AccountStorage, tx_query_address: Option, ) -> Self { let stored_genesis = storage.get_genesis_app_hash(); @@ -261,7 +248,6 @@ impl ChainNodeApp { .expect("failed to decode two last hex digits in chain ID")[0]; ChainNodeApp { storage, - accounts, delivered_txs: Vec::new(), chain_hex_id, genesis_app_hash, @@ -287,7 +273,6 @@ impl ChainNodeApp { /// * `gah` - hex-encoded genesis app hash /// * `chain_id` - the chain ID set in Tendermint genesis.json (our name convention is that the last two characters should be hex digits) /// * `storage` - underlying storage to be used (in-mem or persistent) - /// * `accounts` - underlying storage for account tries to be used (in-mem or persistent) /// * `tx_query_address` - address of tx query enclave to supply to clients (if any) /// * `enclave_server` - connection string which ZeroMQ server wrapper around the transaction validation enclave will listen on pub fn new_with_storage( @@ -295,7 +280,6 @@ impl ChainNodeApp { gah: &str, chain_id: &str, mut storage: Storage, - accounts: AccountStorage, tx_query_address: Option, enclave_server: Option, ) -> Self { @@ -344,7 +328,7 @@ impl ChainNodeApp { // populate the indexing structures in staking table. last_state.staking_table.initialize( - &StakingGetter::new(&accounts, Some(last_state.top_level.account_root)), + &StakingGetter::new(&storage, last_state.staking_version), last_state .top_level .network_params @@ -356,7 +340,6 @@ impl ChainNodeApp { genesis_app_hash, chain_id, storage, - accounts, tx_query_address, ) } else { @@ -374,7 +357,6 @@ impl ChainNodeApp { storage.write_genesis_chain_id(&genesis_app_hash, chain_id); ChainNodeApp { storage, - accounts, delivered_txs: Vec::new(), chain_hex_id, genesis_app_hash, @@ -420,7 +402,7 @@ impl ChainNodeApp { } let network_params = NetworkParameters::Genesis(conf.network_params); - let new_account_root = compute_accounts_root(&mut self.accounts, &accounts); + let new_account_root = self.storage.put_stakings(0, &accounts); let genesis_app_hash = compute_app_hash( &MerkleTree::empty(), &new_account_root, @@ -448,7 +430,7 @@ impl ChainNodeApp { let val_addresses = nodes.iter().map(|(addr, _)| *addr).collect::>(); let staking_table = StakingTable::from_genesis( - &StakingGetter::new(&self.accounts, Some(new_account_root)), + &staking_getter!(self, 0), network_params.get_required_council_node_stake(), network_params.get_max_validators(), &val_addresses, @@ -475,27 +457,30 @@ impl ChainNodeApp { } pub fn staking_store(&mut self, buffer_type: BufferType) -> impl StoreStaking + '_ { - let root = self + let version = self .last_state .as_ref() - .map(|state| state.top_level.account_root); - staking_store!(self, root, buffer_type) + .map(|state| state.staking_version) + .unwrap_or(0); + staking_store!(self, version, buffer_type) } pub fn staking_getter(&self, buffer_type: BufferType) -> impl GetStaking + '_ { - let root = self + let version = self .last_state .as_ref() - .map(|state| state.top_level.account_root); - staking_getter!(self, root, buffer_type) + .map(|state| state.staking_version) + .unwrap_or(0); + staking_getter!(self, version, buffer_type) } - pub fn staking_getter_committed(&self) -> StakingGetter<'_> { + pub fn staking_getter_committed(&self) -> StakingGetter<'_, Storage> { StakingGetter::new( - &self.accounts, + &self.storage, self.last_state .as_ref() - .map(|state| state.top_level.account_root), + .map(|state| state.staking_version) + .unwrap_or(0), ) } diff --git a/chain-abci/src/app/commit.rs b/chain-abci/src/app/commit.rs index c4a2d2929..fdb9297d3 100644 --- a/chain-abci/src/app/commit.rs +++ b/chain-abci/src/app/commit.rs @@ -8,7 +8,8 @@ use chain_core::compute_app_hash; use chain_core::tx::data::input::{TxoPointer, TxoSize}; use chain_core::tx::data::TxId; use chain_core::tx::{TxAux, TxEnclaveAux, TxPublicAux}; -use chain_storage::buffer::{flush_staking_storage, flush_storage, StoreKV}; +use chain_storage::buffer::{flush_storage, StoreKV}; +use chain_storage::jellyfish::flush_stakings; use parity_scale_codec::Encode; /// Given a db and a DB transaction, it will go through TX inputs and mark them as spent @@ -91,14 +92,20 @@ impl ChainNodeApp { top_level.rewards_pool.last_block_height = new_state.last_block_height; self.rewards_pool_updated = false; } + // flush staking storage - top_level.account_root = flush_staking_storage( - &mut self.accounts, - Some(top_level.account_root), - mem::take(&mut self.staking_buffer), - ) - .expect("merkle trie io error") - .expect("merkle trie update should return Some(root)"); + if !self.staking_buffer.is_empty() { + new_state.staking_version = new_state + .staking_version + .checked_add(1) + .expect("staking version overflow, no way to recover"); + top_level.account_root = flush_stakings( + &mut kv_store!(self), + new_state.staking_version, + mem::take(&mut self.staking_buffer), + ) + .expect("merkle trie io error"); + } let app_hash = compute_app_hash( &tree, diff --git a/chain-abci/src/app/end_block.rs b/chain-abci/src/app/end_block.rs index da6b28400..409710e4e 100644 --- a/chain-abci/src/app/end_block.rs +++ b/chain-abci/src/app/end_block.rs @@ -35,7 +35,7 @@ impl ChainNodeApp { // TODO: skipchain-based validator changes? let state = self.last_state.as_mut().expect("executing end block, but no app state stored (i.e. no initchain or recovery was executed)"); let val_updates = state.staking_table.end_block( - &staking_getter!(self, Some(state.top_level.account_root)), + &staking_getter!(self, state.staking_version), state.top_level.network_params.get_max_validators(), ); diff --git a/chain-abci/src/app/macros.rs b/chain-abci/src/app/macros.rs index e8649c746..ef0bee1f3 100644 --- a/chain-abci/src/app/macros.rs +++ b/chain-abci/src/app/macros.rs @@ -2,15 +2,15 @@ //! Ideally these should be methods of `ChainNodeApp`, but that borrow check don't allow that, //! because methods will need to retain the whole reference of `self` rather than individual fields. macro_rules! staking_store { - ($app:expr, $root:expr) => { - chain_storage::buffer::StakingBufferStore::new( - chain_storage::buffer::StakingGetter::new(&$app.accounts, $root), + ($app:expr, $version:expr) => { + chain_storage::jellyfish::StakingBufferStore::new( + chain_storage::jellyfish::StakingGetter::new(&$app.storage, $version), &mut $app.staking_buffer, ) }; - ($app:expr, $root:expr, $buffer_type:expr) => { - chain_storage::buffer::StakingBufferStore::new( - chain_storage::buffer::StakingGetter::new(&$app.accounts, $root), + ($app:expr, $version:expr, $buffer_type:expr) => { + chain_storage::jellyfish::StakingBufferStore::new( + chain_storage::jellyfish::StakingGetter::new(&$app.storage, $version), match $buffer_type { crate::app::app_init::BufferType::Consensus => &mut $app.staking_buffer, crate::app::app_init::BufferType::Mempool => &mut $app.mempool_staking_buffer, @@ -20,15 +20,15 @@ macro_rules! staking_store { } macro_rules! staking_getter { - ($app:expr, $root:expr) => { - chain_storage::buffer::StakingBufferGetter::new( - chain_storage::buffer::StakingGetter::new(&$app.accounts, $root), + ($app:expr, $version:expr) => { + chain_storage::jellyfish::StakingBufferGetter::new( + chain_storage::jellyfish::StakingGetter::new(&$app.storage, $version), &$app.staking_buffer, ) }; - ($app:expr, $root:expr, $buffer_type:expr) => { - chain_storage::buffer::StakingBufferGetter::new( - chain_storage::buffer::StakingGetter::new(&$app.accounts, $root), + ($app:expr, $version:expr, $buffer_type:expr) => { + chain_storage::jellyfish::StakingBufferGetter::new( + chain_storage::jellyfish::StakingGetter::new(&$app.storage, $version), match $buffer_type { crate::app::app_init::BufferType::Consensus => &$app.staking_buffer, crate::app::app_init::BufferType::Mempool => &$app.mempool_staking_buffer, diff --git a/chain-abci/src/app/mod.rs b/chain-abci/src/app/mod.rs index d3f654ee3..9aa4b1027 100644 --- a/chain-abci/src/app/mod.rs +++ b/chain-abci/src/app/mod.rs @@ -14,8 +14,7 @@ use log::info; #[cfg(fuzzing)] pub use self::app_init::check_validators; pub use self::app_init::{ - compute_accounts_root, get_validator_key, init_app_hash, BufferType, ChainNodeApp, - ChainNodeState, + get_validator_key, init_app_hash, BufferType, ChainNodeApp, ChainNodeState, }; use crate::app::validate_tx::ResponseWithCodeAndLog; use crate::enclave_bridge::EnclaveProxy; @@ -141,13 +140,11 @@ impl abci::Application for ChainNodeApp { .last_state .as_mut() .expect("executing begin block, but no app state stored (i.e. no initchain or recovery was executed)"); - let account_root = Some(last_state.top_level.account_root); - last_state.block_time = block_time; last_state.block_height = block_height; let slashes = last_state.staking_table.begin_block( - &mut staking_store!(self, account_root), + &mut staking_store!(self, last_state.staking_version), &last_state.top_level.network_params, block_time, block_height, @@ -193,9 +190,10 @@ impl abci::Application for ChainNodeApp { // FIXME: record based on votes if let Ok(proposer_address) = &proposer_address { - last_state - .staking_table - .reward_record(&staking_getter!(self, account_root), proposer_address); + last_state.staking_table.reward_record( + &staking_getter!(self, last_state.staking_version), + proposer_address, + ); } else { log::error!("invalid proposer address"); } diff --git a/chain-abci/src/app/query.rs b/chain-abci/src/app/query.rs index 407a95adc..223d7aed5 100644 --- a/chain-abci/src/app/query.rs +++ b/chain-abci/src/app/query.rs @@ -2,13 +2,13 @@ use std::convert::{TryFrom, TryInto}; use super::ChainNodeApp; use crate::enclave_bridge::{mock::handle_enc_dec, EnclaveProxy}; -use crate::storage::get_account; use abci::*; use chain_core::common::{MerkleTree, Proof as MerkleProof, H256, HASH_SIZE_256}; use chain_core::state::account::StakedStateAddress; use chain_core::state::tendermint::BlockHeight; use chain_core::state::ChainState; use chain_core::tx::data::{txid_hash, TXID_HASH_ID}; +use chain_storage::jellyfish::get_with_proof; use chain_storage::LookupItem; use parity_scale_codec::{Decode, Encode}; @@ -181,15 +181,15 @@ impl ChainNodeApp { "account" => { let account_address = StakedStateAddress::try_from(_req.data.as_slice()); if let (Some(state), Ok(address)) = (&self.last_state, account_address) { - let account = - get_account(&address, &state.top_level.account_root, &self.accounts); + let (account, _proof) = + get_with_proof(&self.storage, state.staking_version, &address); match account { - Ok(a) => { + Some(a) => { resp.value = a.encode(); // TODO: inclusion proof } - Err(e) => { - resp.log += format!("account lookup failed: {}", e).as_ref(); + None => { + resp.log += "account lookup failed: account not exists"; resp.code = 1; } } diff --git a/chain-abci/src/app/rewards.rs b/chain-abci/src/app/rewards.rs index d46d7e932..8cde9de54 100644 --- a/chain-abci/src/app/rewards.rs +++ b/chain-abci/src/app/rewards.rs @@ -5,7 +5,7 @@ use crate::enclave_bridge::EnclaveProxy; use chain_core::common::fixed::monetary_expansion; use chain_core::init::coin::Coin; use chain_core::state::account::StakedStateAddress; -use chain_storage::buffer::StakingGetter; +use chain_storage::jellyfish::StakingGetter; // rate < 1_000_000, no overflow. fn mul_micro(n: u64, rate: u64) -> u64 { @@ -21,7 +21,6 @@ impl ChainNodeApp { /// Distribute rewards pool pub fn rewards_try_distribute(&mut self) -> Option<(RewardsDistribution, Coin)> { let state = self.last_state.as_mut().unwrap(); - let account_root = Some(state.top_level.account_root); let top_level = &mut state.top_level; let params = &top_level.network_params; @@ -42,7 +41,7 @@ impl ChainNodeApp { let total_staking = state .staking_table - .reward_total_staking(&StakingGetter::new(&self.accounts, account_root)); + .reward_total_staking(&StakingGetter::new(&self.storage, state.staking_version)); let minted = if let Ok(can_mint) = params.get_rewards_monetary_expansion_cap() - top_level.rewards_pool.minted @@ -70,9 +69,10 @@ impl ChainNodeApp { let total_rewards = (top_level.rewards_pool.period_bonus + minted).unwrap(); top_level.rewards_pool.minted = (top_level.rewards_pool.minted + minted).unwrap(); - let (remainer, reward_distribution) = state - .staking_table - .reward_distribute(&mut staking_store!(self, account_root), total_rewards); + let (remainer, reward_distribution) = state.staking_table.reward_distribute( + &mut staking_store!(self, state.staking_version), + total_rewards, + ); top_level.rewards_pool.period_bonus = remainer; Some((reward_distribution, minted)) @@ -97,8 +97,8 @@ mod tests { fn check_rewards_distribution() { let expansion_cap = Coin::new(10_0000_0000_0000_0000).unwrap(); let dist = Coin::new(10_0000_0000_0000_0000).unwrap(); - let (env, storage, account_storage) = ChainEnv::new(dist, expansion_cap, 2); - let mut app = env.chain_node(storage, account_storage); + let (env, storage) = ChainEnv::new(dist, expansion_cap, 2); + let mut app = env.chain_node(storage); let _rsp = app.init_chain(&env.req_init_chain()); let total_staking = dist; @@ -163,8 +163,8 @@ mod tests { #[test] fn empty_block_should_not_change_app_hash() { - let (env, storage, account_storage) = ChainEnv::new(Coin::max(), Coin::zero(), 1); - let mut app = env.chain_node(storage, account_storage); + let (env, storage) = ChainEnv::new(Coin::max(), Coin::zero(), 1); + let mut app = env.chain_node(storage); let _rsp_init_chain = app.init_chain(&env.req_init_chain()); let mut req = env.req_begin_block(1, 0); diff --git a/chain-abci/src/app/validate_tx.rs b/chain-abci/src/app/validate_tx.rs index f404a8bd8..7e33ee916 100644 --- a/chain-abci/src/app/validate_tx.rs +++ b/chain-abci/src/app/validate_tx.rs @@ -71,7 +71,6 @@ impl ChainNodeApp { BufferType::Consensus => self.last_state.as_mut().expect("expect last_state"), BufferType::Mempool => self.mempool_state.as_mut().expect("expect last_state"), }; - let account_root = Some(state.top_level.account_root); let txaux = TxAux::decode(&mut req.tx())?; let txid = txaux.tx_id(); let (fee, maccount) = match &txaux { @@ -80,12 +79,12 @@ impl ChainNodeApp { &mut self.tx_validator, &tx, &extra_info, - &staking_getter!(self, account_root, buffer_type), + &staking_getter!(self, state.staking_version, buffer_type), &kv_store!(self, buffer_type), )?; // execute the action let maccount = execute_enclave_tx( - &mut staking_store!(self, account_root, buffer_type), + &mut staking_store!(self, state.staking_version, buffer_type), &mut kv_store!(self, buffer_type), state, &txid, @@ -94,7 +93,7 @@ impl ChainNodeApp { (action.fee(), maccount) } TxAux::PublicTx(tx) => process_public_tx( - &mut staking_store!(self, account_root, buffer_type), + &mut staking_store!(self, state.staking_version, buffer_type), &mut state.staking_table, &extra_info, &tx, diff --git a/chain-abci/src/main.rs b/chain-abci/src/main.rs index 8588028af..e3f944f67 100644 --- a/chain-abci/src/main.rs +++ b/chain-abci/src/main.rs @@ -9,7 +9,6 @@ use chain_abci::enclave_bridge::mock::MockClient; #[cfg(all(not(feature = "mock-validation"), target_os = "linux"))] use chain_abci::enclave_bridge::real::TxValidationApp; use chain_core::init::network::{get_network, get_network_id, init_chain_id}; -use chain_storage::account::AccountStorage; use chain_storage::{Storage, StorageConfig, StorageType}; #[cfg(any(feature = "mock-validation", not(target_os = "linux")))] use log::warn; @@ -186,11 +185,6 @@ fn main() { &config.genesis_app_hash.unwrap(), &config.chain_id.unwrap(), storage, - AccountStorage::new( - Storage::new(&StorageConfig::new(&opt.data, StorageType::AccountTrie)), - 20, - ) - .expect("account db"), config.tx_query, config.enclave_server, ), diff --git a/chain-abci/src/staking/table.rs b/chain-abci/src/staking/table.rs index 9d36427f6..b5cca4dd9 100644 --- a/chain-abci/src/staking/table.rs +++ b/chain-abci/src/staking/table.rs @@ -19,7 +19,7 @@ use chain_core::state::account::{ use chain_core::state::tendermint::{ BlockHeight, TendermintValidatorAddress, TendermintValidatorPubKey, TendermintVotePower, }; -use chain_storage::buffer::{Get, GetStaking, StakingGetter, StoreStaking}; +use chain_storage::buffer::{GetStaking, StoreStaking}; use crate::liveness::LivenessTracker; @@ -214,7 +214,7 @@ impl StakingTable { } /// The heap should not use the uncommited buffer. - pub fn reward_total_staking(&self, heap: &StakingGetter) -> Coin { + pub fn reward_total_staking(&self, heap: &impl GetStaking) -> Coin { // Sum of all the coins should not overflow max supply, TODO proof. sum_coins( self.chosen_validators diff --git a/chain-abci/src/storage/mod.rs b/chain-abci/src/storage/mod.rs index 9fee03b04..5d1b71862 100644 --- a/chain-abci/src/storage/mod.rs +++ b/chain-abci/src/storage/mod.rs @@ -3,13 +3,10 @@ use crate::staking::StakingTable; use crate::tx_error::PublicTxError; use chain_core::common::Timespec; use chain_core::init::coin::Coin; -use chain_core::state::account::{StakedState, StakedStateAddress, StakedStateOpAttributes}; +use chain_core::state::account::{StakedStateAddress, StakedStateOpAttributes}; use chain_core::tx::data::input::{TxoPointer, TxoSize}; use chain_core::tx::fee::Fee; use chain_core::tx::{TransactionId, TxEnclaveAux, TxObfuscated, TxPublicAux}; -use chain_storage::account::{ - get_staked_state, AccountStorage, StakedStateError, StarlingFixedKey, -}; use chain_storage::buffer::{GetKV, GetStaking, StoreStaking}; use chain_tx_validation::{verify_unjailed, witness::verify_tx_recover_address, ChainInfo, Error}; use enclave_protocol::{IntraEnclaveRequest, IntraEnclaveResponseOk, SealedLog}; @@ -84,19 +81,6 @@ impl TxEnclaveAction { } } -/// checks that the account can be retrieved from the trie storage -pub fn get_account( - account_address: &StakedStateAddress, - last_root: &StarlingFixedKey, - accounts: &AccountStorage, -) -> Result { - match get_staked_state(account_address, last_root, accounts) { - Ok(a) => Ok(a), - Err(StakedStateError::NotFound) => Err(Error::AccountNotFound), - Err(StakedStateError::IoError(_e)) => Err(Error::IoError), - } -} - fn check_spent_input_lookup( kvdb: &impl GetKV, inputs: &[TxoPointer], diff --git a/chain-abci/tests/abci_app.rs b/chain-abci/tests/abci_app.rs index 3c7f7c88e..6a4ce37d2 100644 --- a/chain-abci/tests/abci_app.rs +++ b/chain-abci/tests/abci_app.rs @@ -14,7 +14,7 @@ use chain_core::init::config::{ JailingParameters, RewardsParameters, SlashRatio, SlashingParameters, }; use chain_core::state::account::{ - to_stake_key, ConfidentialInit, CouncilNode, DepositBondTx, StakedState, StakedStateAddress, + ConfidentialInit, CouncilNode, DepositBondTx, StakedState, StakedStateAddress, StakedStateDestination, StakedStateOpAttributes, StakedStateOpWitness, UnbondTx, WithdrawUnbondedTx, }; @@ -41,9 +41,6 @@ use chain_core::tx::{ witness::{TxInWitness, TxWitness}, TxAux, TxEnclaveAux, TxPublicAux, }; -use chain_storage::account::AccountStorage; -use chain_storage::account::AccountWrapper; -use chain_storage::account::StarlingFixedKey; use chain_storage::buffer::Get; use chain_storage::{ LookupItem, Storage, COL_NODE_INFO, GENESIS_APP_HASH_KEY, LAST_STATE_KEY, NUM_COLUMNS, @@ -79,10 +76,6 @@ fn create_db() -> Arc { Arc::new(create(NUM_COLUMNS)) } -fn create_account_db() -> AccountStorage { - AccountStorage::new(Storage::new_db(Arc::new(create(1))), 20).expect("account db") -} - const TEST_CHAIN_ID: &str = "test-00"; #[test] @@ -94,7 +87,6 @@ fn proper_hash_and_chainid_should_be_stored() { example_hash, TEST_CHAIN_ID, Storage::new_db(db.clone()), - create_account_db(), None, None, ); @@ -115,7 +107,6 @@ fn too_long_hash_should_panic() { example_hash, TEST_CHAIN_ID, Storage::new_db(db.clone()), - create_account_db(), None, None, ); @@ -131,7 +122,6 @@ fn chain_id_without_hex_digits_should_panic() { example_hash, "test", Storage::new_db(db.clone()), - create_account_db(), None, None, ); @@ -147,7 +137,6 @@ fn nonhex_hash_should_panic() { example_hash, TEST_CHAIN_ID, Storage::new_db(db.clone()), - create_account_db(), None, None, ); @@ -186,6 +175,7 @@ fn get_dummy_app_state(app_hash: H256) -> ChainNodeState { block_height: BlockHeight::genesis(), genesis_time: 0, staking_table: StakingTable::default(), + staking_version: 0, top_level: ChainState { account_root: [0u8; 32], rewards_pool: RewardsPoolState::new(0, params.get_rewards_monetary_expansion_tau()), @@ -216,7 +206,6 @@ fn previously_stored_hash_should_match() { example_hash2, TEST_CHAIN_ID, Storage::new_db(db.clone()), - create_account_db(), None, None, ); @@ -271,17 +260,8 @@ fn init_chain_for(address: RedeemAddress) -> ChainNodeApp { let result = c.validate_config_get_genesis(t.get_seconds().try_into().unwrap()); if let Ok((accounts, rp, _nodes)) = result { let tx_tree = MerkleTree::empty(); - let mut account_tree = - AccountStorage::new(Storage::new_db(Arc::new(create(1))), 20).expect("account db"); - - let mut keys: Vec = accounts.iter().map(|x| x.key()).collect(); - // TODO: get rid of the extra allocations - let wrapped: Vec = - accounts.iter().map(|x| AccountWrapper(x.clone())).collect(); - let new_account_root = account_tree - .insert(None, &mut keys, &wrapped) - .expect("initial insert"); - + let mut storage = Storage::new_db(db.clone()); + let new_account_root = storage.put_stakings(0, &accounts); let genesis_app_hash = compute_app_hash( &tx_tree, &new_account_root, @@ -294,8 +274,7 @@ fn init_chain_for(address: RedeemAddress) -> ChainNodeApp { get_enclave_bridge_mock(), &example_hash, TEST_CHAIN_ID, - Storage::new_db(db.clone()), - create_account_db(), + storage, None, None, ); @@ -322,15 +301,9 @@ fn init_chain_should_create_db_items() { ChainNodeState::decode(&mut app.storage.get_last_app_state().unwrap().as_slice()).unwrap(); assert_eq!(genesis_app_hash, state.last_apphash); - let key = to_stake_key(&address.into()); - assert_eq!( - 1, - app.accounts - .get(&state.top_level.account_root, &mut [key]) - .expect("account") - .iter() - .count() - ); + app.staking_getter_committed() + .get(&address.into()) + .expect("account not exists"); } #[test] @@ -376,7 +349,6 @@ fn init_chain_panics_with_different_app_hash() { &example_hash, TEST_CHAIN_ID, Storage::new_db(db.clone()), - create_account_db(), None, None, ); @@ -397,7 +369,6 @@ fn init_chain_panics_with_empty_app_bytes() { &example_hash, TEST_CHAIN_ID, Storage::new_db(db.clone()), - create_account_db(), None, None, ); @@ -1035,11 +1006,11 @@ fn all_valid_tx_types_should_commit() { #[test] fn query_should_return_proof_for_committed_tx() { - let (env, storage, account_storage) = + let (env, storage) = ChainEnv::new_with_customizer(Coin::max(), Coin::zero(), 2, |parameters| { parameters.required_council_node_stake = (Coin::max() / 10).unwrap(); }); - let mut app = env.chain_node(storage, account_storage); + let mut app = env.chain_node(storage); let _rsp = app.init_chain(&env.req_init_chain()); app.begin_block(&env.req_begin_block(1, 0)); diff --git a/chain-abci/tests/punishment.rs b/chain-abci/tests/punishment.rs index e0cf4036b..c76a552e8 100644 --- a/chain-abci/tests/punishment.rs +++ b/chain-abci/tests/punishment.rs @@ -7,11 +7,11 @@ use test_common::chain_env::{get_account, ChainEnv}; #[test] fn end_block_should_update_liveness_tracker() { // Init Chain - let (env, storage, account_storage) = + let (env, storage) = ChainEnv::new_with_customizer(Coin::max(), Coin::zero(), 1, |parameters| { parameters.required_council_node_stake = Coin::max(); }); - let mut app = env.chain_node(storage, account_storage); + let mut app = env.chain_node(storage); let _rsp = app.init_chain(&env.req_init_chain()); // Begin Block @@ -49,8 +49,8 @@ fn end_block_should_update_liveness_tracker() { #[test] fn begin_block_should_jail_byzantine_validators() { // Init Chain - let (env, storage, account_storage) = ChainEnv::new(Coin::max(), Coin::zero(), 1); - let mut app = env.chain_node(storage, account_storage); + let (env, storage) = ChainEnv::new(Coin::max(), Coin::zero(), 1); + let mut app = env.chain_node(storage); let _rsp_init_chain = app.init_chain(&env.req_init_chain()); // Begin Block @@ -73,8 +73,8 @@ fn begin_block_should_jail_byzantine_validators() { #[test] fn begin_block_should_punish_non_live_validators() { // Init Chain - let (env, storage, account_storage) = ChainEnv::new(Coin::max(), Coin::zero(), 1); - let mut app = env.chain_node(storage, account_storage); + let (env, storage) = ChainEnv::new(Coin::max(), Coin::zero(), 1); + let mut app = env.chain_node(storage); let _rsp_init_chain = app.init_chain(&env.req_init_chain()); // Begin Block @@ -100,8 +100,8 @@ fn begin_block_should_punish_non_live_validators() { #[test] fn begin_block_should_slash_byzantine_validators() { // Init Chain - let (env, storage, account_storage) = ChainEnv::new(Coin::max(), Coin::zero(), 1); - let mut app = env.chain_node(storage, account_storage); + let (env, storage) = ChainEnv::new(Coin::max(), Coin::zero(), 1); + let mut app = env.chain_node(storage); let _rsp_init_chain = app.init_chain(&env.req_init_chain()); // Begin Block @@ -133,8 +133,8 @@ fn begin_block_should_slash_byzantine_validators() { #[test] fn begin_block_should_slash_non_live_validators() { // Init Chain - let (env, storage, account_storage) = ChainEnv::new(Coin::max(), Coin::zero(), 1); - let mut app = env.chain_node(storage, account_storage); + let (env, storage) = ChainEnv::new(Coin::max(), Coin::zero(), 1); + let mut app = env.chain_node(storage); let _rsp_init_chain = app.init_chain(&env.req_init_chain()); // Begin Block diff --git a/chain-abci/tests/tx_validation.rs b/chain-abci/tests/tx_validation.rs index 4cce61952..8778d639d 100644 --- a/chain-abci/tests/tx_validation.rs +++ b/chain-abci/tests/tx_validation.rs @@ -38,10 +38,8 @@ use chain_core::tx::PlainTxAux; use chain_core::tx::TransactionId; use chain_core::tx::TxObfuscated; use chain_core::tx::{TxAux, TxEnclaveAux, TxPublicAux}; -use chain_storage::account::AccountStorage; -use chain_storage::account::AccountWrapper; -use chain_storage::account::StarlingFixedKey; -use chain_storage::buffer::{Get, StakingBufferStore, StakingGetter}; +use chain_storage::buffer::Get; +use chain_storage::jellyfish::{StakingBufferStore, StakingGetter, Version}; use chain_storage::{Storage, COL_ENCLAVE_TX, COL_TX_META, NUM_COLUMNS}; use chain_tx_validation::{ verify_bonded_deposit_core, verify_transfer, verify_unbonded_withdraw_core, ChainInfo, Error, @@ -60,15 +58,14 @@ fn verify_enclave_tx( tx_validator: &mut T, txaux: &TxEnclaveAux, extra_info: &ChainInfo, - root: &StarlingFixedKey, + version: Version, storage: &Storage, - accounts: &AccountStorage, ) -> Result { verify_enclave_tx_inner( tx_validator, txaux, &extra_info, - &StakingGetter::new(&accounts, Some(*root)), + &StakingGetter::new(storage, version), storage, ) } @@ -77,18 +74,14 @@ fn verify_public_tx( txaux: &TxPublicAux, extra_info: &ChainInfo, info: NodeInfoWrap, - root: &StarlingFixedKey, - accounts: &AccountStorage, + version: Version, + storage: &Storage, ) -> Result<(Fee, Option), TxError> { - let mut tbl = StakingTable::from_genesis( - &StakingGetter::new(&accounts, Some(*root)), - info.0, - 50, - &info.1, - ); + let mut tbl = + StakingTable::from_genesis(&StakingGetter::new(storage, version), info.0, 50, &info.1); let mut buffer = HashMap::new(); - let mut store = StakingBufferStore::new(StakingGetter::new(accounts, Some(*root)), &mut buffer); + let mut store = StakingBufferStore::new(StakingGetter::new(storage, version), &mut buffer); let (fee, maddress) = process_public_tx(&mut store, &mut tbl, extra_info, txaux)?; Ok((fee, maddress.map(|addr| store.get(&addr).unwrap()))) } @@ -235,7 +228,7 @@ fn prepare_app_valid_transfer_tx( TxWitness, MerkleTree, SecretKey, - AccountStorage, + Storage, ) { let (db, txp, addr, merkle_tree, secret_key) = prepate_init_tx(timelocked); let secp = Secp256k1::new(); @@ -260,33 +253,25 @@ fn prepare_app_valid_transfer_tx( }, }; ( - db, + db.clone(), txaux, tx, witness.into(), merkle_tree, secret_key, - AccountStorage::new(Storage::new_db(create_db()), 20).expect("account db"), + Storage::new_db(db), ) } -fn prepare_app_valid_unbond_tx() -> ( - TxPublicAux, - UnbondTx, - SecretKey, - AccountStorage, - StarlingFixedKey, -) { - let mut tree = AccountStorage::new(Storage::new_db(create_db()), 20).expect("account db"); +fn prepare_app_valid_unbond_tx() -> (TxPublicAux, UnbondTx, SecretKey, Storage) { + let mut storage = Storage::new_db(create_db()); let secp = Secp256k1::new(); let secret_key = SecretKey::from_slice(&[0xcd; 32]).expect("32 bytes, within curve order"); let public_key = PublicKey::from_secret_key(&secp, &secret_key); let addr = RedeemAddress::from(&public_key); let account = StakedState::new(1, Coin::one(), Coin::zero(), 0, addr.into(), None); - let key = account.key(); - let wrapped = AccountWrapper(account); - let new_root = tree.insert(None, &mut [key], &[wrapped]).expect("insert"); + storage.put_stakings(0, &[account]); let tx = UnbondTx::new( addr.into(), 1, @@ -295,38 +280,26 @@ fn prepare_app_valid_unbond_tx() -> ( ); let witness = get_account_op_witness(secp, &tx.id(), &secret_key); let txaux = TxPublicAux::UnbondStakeTx(tx.clone(), witness); - (txaux, tx, secret_key, tree, new_root) + (txaux, tx, secret_key, storage) } #[test] fn existing_account_unbond_tx_should_verify() { - let (txaux, _, _, accounts, last_account_root_hash) = prepare_app_valid_unbond_tx(); + let (txaux, _, _, storage) = prepare_app_valid_unbond_tx(); let extra_info = get_chain_info_pub(&txaux); - let result = verify_public_tx( - &txaux, - &extra_info, - NodeInfoWrap::default(), - &last_account_root_hash, - &accounts, - ); + let result = verify_public_tx(&txaux, &extra_info, NodeInfoWrap::default(), 0, &storage); assert!(result.is_ok()); } #[test] fn test_account_unbond_verify_fail() { - let (txaux, tx, secret_key, accounts, last_account_root_hash) = prepare_app_valid_unbond_tx(); + let (txaux, tx, secret_key, storage) = prepare_app_valid_unbond_tx(); let extra_info = get_chain_info_pub(&txaux); // WrongChainHexId { let mut extra_info = extra_info; extra_info.chain_hex_id = DEFAULT_CHAIN_ID + 1; - let result = verify_public_tx( - &txaux, - &extra_info, - NodeInfoWrap::default(), - &last_account_root_hash, - &accounts, - ); + let result = verify_public_tx(&txaux, &extra_info, NodeInfoWrap::default(), 0, &storage); expect_error_public(&result, PublicTxError::WrongChainHexId); } // UnsupportedVersion @@ -337,13 +310,7 @@ fn test_account_unbond_verify_fail() { tx.clone(), get_account_op_witness(Secp256k1::new(), &tx.id(), &secret_key), ); - let result = verify_public_tx( - &txaux, - &extra_info, - NodeInfoWrap::default(), - &last_account_root_hash, - &accounts, - ); + let result = verify_public_tx(&txaux, &extra_info, NodeInfoWrap::default(), 0, &storage); expect_error_public(&result, PublicTxError::UnsupportedVersion); } // AccountNotFound, non exist account treated as a default account, so incorrect nonce. @@ -352,8 +319,8 @@ fn test_account_unbond_verify_fail() { &txaux, &extra_info, NodeInfoWrap::default(), - &[0; 32], - &accounts, + 0, + &create_storage(), ); expect_error_public(&result, PublicTxError::IncorrectNonce); } @@ -365,13 +332,7 @@ fn test_account_unbond_verify_fail() { tx.clone(), get_account_op_witness(Secp256k1::new(), &tx.id(), &secret_key), ); - let result = verify_public_tx( - &txaux, - &extra_info, - NodeInfoWrap::default(), - &last_account_root_hash, - &accounts, - ); + let result = verify_public_tx(&txaux, &extra_info, NodeInfoWrap::default(), 0, &storage); expect_error_public(&result, PublicTxError::IncorrectNonce); } // ZeroCoin @@ -382,13 +343,7 @@ fn test_account_unbond_verify_fail() { tx.clone(), get_account_op_witness(Secp256k1::new(), &tx.id(), &secret_key), ); - let result = verify_public_tx( - &txaux, - &extra_info, - NodeInfoWrap::default(), - &last_account_root_hash, - &accounts, - ); + let result = verify_public_tx(&txaux, &extra_info, NodeInfoWrap::default(), 0, &storage); expect_error_unbond(&result, UnbondError::ZeroValue); } // InputOutputDoNotMatch @@ -399,13 +354,7 @@ fn test_account_unbond_verify_fail() { tx.clone(), get_account_op_witness(Secp256k1::new(), &tx.id(), &secret_key), ); - let result = verify_public_tx( - &txaux, - &extra_info, - NodeInfoWrap::default(), - &last_account_root_hash, - &accounts, - ); + let result = verify_public_tx(&txaux, &extra_info, NodeInfoWrap::default(), 0, &storage); expect_error_unbond(&result, UnbondError::CoinError(CoinError::Negative)); } } @@ -418,10 +367,9 @@ fn prepare_app_valid_withdraw_tx( StakedStateOpWitness, StakedState, SecretKey, - AccountStorage, - StarlingFixedKey, + Storage, ) { - let mut tree = AccountStorage::new(Storage::new_db(create_db()), 20).expect("account db"); + let mut storage = create_storage(); let secp = Secp256k1::new(); let secret_key = SecretKey::from_slice(&[0xcd; 32]).expect("32 bytes, within curve order"); let public_key = PublicKey::from_secret_key(&secp, &secret_key); @@ -435,9 +383,7 @@ fn prepare_app_valid_withdraw_tx( addr.into(), None, ); - let key = account.key(); - let wrapped = AccountWrapper(account.clone()); - let new_root = tree.insert(None, &mut [key], &[wrapped]).expect("insert"); + storage.put_stakings(0, &[account.clone()]); let sk2 = SecretKey::from_slice(&[0x11; 32]).expect("32 bytes, within curve order"); @@ -462,43 +408,33 @@ fn prepare_app_valid_withdraw_tx( txpayload: PlainTxAux::WithdrawUnbondedStakeTx(tx.clone()).encode(), }, }; - (txaux, tx, witness, account, secret_key, tree, new_root) + (txaux, tx, witness, account, secret_key, storage) } #[test] fn existing_account_withdraw_tx_should_verify() { - let (txaux, _, _, _, _, accounts, last_account_root_hash) = prepare_app_valid_withdraw_tx(0); + let (txaux, _, _, _, _, storage) = prepare_app_valid_withdraw_tx(0); let extra_info = get_chain_info_enc(&txaux); let result = verify_enclave_tx( &mut get_enclave_bridge_mock(), &txaux, &extra_info, - &last_account_root_hash, - &create_storage(), - &accounts, + 0, + &storage, ); assert!(result.is_ok()); } #[test] fn test_account_withdraw_verify_fail() { - let storage = create_storage(); - let (txaux, tx, _, account, secret_key, accounts, last_account_root_hash) = - prepare_app_valid_withdraw_tx(0); + let (txaux, tx, _, account, secret_key, storage) = prepare_app_valid_withdraw_tx(0); let extra_info = get_chain_info_enc(&txaux); let mut mock_bridge = get_enclave_bridge_mock(); // WrongChainHexId { let mut extra_info = extra_info; extra_info.chain_hex_id = DEFAULT_CHAIN_ID + 1; - let result = verify_enclave_tx( - &mut mock_bridge, - &txaux, - &extra_info, - &last_account_root_hash, - &storage, - &accounts, - ); + let result = verify_enclave_tx(&mut mock_bridge, &txaux, &extra_info, 0, &storage); assert!(result.is_err()); let result = verify_unbonded_withdraw_core(&tx, &extra_info, &account); expect_error(&result, Error::WrongChainHexId); @@ -514,14 +450,7 @@ fn test_account_withdraw_verify_fail() { Some(witness), None, ); - let result = verify_enclave_tx( - &mut mock_bridge, - &txaux, - &extra_info, - &last_account_root_hash, - &storage, - &accounts, - ); + let result = verify_enclave_tx(&mut mock_bridge, &txaux, &extra_info, 0, &storage); assert!(result.is_err()); let result = verify_unbonded_withdraw_core(&tx, &extra_info, &account); expect_error(&result, Error::UnsupportedVersion); @@ -537,14 +466,7 @@ fn test_account_withdraw_verify_fail() { Some(witness), None, ); - let result = verify_enclave_tx( - &mut mock_bridge, - &txaux, - &extra_info, - &last_account_root_hash, - &storage, - &accounts, - ); + let result = verify_enclave_tx(&mut mock_bridge, &txaux, &extra_info, 0, &storage); assert!(result.is_err()); let result = verify_unbonded_withdraw_core(&tx, &extra_info, &account); expect_error(&result, Error::NoOutputs); @@ -560,14 +482,7 @@ fn test_account_withdraw_verify_fail() { Some(witness), None, ); - let result = verify_enclave_tx( - &mut mock_bridge, - &txaux, - &extra_info, - &last_account_root_hash, - &storage, - &accounts, - ); + let result = verify_enclave_tx(&mut mock_bridge, &txaux, &extra_info, 0, &storage); assert!(result.is_err()); let result = verify_unbonded_withdraw_core(&tx, &extra_info, &account); expect_error(&result, Error::ZeroCoin); @@ -585,14 +500,7 @@ fn test_account_withdraw_verify_fail() { Some(witness), None, ); - let result = verify_enclave_tx( - &mut mock_bridge, - &txaux, - &extra_info, - &last_account_root_hash, - &storage, - &accounts, - ); + let result = verify_enclave_tx(&mut mock_bridge, &txaux, &extra_info, 0, &storage); assert!(result.is_err()); let result = verify_unbonded_withdraw_core(&tx, &extra_info, &account); expect_error( @@ -611,28 +519,14 @@ fn test_account_withdraw_verify_fail() { Some(witness), None, ); - let result = verify_enclave_tx( - &mut mock_bridge, - &txaux, - &extra_info, - &last_account_root_hash, - &storage, - &accounts, - ); + let result = verify_enclave_tx(&mut mock_bridge, &txaux, &extra_info, 0, &storage); assert!(result.is_err()); let result = verify_unbonded_withdraw_core(&tx, &extra_info, &account); expect_error(&result, Error::InputOutputDoNotMatch); } // AccountNotFound { - let result = verify_enclave_tx( - &mut mock_bridge, - &txaux, - &extra_info, - &[0; 32], - &storage, - &accounts, - ); + let result = verify_enclave_tx(&mut mock_bridge, &txaux, &extra_info, 0, &create_storage()); expect_error(&result, Error::AccountNotFound); } // AccountIncorrectNonce @@ -646,14 +540,7 @@ fn test_account_withdraw_verify_fail() { Some(witness), None, ); - let result = verify_enclave_tx( - &mut mock_bridge, - &txaux, - &extra_info, - &last_account_root_hash, - &storage, - &accounts, - ); + let result = verify_enclave_tx(&mut mock_bridge, &txaux, &extra_info, 0, &storage); assert!(result.is_err()); let result = verify_unbonded_withdraw_core(&tx, &extra_info, &account); expect_error(&result, Error::AccountIncorrectNonce); @@ -669,30 +556,15 @@ fn test_account_withdraw_verify_fail() { Some(witness), None, ); - let result = verify_enclave_tx( - &mut mock_bridge, - &txaux, - &extra_info, - &last_account_root_hash, - &storage, - &accounts, - ); + let result = verify_enclave_tx(&mut mock_bridge, &txaux, &extra_info, 0, &storage); assert!(result.is_err()); let result = verify_unbonded_withdraw_core(&tx, &extra_info, &account); expect_error(&result, Error::AccountWithdrawOutputNotLocked); } // AccountNotUnbonded { - let (txaux, _, _, account, _, accounts, last_account_root_hash) = - prepare_app_valid_withdraw_tx(20); - let result = verify_enclave_tx( - &mut mock_bridge, - &txaux, - &extra_info, - &last_account_root_hash, - &storage, - &accounts, - ); + let (txaux, _, _, account, _, storage) = prepare_app_valid_withdraw_tx(20); + let result = verify_enclave_tx(&mut mock_bridge, &txaux, &extra_info, 0, &storage); assert!(result.is_err()); let result = verify_unbonded_withdraw_core(&tx, &extra_info, &account); expect_error(&result, Error::AccountNotUnbonded); @@ -707,7 +579,7 @@ fn prepare_app_valid_deposit_tx( DepositBondTx, TxWitness, SecretKey, - AccountStorage, + Storage, ) { let (db, txp, _, merkle_tree, secret_key) = prepate_init_tx(timelocked); let secp = Secp256k1::new(); @@ -731,12 +603,12 @@ fn prepare_app_valid_deposit_tx( }, }; ( - db, + db.clone(), txaux, tx, witness.into(), secret_key, - AccountStorage::new(create_storage(), 20).expect("account db"), + Storage::new_db(db), ) } @@ -745,30 +617,11 @@ const DEFAULT_CHAIN_ID: u8 = 0; #[test] fn existing_utxo_input_tx_should_verify() { let mut mock_bridge = get_enclave_bridge_mock(); - let (db, txaux, _, _, _, _, accounts) = prepare_app_valid_transfer_tx(false); - let storage = Storage::new_db(db); + let (_, txaux, _, _, _, _, storage) = prepare_app_valid_transfer_tx(false); let extra_info = get_chain_info_enc(&txaux); - let last_account_root_hash = [0u8; 32]; - let result = verify_enclave_tx( - &mut mock_bridge, - &txaux, - &extra_info, - &last_account_root_hash, - &storage, - &accounts, - ); - assert!(result.is_ok()); - let (db, txaux, _, _, _, accounts) = prepare_app_valid_deposit_tx(false); - let storage = Storage::new_db(db); - let result = verify_enclave_tx( - &mut mock_bridge, - &txaux, - &extra_info, - &last_account_root_hash, - &storage, - &accounts, - ); - assert!(result.is_ok()); + verify_enclave_tx(&mut mock_bridge, &txaux, &extra_info, 0, &storage).unwrap(); + let (_, txaux, _, _, _, storage) = prepare_app_valid_deposit_tx(false); + verify_enclave_tx(&mut mock_bridge, &txaux, &extra_info, 0, &storage).unwrap(); } fn expect_error(res: &Result, expected: Error) @@ -820,22 +673,13 @@ fn expect_error_unjail(res: &Result, expected: UnjailError) { #[test] fn test_deposit_verify_fail() { let mut mock_bridge = get_enclave_bridge_mock(); - let (db, txaux, tx, witness, secret_key, accounts) = prepare_app_valid_deposit_tx(false); - let storage = Storage::new_db(db.clone()); + let (db, txaux, tx, witness, secret_key, storage) = prepare_app_valid_deposit_tx(false); let extra_info = get_chain_info_enc(&txaux); - let last_account_root_hash = [0u8; 32]; // WrongChainHexId { let mut extra_info = extra_info; extra_info.chain_hex_id = DEFAULT_CHAIN_ID + 1; - let result = verify_enclave_tx( - &mut mock_bridge, - &txaux, - &extra_info, - &last_account_root_hash, - &storage, - &accounts, - ); + let result = verify_enclave_tx(&mut mock_bridge, &txaux, &extra_info, 0, &storage); assert!(result.is_err()); let result = verify_bonded_deposit_core(&tx, &witness, &extra_info, vec![]); expect_error(&result, Error::WrongChainHexId); @@ -850,14 +694,7 @@ fn test_deposit_verify_fail() { None, Some(tx.clone()), ); - let result = verify_enclave_tx( - &mut mock_bridge, - &txaux, - &extra_info, - &last_account_root_hash, - &storage, - &accounts, - ); + let result = verify_enclave_tx(&mut mock_bridge, &txaux, &extra_info, 0, &storage); assert!(result.is_err()); let result = verify_bonded_deposit_core(&tx, &witness, &extra_info, vec![]); expect_error(&result, Error::UnsupportedVersion); @@ -872,14 +709,7 @@ fn test_deposit_verify_fail() { None, Some(tx.clone()), ); - let result = verify_enclave_tx( - &mut mock_bridge, - &txaux, - &extra_info, - &last_account_root_hash, - &storage, - &accounts, - ); + let result = verify_enclave_tx(&mut mock_bridge, &txaux, &extra_info, 0, &storage); assert!(result.is_err()); let result = verify_bonded_deposit_core(&tx, &witness, &extra_info, vec![]); expect_error(&result, Error::NoInputs); @@ -895,14 +725,7 @@ fn test_deposit_verify_fail() { None, Some(tx.clone()), ); - let result = verify_enclave_tx( - &mut mock_bridge, - &txaux, - &extra_info, - &last_account_root_hash, - &storage, - &accounts, - ); + let result = verify_enclave_tx(&mut mock_bridge, &txaux, &extra_info, 0, &storage); assert!(result.is_err()); let result = verify_bonded_deposit_core(&tx, &witness, &extra_info, vec![]); expect_error(&result, Error::DuplicateInputs); @@ -918,14 +741,7 @@ fn test_deposit_verify_fail() { None, None, ); - let result = verify_enclave_tx( - &mut mock_bridge, - &txaux, - &extra_info, - &last_account_root_hash, - &storage, - &accounts, - ); + let result = verify_enclave_tx(&mut mock_bridge, &txaux, &extra_info, 0, &storage); assert!(result.is_err()); let result = verify_bonded_deposit_core(&tx, &witness, &extra_info, vec![]); expect_error(&result, Error::UnexpectedWitnesses); @@ -938,14 +754,7 @@ fn test_deposit_verify_fail() { None, None, ); - let result = verify_enclave_tx( - &mut mock_bridge, - &txaux, - &extra_info, - &last_account_root_hash, - &storage, - &accounts, - ); + let result = verify_enclave_tx(&mut mock_bridge, &txaux, &extra_info, 0, &storage); assert!(result.is_err()); let result = verify_bonded_deposit_core(&tx, &vec![].into(), &extra_info, vec![]); expect_error(&result, Error::MissingWitnesses); @@ -960,14 +769,7 @@ fn test_deposit_verify_fail() { ); db.write(inittx).unwrap(); - let result = verify_enclave_tx( - &mut mock_bridge, - &txaux, - &extra_info, - &last_account_root_hash, - &storage, - &accounts, - ); + let result = verify_enclave_tx(&mut mock_bridge, &txaux, &extra_info, 0, &storage); expect_error(&result, Error::InputSpent); let mut reset = db.transaction(); @@ -1013,40 +815,19 @@ fn test_deposit_verify_fail() { None, None, ); - let result = verify_enclave_tx( - &mut mock_bridge, - &txaux, - &extra_info, - &last_account_root_hash, - &storage, - &accounts, - ); + let result = verify_enclave_tx(&mut mock_bridge, &txaux, &extra_info, 0, &storage); assert!(result.is_err()); } // InvalidInput { - let result = verify_enclave_tx( - &mut mock_bridge, - &txaux, - &extra_info, - &last_account_root_hash, - &create_storage(), - &accounts, - ); + let result = verify_enclave_tx(&mut mock_bridge, &txaux, &extra_info, 0, &create_storage()); expect_error(&result, Error::InvalidInput); } // InputOutputDoNotMatch { let mut extra_info = extra_info; extra_info.min_fee_computed = Fee::new(Coin::one()); - let result = verify_enclave_tx( - &mut mock_bridge, - &txaux, - &extra_info, - &last_account_root_hash, - &storage, - &accounts, - ); + let result = verify_enclave_tx(&mut mock_bridge, &txaux, &extra_info, 0, &storage); assert!(result.is_err()); let result = verify_bonded_deposit_core(&tx, &witness, &extra_info, vec![]); expect_error(&result, Error::InputOutputDoNotMatch); @@ -1130,23 +911,14 @@ fn replace_tx_payload( #[test] fn test_transfer_verify_fail() { let mut mock_bridge = get_enclave_bridge_mock(); - let (db, txaux, tx, witness, merkle_tree, secret_key, accounts) = + let (db, txaux, tx, witness, merkle_tree, secret_key, storage) = prepare_app_valid_transfer_tx(false); - let storage = Storage::new_db(db.clone()); let extra_info = get_chain_info_enc(&txaux); - let last_account_root_hash = [0u8; 32]; // WrongChainHexId { let mut extra_info = extra_info; extra_info.chain_hex_id = DEFAULT_CHAIN_ID + 1; - let result = verify_enclave_tx( - &mut mock_bridge, - &txaux, - &extra_info, - &last_account_root_hash, - &storage, - &accounts, - ); + let result = verify_enclave_tx(&mut mock_bridge, &txaux, &extra_info, 0, &storage); assert!(result.is_err()); let result = verify_transfer(&tx, &witness, &extra_info, vec![]); expect_error(&result, Error::WrongChainHexId); @@ -1161,23 +933,9 @@ fn test_transfer_verify_fail() { None, None, ); - let result = verify_enclave_tx( - &mut mock_bridge, - &txaux, - &extra_info, - &last_account_root_hash, - &storage, - &accounts, - ); + let result = verify_enclave_tx(&mut mock_bridge, &txaux, &extra_info, 0, &storage); assert!(result.is_err()); - let result = verify_enclave_tx( - &mut mock_bridge, - &txaux, - &extra_info, - &last_account_root_hash, - &storage, - &accounts, - ); + let result = verify_enclave_tx(&mut mock_bridge, &txaux, &extra_info, 0, &storage); expect_error(&result, Error::UnsupportedVersion); } // NoInputs @@ -1190,14 +948,7 @@ fn test_transfer_verify_fail() { None, None, ); - let result = verify_enclave_tx( - &mut mock_bridge, - &txaux, - &extra_info, - &last_account_root_hash, - &storage, - &accounts, - ); + let result = verify_enclave_tx(&mut mock_bridge, &txaux, &extra_info, 0, &storage); expect_error(&result, Error::NoInputs); } // NoOutputs @@ -1212,14 +963,7 @@ fn test_transfer_verify_fail() { None, None, ); - let result = verify_enclave_tx( - &mut mock_bridge, - &txaux, - &extra_info, - &last_account_root_hash, - &storage, - &accounts, - ); + let result = verify_enclave_tx(&mut mock_bridge, &txaux, &extra_info, 0, &storage); assert!(result.is_err()); } // DuplicateInputs @@ -1235,14 +979,7 @@ fn test_transfer_verify_fail() { None, None, ); - let result = verify_enclave_tx( - &mut mock_bridge, - &txaux, - &extra_info, - &last_account_root_hash, - &storage, - &accounts, - ); + let result = verify_enclave_tx(&mut mock_bridge, &txaux, &extra_info, 0, &storage); assert!(result.is_err()); } // ZeroCoin @@ -1257,14 +994,7 @@ fn test_transfer_verify_fail() { None, None, ); - let result = verify_enclave_tx( - &mut mock_bridge, - &txaux, - &extra_info, - &last_account_root_hash, - &storage, - &accounts, - ); + let result = verify_enclave_tx(&mut mock_bridge, &txaux, &extra_info, 0, &storage); assert!(result.is_err()); } // UnexpectedWitnesses @@ -1280,14 +1010,7 @@ fn test_transfer_verify_fail() { None, None, ); - let result = verify_enclave_tx( - &mut mock_bridge, - &txaux, - &extra_info, - &last_account_root_hash, - &storage, - &accounts, - ); + let result = verify_enclave_tx(&mut mock_bridge, &txaux, &extra_info, 0, &storage); assert!(result.is_err()); } // MissingWitnesses @@ -1298,14 +1021,7 @@ fn test_transfer_verify_fail() { None, None, ); - let result = verify_enclave_tx( - &mut mock_bridge, - &txaux, - &extra_info, - &last_account_root_hash, - &storage, - &accounts, - ); + let result = verify_enclave_tx(&mut mock_bridge, &txaux, &extra_info, 0, &storage); assert!(result.is_err()); let result = verify_transfer(&tx.clone(), &vec![].into(), &extra_info, vec![]); expect_error(&result, Error::MissingWitnesses); @@ -1329,14 +1045,7 @@ fn test_transfer_verify_fail() { None, None, ); - let result = verify_enclave_tx( - &mut mock_bridge, - &txaux, - &extra_info, - &last_account_root_hash, - &storage, - &accounts, - ); + let result = verify_enclave_tx(&mut mock_bridge, &txaux, &extra_info, 0, &storage); assert!(result.is_err()); } // InputSpent @@ -1349,14 +1058,7 @@ fn test_transfer_verify_fail() { ); db.write(inittx).unwrap(); - let result = verify_enclave_tx( - &mut mock_bridge, - &txaux, - &extra_info, - &last_account_root_hash, - &storage, - &accounts, - ); + let result = verify_enclave_tx(&mut mock_bridge, &txaux, &extra_info, 0, &storage); expect_error(&result, Error::InputSpent); let mut reset = db.transaction(); @@ -1402,26 +1104,12 @@ fn test_transfer_verify_fail() { None, None, ); - let result = verify_enclave_tx( - &mut mock_bridge, - &txaux, - &extra_info, - &last_account_root_hash, - &storage, - &accounts, - ); + let result = verify_enclave_tx(&mut mock_bridge, &txaux, &extra_info, 0, &storage); assert!(result.is_err()); } // InvalidInput { - let result = verify_enclave_tx( - &mut mock_bridge, - &txaux, - &extra_info, - &last_account_root_hash, - &create_storage(), - &accounts, - ); + let result = verify_enclave_tx(&mut mock_bridge, &txaux, &extra_info, 0, &create_storage()); expect_error(&result, Error::InvalidInput); } // InputOutputDoNotMatch @@ -1434,20 +1122,12 @@ fn test_transfer_verify_fail() { let result = verify_transfer(&tx, &witness, &extra_info, vec![]); expect_error(&result, Error::InputOutputDoNotMatch); let txaux = replace_tx_payload(txaux, PlainTxAux::TransferTx(tx, witness), None, None); - let result = verify_enclave_tx( - &mut mock_bridge, - &txaux, - &extra_info, - &last_account_root_hash, - &storage, - &accounts, - ); + let result = verify_enclave_tx(&mut mock_bridge, &txaux, &extra_info, 0, &storage); assert!(result.is_err()); } // OutputInTimelock { - let (db, txaux, tx, witness, _, _, accounts) = prepare_app_valid_transfer_tx(true); - let storage = Storage::new_db(db); + let (_, txaux, tx, witness, _, _, storage) = prepare_app_valid_transfer_tx(true); let addr = get_address(&Secp256k1::new(), &secret_key).0; let input_tx = get_old_tx(addr, true); let result = verify_transfer( @@ -1457,28 +1137,18 @@ fn test_transfer_verify_fail() { vec![TxWithOutputs::Transfer(input_tx)], ); expect_error(&result, Error::OutputInTimelock); - let result = verify_enclave_tx( - &mut mock_bridge, - &txaux, - &extra_info, - &last_account_root_hash, - &storage, - &accounts, - ); + let result = verify_enclave_tx(&mut mock_bridge, &txaux, &extra_info, 0, &storage); assert!(result.is_err()); } } fn prepare_jailed_accounts() -> ( - Arc, - AccountStorage, + Storage, SecretKey, RedeemAddress, MerkleTree, - StarlingFixedKey, ) { - let db = create_db(); - let mut accounts = AccountStorage::new(Storage::new_db(db.clone()), 20).expect("account db"); + let mut storage = create_storage(); let secp = Secp256k1::new(); let secret_key = SecretKey::from_slice(&[0xcd; 32]).expect("32 bytes, within curve order"); @@ -1507,13 +1177,9 @@ fn prepare_jailed_accounts() -> ( }), ); - let key = account.key(); - let wrapped = AccountWrapper(account); - let root = accounts - .insert(None, &mut [key], &[wrapped]) - .expect("insert"); + storage.put_stakings(0, &[account]); - (db, accounts, secret_key, addr, merkle_tree, root) + (storage, secret_key, addr, merkle_tree) } fn prepare_withdraw_transaction(secret_key: &SecretKey) -> TxEnclaveAux { @@ -1594,22 +1260,14 @@ fn prepare_unjail_transaction( #[test] fn check_verify_fail_for_jailed_account() { let mut mock_bridge = get_enclave_bridge_mock(); - let (db, accounts, secret_key, address, merkle_tree, root) = prepare_jailed_accounts(); - let storage = Storage::new_db(db); + let (storage, secret_key, address, merkle_tree) = prepare_jailed_accounts(); // Withdraw transaction let txaux = prepare_withdraw_transaction(&secret_key); let extra_info = get_chain_info_enc(&txaux); expect_error( - &verify_enclave_tx( - &mut mock_bridge, - &txaux, - &extra_info, - &root, - &storage, - &accounts, - ), + &verify_enclave_tx(&mut mock_bridge, &txaux, &extra_info, 0, &storage), Error::AccountJailed, ); @@ -1619,14 +1277,7 @@ fn check_verify_fail_for_jailed_account() { let extra_info = get_chain_info_enc(&txaux); expect_error( - &verify_enclave_tx( - &mut mock_bridge, - &txaux, - &extra_info, - &root, - &storage, - &accounts, - ), + &verify_enclave_tx(&mut mock_bridge, &txaux, &extra_info, 0, &storage), Error::AccountJailed, ); @@ -1636,13 +1287,7 @@ fn check_verify_fail_for_jailed_account() { let extra_info = get_chain_info_pub(&txaux); expect_error_unbond( - &verify_public_tx( - &txaux, - &extra_info, - NodeInfoWrap::default(), - &root, - &accounts, - ), + &verify_public_tx(&txaux, &extra_info, NodeInfoWrap::default(), 0, &storage), UnbondError::IsJailed, ); @@ -1653,20 +1298,14 @@ fn check_verify_fail_for_jailed_account() { let extra_info = get_chain_info_pub(&txaux); expect_error_joinnode( - &verify_public_tx( - &txaux, - &extra_info, - NodeInfoWrap::default(), - &root, - &accounts, - ), + &verify_public_tx(&txaux, &extra_info, NodeInfoWrap::default(), 0, &storage), NodeJoinError::IsJailed, ); } #[test] fn check_unjail_transaction() { - let (_db, accounts, secret_key, address, _merkle_tree, root) = prepare_jailed_accounts(); + let (storage, secret_key, address, _merkle_tree) = prepare_jailed_accounts(); // Incorrect nonce @@ -1675,13 +1314,7 @@ fn check_unjail_transaction() { let extra_info = get_chain_info_pub(&txaux); expect_error_public( - &verify_public_tx( - &txaux, - &extra_info, - NodeInfoWrap::default(), - &root, - &accounts, - ), + &verify_public_tx(&txaux, &extra_info, NodeInfoWrap::default(), 0, &storage), PublicTxError::IncorrectNonce, ); @@ -1692,13 +1325,7 @@ fn check_unjail_transaction() { let extra_info = get_chain_info_pub(&txaux); expect_error_unjail( - &verify_public_tx( - &txaux, - &extra_info, - NodeInfoWrap::default(), - &root, - &accounts, - ), + &verify_public_tx(&txaux, &extra_info, NodeInfoWrap::default(), 0, &storage), UnjailError::JailTimeNotExpired, ); @@ -1716,14 +1343,9 @@ fn check_unjail_transaction() { unbonding_period: 1, }; - let (fee, new_account) = verify_public_tx( - &txaux, - &extra_info, - NodeInfoWrap::default(), - &root, - &accounts, - ) - .expect("Verification of unjail transaction failed"); + let (fee, new_account) = + verify_public_tx(&txaux, &extra_info, NodeInfoWrap::default(), 0, &storage) + .expect("Verification of unjail transaction failed"); assert_eq!(Fee::new(Coin::zero()), fee); assert!(!new_account.unwrap().is_jailed()); @@ -1760,10 +1382,9 @@ fn prepare_valid_nodejoin_tx( NodeJoinRequestTx, StakedStateAddress, SecretKey, - AccountStorage, - StarlingFixedKey, + Storage, ) { - let mut tree = AccountStorage::new(Storage::new_db(create_db()), 20).expect("account db"); + let mut storage = Storage::new_db(create_db()); let secp = Secp256k1::new(); let secret_key = SecretKey::from_slice(&[0xcd; 32]).expect("32 bytes, within curve order"); let public_key = PublicKey::from_secret_key(&secp, &secret_key); @@ -1786,26 +1407,19 @@ fn prepare_valid_nodejoin_tx( None }, ); - let key = account.key(); - let wrapped = AccountWrapper(account); - let new_root = tree.insert(None, &mut [key], &[wrapped]).expect("insert"); + storage.put_stakings(0, &[account]); let (txaux, tx) = prepare_nodejoin_transaction(&secret_key, addr.into()); - (txaux, tx, addr.into(), secret_key, tree, new_root) + (txaux, tx, addr.into(), secret_key, storage) } #[test] fn test_nodejoin_success() { - let (txaux, _, _, _, accounts, root) = prepare_valid_nodejoin_tx(false); + let (txaux, _, _, _, storage) = prepare_valid_nodejoin_tx(false); let extra_info = get_chain_info_pub(&txaux); - let (fee, new_account) = verify_public_tx( - &txaux, - &extra_info, - NodeInfoWrap::default(), - &root, - &accounts, - ) - .expect("Verification of node join transaction failed"); + let (fee, new_account) = + verify_public_tx(&txaux, &extra_info, NodeInfoWrap::default(), 0, &storage) + .expect("Verification of node join transaction failed"); assert_eq!(Fee::new(Coin::zero()), fee); assert!(new_account.unwrap().validator.is_some()); @@ -1813,19 +1427,13 @@ fn test_nodejoin_success() { #[test] fn test_nodejoin_fail() { - let (txaux, tx, _addr, secret_key, accounts, root) = prepare_valid_nodejoin_tx(false); + let (txaux, tx, _addr, secret_key, storage) = prepare_valid_nodejoin_tx(false); let extra_info = get_chain_info_pub(&txaux); // WrongChainHexId { let mut extra_info = extra_info; extra_info.chain_hex_id = DEFAULT_CHAIN_ID + 1; - let result = verify_public_tx( - &txaux, - &extra_info, - NodeInfoWrap::default(), - &root, - &accounts, - ); + let result = verify_public_tx(&txaux, &extra_info, NodeInfoWrap::default(), 0, &storage); expect_error_public(&result, PublicTxError::WrongChainHexId); } // UnsupportedVersion @@ -1836,13 +1444,7 @@ fn test_nodejoin_fail() { tx.clone(), get_account_op_witness(Secp256k1::new(), &tx.id(), &secret_key), ); - let result = verify_public_tx( - &txaux, - &extra_info, - NodeInfoWrap::default(), - &root, - &accounts, - ); + let result = verify_public_tx(&txaux, &extra_info, NodeInfoWrap::default(), 0, &storage); expect_error_public(&result, PublicTxError::UnsupportedVersion); } // AccountNotFound, not exist account treated as empty account, so incorrect nonce @@ -1851,8 +1453,8 @@ fn test_nodejoin_fail() { &txaux, &extra_info, NodeInfoWrap::default(), - &[0; 32], - &accounts, + 0, + &create_storage(), ); expect_error_public(&result, PublicTxError::IncorrectNonce); } @@ -1864,13 +1466,7 @@ fn test_nodejoin_fail() { tx.clone(), get_account_op_witness(Secp256k1::new(), &tx.id(), &secret_key), ); - let result = verify_public_tx( - &txaux, - &extra_info, - NodeInfoWrap::default(), - &root, - &accounts, - ); + let result = verify_public_tx(&txaux, &extra_info, NodeInfoWrap::default(), 0, &storage); expect_error_public(&result, PublicTxError::IncorrectNonce); } // MismatchAccountAddress @@ -1881,26 +1477,20 @@ fn test_nodejoin_fail() { tx.clone(), get_account_op_witness(Secp256k1::new(), &tx.id(), &secret_key), ); - let result = verify_public_tx( - &txaux, - &extra_info, - NodeInfoWrap::default(), - &root, - &accounts, - ); + let result = verify_public_tx(&txaux, &extra_info, NodeInfoWrap::default(), 0, &storage); expect_error_public(&result, PublicTxError::StakingWitnessNotMatch); } // BondedNotEnough { let wrap = NodeInfoWrap::custom((Coin::one() + Coin::one()).unwrap(), Vec::new()); - let result = verify_public_tx(&txaux, &extra_info, wrap, &root, &accounts); + let result = verify_public_tx(&txaux, &extra_info, wrap, 0, &storage); expect_error_joinnode(&result, NodeJoinError::BondedNotEnough); } - let (txaux, _tx, addr, _secret_key, accounts, root) = prepare_valid_nodejoin_tx(true); + let (txaux, _tx, addr, _secret_key, storage) = prepare_valid_nodejoin_tx(true); // AlreadyJoined { let wrap = NodeInfoWrap::custom(Coin::one(), vec![addr]); - let result = verify_public_tx(&txaux, &extra_info, wrap, &root, &accounts); + let result = verify_public_tx(&txaux, &extra_info, wrap, 0, &storage); expect_error_joinnode(&result, NodeJoinError::AlreadyJoined); } } diff --git a/chain-abci/tests/validator_changes.rs b/chain-abci/tests/validator_changes.rs index 10e6d0b23..0cd8a9e03 100644 --- a/chain-abci/tests/validator_changes.rs +++ b/chain-abci/tests/validator_changes.rs @@ -12,11 +12,11 @@ use test_common::chain_env::{get_account, get_validator, ChainEnv}; #[test] fn check_unbonding_without_removing_validator() { // Init Chain - let (env, storage, account_storage) = + let (env, storage) = ChainEnv::new_with_customizer(Coin::max(), Coin::zero(), 2, |parameters| { parameters.required_council_node_stake = (Coin::max() / 10).unwrap(); }); - let mut app = env.chain_node(storage, account_storage); + let mut app = env.chain_node(storage); let _rsp = app.init_chain(&env.req_init_chain()); // Note: At this point, there are two validators with `Coin::max() / 2` staked amount each. @@ -58,11 +58,11 @@ fn check_unbonding_without_removing_validator() { #[test] fn check_unbonding_with_removing_validator() { // Init Chain - let (env, storage, account_storage) = + let (env, storage) = ChainEnv::new_with_customizer(Coin::max(), Coin::zero(), 2, |parameters| { parameters.required_council_node_stake = (Coin::max() / 10).unwrap(); }); - let mut app = env.chain_node(storage, account_storage); + let mut app = env.chain_node(storage); let _rsp = app.init_chain(&env.req_init_chain()); let state = app.last_state.as_ref().unwrap(); let tm_address = &env.validator_address(0); @@ -145,7 +145,7 @@ fn check_unbonding_with_removing_validator() { #[test] fn check_rejoin() { // Init Chain - let (env, storage, account_storage) = ChainEnv::new_with_customizer( + let (env, storage) = ChainEnv::new_with_customizer( (Coin::max() / 2).unwrap(), (Coin::max() / 2).unwrap(), 2, @@ -159,7 +159,7 @@ fn check_rejoin() { parameters.rewards_config.monetary_expansion_decay = 0; }, ); - let mut app = env.chain_node(storage, account_storage); + let mut app = env.chain_node(storage); let _rsp = app.init_chain(&env.req_init_chain()); let state = app.last_state.as_ref().unwrap(); let tm_address = &env.validator_address(0); diff --git a/chain-storage/Cargo.toml b/chain-storage/Cargo.toml index e72e70aab..e29991368 100644 --- a/chain-storage/Cargo.toml +++ b/chain-storage/Cargo.toml @@ -13,7 +13,6 @@ kvdb-rocksdb = "0.7" kvdb-memorydb = "0.5" chain-core = { path = "../chain-core" } bit-vec = { version = "0.6.1", features = ["serde_no_std"] } -starling = "3.1.4" parity-scale-codec = { features = ["derive"], version = "1.3" } integer-encoding = "1.1.5" anyhow = "1.0" diff --git a/chain-storage/src/account/mod.rs b/chain-storage/src/account/mod.rs deleted file mode 100644 index 8a2c3cbea..000000000 --- a/chain-storage/src/account/mod.rs +++ /dev/null @@ -1,311 +0,0 @@ -#![allow(dead_code)] -/// TODO: WIP usage -- disallow dead_code when new TX types are added to work with accounts and use this -/// Internal definitions -mod tree; - -use std::path::PathBuf; -use std::sync::Arc; - -use kvdb_memorydb::create as create_memorydb; -use parity_scale_codec::{Decode as ScaleDecode, Encode as ScaleEncode}; -use starling::constants::KEY_LEN; -use starling::merkle_bit::BinaryMerkleTreeResult; -use starling::traits::{Database, Decode, Encode, Exception}; - -use crate::Storage; -use chain_core::common::H256; -use chain_core::state::account::{to_stake_key, StakedState, StakedStateAddress}; - -/// key type for looking up accounts/staked states in the merkle tree storage -pub type StarlingFixedKey = [u8; KEY_LEN]; - -#[derive(Debug)] -pub enum StakedStateError { - NotFound, - IoError(std::io::Error), -} - -/// checks that the staked state can be retrieved from the trie storage -pub fn get_staked_state( - account_address: &StakedStateAddress, - last_root: &StarlingFixedKey, - accounts: &AccountStorage, -) -> Result { - let account_key = to_stake_key(account_address); - let account = accounts.get_one(last_root, &account_key); - match account { - Err(e) => Err(StakedStateError::IoError(std::io::Error::new( - std::io::ErrorKind::Other, - e, - ))), - Ok(None) => Err(StakedStateError::NotFound), - Ok(Some(AccountWrapper(a))) => Ok(a), - } -} - -/// Given the Account state storage and the current / uncommitted account storage root, -/// it inserts the updated account state into the account storage and returns the new root hash of the account state trie. -pub fn update_staked_state( - account: StakedState, - account_root_hash: &StarlingFixedKey, - accounts: &mut AccountStorage, -) -> (StarlingFixedKey, Option) { - ( - accounts - .insert_one( - Some(account_root_hash), - &account.key(), - &AccountWrapper(account.clone()), - ) - .expect("update account"), - Some(account), - ) -} - -pub type AccountStorage = tree::HashTree; - -pub fn pure_account_storage(depth: usize) -> BinaryMerkleTreeResult { - AccountStorage::new(Storage::new_db(Arc::new(create_memorydb(1))), depth) -} - -#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] -pub struct AccountWrapper(pub StakedState); - -impl Encode for AccountWrapper { - #[inline] - fn encode(&self) -> Result, Exception> { - Ok(self.0.encode()) - } -} - -impl Decode for AccountWrapper { - #[inline] - fn decode(buffer: &[u8]) -> Result { - let data = Vec::from(buffer); - let account = StakedState::decode(&mut data.as_slice()) - .map_err(|e| Exception::new(&format!("failed to decode: {}", e.what())))?; - Ok(AccountWrapper(account)) - } -} - -impl Database for Storage { - type NodeType = tree::TreeNode; - type EntryType = ([u8; KEY_LEN], Vec); - - #[inline] - fn open(path: &PathBuf) -> Result { - Ok(Storage::new_db(Arc::new( - kvdb_rocksdb::Database::open( - &kvdb_rocksdb::DatabaseConfig::default(), - path.to_str().expect("invalid account db path"), - ) - .map_err(tree::convert_io_err)?, - ))) - } - - #[inline] - fn get_node(&self, key: H256) -> Result, Exception> { - if let Some(buffer) = self.db.get(0, &key).map_err(tree::convert_io_err)? { - let data = buffer.to_vec(); - let storage = Self::NodeType::decode(&mut data.as_slice()) - .map_err(|e| Exception::new(e.what()))?; - Ok(Some(storage)) - } else { - Ok(None) - } - } - - #[inline] - fn insert(&mut self, key: H256, value: Self::NodeType) -> Result<(), Exception> { - let serialized = value.encode(); - let insert_tx = self.get_or_create_tx(); - insert_tx.put(0, &key, &serialized); - Ok(()) - } - - #[inline] - fn remove(&mut self, key: &[u8; KEY_LEN]) -> Result<(), Exception> { - let delete_tx = self.get_or_create_tx(); - delete_tx.delete(0, key); - Ok(()) - } - - #[inline] - fn batch_write(&mut self) -> Result<(), Exception> { - if let Some(dbtx) = self.current_tx.take() { - self.db.write(dbtx).map_err(tree::convert_io_err) - } else { - Ok(()) - } - } -} - -#[cfg(test)] -mod test { - - use super::*; - use chain_core::common::Timespec; - use chain_core::init::address::RedeemAddress; - use chain_core::init::coin::Coin; - use chain_core::state::account::{ - ConfidentialInit, CouncilNode, Nonce, StakedState, StakedStateAddress, Validator, - }; - use chain_core::state::tendermint::{BlockHeight, TendermintValidatorPubKey}; - use kvdb_memorydb::create; - use quickcheck::quickcheck; - use quickcheck::Arbitrary; - use quickcheck::Gen; - use std::sync::Arc; - - impl Arbitrary for AccountWrapper { - fn arbitrary(g: &mut G) -> Self { - let nonce: Nonce = g.next_u64(); - let bonded: u32 = g.next_u64() as u32; - let unbonded: u32 = g.next_u64() as u32; - let unbonded_from: Timespec = g.next_u64(); - let mut raw_address = [0u8; 20]; - g.fill_bytes(&mut raw_address); - let address: StakedStateAddress = - StakedStateAddress::from(RedeemAddress::from(raw_address)); - let validator = if bool::arbitrary(g) { - let mut raw_pubkey = [0u8; 32]; - g.fill_bytes(&mut raw_pubkey); - let jailed_until = if bool::arbitrary(g) { - Some(u64::arbitrary(g)) - } else { - None - }; - // TODO: cert - let cert: Vec = Vec::arbitrary(g); - let init_confid = ConfidentialInit { cert }; - Some(Validator { - council_node: CouncilNode::new( - TendermintValidatorPubKey::Ed25519(raw_pubkey), - init_confid, - ), - jailed_until, - inactive_time: Some(0), - inactive_block: Some(BlockHeight::genesis()), - used_validator_addresses: vec![], - }) - } else { - None - }; - AccountWrapper(StakedState { - nonce, - bonded: Coin::from(bonded), - unbonded: Coin::from(unbonded), - unbonded_from, - address, - validator, - last_slash: None, - }) - } - } - - fn create_db() -> Storage { - Storage::new_db(Arc::new(create(1))) - } - - quickcheck! { - // test whether insertions in different order leads to the same root hash - fn staked_state_insert_order(accounts: Vec) -> bool { - let mut tree1 = AccountStorage::new(create_db(), 20).expect("account db"); - let mut tree2 = AccountStorage::new(create_db(), 20).expect("account db"); - let mut root1 = None; - let mut root2 = None; - for account in accounts.iter() { - let key = account.0.key(); - let new_root = tree1.insert_one(root1.as_ref(), &key, &account).expect("insert"); - root1 = Some(new_root); - } - for account in accounts.iter().rev() { - let key = account.0.key(); - let new_root = tree2.insert_one(root2.as_ref(), &key, &account).expect("insert"); - root2 = Some(new_root); - } - root1 == root2 - } - } - - #[test] - fn test_account_insert_can_find() { - let mut tree = AccountStorage::new(create_db(), 20).expect("account db"); - let account = - StakedState::default(StakedStateAddress::BasicRedeem(RedeemAddress::default())); - let key = account.key(); - let wrapped = AccountWrapper(account); - let new_root = tree - .insert(None, &mut [key], &mut vec![wrapped.clone()]) - .expect("insert"); - let items = tree.get(&new_root, &mut [key]).expect("get"); - assert_eq!(items[&key], Some(wrapped)); - } - - #[test] - fn test_account_update_can_find() { - let mut tree = AccountStorage::new(create_db(), 20).expect("account db"); - let account = - StakedState::default(StakedStateAddress::BasicRedeem(RedeemAddress::default())); - let key = account.key(); - let wrapped = AccountWrapper(account); - let old_root = tree - .insert(None, &mut [key], &mut vec![wrapped.clone()]) - .expect("insert"); - let updated_account = StakedState::new( - 1, - Coin::unit(), - Coin::unit(), - 1, - RedeemAddress::default().into(), - None, - ); - let wrapped_updated = AccountWrapper(updated_account); - assert_ne!(wrapped, wrapped_updated); - let new_root = tree - .insert( - Some(&old_root), - &mut [key], - &mut vec![wrapped_updated.clone()], - ) - .expect("insert 2"); - assert_ne!(old_root, new_root); - let items = tree.get(&new_root, &mut [key]).expect("get"); - assert_eq!(items[&key], Some(wrapped_updated)); - let old_items = tree.get(&old_root, &mut [key]).expect("get 2"); - assert_eq!(old_items[&key], Some(wrapped)); - } - - #[test] - fn test_account_remove_cannot_find() { - let mut tree = AccountStorage::new(create_db(), 20).expect("account db"); - let account = - StakedState::default(StakedStateAddress::BasicRedeem(RedeemAddress::default())); - let key = account.key(); - let wrapped = AccountWrapper(account); - let old_root = tree - .insert(None, &mut [key], &mut vec![wrapped]) - .expect("insert"); - let updated_account = StakedState::new( - 1, - Coin::unit(), - Coin::unit(), - 1, - RedeemAddress::default().into(), - None, - ); - let wrapped_updated = AccountWrapper(updated_account); - let new_root = tree - .insert( - Some(&old_root), - &mut [key], - &mut vec![wrapped_updated.clone()], - ) - .expect("insert 2"); - tree.remove(&old_root).expect("remove"); - let items = tree.get(&new_root, &mut [key]).expect("get"); - assert_eq!(items[&key], Some(wrapped_updated)); - let old_items = tree.get(&old_root, &mut [key]).expect("get 2"); - assert_eq!(old_items[&key], None); - } -} diff --git a/chain-storage/src/account/tree.rs b/chain-storage/src/account/tree.rs deleted file mode 100644 index f0187fda9..000000000 --- a/chain-storage/src/account/tree.rs +++ /dev/null @@ -1,413 +0,0 @@ -//! # Value with associated properties (e.g. min/max bounds) -//! adapted from https://github.com/ChosunOne/merkle_bit examples (Merkle-BIT) -//! Copyright (c) 2019, Josiah Evans (licensed under the MIT License and the Apache License, Version 2.0) -//! Modifications Copyright (c) 2019-2020, Foris Limited (licensed under the Apache License, Version 2.0) -//! - -use chain_core::common::H256; -use chain_core::state::account::Count; -use parity_scale_codec::{ - Decode as ScaleDecode, Encode as ScaleEncode, Error as ScaleError, Input, Output, -}; -use starling::constants::KEY_LEN; -use starling::merkle_bit::{BinaryMerkleTreeResult, MerkleBIT}; -use starling::traits::Hasher; -use starling::traits::{ - Branch, Data, Database, Decode, Encode, Exception, Leaf, Node, NodeVariant, -}; -use starling::tree::tree_data::TreeData; -use std::collections::HashMap; - -#[derive(Clone)] -pub struct Blake3Hasher(blake3::Hasher); -impl Hasher for Blake3Hasher { - type HashType = Self; - - #[inline] - fn new(_size: usize) -> Self { - let hasher = blake3::Hasher::new(); - Self(hasher) - } - - #[inline] - fn update(&mut self, data: &[u8]) { - self.0.update(data); - } - - #[inline] - fn finalize(self) -> [u8; KEY_LEN] { - self.0.finalize().into() - } -} - -pub type TreeHasher = Blake3Hasher; - -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub struct TreeBranch { - /// The number of leaf nodes under this branch. - count: Count, - /// The location of the next node when traversing the zero branch. - zero: H256, - /// The location of the next node when traversing the one branch. - one: H256, - /// The index bit of the associated key on which to make a decision to go down the zero or one branch. - split_index: usize, - /// The associated key with this branch. - key: H256, -} - -impl Branch for TreeBranch { - #[inline] - fn new() -> Self { - let zero = [0u8; 32]; - TreeBranch { - count: 0, - zero, - one: zero, - split_index: 0, - key: zero, - } - } - - #[inline] - fn get_count(&self) -> u64 { - self.count - } - #[inline] - fn get_zero(&self) -> &[u8; KEY_LEN] { - &self.zero - } - #[inline] - fn get_one(&self) -> &[u8; KEY_LEN] { - &self.one - } - #[inline] - fn get_split_index(&self) -> usize { - self.split_index - } - #[inline] - fn get_key(&self) -> &[u8; KEY_LEN] { - &self.key - } - - #[inline] - fn set_count(&mut self, count: u64) { - self.count = count - } - #[inline] - fn set_zero(&mut self, zero: [u8; KEY_LEN]) { - self.zero = zero - } - #[inline] - fn set_one(&mut self, one: [u8; KEY_LEN]) { - self.one = one - } - #[inline] - fn set_split_index(&mut self, index: usize) { - self.split_index = index - } - #[inline] - fn set_key(&mut self, key: [u8; KEY_LEN]) { - self.key = key - } - - #[inline] - fn decompose(self) -> (u64, [u8; KEY_LEN], [u8; KEY_LEN], usize, [u8; KEY_LEN]) { - ( - self.get_count(), - self.zero, - self.one, - self.get_split_index(), - self.key, - ) - } -} - -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub struct TreeLeaf { - /// The associated key with this node. - key: H256, - /// The location of the `Data` node in the tree. - data: H256, -} - -impl Leaf for TreeLeaf { - /// Creates a new `TreeLeaf` - #[inline] - fn new() -> Self { - let zero = [0u8; 32]; - TreeLeaf { - key: zero, - data: zero, - } - } - - /// Gets the associated key with this node. - #[inline] - fn get_key(&self) -> &[u8; KEY_LEN] { - &self.key - } - - /// Gets the location of the `Data` node. - #[inline] - fn get_data(&self) -> &[u8; KEY_LEN] { - &self.data - } - - /// Sets the associated key with this node. - #[inline] - fn set_key(&mut self, key: [u8; KEY_LEN]) { - self.key = key - } - - /// Sets the location for the `Data` node. - #[inline] - fn set_data(&mut self, data: [u8; KEY_LEN]) { - self.data = data - } - - /// Decomposes the struct into its constituent parts. - #[inline] - fn decompose(self) -> ([u8; KEY_LEN], [u8; KEY_LEN]) { - (self.key, self.data) - } -} - -/// A node in the tree. -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct TreeNode { - /// The number of references to this node. - pub references: Count, - /// The `NodeVariant` of the node. - pub node: NodeVariant, -} - -impl ScaleEncode for TreeNode { - fn encode_to(&self, dest: &mut W) { - match self.node { - NodeVariant::Phantom(_) => { - // TODO: panic or nothing? - } - NodeVariant::Branch(ref tb) => { - // TreeBranch{count, zero, one, split_index, key} - dest.push_byte(0); - self.references.encode_to(dest); - tb.count.encode_to(dest); - tb.zero.encode_to(dest); - tb.one.encode_to(dest); - (tb.split_index as u64).encode_to(dest); - tb.key.encode_to(dest); - } - NodeVariant::Leaf(ref tl) => { - // TreeLeaf{key, data} - dest.push_byte(1); - self.references.encode_to(dest); - tl.key.encode_to(dest); - tl.data.encode_to(dest); - } - NodeVariant::Data(ref td) => { - // TreeData{value} - dest.push_byte(2); - self.references.encode_to(dest); - td.get_value().encode_to(dest); - } - } - } -} - -impl ScaleDecode for TreeNode { - fn decode(input: &mut I) -> Result { - let tag = input.read_byte()?; - let references = u64::decode(input)?; - match tag { - 0 => { - let count = Count::decode(input)?; - let zero = H256::decode(input)?; - let one = H256::decode(input)?; - let split_index: usize = u64::decode(input)? as usize; - let key = H256::decode(input)?; - Ok(TreeNode { - references, - node: NodeVariant::Branch(TreeBranch { - count, - zero, - one, - split_index, - key, - }), - }) - } - 1 => { - let key = H256::decode(input)?; - let data = H256::decode(input)?; - Ok(TreeNode { - references, - node: NodeVariant::Leaf(TreeLeaf { key, data }), - }) - } - 2 => { - let data: Vec = ScaleDecode::decode(input)?; - let mut tree_data = TreeData::new(); - tree_data.set_value(&data); - Ok(TreeNode { - references, - node: NodeVariant::Data(tree_data), - }) - } - _ => Err(ScaleError::from("Invalid tag")), - } - } -} - -impl TreeNode { - /// Creates a new `TreeNode`. - #[inline] - pub fn new(node_variant: NodeVariant) -> Self { - Self { - references: 0, - node: node_variant, - } - } - - /// Gets the number of references to the node. - fn get_references(&self) -> u64 { - self.references - } - - /// Sets the number of references to the node. - fn set_references(&mut self, references: u64) { - self.references = references; - } - - /// Sets the node as a `NodeVariant::Branch`. - fn set_branch(&mut self, branch: TreeBranch) { - self.node = NodeVariant::Branch(branch); - } - - /// Sets the node as a `NodeVariant::Leaf`. - fn set_leaf(&mut self, leaf: TreeLeaf) { - self.node = NodeVariant::Leaf(leaf); - } - - /// Sets the node as a `NodeVariant::Data`. - fn set_data(&mut self, data: TreeData) { - self.node = NodeVariant::Data(data); - } -} - -impl Node for TreeNode { - #[inline] - fn new(node_variant: NodeVariant) -> Self { - Self::new(node_variant) - } - #[inline] - fn get_references(&self) -> u64 { - Self::get_references(self) - } - #[inline] - fn get_variant(self) -> NodeVariant { - self.node - } - #[inline] - fn set_references(&mut self, references: u64) { - Self::set_references(self, references) - } - #[inline] - fn set_branch(&mut self, branch: TreeBranch) { - Self::set_branch(self, branch) - } - #[inline] - fn set_leaf(&mut self, leaf: TreeLeaf) { - Self::set_leaf(self, leaf) - } - #[inline] - fn set_data(&mut self, data: TreeData) { - Self::set_data(self, data) - } -} - -pub struct HashTree -where - ValueType: Encode + Decode + Sync + Send, - DatabaseType: Database, -{ - tree: MerkleBIT< - DatabaseType, - TreeBranch, - TreeLeaf, - TreeData, - TreeNode, - TreeHasher, - ValueType, - H256, - >, -} - -impl HashTree -where - ValueType: Encode + Decode + Sync + Send, - DatabaseType: Database, -{ - /// Creates a new `HashTree`. - #[inline] - pub fn new(db: DatabaseType, depth: usize) -> BinaryMerkleTreeResult { - let tree = MerkleBIT::from_db(db, depth)?; - Ok(Self { tree }) - } - - /// Gets the values associated with `keys` from the tree. - #[inline] - pub fn get( - &self, - root_hash: &[u8; KEY_LEN], - keys: &mut [[u8; KEY_LEN]], - ) -> BinaryMerkleTreeResult>> { - self.tree.get(root_hash, keys) - } - - /// Gets one value associated with `key` from the tree. - #[inline] - pub fn get_one( - &self, - root_hash: &[u8; KEY_LEN], - key: &[u8; KEY_LEN], - ) -> BinaryMerkleTreeResult> { - self.tree.get_one(root_hash, key) - } - - /// Inserts elements into the tree. Using `previous_root` specifies that the insert depends on - /// the state from the previous root, and will update references accordingly. - #[inline] - pub fn insert( - &mut self, - previous_root: Option<&[u8; KEY_LEN]>, - keys: &mut [[u8; KEY_LEN]], - values: &[ValueType], - ) -> BinaryMerkleTreeResult<[u8; KEY_LEN]> { - self.tree.insert(previous_root, keys, values) - } - - /// Inserts one element into the tree. Using `previous_root` specifies that the insert depends on - /// the state from the previous root, and will update references accordingly. - #[inline] - pub fn insert_one( - &mut self, - previous_root: Option<&[u8; KEY_LEN]>, - key: &[u8; KEY_LEN], - value: &ValueType, - ) -> BinaryMerkleTreeResult<[u8; KEY_LEN]> { - self.tree.insert_one(previous_root, key, value) - } - - /// Removes a root from the tree. This will remove all elements with less than two references - /// under the given root. - #[inline] - pub fn remove(&mut self, root_hash: &[u8; KEY_LEN]) -> BinaryMerkleTreeResult<()> { - self.tree.remove(root_hash) - } -} - -#[inline] -pub fn convert_io_err(e: std::io::Error) -> Exception { - Exception::new(&e.to_string()) -} diff --git a/chain-storage/src/buffer.rs b/chain-storage/src/buffer.rs index 219d80270..16c8b4045 100644 --- a/chain-storage/src/buffer.rs +++ b/chain-storage/src/buffer.rs @@ -37,11 +37,9 @@ use std::collections::HashMap; use std::hash::{BuildHasher, Hash}; use kvdb::KeyValueDB; -use starling::traits::Exception; -use chain_core::state::account::{to_stake_key, StakedState, StakedStateAddress}; +use chain_core::state::account::{StakedState, StakedStateAddress}; -use crate::account::{AccountStorage as StakingStorage, AccountWrapper, StarlingFixedKey}; use crate::Storage; pub trait Get { @@ -88,29 +86,6 @@ pub trait StoreStaking: SimpleStore StoreStaking for S where S: SimpleStore {} -/// Implement `Get` for readonly merkle trie. -pub struct StakingGetter<'a> { - storage: &'a StakingStorage, - root: Option, -} -impl<'a> StakingGetter<'a> { - pub fn new(storage: &'a StakingStorage, root: Option) -> Self { - Self { storage, root } - } -} -impl<'a> Get for StakingGetter<'a> { - type Key = StakedStateAddress; - type Value = StakedState; - fn get(&self, key: &Self::Key) -> Option { - self.root.as_ref().and_then(|root| { - self.storage - .get_one(root, &to_stake_key(key)) - .unwrap() - .map(|AccountWrapper(o)| o) - }) - } -} - /// Generic readonly buffered storage implementation pub struct BufferGetter<'a, S: Get, H> { storage: S, @@ -235,11 +210,6 @@ where } } -/// Specialized for staking -pub type StakingBufferStore<'a, H> = BufferSimpleStore<'a, StakingGetter<'a>, H>; -/// Specialized for staking -pub type StakingBufferGetter<'a, H> = BufferGetter<'a, StakingGetter<'a>, H>; - /// Dummy storage implemented with a HashMap in memory. #[derive(Debug, Clone)] pub struct MemStore(HashMap); @@ -290,22 +260,6 @@ where /// Buffer used for staking storage pub type StakingBuffer = HashMap; -/// Flush buffer to merkle trie, and return the new root -pub fn flush_staking_storage( - storage: &mut StakingStorage, - root: Option, - buffer: StakingBuffer, -) -> Result, Exception> { - let (mut keys, values): (Vec<_>, Vec<_>) = buffer - .into_iter() - .map(|(addr, v)| (to_stake_key(&addr), AccountWrapper(v))) - .unzip(); - if keys.is_empty() { - return Ok(root); - } - Ok(Some(storage.insert(root.as_ref(), &mut keys, &values)?)) -} - /// Buffer used for key-value storage pub type KVBuffer = HashMap<(u32, Vec), Option>>; @@ -353,65 +307,33 @@ mod tests { use kvdb::KeyValueDB; use kvdb_memorydb::{create as create_memorydb, InMemory}; - use chain_core::state::account::{StakedState, StakedStateAddress}; - use super::*; - use crate::account::{pure_account_storage, AccountStorage as StakingStorage}; // demonstrate how to use buffered store in the chain abci app. struct App { kvdb: D, - trie: StakingStorage, - root: Option, - tmp_kv_buffer: KVBuffer, - tmp_trie_buffer: StakingBuffer, - kv_buffer: KVBuffer, - trie_buffer: StakingBuffer, } impl App { - fn new(kvdb: D, trie: StakingStorage) -> Self { + fn new(kvdb: D) -> Self { Self { kvdb, - trie, - root: None, tmp_kv_buffer: HashMap::new(), - tmp_trie_buffer: HashMap::new(), kv_buffer: HashMap::new(), - trie_buffer: HashMap::new(), } } fn commit(&mut self) { flush_kvdb(&self.kvdb, mem::take(&mut self.kv_buffer)).unwrap(); - self.root = - flush_staking_storage(&mut self.trie, self.root, mem::take(&mut self.trie_buffer)) - .unwrap(); - self.tmp_kv_buffer.clear(); - self.tmp_trie_buffer.clear(); - } - - fn staking_store(&mut self) -> impl StoreStaking + '_ { - StakingBufferStore::new( - StakingGetter::new(&self.trie, self.root), - &mut self.trie_buffer, - ) } fn kv_store(&mut self) -> impl StoreKV + '_ { BufferStore::new(&self.kvdb, &mut self.kv_buffer) } - fn tmp_staking_store(&mut self) -> impl StoreStaking + '_ { - StakingBufferStore::new( - StakingGetter::new(&self.trie, self.root), - &mut self.tmp_trie_buffer, - ) - } - fn tmp_kv_store(&mut self) -> impl StoreKV + '_ { BufferStore::new(&self.kvdb, &mut self.tmp_kv_buffer) } @@ -419,44 +341,10 @@ mod tests { impl App { fn new_memory() -> Self { - Self::new(create_memorydb(1), pure_account_storage(20).unwrap()) + Self::new(create_memorydb(1)) } } - #[test] - fn check_staking_store() { - let mut app = App::new_memory(); - let staking1 = StakedState::default(StakedStateAddress::BasicRedeem([0xcc; 20].into())); - let staking2 = StakedState::default(StakedStateAddress::BasicRedeem([0xcd; 20].into())); - app.staking_store().set_staking(staking1.clone()); - app.tmp_staking_store().set_staking(staking2.clone()); - assert_eq!( - app.staking_store().get(&staking1.address).unwrap(), - staking1 - ); - assert_eq!( - app.tmp_staking_store().get(&staking2.address).unwrap(), - staking2 - ); - // no conflict between two buffers. - assert!(app.staking_store().get(&staking2.address).is_none()); - assert!(app.tmp_staking_store().get(&staking1.address).is_none()); - - app.commit(); - - // after commit, staking1 is committed, staking2 is dropped - assert_eq!( - app.staking_store().get(&staking1.address).unwrap(), - staking1 - ); - assert_eq!( - app.tmp_staking_store().get(&staking1.address).unwrap(), - staking1 - ); - assert!(app.staking_store().get(&staking2.address).is_none(),); - assert!(app.tmp_staking_store().get(&staking2.address).is_none(),); - } - #[test] fn check_kvdb() { let mut app = App::new_memory(); diff --git a/chain-storage/src/lib.rs b/chain-storage/src/lib.rs index e11ae9da0..2c521e96a 100644 --- a/chain-storage/src/lib.rs +++ b/chain-storage/src/lib.rs @@ -1,10 +1,11 @@ -pub mod account; mod api; pub mod buffer; pub mod jellyfish; -use crate::buffer::Get; +use crate::buffer::{flush_storage, BufferStore, Get, KVBuffer}; +use crate::jellyfish::{put_stakings, Version}; use chain_core::common::H256; +use chain_core::state::account::StakedState; use chain_core::state::tendermint::BlockHeight; use chain_core::tx::data::TxId; use kvdb::{DBTransaction, KeyValueDB}; @@ -233,4 +234,16 @@ impl Storage { Ok(()) } } + + pub fn put_stakings(&mut self, version: Version, stakings: &[StakedState]) -> H256 { + let mut kv_buffer = KVBuffer::new(); + let root_hash = put_stakings( + &mut BufferStore::new(self, &mut kv_buffer), + version, + stakings.iter(), + ) + .unwrap(); + flush_storage(self, kv_buffer).unwrap(); + root_hash + } } diff --git a/docker/config/devnet/tendermint/genesis.json b/docker/config/devnet/tendermint/genesis.json index 0358feeea..1a2d772d8 100644 --- a/docker/config/devnet/tendermint/genesis.json +++ b/docker/config/devnet/tendermint/genesis.json @@ -1,5 +1,5 @@ { - "app_hash": "D3F1EC00E0ACE10FB23DD7538241FF0DC597A1AFE4B3C37AEFC4C0A0B755B63E", + "app_hash": "64E09203F3CCE28BBAF78CE03BCE494835CB13D1808299DEE2A8BD76CF30D4AB", "app_state": { "council_nodes": { "0x45c1851c2f0dc6138935857b9e23b173185fea15": [ diff --git a/test-common/src/block_generator.rs b/test-common/src/block_generator.rs index 2a2f12fce..3df76a72f 100644 --- a/test-common/src/block_generator.rs +++ b/test-common/src/block_generator.rs @@ -17,7 +17,7 @@ use tendermint::{ Signature, Time, }; -use chain_abci::app::{compute_accounts_root, ChainNodeState}; +use chain_abci::app::ChainNodeState; use chain_abci::staking::StakingTable; use chain_core::common::MerkleTree; use chain_core::compute_app_hash; @@ -34,8 +34,8 @@ use chain_core::state::tendermint::{ use chain_core::state::ChainState; use chain_core::tx::fee::{LinearFee, Milli}; use chain_core::tx::TxAux; -use chain_storage::account::{pure_account_storage, AccountStorage}; -use chain_storage::buffer::StakingGetter; +use chain_storage::buffer::MemStore; +use chain_storage::jellyfish::{put_stakings, StakingGetter}; use client_common::tendermint::types::{ AbciQuery, BlockResults, BroadcastTxResponse, Genesis, Results, }; @@ -274,8 +274,8 @@ impl TestnetSpec { } } - pub fn gen_genesis(&self) -> (Genesis, ChainNodeState, Arc) { - let mut account_storage = Arc::new(pure_account_storage(20).unwrap()); + pub fn gen_genesis(&self) -> (Genesis, ChainNodeState) { + let mut store = MemStore::new(); let config = self.init_config(); let genesis_seconds = self @@ -286,8 +286,7 @@ impl TestnetSpec { let (accounts, rewards_pool, nodes) = config .validate_config_get_genesis(genesis_seconds) .expect("distribution validation error"); - let account_root = - compute_accounts_root(Arc::get_mut(&mut account_storage).unwrap(), &accounts); + let account_root = put_stakings(&mut store, 0, accounts.iter()).unwrap(); let network_params = NetworkParameters::Genesis(config.network_params.clone()); let app_hash = compute_app_hash( &MerkleTree::empty(), @@ -328,10 +327,7 @@ impl TestnetSpec { }; let staking_table = StakingTable::from_genesis( - &StakingGetter::new( - Arc::get_mut(&mut account_storage).unwrap(), - Some(account_root), - ), + &StakingGetter::new(&store, 0), network_params.get_required_council_node_stake(), network_params.get_max_validators(), &nodes.iter().map(|(addr, _)| *addr).collect::>(), @@ -346,7 +342,7 @@ impl TestnetSpec { staking_table, ); - (genesis, state, account_storage) + (genesis, state) } pub fn validator_set(&self) -> validator::Set { @@ -379,7 +375,6 @@ pub struct BlockGenerator { pub spec: TestnetSpec, pub genesis: Genesis, pub genesis_state: ChainNodeState, - pub account_storage: Arc, pub validators: validator::Set, pub blocks: Vec, pub current_height: Option, @@ -388,13 +383,12 @@ pub struct BlockGenerator { impl BlockGenerator { pub fn new(spec: TestnetSpec) -> BlockGenerator { - let (genesis, genesis_state, account_storage) = spec.gen_genesis(); + let (genesis, genesis_state) = spec.gen_genesis(); let validators = spec.validator_set(); BlockGenerator { spec, genesis, genesis_state, - account_storage, validators, blocks: vec![], current_height: None, diff --git a/test-common/src/chain_env.rs b/test-common/src/chain_env.rs index 3e1d1257c..900f6998f 100644 --- a/test-common/src/chain_env.rs +++ b/test-common/src/chain_env.rs @@ -33,7 +33,6 @@ use chain_core::state::validator::NodeJoinRequestTx; use chain_core::tx::fee::{LinearFee, Milli}; use chain_core::tx::witness::EcdsaSignature; use chain_core::tx::{data::TxId, TransactionId, TxAux, TxPublicAux}; -use chain_storage::account::{AccountStorage, AccountWrapper, StarlingFixedKey}; use chain_storage::buffer::Get; use chain_storage::{Storage, NUM_COLUMNS}; @@ -80,12 +79,8 @@ pub fn get_enclave_bridge_mock() -> MockClient { MockClient::new(0) } -pub fn create_storage() -> (Storage, AccountStorage) { - ( - Storage::new_db(Arc::new(create(NUM_COLUMNS))), - AccountStorage::new(Storage::new_db(Arc::new(create(1))), 20) - .expect("Unable to create account storage"), - ) +pub fn create_storage() -> Storage { + Storage::new_db(Arc::new(create(NUM_COLUMNS))) } pub fn get_init_network_params(expansion_cap: Coin) -> InitNetworkParameters { @@ -184,11 +179,7 @@ pub struct ChainEnv { } impl ChainEnv { - pub fn new( - dist_coin: Coin, - expansion_cap: Coin, - count: usize, - ) -> (ChainEnv, Storage, AccountStorage) { + pub fn new(dist_coin: Coin, expansion_cap: Coin, count: usize) -> (ChainEnv, Storage) { ChainEnv::new_with_customizer(dist_coin, expansion_cap, count, |_| {}) } @@ -197,8 +188,8 @@ impl ChainEnv { expansion_cap: Coin, count: usize, customize_network_params: F, - ) -> (ChainEnv, Storage, AccountStorage) { - let (storage, mut account_storage) = create_storage(); + ) -> (ChainEnv, Storage) { + let mut storage = create_storage(); let locked = (Coin::max() - dist_coin - expansion_cap).unwrap(); let accounts: Vec = (0..count) .map(|i| { @@ -238,14 +229,7 @@ impl ChainEnv { .validate_config_get_genesis(timestamp.get_seconds().try_into().unwrap()) .expect("Error while validating distribution"); - let mut keys: Vec = states.iter().map(|account| account.key()).collect(); - let wrapped: Vec = - states.iter().map(|st| AccountWrapper(st.clone())).collect(); - - let new_account_root = account_storage - .insert(None, &mut keys, &wrapped) - .expect("initial insert"); - + let new_account_root = storage.put_stakings(0, &states); let genesis_app_hash = compute_app_hash( &MerkleTree::empty(), &new_account_root, @@ -263,7 +247,6 @@ impl ChainEnv { accounts, }, storage, - account_storage, ) } @@ -271,17 +254,12 @@ impl ChainEnv { (self.dist_coin / (self.accounts.len() as u64)).unwrap() } - pub fn chain_node( - &self, - storage: Storage, - account_storage: AccountStorage, - ) -> ChainNodeApp { + pub fn chain_node(&self, storage: Storage) -> ChainNodeApp { ChainNodeApp::new_with_storage( get_enclave_bridge_mock(), &hex::encode_upper(self.genesis_app_hash), TEST_CHAIN_ID, storage, - account_storage, None, None, )