From 7dd2d317c2a9c510b8b2e1cd5e58bf63080cf3a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Fri, 4 Nov 2022 12:47:47 +0100 Subject: [PATCH] update paths for core split and apply many small fixes --- Cargo.lock | 47 ++- Makefile | 3 +- apps/Cargo.toml | 10 +- apps/src/lib/client/rpc.rs | 13 +- apps/src/lib/client/tx.rs | 4 +- apps/src/lib/client/types.rs | 2 +- apps/src/lib/node/ledger/rpc.rs | 123 ------ apps/src/lib/node/ledger/shell/governance.rs | 9 +- apps/src/lib/node/ledger/storage/rocksdb.rs | 4 +- core/Cargo.toml | 52 ++- core/src/ledger/governance/mod.rs | 5 + core/src/ledger/ibc/actions.rs | 8 +- core/src/ledger/ibc/mod.rs | 3 + core/src/ledger/mod.rs | 3 +- core/src/ledger/parameters/mod.rs | 131 +----- core/src/ledger/slash_fund/mod.rs | 8 + .../src/ledger/slash_fund/storage.rs | 5 +- core/src/ledger/storage/merkle_tree.rs | 155 ++++--- core/src/ledger/storage/mockdb.rs | 2 +- core/src/ledger/storage/mod.rs | 64 +-- core/src/ledger/storage/traits.rs | 1 - core/src/ledger/vp_env.rs | 373 ----------------- core/src/lib.rs | 15 + core/src/proto/types.rs | 21 +- core/src/types/governance.rs | 26 -- core/src/types/hash.rs | 93 +++-- core/src/types/ibc.rs | 63 +-- core/src/types/internal.rs | 49 +++ core/src/types/key/common.rs | 14 +- core/src/types/key/mod.rs | 21 +- core/src/types/mod.rs | 3 + core/src/types/storage.rs | 180 ++++++--- core/src/types/time.rs | 92 +++-- core/src/types/token.rs | 9 +- core/src/types/transaction/decrypted.rs | 1 + core/src/types/transaction/governance.rs | 3 +- core/src/types/transaction/mod.rs | 8 +- proof_of_stake/Cargo.toml | 6 +- proof_of_stake/src/lib.rs | 71 +++- proof_of_stake/src/storage.rs | 200 ++++++++- proof_of_stake/src/types.rs | 114 +----- proof_of_stake/src/validation.rs | 7 +- shared/Cargo.toml | 80 ++-- shared/src/ledger/events.rs | 6 +- shared/src/ledger/ibc/mod.rs | 2 +- shared/src/ledger/ibc/vp/channel.rs | 66 +-- shared/src/ledger/ibc/vp/mod.rs | 10 +- shared/src/ledger/ibc/vp/packet.rs | 9 +- shared/src/ledger/mod.rs | 4 +- shared/src/ledger/native_vp/governance/mod.rs | 61 ++- .../src/ledger/native_vp/governance/utils.rs | 2 + shared/src/ledger/native_vp/mod.rs | 81 ++-- shared/src/ledger/native_vp/parameters.rs | 11 +- shared/src/ledger/pos/mod.rs | 206 +--------- shared/src/ledger/pos/vp.rs | 7 +- shared/src/ledger/protocol/mod.rs | 4 +- shared/src/ledger/queries/mod.rs | 17 +- shared/src/ledger/queries/shell.rs | 50 +-- shared/src/ledger/queries/types.rs | 7 +- shared/src/ledger/slash_fund/mod.rs | 17 +- shared/src/ledger/storage/mod.rs | 7 + shared/src/ledger/storage/write_log.rs | 4 +- shared/src/ledger/storage_api.rs | 3 + shared/src/ledger/vp_host_fns.rs | 381 ++++++++++++++++++ shared/src/lib.rs | 8 +- shared/src/vm/host_env.rs | 194 ++++----- shared/src/vm/types.rs | 15 +- tests/src/e2e/ibc_tests.rs | 2 +- tests/src/native_vp/pos.rs | 38 +- tests/src/vm_host_env/ibc.rs | 2 +- tests/src/vm_host_env/mod.rs | 2 +- tx_prelude/Cargo.toml | 5 +- tx_prelude/src/governance.rs | 6 +- tx_prelude/src/ibc.rs | 14 +- tx_prelude/src/key.rs | 2 +- tx_prelude/src/lib.rs | 64 ++- tx_prelude/src/proof_of_stake.rs | 17 +- tx_prelude/src/token.rs | 16 +- vm_env/Cargo.toml | 12 +- vm_env/src/lib.rs | 3 +- vp_prelude/Cargo.toml | 5 +- vp_prelude/src/key.rs | 4 +- vp_prelude/src/lib.rs | 25 +- vp_prelude/src/token.rs | 12 +- wasm/Cargo.lock | 82 +++- wasm/wasm_source/src/tx_bond.rs | 5 +- .../src/tx_change_validator_commission.rs | 4 +- wasm/wasm_source/src/tx_unbond.rs | 5 +- wasm/wasm_source/src/tx_withdraw.rs | 3 +- wasm_for_tests/wasm_source/Cargo.lock | 82 +++- 90 files changed, 1827 insertions(+), 1856 deletions(-) delete mode 100644 apps/src/lib/node/ledger/rpc.rs create mode 100644 core/src/ledger/slash_fund/mod.rs rename {shared => core}/src/ledger/slash_fund/storage.rs (80%) create mode 100644 shared/src/ledger/storage/mod.rs create mode 100644 shared/src/ledger/storage_api.rs create mode 100644 shared/src/ledger/vp_host_fns.rs diff --git a/Cargo.lock b/Cargo.lock index 397365b4e5..ae8122518d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3629,32 +3629,20 @@ checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a" name = "namada" version = "0.10.0" dependencies = [ - "ark-bls12-381", - "ark-ec", - "ark-serialize", "assert_matches", "async-trait", - "bech32", "bellman", - "bit-vec", "bls12_381", "borsh", "byte-unit", - "chrono", "circular-queue", "clru", "data-encoding", "derivative", - "ed25519-consensus", - "ferveo", - "ferveo-common", - "group-threshold-cryptography", - "hex", "ibc 0.14.0 (git+https://github.com/heliaxdev/ibc-rs?rev=9fcc1c8c19db6af50806ffe5b2f6c214adcbfd5d)", "ibc 0.14.0 (git+https://github.com/heliaxdev/ibc-rs.git?rev=f4703dfe2c1f25cc431279ab74f10f3e0f6827e2)", "ibc-proto 0.17.1 (git+https://github.com/heliaxdev/ibc-rs?rev=9fcc1c8c19db6af50806ffe5b2f6c214adcbfd5d)", "ibc-proto 0.17.1 (git+https://github.com/heliaxdev/ibc-rs.git?rev=f4703dfe2c1f25cc431279ab74f10f3e0f6827e2)", - "ics23", "itertools", "libsecp256k1", "loupe", @@ -3667,17 +3655,11 @@ dependencies = [ "pretty_assertions", "proptest", "prost", - "prost-types", "pwasm-utils", - "rand 0.8.5", - "rand_core 0.6.4", "rayon", "rust_decimal", - "rust_decimal_macros", - "serde 1.0.147", "serde_json", "sha2 0.9.9", - "sparse-merkle-tree", "tempfile", "tendermint 0.23.5", "tendermint 0.23.6", @@ -3688,7 +3670,6 @@ dependencies = [ "test-log", "thiserror", "tokio", - "tonic-build", "tracing 0.1.37", "tracing-subscriber 0.3.16", "wasmer", @@ -3791,28 +3772,46 @@ dependencies = [ name = "namada_core" version = "0.9.0" dependencies = [ + "ark-bls12-381", "ark-ec", + "ark-serialize", "assert_matches", "bech32", + "bellman", + "bit-vec", "borsh", "chrono", "data-encoding", "derivative", "ed25519-consensus", "ferveo", + "ferveo-common", "group-threshold-cryptography", + "ibc 0.14.0 (git+https://github.com/heliaxdev/ibc-rs?rev=9fcc1c8c19db6af50806ffe5b2f6c214adcbfd5d)", + "ibc 0.14.0 (git+https://github.com/heliaxdev/ibc-rs.git?rev=f4703dfe2c1f25cc431279ab74f10f3e0f6827e2)", + "ibc-proto 0.17.1 (git+https://github.com/heliaxdev/ibc-rs?rev=9fcc1c8c19db6af50806ffe5b2f6c214adcbfd5d)", + "ibc-proto 0.17.1 (git+https://github.com/heliaxdev/ibc-rs.git?rev=f4703dfe2c1f25cc431279ab74f10f3e0f6827e2)", "ics23", "itertools", "libsecp256k1", + "masp_primitives", "pretty_assertions", "proptest", + "prost", + "prost-types", "rand 0.8.5", "rand_core 0.6.4", + "rayon", "rust_decimal", + "rust_decimal_macros", "serde 1.0.147", "serde_json", "sha2 0.9.9", "sparse-merkle-tree", + "tendermint 0.23.5", + "tendermint 0.23.6", + "tendermint-proto 0.23.5", + "tendermint-proto 0.23.6", "test-log", "thiserror", "tonic-build", @@ -3846,10 +3845,12 @@ version = "0.10.0" dependencies = [ "borsh", "derivative", + "namada_core", "proptest", "rust_decimal", "rust_decimal_macros", "thiserror", + "tracing 0.1.37", ] [[package]] @@ -3902,8 +3903,9 @@ version = "0.10.0" dependencies = [ "borsh", "masp_primitives", - "namada", + "namada_core", "namada_macros", + "namada_proof_of_stake", "namada_vm_env", "rust_decimal", "sha2 0.10.6", @@ -3918,7 +3920,7 @@ dependencies = [ "hex", "masp_primitives", "masp_proofs", - "namada", + "namada_core", ] [[package]] @@ -3926,8 +3928,9 @@ name = "namada_vp_prelude" version = "0.10.0" dependencies = [ "borsh", - "namada", + "namada_core", "namada_macros", + "namada_proof_of_stake", "namada_vm_env", "sha2 0.10.6", "thiserror", diff --git a/Makefile b/Makefile index 829488eca6..9116554558 100644 --- a/Makefile +++ b/Makefile @@ -64,8 +64,7 @@ clippy-abcipp: $(cargo) +$(nightly) clippy \ --all-targets \ --manifest-path ./vm_env/Cargo.toml \ - --no-default-features \ - --features "abcipp" && \ + --no-default-features && \ make -C $(wasms) clippy && \ $(foreach wasm,$(wasm_templates),$(clippy-wasm) && ) true diff --git a/apps/Cargo.toml b/apps/Cargo.toml index ed16372841..2724f08d15 100644 --- a/apps/Cargo.toml +++ b/apps/Cargo.toml @@ -47,25 +47,27 @@ std = ["ed25519-consensus/std", "rand/std", "rand_core/std"] testing = ["dev"] abcipp = [ + "namada/abcipp", + "namada/tendermint-rpc-abcipp", "tendermint-abcipp", "tendermint-config-abcipp", "tendermint-proto-abcipp", "tendermint-rpc-abcipp", "tower-abci-abcipp", - "namada/abcipp" ] abciplus = [ + "namada/abciplus", + "namada/tendermint-rpc", "tendermint", "tendermint-config", "tendermint-rpc", "tendermint-proto", "tower-abci", - "namada/abciplus" ] [dependencies] -namada = {path = "../shared", features = ["wasm-runtime", "ferveo-tpke", "rand", "tendermint-rpc", "secp256k1-sign-verify"]} +namada = {path = "../shared", default-features = false, features = ["wasm-runtime", "ferveo-tpke"]} ark-serialize = "0.3.0" ark-std = "0.3.0" # branch = "bat/arse-merkle-tree" @@ -148,7 +150,7 @@ rust_decimal = "1.26.1" rust_decimal_macros = "1.26.1" [dev-dependencies] -namada = {path = "../shared", features = ["testing", "wasm-runtime"]} +namada = {path = "../shared", default-features = false, features = ["testing", "wasm-runtime"]} bit-set = "0.5.2" # A fork with state machime testing proptest = {git = "https://github.com/heliaxdev/proptest", branch = "tomas/sm"} diff --git a/apps/src/lib/client/rpc.rs b/apps/src/lib/client/rpc.rs index 14028771ef..e95e50292c 100644 --- a/apps/src/lib/client/rpc.rs +++ b/apps/src/lib/client/rpc.rs @@ -25,7 +25,7 @@ use masp_primitives::zip32::ExtendedFullViewingKey; use namada::ledger::events::Event; use namada::ledger::governance::parameters::GovParams; use namada::ledger::governance::storage as gov_storage; -use namada::ledger::governance::utils::Votes; +use namada::ledger::native_vp::governance::utils::Votes; use namada::ledger::parameters::{storage as param_storage, EpochDuration}; use namada::ledger::pos::types::{ decimal_mult_u64, Epoch as PosEpoch, WeightedValidator, @@ -131,7 +131,7 @@ pub async fn query_epoch(args: args::Query) -> Epoch { /// Query the last committed block pub async fn query_block( args: args::Query, -) -> tendermint_rpc::endpoint::block::Response { +) -> crate::facade::tendermint_rpc::endpoint::block::Response { let client = HttpClient::new(args.ledger_address).unwrap(); let response = client.latest_block().await.unwrap(); println!( @@ -2065,8 +2065,7 @@ fn process_bonds_query( .unwrap(); delta = apply_slashes(slashes, delta, *epoch_start, None, Some(w)); current_total += delta; - let epoch_start: Epoch = (*epoch_start).into(); - if epoch >= &epoch_start { + if epoch >= epoch_start { total_active += delta; } } @@ -2121,8 +2120,7 @@ fn process_unbonds_query( Some(w), ); current_total += delta; - let epoch_end: Epoch = (*epoch_end).into(); - if epoch > &epoch_end { + if epoch > epoch_end { withdrawable += delta; } } @@ -2800,8 +2798,7 @@ pub async fn get_bond_amount_at( None, None, ); - let epoch_start: Epoch = (*epoch_start).into(); - if epoch >= epoch_start { + if epoch >= *epoch_start { delegated_amount += delta; } } diff --git a/apps/src/lib/client/tx.rs b/apps/src/lib/client/tx.rs index 65a0fac3f5..bb667cabec 100644 --- a/apps/src/lib/client/tx.rs +++ b/apps/src/lib/client/tx.rs @@ -2179,7 +2179,7 @@ async fn is_safe_voting_window( match proposal_end_epoch { Some(proposal_end_epoch) => { - !namada::ledger::governance::utils::is_valid_validator_voting_period( + !namada::ledger::native_vp::governance::utils::is_valid_validator_voting_period( current_epoch, proposal_start_epoch, proposal_end_epoch, @@ -2486,7 +2486,7 @@ pub async fn submit_validator_commission_change( match (commission_rates, max_change) { (Some(rates), Some(max_change)) => { // Assuming that pipeline length = 2 - let rate_next_epoch = rates.get(epoch + 1).unwrap(); + let rate_next_epoch = rates.get(epoch.next()).unwrap(); if (args.rate - rate_next_epoch).abs() > max_change { eprintln!( "New rate is too large of a change with respect to \ diff --git a/apps/src/lib/client/types.rs b/apps/src/lib/client/types.rs index 1f94838d25..5a26244474 100644 --- a/apps/src/lib/client/types.rs +++ b/apps/src/lib/client/types.rs @@ -8,11 +8,11 @@ use namada::types::masp::{TransferSource, TransferTarget}; use namada::types::storage::Epoch; use namada::types::transaction::GasLimit; use namada::types::{key, token}; -use tendermint_config::net::Address as TendermintAddress; use super::rpc; use crate::cli::{args, Context}; use crate::client::tx::Conversions; +use crate::facade::tendermint_config::net::Address as TendermintAddress; #[derive(Clone, Debug)] pub struct ParsedTxArgs { diff --git a/apps/src/lib/node/ledger/rpc.rs b/apps/src/lib/node/ledger/rpc.rs deleted file mode 100644 index b7a1ebcfad..0000000000 --- a/apps/src/lib/node/ledger/rpc.rs +++ /dev/null @@ -1,123 +0,0 @@ -//! RPC endpoint is used for ledger state queries - -use std::fmt::Display; -use std::str::FromStr; - -use masp_primitives::asset_type::AssetType; -use namada::types::address::Address; -use namada::types::storage; -use namada::types::token::CONVERSION_KEY_PREFIX; -use thiserror::Error; - -use crate::facade::tendermint::abci::Path as AbciPath; - -/// RPC query path -#[derive(Debug, Clone)] -pub enum Path { - /// Dry run a transaction - DryRunTx, - /// Epoch of the last committed block - Epoch, - /// Results of all committed blocks - Results, - /// Read a storage value with exact storage key - Value(storage::Key), - /// Read a range of storage values with a matching key prefix - Prefix(storage::Key), - /// Check if the given storage key exists - HasKey(storage::Key), - /// Conversion associated with given asset type - Conversion(AssetType), -} - -#[derive(Debug, Clone)] -pub struct BalanceQuery { - #[allow(dead_code)] - owner: Option
, - #[allow(dead_code)] - token: Option
, -} - -const DRY_RUN_TX_PATH: &str = "dry_run_tx"; -const EPOCH_PATH: &str = "epoch"; -const RESULTS_PATH: &str = "results"; -const VALUE_PREFIX: &str = "value"; -const PREFIX_PREFIX: &str = "prefix"; -const HAS_KEY_PREFIX: &str = "has_key"; - -impl Display for Path { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Path::DryRunTx => write!(f, "{}", DRY_RUN_TX_PATH), - Path::Epoch => write!(f, "{}", EPOCH_PATH), - Path::Results => write!(f, "{}", RESULTS_PATH), - Path::Value(storage_key) => { - write!(f, "{}/{}", VALUE_PREFIX, storage_key) - } - Path::Prefix(storage_key) => { - write!(f, "{}/{}", PREFIX_PREFIX, storage_key) - } - Path::HasKey(storage_key) => { - write!(f, "{}/{}", HAS_KEY_PREFIX, storage_key) - } - Path::Conversion(asset_type) => { - write!(f, "{}/{}", CONVERSION_KEY_PREFIX, asset_type) - } - } - } -} - -impl FromStr for Path { - type Err = PathParseError; - - fn from_str(s: &str) -> Result { - match s { - DRY_RUN_TX_PATH => Ok(Self::DryRunTx), - EPOCH_PATH => Ok(Self::Epoch), - RESULTS_PATH => Ok(Self::Results), - _ => match s.split_once('/') { - Some((VALUE_PREFIX, storage_key)) => { - let key = storage::Key::parse(storage_key) - .map_err(PathParseError::InvalidStorageKey)?; - Ok(Self::Value(key)) - } - Some((PREFIX_PREFIX, storage_key)) => { - let key = storage::Key::parse(storage_key) - .map_err(PathParseError::InvalidStorageKey)?; - Ok(Self::Prefix(key)) - } - Some((HAS_KEY_PREFIX, storage_key)) => { - let key = storage::Key::parse(storage_key) - .map_err(PathParseError::InvalidStorageKey)?; - Ok(Self::HasKey(key)) - } - Some((CONVERSION_KEY_PREFIX, asset_type)) => { - let key = AssetType::from_str(asset_type) - .map_err(PathParseError::InvalidAssetType)?; - Ok(Self::Conversion(key)) - } - _ => Err(PathParseError::InvalidPath(s.to_string())), - }, - } - } -} - -impl From for AbciPath { - fn from(path: Path) -> Self { - let path = path.to_string(); - // TODO: update in tendermint-rs to allow to construct this from owned - // string. It's what `from_str` does anyway - AbciPath::from_str(&path).unwrap() - } -} - -#[allow(missing_docs)] -#[derive(Error, Debug)] -pub enum PathParseError { - #[error("Unrecognized query path: {0}")] - InvalidPath(String), - #[error("Invalid storage key: {0}")] - InvalidStorageKey(storage::Error), - #[error("Unrecognized asset type: {0}")] - InvalidAssetType(std::io::Error), -} diff --git a/apps/src/lib/node/ledger/shell/governance.rs b/apps/src/lib/node/ledger/shell/governance.rs index 71c6049afd..02a22b1caa 100644 --- a/apps/src/lib/node/ledger/shell/governance.rs +++ b/apps/src/lib/node/ledger/shell/governance.rs @@ -1,11 +1,12 @@ +use namada::core::ledger::slash_fund::ADDRESS as slash_fund_address; use namada::ledger::events::EventType; -use namada::ledger::governance::utils::{ - compute_tally, get_proposal_votes, ProposalEvent, -}; use namada::ledger::governance::{ storage as gov_storage, ADDRESS as gov_address, }; -use namada::ledger::slash_fund::ADDRESS as slash_fund_address; +use namada::ledger::native_vp::governance::utils::{ + compute_tally, get_proposal_votes, ProposalEvent, +}; +use namada::ledger::protocol; use namada::ledger::storage::types::encode; use namada::ledger::storage::{DBIter, StorageHasher, DB}; use namada::types::address::Address; diff --git a/apps/src/lib/node/ledger/storage/rocksdb.rs b/apps/src/lib/node/ledger/storage/rocksdb.rs index ab6dbfe1a8..b25862d8d9 100644 --- a/apps/src/lib/node/ledger/storage/rocksdb.rs +++ b/apps/src/lib/node/ledger/storage/rocksdb.rs @@ -37,9 +37,9 @@ use namada::ledger::storage::{ types, BlockStateRead, BlockStateWrite, DBIter, DBWriteBatch, Error, MerkleTreeStoresRead, Result, StoreType, DB, }; +use namada::types::internal::TxQueue; use namada::types::storage::{ - BlockHeight, BlockResults, Header, Key, KeySeg, TxQueue, - KEY_SEGMENT_SEPARATOR, + BlockHeight, BlockResults, Header, Key, KeySeg, KEY_SEGMENT_SEPARATOR, }; use namada::types::time::DateTimeUtc; use rocksdb::{ diff --git a/core/Cargo.toml b/core/Cargo.toml index 5dbf217d34..50015c4f50 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -15,34 +15,84 @@ ferveo-tpke = [ "rand_core", "rand", ] +wasm-runtime = [ + "rayon", +] +# secp256k1 key signing and verification, disabled in WASM build by default as +# it bloats the build a lot +secp256k1-sign-verify = [ + "libsecp256k1/hmac", +] + +abcipp = [ + "ibc-proto-abcipp", + "ibc-abcipp", + "tendermint-abcipp", + "tendermint-proto-abcipp" +] +abciplus = [ + "ibc", + "ibc-proto", + "tendermint", + "tendermint-proto", +] + +ibc-mocks = [ + "ibc/mocks", +] +ibc-mocks-abcipp = [ + "ibc-abcipp/mocks", +] + # for integration tests and test utilies testing = [ "rand", "rand_core", + "proptest", ] [dependencies] +ark-bls12-381 = {version = "0.3"} ark-ec = {version = "0.3", optional = true} +ark-serialize = {version = "0.3"} # We switch off "blake2b" because it cannot be compiled to wasm # branch = "bat/arse-merkle-tree" arse-merkle-tree = {package = "sparse-merkle-tree", git = "https://github.com/heliaxdev/sparse-merkle-tree", rev = "04ad1eeb28901b57a7599bbe433b3822965dabe8", default-features = false, features = ["std", "borsh"]} bech32 = "0.8.0" +bellman = "0.11.2" +bit-vec = "0.6.3" borsh = "0.9.0" chrono = {version = "0.4.22", default-features = false, features = ["clock", "std"]} data-encoding = "2.3.2" derivative = "2.2.0" ed25519-consensus = "1.2.0" ferveo = {optional = true, git = "https://github.com/anoma/ferveo"} +ferveo-common = {git = "https://github.com/anoma/ferveo"} tpke = {package = "group-threshold-cryptography", optional = true, git = "https://github.com/anoma/ferveo"} +# TODO using the same version of tendermint-rs as we do here. +ibc = {version = "0.14.0", default-features = false, optional = true} +ibc-proto = {version = "0.17.1", default-features = false, optional = true} +ibc-abcipp = {package = "ibc", git = "https://github.com/heliaxdev/ibc-rs", rev = "9fcc1c8c19db6af50806ffe5b2f6c214adcbfd5d", default-features = false, optional = true} +ibc-proto-abcipp = {package = "ibc-proto", git = "https://github.com/heliaxdev/ibc-rs", rev = "9fcc1c8c19db6af50806ffe5b2f6c214adcbfd5d", default-features = false, optional = true} ics23 = "0.7.0" itertools = "0.10.0" libsecp256k1 = {git = "https://github.com/heliaxdev/libsecp256k1", rev = "bbb3bd44a49db361f21d9db80f9a087c194c0ae9", default-features = false, features = ["std", "static-context"]} +masp_primitives = { git = "https://github.com/anoma/masp", rev = "bee40fc465f6afbd10558d12fe96eb1742eee45c" } +proptest = {git = "https://github.com/heliaxdev/proptest", branch = "tomas/sm", optional = true} +prost = "0.9.0" +prost-types = "0.9.0" rand = {version = "0.8", optional = true} rand_core = {version = "0.6", optional = true} -rust_decimal = "1.26.1" +rayon = {version = "=1.5.3", optional = true} +rust_decimal = { version = "1.26.1", features = ["borsh"] } +rust_decimal_macros = "1.26.1" serde = {version = "1.0.125", features = ["derive"]} serde_json = "1.0.62" sha2 = "0.9.3" +tendermint = {version = "0.23.6", optional = true} +tendermint-proto = {version = "0.23.6", optional = true} +tendermint-abcipp = {package = "tendermint", git = "https://github.com/heliaxdev/tendermint-rs", rev = "95c52476bc37927218374f94ac8e2a19bd35bec9", optional = true} +tendermint-proto-abcipp = {package = "tendermint-proto", git = "https://github.com/heliaxdev/tendermint-rs", rev = "95c52476bc37927218374f94ac8e2a19bd35bec9", optional = true} thiserror = "1.0.30" tracing = "0.1.30" zeroize = {version = "1.5.5", features = ["zeroize_derive"]} diff --git a/core/src/ledger/governance/mod.rs b/core/src/ledger/governance/mod.rs index 44d20f50e0..8e3fb977f3 100644 --- a/core/src/ledger/governance/mod.rs +++ b/core/src/ledger/governance/mod.rs @@ -1,6 +1,11 @@ //! Governance library code +use crate::types::address::{Address, InternalAddress}; + /// governance parameters pub mod parameters; /// governance storage pub mod storage; + +/// The governance internal address +pub const ADDRESS: Address = Address::Internal(InternalAddress::Governance); diff --git a/core/src/ledger/ibc/actions.rs b/core/src/ledger/ibc/actions.rs index 0c76f086eb..a9f2e2c811 100644 --- a/core/src/ledger/ibc/actions.rs +++ b/core/src/ledger/ibc/actions.rs @@ -68,15 +68,15 @@ use crate::ibc::events::IbcEvent; #[cfg(any(feature = "ibc-mocks-abcipp", feature = "ibc-mocks"))] use crate::ibc::mock::client_state::{MockClientState, MockConsensusState}; use crate::ibc::timestamp::Timestamp; +use crate::ledger::ibc::data::{ + Error as IbcDataError, FungibleTokenPacketData, IbcMessage, PacketAck, + PacketReceipt, +}; use crate::ledger::ibc::storage; use crate::ledger::storage_api; use crate::tendermint::Time; use crate::tendermint_proto::{Error as ProtoError, Protobuf}; use crate::types::address::{Address, InternalAddress}; -use crate::types::ibc::data::{ - Error as IbcDataError, FungibleTokenPacketData, IbcMessage, PacketAck, - PacketReceipt, -}; use crate::types::ibc::IbcEvent as AnomaIbcEvent; use crate::types::storage::{BlockHeight, Key}; use crate::types::time::Rfc3339String; diff --git a/core/src/ledger/ibc/mod.rs b/core/src/ledger/ibc/mod.rs index efb7f7f0d5..f98fb2e432 100644 --- a/core/src/ledger/ibc/mod.rs +++ b/core/src/ledger/ibc/mod.rs @@ -1,2 +1,5 @@ +//! IBC library code + pub mod actions; +pub mod data; pub mod storage; diff --git a/core/src/ledger/mod.rs b/core/src/ledger/mod.rs index bbd12bc60d..83568c0da7 100644 --- a/core/src/ledger/mod.rs +++ b/core/src/ledger/mod.rs @@ -2,9 +2,10 @@ pub mod gas; pub mod governance; -#[cfg(feature = "ibc-rs")] +#[cfg(any(feature = "abciplus", feature = "abcipp"))] pub mod ibc; pub mod parameters; +pub mod slash_fund; pub mod storage; pub mod storage_api; pub mod tx_env; diff --git a/core/src/ledger/parameters/mod.rs b/core/src/ledger/parameters/mod.rs index 75862404db..cb84bd56e7 100644 --- a/core/src/ledger/parameters/mod.rs +++ b/core/src/ledger/parameters/mod.rs @@ -1,104 +1,19 @@ //! Protocol parameters pub mod storage; -use std::collections::BTreeSet; - use borsh::{BorshDeserialize, BorshSchema, BorshSerialize}; use rust_decimal::Decimal; use thiserror::Error; -use self::storage as parameter_storage; -use super::governance::{self}; use super::storage::types::{decode, encode}; use super::storage::{types, Storage}; -use crate::ledger::native_vp::{self, Ctx, NativeVp}; -use crate::ledger::storage::{self as ledger_storage, StorageHasher}; +use crate::ledger::storage::{self as ledger_storage}; use crate::types::address::{Address, InternalAddress}; use crate::types::storage::Key; use crate::types::time::DurationSecs; -use crate::vm::WasmCacheAccess; const ADDRESS: Address = Address::Internal(InternalAddress::Parameters); -#[allow(missing_docs)] -#[derive(Error, Debug)] -pub enum Error { - #[error("Native VP error: {0}")] - NativeVpError(native_vp::Error), -} - -/// Parameters functions result -pub type Result = std::result::Result; - -/// Parameters VP -pub struct ParametersVp<'a, DB, H, CA> -where - DB: ledger_storage::DB + for<'iter> ledger_storage::DBIter<'iter>, - H: StorageHasher, - CA: WasmCacheAccess, -{ - /// Context to interact with the host structures. - pub ctx: Ctx<'a, DB, H, CA>, -} - -impl<'a, DB, H, CA> NativeVp for ParametersVp<'a, DB, H, CA> -where - DB: 'static + ledger_storage::DB + for<'iter> ledger_storage::DBIter<'iter>, - H: 'static + StorageHasher, - CA: 'static + WasmCacheAccess, -{ - type Error = Error; - - const ADDR: InternalAddress = InternalAddress::Parameters; - - fn validate_tx( - &self, - tx_data: &[u8], - keys_changed: &BTreeSet, - _verifiers: &BTreeSet
, - ) -> Result { - let result = keys_changed.iter().all(|key| { - let key_type: KeyType = key.into(); - match key_type { - KeyType::PARAMETER => governance::utils::is_proposal_accepted( - self.ctx.storage, - tx_data, - ) - .unwrap_or(false), - KeyType::UNKNOWN_PARAMETER => false, - KeyType::UNKNOWN => true, - } - }); - Ok(result) - } -} - -impl From for Error { - fn from(err: native_vp::Error) -> Self { - Self::NativeVpError(err) - } -} - -#[allow(missing_docs)] -#[derive(Error, Debug)] -pub enum ReadError { - #[error("Storage error: {0}")] - StorageError(ledger_storage::Error), - #[error("Storage type error: {0}")] - StorageTypeError(types::Error), - #[error("Protocol parameters are missing, they must be always set")] - ParametersMissing, -} - -#[allow(missing_docs)] -#[derive(Error, Debug)] -pub enum WriteError { - #[error("Storage error: {0}")] - StorageError(ledger_storage::Error), - #[error("Serialize error: {0}")] - SerializeError(String), -} - /// Protocol parameters #[derive( Clone, @@ -156,6 +71,26 @@ pub struct EpochDuration { pub min_duration: DurationSecs, } +#[allow(missing_docs)] +#[derive(Error, Debug)] +pub enum ReadError { + #[error("Storage error: {0}")] + StorageError(ledger_storage::Error), + #[error("Storage type error: {0}")] + StorageTypeError(types::Error), + #[error("Protocol parameters are missing, they must be always set")] + ParametersMissing, +} + +#[allow(missing_docs)] +#[derive(Error, Debug)] +pub enum WriteError { + #[error("Storage error: {0}")] + StorageError(ledger_storage::Error), + #[error("Serialize error: {0}")] + SerializeError(String), +} + impl Parameters { /// Initialize parameters in storage in the genesis block. pub fn init_storage(&self, storage: &mut Storage) @@ -254,7 +189,6 @@ impl Parameters { ); } } - /// Update the max_expected_time_per_block parameter in storage. Returns the /// parameters and gas cost. pub fn update_max_expected_time_per_block_parameter( @@ -555,26 +489,3 @@ where + gas_reward, )) } - -#[allow(clippy::upper_case_acronyms)] -enum KeyType { - #[allow(clippy::upper_case_acronyms)] - PARAMETER, - #[allow(clippy::upper_case_acronyms)] - #[allow(non_camel_case_types)] - UNKNOWN_PARAMETER, - #[allow(clippy::upper_case_acronyms)] - UNKNOWN, -} - -impl From<&Key> for KeyType { - fn from(value: &Key) -> Self { - if parameter_storage::is_protocol_parameter_key(value) { - KeyType::PARAMETER - } else if parameter_storage::is_parameter_key(value) { - KeyType::UNKNOWN_PARAMETER - } else { - KeyType::UNKNOWN - } - } -} diff --git a/core/src/ledger/slash_fund/mod.rs b/core/src/ledger/slash_fund/mod.rs new file mode 100644 index 0000000000..7a7d53963b --- /dev/null +++ b/core/src/ledger/slash_fund/mod.rs @@ -0,0 +1,8 @@ +//! SlashFund library code + +use crate::types::address::{Address, InternalAddress}; + +/// Internal SlashFund address +pub const ADDRESS: Address = Address::Internal(InternalAddress::SlashFund); + +pub mod storage; diff --git a/shared/src/ledger/slash_fund/storage.rs b/core/src/ledger/slash_fund/storage.rs similarity index 80% rename from shared/src/ledger/slash_fund/storage.rs rename to core/src/ledger/slash_fund/storage.rs index 60d29f0f48..9c437da591 100644 --- a/shared/src/ledger/slash_fund/storage.rs +++ b/core/src/ledger/slash_fund/storage.rs @@ -1,7 +1,8 @@ -use super::ADDRESS; +//! Slash fund storage + use crate::types::storage::{DbKeySeg, Key}; /// Check if a key is a slash fund key pub fn is_slash_fund_key(key: &Key) -> bool { - matches!(&key.segments[0], DbKeySeg::AddressSeg(addr) if addr == &ADDRESS) + matches!(&key.segments[0], DbKeySeg::AddressSeg(addr) if addr == &super::ADDRESS) } diff --git a/core/src/ledger/storage/merkle_tree.rs b/core/src/ledger/storage/merkle_tree.rs index 5a7e33e2d9..1eb3592685 100644 --- a/core/src/ledger/storage/merkle_tree.rs +++ b/core/src/ledger/storage/merkle_tree.rs @@ -10,19 +10,17 @@ use arse_merkle_tree::{ use borsh::{BorshDeserialize, BorshSerialize}; use ics23::commitment_proof::Proof as Ics23Proof; use ics23::{CommitmentProof, ExistenceProof, NonExistenceProof}; -use namada_core::types::storage::{TreeKeyError, IBC_KEY_LIMIT}; -use prost::Message; use thiserror::Error; use super::traits::{StorageHasher, SubTreeRead, SubTreeWrite}; use crate::bytes::ByteBuf; use crate::ledger::storage::ics23_specs::{self, ibc_leaf_spec}; use crate::ledger::storage::types; -use crate::tendermint::merkle::proof::{Proof, ProofOp}; use crate::types::address::{Address, InternalAddress}; use crate::types::hash::Hash; use crate::types::storage::{ - DbKeySeg, Error as StorageError, Key, MerkleValue, StringKey, TreeBytes, + self, DbKeySeg, Error as StorageError, Key, MerkleValue, StringKey, + TreeBytes, TreeKeyError, IBC_KEY_LIMIT, }; #[allow(missing_docs)] @@ -53,10 +51,14 @@ pub enum Error { /// Result for functions that may fail type Result = std::result::Result; -/// Type aliases for the different merkle trees and backing stores +// Type aliases for the different merkle trees and backing stores +/// Sparse-merkle-tree store pub type SmtStore = DefaultStore; +/// Arse-merkle-tree store pub type AmtStore = DefaultStore; +/// Sparse-merkle-tree pub type Smt = ArseMerkleTree; +/// Arse-merkle-tree pub type Amt = ArseMerkleTree; @@ -96,6 +98,7 @@ pub enum Store { } impl Store { + /// Convert to a `StoreRef` with borrowed store pub fn as_ref(&self) -> StoreRef { match self { Self::Base(store) => StoreRef::Base(store), @@ -119,6 +122,7 @@ pub enum StoreRef<'a> { } impl<'a> StoreRef<'a> { + /// Convert to an owned `Store`. pub fn to_owned(&self) -> Store { match *self { Self::Base(store) => Store::Base(store.to_owned()), @@ -128,6 +132,7 @@ impl<'a> StoreRef<'a> { } } + /// Encode a `StoreRef`. pub fn encode(&self) -> Vec { match self { Self::Base(store) => store.try_to_vec(), @@ -392,25 +397,15 @@ impl MerkleTree { } // Get a proof of the sub tree - self.get_tendermint_proof(key, nep) + self.get_sub_tree_proof(key, nep) } /// Get the Tendermint proof with the base proof - pub fn get_tendermint_proof( + pub fn get_sub_tree_proof( &self, key: &Key, sub_proof: CommitmentProof, ) -> Result { - let mut data = vec![]; - sub_proof - .encode(&mut data) - .expect("Encoding proof shouldn't fail"); - let sub_proof_op = ProofOp { - field_type: "ics23_CommitmentProof".to_string(), - key: key.to_string().as_bytes().to_vec(), - data, - }; - // Get a membership proof of the base tree because the sub root should // exist let (store_type, _) = StoreType::sub_key(key)?; @@ -429,19 +424,10 @@ impl MerkleTree { _ => unreachable!(), }; - let mut data = vec![]; - base_proof - .encode(&mut data) - .expect("Encoding proof shouldn't fail"); - let base_proof_op = ProofOp { - field_type: "ics23_CommitmentProof".to_string(), - key: key.to_string().as_bytes().to_vec(), - data, - }; - - // Set ProofOps from leaf to root Ok(Proof { - ops: vec![sub_proof_op, base_proof_op], + key: key.clone(), + sub_proof, + base_proof, }) } } @@ -541,6 +527,69 @@ impl From for Error { } } +/// Type of membership proof from a merkle tree +pub enum MembershipProof { + /// ICS23 compliant membership proof + ICS23(CommitmentProof), +} + +impl From for MembershipProof { + fn from(proof: CommitmentProof) -> Self { + Self::ICS23(proof) + } +} + +/// A storage key existence or non-existence proof +#[derive(Debug)] +pub struct Proof { + /// Storage key + pub key: storage::Key, + /// Sub proof + pub sub_proof: CommitmentProof, + /// Base proof + pub base_proof: CommitmentProof, +} + +#[cfg(any(feature = "tendermint", feature = "tendermint-abcipp"))] +impl From for crate::tendermint::merkle::proof::Proof { + fn from( + Proof { + key, + sub_proof, + base_proof, + }: Proof, + ) -> Self { + use prost::Message; + + use crate::tendermint::merkle::proof::{Proof, ProofOp}; + + let mut data = vec![]; + sub_proof + .encode(&mut data) + .expect("Encoding proof shouldn't fail"); + let sub_proof_op = ProofOp { + field_type: "ics23_CommitmentProof".to_string(), + key: key.to_string().as_bytes().to_vec(), + data, + }; + + let mut data = vec![]; + base_proof + .encode(&mut data) + .expect("Encoding proof shouldn't fail"); + let base_proof_op = ProofOp { + field_type: "ics23_CommitmentProof".to_string(), + key: key.to_string().as_bytes().to_vec(), + data, + }; + + // Set ProofOps from leaf to root + Proof { + ops: vec![sub_proof_op, base_proof_op], + } + } +} + #[cfg(test)] mod test { use super::*; @@ -585,9 +634,7 @@ mod test { let nep = tree .get_non_existence_proof(&ibc_non_key) .expect("Test failed"); - let subtree_nep = nep.ops.get(0).expect("Test failed"); - let nep_commitment_proof = - CommitmentProof::decode(&*subtree_nep.data).expect("Test failed"); + let nep_commitment_proof = nep.sub_proof; let non_existence_proof = match nep_commitment_proof.clone().proof.expect("Test failed") { Ics23Proof::Nonexist(nep) => nep, @@ -611,9 +658,7 @@ mod test { sub_key.to_string().as_bytes(), ); assert!(nep_verification_res); - let basetree_ep = nep.ops.get(1).unwrap(); - let basetree_ep_commitment_proof = - CommitmentProof::decode(&*basetree_ep.data).unwrap(); + let basetree_ep_commitment_proof = nep.base_proof; let basetree_ics23_ep = match basetree_ep_commitment_proof.clone().proof.unwrap() { Ics23Proof::Exist(ep) => ep, @@ -679,17 +724,19 @@ mod test { vec![ibc_val.clone().into()], ) .unwrap(); - let proof = tree.get_tendermint_proof(&ibc_key, proof).unwrap(); + let proof = tree.get_sub_tree_proof(&ibc_key, proof).unwrap(); let (store_type, sub_key) = StoreType::sub_key(&ibc_key).unwrap(); let paths = vec![sub_key.to_string(), store_type.to_string()]; let mut sub_root = ibc_val.clone(); let mut value = ibc_val; // First, the sub proof is verified. Next the base proof is verified // with the sub root - for ((p, spec), key) in - proof.ops.iter().zip(specs.iter()).zip(paths.iter()) + for ((commitment_proof, spec), key) in + [proof.sub_proof, proof.base_proof] + .into_iter() + .zip(specs.iter()) + .zip(paths.iter()) { - let commitment_proof = CommitmentProof::decode(&*p.data).unwrap(); let existence_proof = match commitment_proof.clone().proof.unwrap() { Ics23Proof::Exist(ep) => ep, @@ -734,17 +781,19 @@ mod test { vec![pos_val.clone().into()], ) .unwrap(); - let proof = tree.get_tendermint_proof(&pos_key, proof).unwrap(); + let proof = tree.get_sub_tree_proof(&pos_key, proof).unwrap(); let (store_type, sub_key) = StoreType::sub_key(&pos_key).unwrap(); let paths = vec![sub_key.to_string(), store_type.to_string()]; let mut sub_root = pos_val.clone(); let mut value = pos_val; // First, the sub proof is verified. Next the base proof is verified // with the sub root - for ((p, spec), key) in - proof.ops.iter().zip(specs.iter()).zip(paths.iter()) + for ((commitment_proof, spec), key) in + [proof.sub_proof, proof.base_proof] + .into_iter() + .zip(specs.iter()) + .zip(paths.iter()) { - let commitment_proof = CommitmentProof::decode(&*p.data).unwrap(); let existence_proof = match commitment_proof.clone().proof.unwrap() { Ics23Proof::Exist(ep) => ep, @@ -784,9 +833,7 @@ mod test { let nep = tree .get_non_existence_proof(&ibc_non_key) .expect("Test failed"); - let subtree_nep = nep.ops.get(0).expect("Test failed"); - let nep_commitment_proof = - CommitmentProof::decode(&*subtree_nep.data).expect("Test failed"); + let nep_commitment_proof = nep.sub_proof; let non_existence_proof = match nep_commitment_proof.clone().proof.expect("Test failed") { Ics23Proof::Nonexist(nep) => nep, @@ -810,9 +857,7 @@ mod test { sub_key.to_string().as_bytes(), ); assert!(nep_verification_res); - let basetree_ep = nep.ops.get(1).unwrap(); - let basetree_ep_commitment_proof = - CommitmentProof::decode(&*basetree_ep.data).unwrap(); + let basetree_ep_commitment_proof = nep.base_proof; let basetree_ics23_ep = match basetree_ep_commitment_proof.clone().proof.unwrap() { Ics23Proof::Exist(ep) => ep, @@ -830,15 +875,3 @@ mod test { assert!(basetree_verification_res); } } - -/// Type of membership proof from a merkle tree -pub enum MembershipProof { - /// ICS23 compliant membership proof - ICS23(CommitmentProof), -} - -impl From for MembershipProof { - fn from(proof: CommitmentProof) -> Self { - Self::ICS23(proof) - } -} diff --git a/core/src/ledger/storage/mockdb.rs b/core/src/ledger/storage/mockdb.rs index 0daa61d935..011d8faac8 100644 --- a/core/src/ledger/storage/mockdb.rs +++ b/core/src/ledger/storage/mockdb.rs @@ -14,7 +14,7 @@ use super::{ }; use crate::ledger::storage::types::{self, KVBytes, PrefixIterator}; #[cfg(feature = "ferveo-tpke")] -use crate::types::storage::TxQueue; +use crate::types::internal::TxQueue; use crate::types::storage::{ BlockHeight, BlockResults, Header, Key, KeySeg, KEY_SEGMENT_SEPARATOR, }; diff --git a/core/src/ledger/storage/mod.rs b/core/src/ledger/storage/mod.rs index 80da1db9f6..b55c36e192 100644 --- a/core/src/ledger/storage/mod.rs +++ b/core/src/ledger/storage/mod.rs @@ -1,13 +1,13 @@ //! Ledger's state storage with key-value backed store and a merkle tree pub mod ics23_specs; -mod merkle_tree; +pub mod merkle_tree; #[cfg(any(test, feature = "testing"))] pub mod mockdb; +pub mod traits; pub mod types; use core::fmt::Debug; -use std::array; use std::collections::BTreeMap; use borsh::{BorshDeserialize, BorshSerialize}; @@ -15,6 +15,10 @@ use masp_primitives::asset_type::AssetType; use masp_primitives::convert::AllowedConversion; use masp_primitives::merkle_tree::FrozenCommitmentTree; use masp_primitives::sapling::Node; +pub use merkle_tree::{ + MembershipProof, MerkleTree, MerkleTreeStoresRead, MerkleTreeStoresWrite, + StoreType, +}; #[cfg(feature = "wasm-runtime")] use rayon::iter::{ IndexedParallelIterator, IntoParallelIterator, ParallelIterator, @@ -22,29 +26,27 @@ use rayon::iter::{ #[cfg(feature = "wasm-runtime")] use rayon::prelude::ParallelSlice; use thiserror::Error; +pub use traits::{Sha256Hasher, StorageHasher}; -use super::parameters::{self, Parameters}; -use super::storage_api; -use super::storage_api::{ResultExt, StorageRead, StorageWrite}; use crate::ledger::gas::MIN_STORAGE_GAS; -use crate::ledger::parameters::EpochDuration; +use crate::ledger::parameters::{self, EpochDuration, Parameters}; use crate::ledger::storage::merkle_tree::{ Error as MerkleTreeError, MerkleRoot, }; -pub use crate::ledger::storage::merkle_tree::{ - MerkleTree, MerkleTreeStoresRead, MerkleTreeStoresWrite, StoreType, -}; -pub use crate::ledger::storage::traits::{Sha256Hasher, StorageHasher}; +use crate::ledger::storage_api; +use crate::ledger::storage_api::{ResultExt, StorageRead, StorageWrite}; +#[cfg(any(feature = "tendermint", feature = "tendermint-abcipp"))] use crate::tendermint::merkle::proof::Proof; use crate::types::address::{ masp, Address, EstablishedAddressGen, InternalAddress, }; use crate::types::chain::{ChainId, CHAIN_ID_LENGTH}; +// TODO #[cfg(feature = "ferveo-tpke")] -use crate::types::storage::TxQueue; +use crate::types::internal::TxQueue; use crate::types::storage::{ BlockHash, BlockHeight, BlockResults, Epoch, Epochs, Header, Key, KeySeg, - MembershipProof, MerkleValue, TxIndex, BLOCK_HASH_LENGTH, + TxIndex, BLOCK_HASH_LENGTH, }; use crate::types::time::DateTimeUtc; use crate::types::token; @@ -61,8 +63,6 @@ pub struct ConversionState { /// Map assets to their latest conversion and position in Merkle tree pub assets: BTreeMap, } -/// The maximum size of an IBC key (in bytes) allowed in merkle-ized storage -pub const IBC_KEY_LIMIT: usize = 120; /// The storage data #[derive(Debug)] @@ -334,6 +334,7 @@ where pub fn open( db_path: impl AsRef, chain_id: ChainId, + native_token: Address, cache: Option<&D::Cache>, ) -> Self { let block = BlockStorage { @@ -360,6 +361,7 @@ where conversion_state: ConversionState::default(), #[cfg(feature = "ferveo-tpke")] tx_queue: TxQueue::default(), + native_token, } } @@ -618,12 +620,15 @@ where } /// Get the existence proof + #[cfg(any(feature = "tendermint", feature = "tendermint-abcipp"))] pub fn get_existence_proof( &self, key: &Key, - value: MerkleValue, + value: crate::types::storage::MerkleValue, height: BlockHeight, ) -> Result { + use std::array; + if height >= self.get_block_height().0 { let MembershipProof::ICS23(proof) = self .block @@ -632,7 +637,8 @@ where .map_err(Error::MerkleTreeError)?; self.block .tree - .get_tendermint_proof(key, proof) + .get_sub_tree_proof(key, proof) + .map(Into::into) .map_err(Error::MerkleTreeError) } else { match self.db.read_merkle_tree_stores(height)? { @@ -644,7 +650,8 @@ where vec![value], ) .map_err(Error::MerkleTreeError)?; - tree.get_tendermint_proof(key, proof) + tree.get_sub_tree_proof(key, proof) + .map(Into::into) .map_err(Error::MerkleTreeError) } None => Err(Error::NoMerkleTree { height }), @@ -653,17 +660,24 @@ where } /// Get the non-existence proof + #[cfg(any(feature = "tendermint", feature = "tendermint-abcipp"))] pub fn get_non_existence_proof( &self, key: &Key, height: BlockHeight, ) -> Result { if height >= self.last_height { - Ok(self.block.tree.get_non_existence_proof(key)?) + self.block + .tree + .get_non_existence_proof(key) + .map(Into::into) + .map_err(Error::MerkleTreeError) } else { match self.db.read_merkle_tree_stores(height)? { - Some(stores) => Ok(MerkleTree::::new(stores) - .get_non_existence_proof(key)?), + Some(stores) => MerkleTree::::new(stores) + .get_non_existence_proof(key) + .map(Into::into) + .map_err(Error::MerkleTreeError), None => Err(Error::NoMerkleTree { height }), } } @@ -952,19 +966,19 @@ where } /// Start write batch. - fn batch() -> D::WriteBatch { + pub fn batch() -> D::WriteBatch { D::batch() } /// Execute write batch. - fn exec_batch(&mut self, batch: D::WriteBatch) -> Result<()> { + pub fn exec_batch(&mut self, batch: D::WriteBatch) -> Result<()> { self.db.exec_batch(batch) } /// Batch write the value with the given height and account subspace key to /// the DB. Returns the size difference from previous value, if any, or /// the size of the value otherwise. - fn batch_write_subspace_val( + pub fn batch_write_subspace_val( &mut self, batch: &mut D::WriteBatch, key: &Key, @@ -979,7 +993,7 @@ where /// Batch delete the value with the given height and account subspace key /// from the DB. Returns the size of the removed value, if any, 0 if no /// previous value was found. - fn batch_delete_subspace_val( + pub fn batch_delete_subspace_val( &mut self, batch: &mut D::WriteBatch, key: &Key, @@ -1154,6 +1168,7 @@ pub mod testing { use super::mockdb::MockDB; use super::*; use crate::ledger::storage::traits::Sha256Hasher; + use crate::types::address; /// Storage with a mock DB for testing pub type TestStorage = Storage; @@ -1185,6 +1200,7 @@ pub mod testing { conversion_state: ConversionState::default(), #[cfg(feature = "ferveo-tpke")] tx_queue: TxQueue::default(), + native_token: address::nam(), } } } diff --git a/core/src/ledger/storage/traits.rs b/core/src/ledger/storage/traits.rs index 1f84c6e421..79427c06fb 100644 --- a/core/src/ledger/storage/traits.rs +++ b/core/src/ledger/storage/traits.rs @@ -7,7 +7,6 @@ use arse_merkle_tree::traits::{Hasher, Value}; use arse_merkle_tree::{Key as TreeKey, H256}; use ics23::commitment_proof::Proof as Ics23Proof; use ics23::{CommitmentProof, ExistenceProof}; -use namada_core::types::storage::IBC_KEY_LIMIT; use sha2::{Digest, Sha256}; use super::ics23_specs; diff --git a/core/src/ledger/vp_env.rs b/core/src/ledger/vp_env.rs index 3ae81bc777..49bd5d515c 100644 --- a/core/src/ledger/vp_env.rs +++ b/core/src/ledger/vp_env.rs @@ -1,18 +1,9 @@ //! Validity predicate environment contains functions that can be called from //! inside validity predicates. -use std::num::TryFromIntError; - use borsh::BorshDeserialize; -use thiserror::Error; -use super::gas::MIN_STORAGE_GAS; use super::storage_api::{self, StorageRead}; -use crate::ledger::gas; -use crate::ledger::gas::VpGasMeter; -use crate::ledger::storage::write_log::WriteLog; -use crate::ledger::storage::{self, write_log, Storage, StorageHasher}; -use crate::proto::Tx; use crate::types::address::Address; use crate::types::hash::Hash; use crate::types::key::common; @@ -187,367 +178,3 @@ pub trait VpEnv<'view> { self.post().iter_next(iter) } } - -/// These runtime errors will abort VP execution immediately -#[allow(missing_docs)] -#[derive(Error, Debug)] -pub enum RuntimeError { - #[error("Out of gas: {0}")] - OutOfGas(gas::Error), - #[error("Storage error: {0}")] - StorageError(storage::Error), - #[error("Storage data error: {0}")] - StorageDataError(crate::types::storage::Error), - #[error("Encoding error: {0}")] - EncodingError(std::io::Error), - #[error("Numeric conversion error: {0}")] - NumConversionError(TryFromIntError), - #[error("Memory error: {0}")] - MemoryError(Box), - #[error("Trying to read a temporary value with read_post")] - ReadTemporaryValueError, - #[error("Trying to read a permament value with read_temp")] - ReadPermanentValueError, -} - -/// VP environment function result -pub type EnvResult = std::result::Result; - -/// Add a gas cost incured in a validity predicate -pub fn add_gas(gas_meter: &mut VpGasMeter, used_gas: u64) -> EnvResult<()> { - let result = gas_meter.add(used_gas).map_err(RuntimeError::OutOfGas); - if let Err(err) = &result { - tracing::info!("Stopping VP execution because of gas error: {}", err); - } - result -} - -/// Storage read prior state (before tx execution). It will try to read from the -/// storage. -pub fn read_pre( - gas_meter: &mut VpGasMeter, - storage: &Storage, - write_log: &WriteLog, - key: &Key, -) -> EnvResult>> -where - DB: storage::DB + for<'iter> storage::DBIter<'iter>, - H: StorageHasher, -{ - let (log_val, gas) = write_log.read_pre(key); - add_gas(gas_meter, gas)?; - match log_val { - Some(&write_log::StorageModification::Write { ref value }) => { - Ok(Some(value.clone())) - } - Some(&write_log::StorageModification::Delete) => { - // Given key has been deleted - Ok(None) - } - Some(&write_log::StorageModification::InitAccount { - ref vp, .. - }) => { - // Read the VP of a new account - Ok(Some(vp.clone())) - } - Some(&write_log::StorageModification::Temp { .. }) => { - Err(RuntimeError::ReadTemporaryValueError) - } - None => { - // When not found in write log, try to read from the storage - let (value, gas) = - storage.read(key).map_err(RuntimeError::StorageError)?; - add_gas(gas_meter, gas)?; - Ok(value) - } - } -} - -/// Storage read posterior state (after tx execution). It will try to read from -/// the write log first and if no entry found then from the storage. -pub fn read_post( - gas_meter: &mut VpGasMeter, - storage: &Storage, - write_log: &WriteLog, - key: &Key, -) -> EnvResult>> -where - DB: storage::DB + for<'iter> storage::DBIter<'iter>, - H: StorageHasher, -{ - // Try to read from the write log first - let (log_val, gas) = write_log.read(key); - add_gas(gas_meter, gas)?; - match log_val { - Some(&write_log::StorageModification::Write { ref value }) => { - Ok(Some(value.clone())) - } - Some(&write_log::StorageModification::Delete) => { - // Given key has been deleted - Ok(None) - } - Some(&write_log::StorageModification::InitAccount { - ref vp, .. - }) => { - // Read the VP of a new account - Ok(Some(vp.clone())) - } - Some(&write_log::StorageModification::Temp { .. }) => { - Err(RuntimeError::ReadTemporaryValueError) - } - None => { - // When not found in write log, try to read from the storage - let (value, gas) = - storage.read(key).map_err(RuntimeError::StorageError)?; - add_gas(gas_meter, gas)?; - Ok(value) - } - } -} - -/// Storage read temporary state (after tx execution). It will try to read from -/// only the write log. -pub fn read_temp( - gas_meter: &mut VpGasMeter, - write_log: &WriteLog, - key: &Key, -) -> EnvResult>> { - // Try to read from the write log first - let (log_val, gas) = write_log.read(key); - add_gas(gas_meter, gas)?; - match log_val { - Some(&write_log::StorageModification::Temp { ref value }) => { - Ok(Some(value.clone())) - } - None => Ok(None), - _ => Err(RuntimeError::ReadPermanentValueError), - } -} - -/// Storage `has_key` in prior state (before tx execution). It will try to read -/// from the storage. -pub fn has_key_pre( - gas_meter: &mut VpGasMeter, - storage: &Storage, - key: &Key, -) -> EnvResult -where - DB: storage::DB + for<'iter> storage::DBIter<'iter>, - H: StorageHasher, -{ - let (present, gas) = - storage.has_key(key).map_err(RuntimeError::StorageError)?; - add_gas(gas_meter, gas)?; - Ok(present) -} - -/// Storage `has_key` in posterior state (after tx execution). It will try to -/// check the write log first and if no entry found then the storage. -pub fn has_key_post( - gas_meter: &mut VpGasMeter, - storage: &Storage, - write_log: &WriteLog, - key: &Key, -) -> EnvResult -where - DB: storage::DB + for<'iter> storage::DBIter<'iter>, - H: StorageHasher, -{ - // Try to read from the write log first - let (log_val, gas) = write_log.read(key); - add_gas(gas_meter, gas)?; - match log_val { - Some(&write_log::StorageModification::Write { .. }) => Ok(true), - Some(&write_log::StorageModification::Delete) => { - // The given key has been deleted - Ok(false) - } - Some(&write_log::StorageModification::InitAccount { .. }) => Ok(true), - Some(&write_log::StorageModification::Temp { .. }) => Ok(true), - None => { - // When not found in write log, try to check the storage - let (present, gas) = - storage.has_key(key).map_err(RuntimeError::StorageError)?; - add_gas(gas_meter, gas)?; - Ok(present) - } - } -} - -/// Getting the chain ID. -pub fn get_chain_id( - gas_meter: &mut VpGasMeter, - storage: &Storage, -) -> EnvResult -where - DB: storage::DB + for<'iter> storage::DBIter<'iter>, - H: StorageHasher, -{ - let (chain_id, gas) = storage.get_chain_id(); - add_gas(gas_meter, gas)?; - Ok(chain_id) -} - -/// Getting the block height. The height is that of the block to which the -/// current transaction is being applied. -pub fn get_block_height( - gas_meter: &mut VpGasMeter, - storage: &Storage, -) -> EnvResult -where - DB: storage::DB + for<'iter> storage::DBIter<'iter>, - H: StorageHasher, -{ - let (height, gas) = storage.get_block_height(); - add_gas(gas_meter, gas)?; - Ok(height) -} - -/// Getting the block hash. The height is that of the block to which the -/// current transaction is being applied. -pub fn get_block_hash( - gas_meter: &mut VpGasMeter, - storage: &Storage, -) -> EnvResult -where - DB: storage::DB + for<'iter> storage::DBIter<'iter>, - H: StorageHasher, -{ - let (hash, gas) = storage.get_block_hash(); - add_gas(gas_meter, gas)?; - Ok(hash) -} - -/// Getting the block hash. The height is that of the block to which the -/// current transaction is being applied. -pub fn get_tx_code_hash( - gas_meter: &mut VpGasMeter, - tx: &Tx, -) -> EnvResult { - let hash = Hash(tx.code_hash()); - add_gas(gas_meter, MIN_STORAGE_GAS)?; - Ok(hash) -} - -/// Getting the block epoch. The epoch is that of the block to which the -/// current transaction is being applied. -pub fn get_block_epoch( - gas_meter: &mut VpGasMeter, - storage: &Storage, -) -> EnvResult -where - DB: storage::DB + for<'iter> storage::DBIter<'iter>, - H: StorageHasher, -{ - let (epoch, gas) = storage.get_current_epoch(); - add_gas(gas_meter, gas)?; - Ok(epoch) -} - -/// Getting the block epoch. The epoch is that of the block to which the -/// current transaction is being applied. -pub fn get_tx_index( - gas_meter: &mut VpGasMeter, - tx_index: &TxIndex, -) -> EnvResult { - add_gas(gas_meter, MIN_STORAGE_GAS)?; - Ok(*tx_index) -} - -/// Getting the chain ID. -pub fn get_native_token( - gas_meter: &mut VpGasMeter, - storage: &Storage, -) -> EnvResult
-where - DB: storage::DB + for<'iter> storage::DBIter<'iter>, - H: StorageHasher, -{ - add_gas(gas_meter, MIN_STORAGE_GAS)?; - Ok(storage.native_token.clone()) -} - -/// Storage prefix iterator, ordered by storage keys. It will try to get an -/// iterator from the storage. -pub fn iter_prefix<'a, DB, H>( - gas_meter: &mut VpGasMeter, - storage: &'a Storage, - prefix: &Key, -) -> EnvResult<>::PrefixIter> -where - DB: storage::DB + for<'iter> storage::DBIter<'iter>, - H: StorageHasher, -{ - let (iter, gas) = storage.iter_prefix(prefix); - add_gas(gas_meter, gas)?; - Ok(iter) -} - -/// Storage prefix iterator, reverse ordered by storage keys. It will try to get -/// an iterator from the storage. -pub fn rev_iter_prefix<'a, DB, H>( - gas_meter: &mut VpGasMeter, - storage: &'a Storage, - prefix: &Key, -) -> EnvResult<>::PrefixIter> -where - DB: storage::DB + for<'iter> storage::DBIter<'iter>, - H: StorageHasher, -{ - let (iter, gas) = storage.rev_iter_prefix(prefix); - add_gas(gas_meter, gas)?; - Ok(iter) -} - -/// Storage prefix iterator for prior state (before tx execution). It will try -/// to read from the storage. -pub fn iter_pre_next( - gas_meter: &mut VpGasMeter, - iter: &mut >::PrefixIter, -) -> EnvResult)>> -where - DB: storage::DB + for<'iter> storage::DBIter<'iter>, -{ - if let Some((key, val, gas)) = iter.next() { - add_gas(gas_meter, gas)?; - return Ok(Some((key, val))); - } - Ok(None) -} - -/// Storage prefix iterator next for posterior state (after tx execution). It -/// will try to read from the write log first and if no entry found then from -/// the storage. -pub fn iter_post_next( - gas_meter: &mut VpGasMeter, - write_log: &WriteLog, - iter: &mut >::PrefixIter, -) -> EnvResult)>> -where - DB: storage::DB + for<'iter> storage::DBIter<'iter>, -{ - for (key, val, iter_gas) in iter { - let (log_val, log_gas) = write_log.read( - &Key::parse(key.clone()).map_err(RuntimeError::StorageDataError)?, - ); - add_gas(gas_meter, iter_gas + log_gas)?; - match log_val { - Some(&write_log::StorageModification::Write { ref value }) => { - return Ok(Some((key, value.clone()))); - } - Some(&write_log::StorageModification::Delete) => { - // check the next because the key has already deleted - continue; - } - Some(&write_log::StorageModification::InitAccount { .. }) => { - // a VP of a new account doesn't need to be iterated - continue; - } - Some(&write_log::StorageModification::Temp { .. }) => { - return Err(RuntimeError::ReadTemporaryValueError); - } - None => return Ok(Some((key, val))), - } - } - Ok(None) -} diff --git a/core/src/lib.rs b/core/src/lib.rs index 5ee0ebba0e..b20e4aa150 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -8,4 +8,19 @@ pub mod bytes; pub mod ledger; +pub mod proto; pub mod types; + +#[cfg(feature = "abciplus")] +pub use {ibc, ibc_proto, tendermint, tendermint_proto}; +#[cfg(feature = "abcipp")] +pub use { + ibc_abcipp as ibc, ibc_proto_abcipp as ibc_proto, + tendermint_abcipp as tendermint, + tendermint_proto_abcipp as tendermint_proto, +}; + +// A handy macro for tests +#[cfg(test)] +#[macro_use] +extern crate assert_matches; diff --git a/core/src/proto/types.rs b/core/src/proto/types.rs index 3a0bb1d8ea..39652a5998 100644 --- a/core/src/proto/types.rs +++ b/core/src/proto/types.rs @@ -4,25 +4,11 @@ use std::hash::{Hash, Hasher}; use borsh::{BorshDeserialize, BorshSchema, BorshSerialize}; use prost::Message; use serde::{Deserialize, Serialize}; -#[cfg(not(feature = "ABCI"))] -#[cfg(feature = "ferveo-tpke")] -use tendermint_proto::abci::Event; -#[cfg(not(feature = "ABCI"))] -#[cfg(feature = "ferveo-tpke")] -use tendermint_proto::abci::EventAttribute; -#[cfg(not(feature = "ABCI"))] -use tendermint_proto::abci::ResponseDeliverTx; -#[cfg(feature = "ABCI")] -#[cfg(feature = "ferveo-tpke")] -use tendermint_proto_abci::abci::Event; -#[cfg(feature = "ABCI")] -#[cfg(feature = "ferveo-tpke")] -use tendermint_proto_abci::abci::EventAttribute; -#[cfg(feature = "ABCI")] -use tendermint_proto_abci::abci::ResponseDeliverTx; use thiserror::Error; use super::generated::types; +#[cfg(any(feature = "tendermint", feature = "tendermint-abcipp"))] +use crate::tendermint_proto::abci::ResponseDeliverTx; use crate::types::key::*; use crate::types::time::DateTimeUtc; #[cfg(feature = "ferveo-tpke")] @@ -175,6 +161,7 @@ impl From for types::Tx { } } +#[cfg(any(feature = "tendermint", feature = "tendermint-abcipp"))] impl From for ResponseDeliverTx { #[cfg(not(feature = "ferveo-tpke"))] fn from(_tx: Tx) -> ResponseDeliverTx { @@ -184,6 +171,8 @@ impl From for ResponseDeliverTx { /// Annotate the Tx with meta-data based on its contents #[cfg(feature = "ferveo-tpke")] fn from(tx: Tx) -> ResponseDeliverTx { + use crate::tendermint_proto::abci::{Event, EventAttribute}; + #[cfg(feature = "ABCI")] fn encode_str(x: &str) -> Vec { x.as_bytes().to_vec() diff --git a/core/src/types/governance.rs b/core/src/types/governance.rs index 8a3a338db6..628679a396 100644 --- a/core/src/types/governance.rs +++ b/core/src/types/governance.rs @@ -15,7 +15,6 @@ use crate::types::key::common::{self, Signature}; use crate::types::key::SigScheme; use crate::types::storage::Epoch; use crate::types::token::SCALE; -use crate::types::transaction::governance::InitProposalData; /// Type alias for vote power pub type VotePower = u128; @@ -163,31 +162,6 @@ pub enum ProposalError { InvalidProposalData, } -impl TryFrom for InitProposalData { - type Error = ProposalError; - - fn try_from(proposal: Proposal) -> Result { - let proposal_code = if let Some(path) = proposal.proposal_code_path { - match std::fs::read(path) { - Ok(bytes) => Some(bytes), - Err(_) => return Err(Self::Error::InvalidProposalData), - } - } else { - None - }; - - Ok(InitProposalData { - id: proposal.id, - content: proposal.content.try_to_vec().unwrap(), - author: proposal.author, - voting_start_epoch: proposal.voting_start_epoch, - voting_end_epoch: proposal.voting_end_epoch, - grace_epoch: proposal.grace_epoch, - proposal_code, - }) - } -} - #[derive( Debug, Clone, BorshSerialize, BorshDeserialize, Serialize, Deserialize, )] diff --git a/core/src/types/hash.rs b/core/src/types/hash.rs index 5f8b1be95f..74bfe3dd45 100644 --- a/core/src/types/hash.rs +++ b/core/src/types/hash.rs @@ -4,18 +4,15 @@ use std::fmt::{self, Display}; use std::ops::Deref; use std::str::FromStr; -// use arse_merkle_tree::traits::Value; -// use arse_merkle_tree::Hash as TreeHash; +use arse_merkle_tree::traits::Value; +use arse_merkle_tree::{Hash as TreeHash, H256}; use borsh::{BorshDeserialize, BorshSchema, BorshSerialize}; -use hex::FromHex; +use data_encoding::HEXUPPER; use serde::{Deserialize, Serialize}; use sha2::{Digest, Sha256}; use thiserror::Error; -// use crate::tendermint::abci::transaction; -// use crate::tendermint::Hash as TmHash; - -/// The length of the raw transaction hash. +/// The length of the transaction hash string pub const HASH_LENGTH: usize = 32; #[allow(missing_docs)] @@ -26,7 +23,7 @@ pub enum Error { #[error("Failed trying to convert slice to a hash: {0}")] ConversionFailed(std::array::TryFromSliceError), #[error("Failed to convert string into a hash: {0}")] - FromStringError(hex::FromHexError), + FromStringError(data_encoding::DecodeError), } /// Result for functions that may fail @@ -46,14 +43,11 @@ pub type HashResult = std::result::Result; Deserialize, )] /// A hash, typically a sha-2 hash of a tx -pub struct Hash(pub [u8; 32]); +pub struct Hash(pub [u8; HASH_LENGTH]); impl Display for Hash { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - for byte in &self.0 { - write!(f, "{:02X}", byte)?; - } - Ok(()) + write!(f, "{}", HEXUPPER.encode(&self.0)) } } @@ -64,7 +58,7 @@ impl AsRef<[u8]> for Hash { } impl Deref for Hash { - type Target = [u8; 32]; + type Target = [u8; HASH_LENGTH]; fn deref(&self) -> &Self::Target { &self.0 @@ -84,7 +78,7 @@ impl TryFrom<&[u8]> for Hash { ), }); } - let hash: [u8; 32] = + let hash: [u8; HASH_LENGTH] = TryFrom::try_from(value).map_err(Error::ConversionFailed)?; Ok(Hash(hash)) } @@ -102,16 +96,10 @@ impl TryFrom<&str> for Hash { type Error = self::Error; fn try_from(string: &str) -> HashResult { - Ok(Self( - <[u8; HASH_LENGTH]>::from_hex(string) - .map_err(Error::FromStringError)?, - )) - } -} - -impl From for transaction::Hash { - fn from(hash: Hash) -> Self { - Self::new(hash.0) + let vec = HEXUPPER + .decode(string.as_ref()) + .map_err(Error::FromStringError)?; + Self::try_from(&vec[..]) } } @@ -131,7 +119,7 @@ impl Hash { } fn zero() -> Self { - Self([0u8; 32]) + Self([0u8; HASH_LENGTH]) } /// Check if the hash is all zeros @@ -140,11 +128,19 @@ impl Hash { } } -// impl From for TmHash { -// fn from(hash: Hash) -> Self { -// TmHash::Sha256(hash.0) -// } -// } +#[cfg(any(feature = "tendermint", feature = "tendermint-abcipp"))] +impl From for crate::tendermint::abci::transaction::Hash { + fn from(hash: Hash) -> Self { + Self::new(hash.0) + } +} + +#[cfg(any(feature = "tendermint", feature = "tendermint-abcipp"))] +impl From for crate::tendermint::Hash { + fn from(hash: Hash) -> Self { + Self::Sha256(hash.0) + } +} impl From for TreeHash { fn from(hash: Hash) -> Self { @@ -152,22 +148,31 @@ impl From for TreeHash { } } -#[cfg(test)] -mod tests { - use proptest::prelude::*; - use proptest::string::{string_regex, RegexGeneratorStrategy}; +impl Value for Hash { + fn as_slice(&self) -> &[u8] { + self.0.as_slice() + } + + fn zero() -> Self { + Hash([0u8; HASH_LENGTH]) + } +} - use super::*; +impl From for H256 { + fn from(hash: Hash) -> Self { + hash.0.into() + } +} - /// Returns a proptest strategy that yields hex encoded hashes. - fn hex_encoded_hash_strat() -> RegexGeneratorStrategy { - string_regex(r"[a-fA-F0-9]{64}").unwrap() +impl From for Hash { + fn from(hash: H256) -> Self { + Self(hash.into()) } +} - proptest! { - #[test] - fn test_hash_string(hex_hash in hex_encoded_hash_strat()) { - let _: Hash = hex_hash.try_into().unwrap(); - } +impl From<&H256> for Hash { + fn from(hash: &H256) -> Self { + let hash = hash.to_owned(); + Self(hash.into()) } } diff --git a/core/src/types/ibc.rs b/core/src/types/ibc.rs index ad31a5f3d4..3d537cb025 100644 --- a/core/src/types/ibc.rs +++ b/core/src/types/ibc.rs @@ -3,20 +3,6 @@ use std::collections::HashMap; use borsh::{BorshDeserialize, BorshSchema, BorshSerialize}; -use thiserror::Error; - -use crate::ibc::events::{Error as IbcEventError, IbcEvent as RawIbcEvent}; -use crate::tendermint::abci::Event as AbciEvent; - -#[allow(missing_docs)] -#[derive(Error, Debug)] -pub enum Error { - #[error("IBC event error: {0}")] - IbcEvent(IbcEventError), -} - -/// Conversion functions result -pub type Result = std::result::Result; /// Wrapped IbcEvent #[derive( @@ -45,19 +31,44 @@ impl std::fmt::Display for IbcEvent { } } -impl TryFrom for IbcEvent { - type Error = Error; +#[cfg(any(feature = "abciplus", feature = "abcipp"))] +mod ibc_rs_conversion { + use std::collections::HashMap; + + use thiserror::Error; + + use super::IbcEvent; + use crate::ibc::events::{Error as IbcEventError, IbcEvent as RawIbcEvent}; + use crate::tendermint::abci::Event as AbciEvent; - fn try_from(e: RawIbcEvent) -> Result { - let event_type = e.event_type().as_str().to_string(); - let mut attributes = HashMap::new(); - let abci_event = AbciEvent::try_from(e).map_err(Error::IbcEvent)?; - for tag in abci_event.attributes.iter() { - attributes.insert(tag.key.to_string(), tag.value.to_string()); + #[allow(missing_docs)] + #[derive(Error, Debug)] + pub enum Error { + #[error("IBC event error: {0}")] + IbcEvent(IbcEventError), + } + + /// Conversion functions result + pub type Result = std::result::Result; + + impl TryFrom for IbcEvent { + type Error = Error; + + fn try_from(e: RawIbcEvent) -> Result { + let event_type = e.event_type().as_str().to_string(); + let abci_event = AbciEvent::try_from(e).map_err(Error::IbcEvent)?; + let attributes: HashMap<_, _> = abci_event + .attributes + .iter() + .map(|tag| (tag.key.to_string(), tag.value.to_string())) + .collect(); + Ok(Self { + event_type, + attributes, + }) } - Ok(Self { - event_type, - attributes, - }) } } + +#[cfg(any(feature = "abciplus", feature = "abcipp"))] +pub use ibc_rs_conversion::*; diff --git a/core/src/types/internal.rs b/core/src/types/internal.rs index 383de2b7b1..848c09bec1 100644 --- a/core/src/types/internal.rs +++ b/core/src/types/internal.rs @@ -1,5 +1,7 @@ //! Shared internal types between the host env and guest (wasm). +use borsh::{BorshDeserialize, BorshSerialize}; + /// A result of a wasm call to host functions that may fail. #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum HostEnvResult { @@ -9,6 +11,16 @@ pub enum HostEnvResult { Fail = -1, } +/// Key-value pair represents data from account's subspace. +/// It is used for prefix iterator's WASM host_env functions. +#[derive(Clone, Debug, BorshSerialize, BorshDeserialize)] +pub struct KeyVal { + /// The storage key + pub key: String, + /// The value as arbitrary bytes + pub val: Vec, +} + impl HostEnvResult { /// Convert result to `i64`, which can be passed to wasm pub fn to_i64(self) -> i64 { @@ -31,3 +43,40 @@ impl From for HostEnvResult { if success { Self::Success } else { Self::Fail } } } + +#[cfg(feature = "ferveo-tpke")] +mod tx_queue { + use borsh::{BorshDeserialize, BorshSerialize}; + + use crate::types::transaction::WrapperTx; + + #[derive(Default, Debug, Clone, BorshDeserialize, BorshSerialize)] + /// Wrapper txs to be decrypted in the next block proposal + pub struct TxQueue(std::collections::VecDeque); + + impl TxQueue { + /// Add a new wrapper at the back of the queue + pub fn push(&mut self, wrapper: WrapperTx) { + self.0.push_back(wrapper); + } + + /// Remove the wrapper at the head of the queue + pub fn pop(&mut self) -> Option { + self.0.pop_front() + } + + /// Get an iterator over the queue + pub fn iter(&self) -> impl std::iter::Iterator { + self.0.iter() + } + + /// Check if there are any txs in the queue + #[allow(dead_code)] + pub fn is_empty(&self) -> bool { + self.0.is_empty() + } + } +} + +#[cfg(feature = "ferveo-tpke")] +pub use tx_queue::TxQueue; diff --git a/core/src/types/key/common.rs b/core/src/types/key/common.rs index 7ec041d638..e928579367 100644 --- a/core/src/types/key/common.rs +++ b/core/src/types/key/common.rs @@ -5,15 +5,14 @@ use std::str::FromStr; use borsh::{BorshDeserialize, BorshSchema, BorshSerialize}; use data_encoding::HEXLOWER; -// use namada_proof_of_stake::types::PublicKeyTmRawHash; #[cfg(feature = "rand")] use rand::{CryptoRng, RngCore}; use serde::{Deserialize, Serialize}; use super::{ - ed25519, secp256k1, tm_consensus_key_raw_hash, ParsePublicKeyError, - ParseSecretKeyError, ParseSignatureError, RefTo, SchemeType, - SigScheme as SigSchemeTrait, VerifySigError, + ed25519, secp256k1, ParsePublicKeyError, ParseSecretKeyError, + ParseSignatureError, RefTo, SchemeType, SigScheme as SigSchemeTrait, + VerifySigError, }; /// Public key @@ -324,10 +323,3 @@ impl super::SigScheme for SigScheme { } } } - -// TODO -// impl PublicKeyTmRawHash for PublicKey { -// fn tm_raw_hash(&self) -> String { -// tm_consensus_key_raw_hash(self) -// } -// } diff --git a/core/src/types/key/mod.rs b/core/src/types/key/mod.rs index 17f6fd34ba..8823fdcf44 100644 --- a/core/src/types/key/mod.rs +++ b/core/src/types/key/mod.rs @@ -1,5 +1,10 @@ //! Cryptographic keys +pub mod common; +pub mod dkg_session_keys; +pub mod ed25519; +pub mod secp256k1; + use std::fmt::{Debug, Display}; use std::hash::Hash; use std::str::FromStr; @@ -16,10 +21,6 @@ use super::address::Address; use super::storage::{self, DbKeySeg, Key, KeySeg}; use crate::types::address; -pub mod common; -pub mod ed25519; -pub mod secp256k1; - const PK_STORAGE_KEY: &str = "public_key"; const PROTOCOL_PK_STORAGE_KEY: &str = "protocol_public_key"; @@ -350,6 +351,18 @@ impl From<&PK> for PublicKeyHash { } } +/// Derive Tendermint raw hash from the public key +pub trait PublicKeyTmRawHash { + /// Derive Tendermint raw hash from the public key + fn tm_raw_hash(&self) -> String; +} + +impl PublicKeyTmRawHash for common::PublicKey { + fn tm_raw_hash(&self) -> String { + tm_consensus_key_raw_hash(self) + } +} + /// Convert validator's consensus key into address raw hash that is compatible /// with Tendermint pub fn tm_consensus_key_raw_hash(pk: &common::PublicKey) -> String { diff --git a/core/src/types/mod.rs b/core/src/types/mod.rs index 6e7b71dcda..0550060498 100644 --- a/core/src/types/mod.rs +++ b/core/src/types/mod.rs @@ -4,9 +4,12 @@ pub mod address; pub mod chain; pub mod governance; pub mod hash; +pub mod ibc; +pub mod internal; pub mod key; pub mod masp; pub mod storage; pub mod time; pub mod token; +pub mod transaction; pub mod validity_predicate; diff --git a/core/src/types/storage.rs b/core/src/types/storage.rs index 5dc05ae716..bdd68e070e 100644 --- a/core/src/types/storage.rs +++ b/core/src/types/storage.rs @@ -1,27 +1,27 @@ //! Storage types use std::convert::{TryFrom, TryInto}; use std::fmt::Display; +use std::io::Write; use std::num::ParseIntError; use std::ops::{Add, Deref, Div, Mul, Rem, Sub}; use std::str::FromStr; -use arse_merkle_tree::InternalKey; +use arse_merkle_tree::traits::Value; +use arse_merkle_tree::{InternalKey, Key as TreeKey}; use bit_vec::BitVec; -use borsh::maybestd::io::Write; use borsh::{BorshDeserialize, BorshSchema, BorshSerialize}; use data_encoding::BASE32HEX_NOPAD; -use ics23::CommitmentProof; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use thiserror::Error; -#[cfg(feature = "ferveo-tpke")] -use super::transaction::WrapperTx; use crate::bytes::ByteBuf; -use crate::ledger::storage::IBC_KEY_LIMIT; use crate::types::address::{self, Address}; use crate::types::hash::Hash; use crate::types::time::DateTimeUtc; +/// The maximum size of an IBC key (in bytes) allowed in merkle-ized storage +pub const IBC_KEY_LIMIT: usize = 120; + #[allow(missing_docs)] #[derive(Error, Debug)] pub enum Error { @@ -389,6 +389,42 @@ pub struct StringKey { pub length: usize, } +#[allow(missing_docs)] +#[derive(Error, Debug)] +pub enum TreeKeyError { + #[error("Invalid key for merkle tree: {0}")] + InvalidMerkleKey(String), +} + +impl TreeKey for StringKey { + type Error = TreeKeyError; + + fn as_slice(&self) -> &[u8] { + &self.original.as_slice()[..self.length] + } + + fn try_from_bytes(bytes: &[u8]) -> std::result::Result { + let mut tree_key = [0u8; IBC_KEY_LIMIT]; + let mut original = [0u8; IBC_KEY_LIMIT]; + let mut length = 0; + for (i, byte) in bytes.iter().enumerate() { + if i >= IBC_KEY_LIMIT { + return Err(TreeKeyError::InvalidMerkleKey( + "Input IBC key is too large".into(), + )); + } + original[i] = *byte; + tree_key[i] = byte.wrapping_add(1); + length += 1; + } + Ok(Self { + original, + tree_key: tree_key.into(), + length, + }) + } +} + impl Deref for StringKey { type Target = InternalKey; @@ -456,18 +492,15 @@ impl From for Vec { } } -// TODO not sure -// /// Type of membership proof from a merkle tree -// pub enum MembershipProof { -// /// ICS23 compliant membership proof -// ICS23(CommitmentProof), -// } +impl Value for TreeBytes { + fn as_slice(&self) -> &[u8] { + self.0.as_slice() + } -// impl From for MembershipProof { -// fn from(proof: CommitmentProof) -> Self { -// Self::ICS23(proof) -// } -// } + fn zero() -> Self { + TreeBytes::zero() + } +} impl Key { /// Parses string and returns a key @@ -893,6 +926,53 @@ impl Epoch { pub fn prev(&self) -> Self { Self(self.0 - 1) } + + /// Iterate a range of consecutive epochs starting from `self` of a given + /// length. Work-around for `Step` implementation pending on stabilization of . + pub fn iter_range(self, len: u64) -> impl Iterator + Clone { + let start_ix: u64 = self.into(); + let end_ix: u64 = start_ix + len; + (start_ix..end_ix).map(Epoch::from) + } + + /// Checked epoch subtraction. Computes self - rhs, returning None if + /// overflow occurred. + #[must_use = "this returns the result of the operation, without modifying \ + the original"] + pub fn checked_sub(self, rhs: Epoch) -> Option { + if rhs.0 > self.0 { + None + } else { + Some(Self(self.0 - rhs.0)) + } + } + + /// Checked epoch subtraction. Computes self - rhs, returning default + /// `Epoch(0)` if overflow occurred. + #[must_use = "this returns the result of the operation, without modifying \ + the original"] + pub fn sub_or_default(self, rhs: Epoch) -> Self { + self.checked_sub(rhs).unwrap_or_default() + } +} + +impl From for Epoch { + fn from(epoch: u64) -> Self { + Epoch(epoch) + } +} + +impl From for u64 { + fn from(epoch: Epoch) -> Self { + epoch.0 + } +} + +// TODO remove this once it's not being used +impl From for usize { + fn from(epoch: Epoch) -> Self { + epoch.0 as usize + } } impl Add for Epoch { @@ -903,6 +983,15 @@ impl Add for Epoch { } } +// TODO remove this once it's not being used +impl Add for Epoch { + type Output = Self; + + fn add(self, rhs: usize) -> Self::Output { + Epoch(self.0 + rhs as u64) + } +} + impl Sub for Epoch { type Output = Epoch; @@ -911,6 +1000,14 @@ impl Sub for Epoch { } } +impl Sub for Epoch { + type Output = Self; + + fn sub(self, rhs: Epoch) -> Self::Output { + Epoch(self.0 - rhs.0) + } +} + impl Mul for Epoch { type Output = Epoch; @@ -935,14 +1032,6 @@ impl Rem for Epoch { } } -impl Sub for Epoch { - type Output = Epoch; - - fn sub(self, rhs: Self) -> Self::Output { - Self(self.0 - rhs.0) - } -} - impl Add for Epoch { type Output = Epoch; @@ -959,18 +1048,6 @@ impl Mul for Epoch { } } -impl From for u64 { - fn from(epoch: Epoch) -> Self { - epoch.0 - } -} - -impl From for Epoch { - fn from(value: u64) -> Self { - Self(value) - } -} - /// Predecessor block epochs #[derive( Clone, @@ -1051,35 +1128,6 @@ impl Epochs { } } -#[cfg(feature = "ferveo-tpke")] -#[derive(Default, Debug, Clone, BorshDeserialize, BorshSerialize)] -/// Wrapper txs to be decrypted in the next block proposal -pub struct TxQueue(std::collections::VecDeque); - -#[cfg(feature = "ferveo-tpke")] -impl TxQueue { - /// Add a new wrapper at the back of the queue - pub fn push(&mut self, wrapper: WrapperTx) { - self.0.push_back(wrapper); - } - - /// Remove the wrapper at the head of the queue - pub fn pop(&mut self) -> Option { - self.0.pop_front() - } - - /// Get an iterator over the queue - pub fn iter(&self) -> impl std::iter::Iterator { - self.0.iter() - } - - /// Check if there are any txs in the queue - #[allow(dead_code)] - pub fn is_empty(&self) -> bool { - self.0.is_empty() - } -} - /// A value of a storage prefix iterator. #[derive(Debug, Clone, BorshSerialize, BorshDeserialize, BorshSchema)] pub struct PrefixValue { diff --git a/core/src/types/time.rs b/core/src/types/time.rs index 13de2685ad..a508501d94 100644 --- a/core/src/types/time.rs +++ b/core/src/types/time.rs @@ -175,37 +175,40 @@ impl From> for DateTimeUtc { } } -// TODO move -// impl TryFrom for DateTimeUtc { -// type Error = prost_types::TimestampOutOfSystemRangeError; - -// fn try_from( -// timestamp: prost_types::Timestamp, -// ) -> Result { -// let system_time: std::time::SystemTime = timestamp.try_into()?; -// Ok(Self(system_time.into())) -// } -// } - -// impl From for prost_types::Timestamp { -// fn from(dt: DateTimeUtc) -> Self { -// let seconds = dt.0.timestamp(); -// let nanos = dt.0.timestamp_subsec_nanos() as i32; -// prost_types::Timestamp { seconds, nanos } -// } -// } - -// TODO move -// impl TryFrom for DateTimeUtc { -// type Error = prost_types::TimestampOutOfSystemRangeError; - -// fn try_from(timestamp: protobuf::Timestamp) -> Result -// { Self::try_from(prost_types::Timestamp { -// seconds: timestamp.seconds, -// nanos: timestamp.nanos, -// }) -// } -// } +impl TryFrom for DateTimeUtc { + type Error = prost_types::TimestampOutOfSystemRangeError; + + fn try_from( + timestamp: prost_types::Timestamp, + ) -> Result { + let system_time: std::time::SystemTime = timestamp.try_into()?; + Ok(Self(system_time.into())) + } +} + +impl From for prost_types::Timestamp { + fn from(dt: DateTimeUtc) -> Self { + let seconds = dt.0.timestamp(); + let nanos = dt.0.timestamp_subsec_nanos() as i32; + prost_types::Timestamp { seconds, nanos } + } +} + +#[cfg(any(feature = "tendermint", feature = "tendermint-abcipp"))] +impl TryFrom + for DateTimeUtc +{ + type Error = prost_types::TimestampOutOfSystemRangeError; + + fn try_from( + timestamp: crate::tendermint_proto::google::protobuf::Timestamp, + ) -> Result { + Self::try_from(prost_types::Timestamp { + seconds: timestamp.seconds, + nanos: timestamp.nanos, + }) + } +} impl From for std::time::SystemTime { fn from(dt: DateTimeUtc) -> Self { @@ -228,19 +231,20 @@ impl From for Rfc3339String { } } -// TODO move -// impl TryFrom for Time { -// type Error = TendermintError; +#[cfg(any(feature = "tendermint", feature = "tendermint-abcipp"))] +impl TryFrom for crate::tendermint::time::Time { + type Error = crate::tendermint::Error; -// fn try_from(dt: DateTimeUtc) -> Result { -// Self::parse_from_rfc3339(&DateTime::to_rfc3339(&dt.0)) -// } -// } + fn try_from(dt: DateTimeUtc) -> Result { + Self::parse_from_rfc3339(&DateTime::to_rfc3339(&dt.0)) + } +} -// impl TryFrom