diff --git a/.github/workflows/scripts/e2e.json b/.github/workflows/scripts/e2e.json index fd8939ba9a..e2fc12f9ea 100644 --- a/.github/workflows/scripts/e2e.json +++ b/.github/workflows/scripts/e2e.json @@ -1,5 +1,6 @@ { "e2e::eth_bridge_tests::everything": 4, + "e2e::eth_bridge_tests::test_add_to_bridge_pool": 10, "e2e::ledger_tests::double_signing_gets_slashed": 12, "e2e::ledger_tests::invalid_transactions": 8, "e2e::ledger_tests::ledger_many_txs_in_a_block": 41, diff --git a/apps/src/lib/client/eth_bridge_pool.rs b/apps/src/lib/client/eth_bridge_pool.rs index b697995eb4..a33c03bac4 100644 --- a/apps/src/lib/client/eth_bridge_pool.rs +++ b/apps/src/lib/client/eth_bridge_pool.rs @@ -1,9 +1,13 @@ +use std::collections::HashMap; + use borsh::BorshSerialize; use namada::ledger::queries::RPC; use namada::proto::Tx; +use namada::types::eth_abi::Encode; use namada::types::eth_bridge_pool::{ GasFee, PendingTransfer, TransferToEthereum, }; +use serde::{Deserialize, Serialize}; use super::signing::TxSigningKey; use super::tx::process_tx; @@ -63,15 +67,32 @@ pub async fn construct_bridge_pool_proof(args: args::BridgePoolProof) { ); } +/// A json serializable representation of the Ethereum +/// bridge pool. +#[derive(Serialize, Deserialize)] +struct BridgePoolResponse { + bridge_pool_contents: HashMap, +} + /// Query the contents of the Ethereum bridge pool. /// Prints out a json payload. pub async fn query_bridge_pool(args: args::Query) { let client = HttpClient::new(args.ledger_address).unwrap(); - let response = RPC + let response: Vec = RPC .shell() .read_ethereum_bridge_pool(&client) .await .unwrap(); - - println!("{:#?}", serde_json::to_string_pretty(&response)); + let pool_contents: HashMap = response + .into_iter() + .map(|transfer| (transfer.keccak256().to_string(), transfer)) + .collect(); + if pool_contents.is_empty() { + println!("Bridge pool is empty."); + return; + } + let contents = BridgePoolResponse { + bridge_pool_contents: pool_contents, + }; + println!("{}", serde_json::to_string_pretty(&contents).unwrap()); } diff --git a/apps/src/lib/config/genesis.rs b/apps/src/lib/config/genesis.rs index d27827a1d9..ffbc22cbba 100644 --- a/apps/src/lib/config/genesis.rs +++ b/apps/src/lib/config/genesis.rs @@ -6,13 +6,16 @@ use std::path::Path; use borsh::{BorshDeserialize, BorshSerialize}; use derivative::Derivative; -use namada::ledger::eth_bridge::parameters::EthereumBridgeConfig; +use namada::ledger::eth_bridge::parameters::{ + Contracts, EthereumBridgeConfig, UpgradeableContract, +}; use namada::ledger::governance::parameters::GovParams; use namada::ledger::parameters::Parameters; use namada::ledger::pos::{GenesisValidator, PosParams}; -use namada::types::address::Address; +use namada::types::address::{wnam, Address}; #[cfg(not(feature = "dev"))] use namada::types::chain::ChainId; +use namada::types::ethereum_events::EthAddress; use namada::types::key::dkg_session_keys::DkgPublicKey; use namada::types::key::*; use namada::types::time::DateTimeUtc; @@ -881,7 +884,20 @@ pub fn genesis() -> Genesis { parameters, pos_params: PosParams::default(), gov_params: GovParams::default(), - ethereum_bridge_params: None, + ethereum_bridge_params: Some(EthereumBridgeConfig { + min_confirmations: Default::default(), + contracts: Contracts { + native_erc20: wnam(), + bridge: UpgradeableContract { + address: EthAddress([0; 20]), + version: Default::default(), + }, + governance: UpgradeableContract { + address: EthAddress([1; 20]), + version: Default::default(), + }, + }, + }), } } diff --git a/apps/src/lib/node/ledger/ethereum_node/events.rs b/apps/src/lib/node/ledger/ethereum_node/events.rs index d2e98de2dd..afd8e086fe 100644 --- a/apps/src/lib/node/ledger/ethereum_node/events.rs +++ b/apps/src/lib/node/ledger/ethereum_node/events.rs @@ -67,6 +67,7 @@ pub mod eth_events { pub type Result = std::result::Result; + #[derive(Clone, Debug, PartialEq)] /// An event waiting for a certain number of confirmations /// before being sent to the ledger pub(in super::super) struct PendingEvent { @@ -126,7 +127,8 @@ pub mod eth_events { match signature { signatures::TRANSFER_TO_NAMADA_SIG => { RawTransfersToNamada::decode(data).map(|txs| PendingEvent { - confirmations: txs.confirmations.into(), + confirmations: min_confirmations + .max(txs.confirmations.into()), block_height, event: EthereumEvent::TransfersToNamada { nonce: txs.nonce, @@ -137,7 +139,8 @@ pub mod eth_events { signatures::TRANSFER_TO_ETHEREUM_SIG => { RawTransfersToEthereum::decode(data).map(|txs| { PendingEvent { - confirmations: txs.confirmations.into(), + confirmations: min_confirmations + .max(txs.confirmations.into()), block_height, event: EthereumEvent::TransfersToEthereum { nonce: txs.nonce, @@ -757,6 +760,8 @@ pub mod eth_events { #[cfg(test)] mod test_events { + use assert_matches::assert_matches; + use super::*; #[test] @@ -798,6 +803,104 @@ pub mod eth_events { }] ) } + + /// Test that for Ethereum events for which a custom number of + /// confirmations may be specified, if a value lower than the + /// protocol-specified minimum confirmations is attempted to be used, + /// then the protocol-specified minimum confirmations is used instead. + #[test] + fn test_min_confirmations_enforced() -> Result<()> { + let arbitrary_block_height: Uint256 = 123u64.into(); + let min_confirmations: Uint256 = 100u64.into(); + let lower_than_min_confirmations = 5; + + let (sig, event) = ( + signatures::TRANSFER_TO_NAMADA_SIG, + RawTransfersToNamada { + transfers: vec![], + nonce: 0.into(), + confirmations: lower_than_min_confirmations, + }, + ); + let data = event.encode(); + let pending_event = PendingEvent::decode( + sig, + arbitrary_block_height.clone(), + &data, + min_confirmations.clone(), + )?; + + assert_matches!(pending_event, PendingEvent { confirmations, .. } if confirmations == min_confirmations); + + let (sig, event) = ( + signatures::TRANSFER_TO_ETHEREUM_SIG, + RawTransfersToEthereum { + transfers: vec![], + nonce: 0.into(), + confirmations: lower_than_min_confirmations, + }, + ); + let data = event.encode(); + let pending_event = PendingEvent::decode( + sig, + arbitrary_block_height, + &data, + min_confirmations.clone(), + )?; + + assert_matches!(pending_event, PendingEvent { confirmations, .. } if confirmations == min_confirmations); + + Ok(()) + } + + /// Test that for Ethereum events for which a custom number of + /// confirmations may be specified, the custom number is used if it is + /// at least the protocol-specified minimum confirmations. + #[test] + fn test_custom_confirmations_used() { + let arbitrary_block_height: Uint256 = 123u64.into(); + let min_confirmations: Uint256 = 100u64.into(); + let higher_than_min_confirmations = 200; + + let (sig, event) = ( + signatures::TRANSFER_TO_NAMADA_SIG, + RawTransfersToNamada { + transfers: vec![], + nonce: 0.into(), + confirmations: higher_than_min_confirmations, + }, + ); + let data = event.encode(); + let pending_event = PendingEvent::decode( + sig, + arbitrary_block_height.clone(), + &data, + min_confirmations.clone(), + ) + .unwrap(); + + assert_matches!(pending_event, PendingEvent { confirmations, .. } if confirmations == higher_than_min_confirmations.into()); + + let (sig, event) = ( + signatures::TRANSFER_TO_ETHEREUM_SIG, + RawTransfersToEthereum { + transfers: vec![], + nonce: 0.into(), + confirmations: higher_than_min_confirmations, + }, + ); + let data = event.encode(); + let pending_event = PendingEvent::decode( + sig, + arbitrary_block_height, + &data, + min_confirmations, + ) + .unwrap(); + + assert_matches!(pending_event, PendingEvent { confirmations, .. } if confirmations == higher_than_min_confirmations.into()); + } + /// For each of the basic types, test that roundtrip /// encoding - decoding is a no-op #[test] diff --git a/apps/src/lib/node/ledger/shell/init_chain.rs b/apps/src/lib/node/ledger/shell/init_chain.rs index 6bc119a6e0..7ead94a34a 100644 --- a/apps/src/lib/node/ledger/shell/init_chain.rs +++ b/apps/src/lib/node/ledger/shell/init_chain.rs @@ -73,6 +73,7 @@ where genesis.gov_params.init_storage(&mut self.storage); // configure the Ethereum bridge if the configuration is set. if let Some(config) = genesis.ethereum_bridge_params { + tracing::debug!("Initializing Ethereum bridge storage."); config.init_storage(&mut self.storage); } diff --git a/proof_of_stake/src/lib.rs b/proof_of_stake/src/lib.rs index c2fd2523cc..b6be63cf44 100644 --- a/proof_of_stake/src/lib.rs +++ b/proof_of_stake/src/lib.rs @@ -1046,8 +1046,6 @@ pub enum BondError { InactiveValidator(Address), #[error("Voting power overflow: {0}")] VotingPowerOverflow(TryFromIntError), - #[error("Given zero amount to bond")] - ZeroAmount, } #[allow(missing_docs)] @@ -1065,8 +1063,6 @@ pub enum UnbondError { ValidatorHasNoVotingPower(Address), #[error("Voting power overflow: {0}")] VotingPowerOverflow(TryFromIntError), - #[error("Given zero amount to unbond")] - ZeroAmount, } #[allow(missing_docs)] @@ -1611,9 +1607,6 @@ where + BorshSerialize + BorshSchema, { - if amount == TokenAmount::default() { - return Err(BondError::ZeroAmount); - } // Check the validator state match validator_state { None => { @@ -1791,9 +1784,6 @@ where + BorshSerialize + BorshSchema, { - if amount == TokenAmount::default() { - return Err(UnbondError::ZeroAmount); - } // We can unbond tokens that are bonded for a future epoch (not yet // active), hence we check the total at the pipeline offset let unbondable_amount = bond diff --git a/shared/src/ledger/queries/shell.rs b/shared/src/ledger/queries/shell.rs index 335f9b8203..516992aa94 100644 --- a/shared/src/ledger/queries/shell.rs +++ b/shared/src/ledger/queries/shell.rs @@ -250,11 +250,7 @@ where let proof = if request.prove { let proof = ctx .storage - .get_existence_proof( - &storage_key, - value.clone(), - request.height, - ) + .get_existence_proof(&storage_key, &value, request.height) .into_storage_result()?; Some(proof) } else { @@ -309,7 +305,7 @@ where for PrefixValue { key, value } in &data { let mut proof = ctx .storage - .get_existence_proof(key, value.clone(), request.height) + .get_existence_proof(key, value, request.height) .into_storage_result()?; ops.append(&mut proof.ops); } @@ -477,7 +473,10 @@ where ))); } // get the membership proof - match tree.get_sub_tree_existence_proof(&keys, values) { + match tree.get_sub_tree_existence_proof( + &keys, + values.iter().map(|v| v.as_slice()).collect(), + ) { Ok(BridgePool(proof)) => { let data = EncodeCell::new(&RelayProof { // TODO: use actual validators diff --git a/shared/src/ledger/storage/merkle_tree.rs b/shared/src/ledger/storage/merkle_tree.rs index 34d5f4bf73..edd4d2b452 100644 --- a/shared/src/ledger/storage/merkle_tree.rs +++ b/shared/src/ledger/storage/merkle_tree.rs @@ -56,7 +56,7 @@ pub enum Error { type Result = std::result::Result; /// Type alias for bytes to be put into the Merkle storage -pub(super) type StorageBytes = Vec; +pub(super) type StorageBytes<'a> = &'a [u8]; /// Type aliases for the different merkle trees and backing stores pub type SmtStore = DefaultStore; @@ -733,7 +733,7 @@ mod test { let proof = match tree .get_sub_tree_existence_proof( std::array::from_ref(&ibc_key), - vec![ibc_val.clone()], + vec![&ibc_val], ) .unwrap() { @@ -792,7 +792,7 @@ mod test { let proof = match tree .get_sub_tree_existence_proof( std::array::from_ref(&pos_key), - vec![pos_val.clone()], + vec![&pos_val], ) .unwrap() { diff --git a/shared/src/ledger/storage/mod.rs b/shared/src/ledger/storage/mod.rs index 34acc656e6..da517989d8 100644 --- a/shared/src/ledger/storage/mod.rs +++ b/shared/src/ledger/storage/mod.rs @@ -36,7 +36,7 @@ use crate::ledger::pos::namada_proof_of_stake::PosBase; use crate::ledger::pos::types::WeightedValidator; use crate::ledger::pos::PosParams; use crate::ledger::storage::merkle_tree::{ - Error as MerkleTreeError, MerkleRoot, + Error as MerkleTreeError, MerkleRoot, StorageBytes, }; pub use crate::ledger::storage::merkle_tree::{ MerkleTree, MerkleTreeStoresRead, MerkleTreeStoresWrite, StoreRef, @@ -619,7 +619,7 @@ where pub fn get_existence_proof( &self, key: &Key, - value: Vec, + value: StorageBytes, height: BlockHeight, ) -> Result { if height >= self.get_block_height().0 { diff --git a/shared/src/ledger/storage/traits.rs b/shared/src/ledger/storage/traits.rs index ac426c563c..b615abaa98 100644 --- a/shared/src/ledger/storage/traits.rs +++ b/shared/src/ledger/storage/traits.rs @@ -38,7 +38,7 @@ pub trait SubTreeWrite { fn subtree_update( &mut self, key: &Key, - value: &[u8], + value: StorageBytes, ) -> Result; /// Delete a key from the sub-tree fn subtree_delete(&mut self, key: &Key) -> Result; @@ -68,7 +68,7 @@ impl<'a, H: StorageHasher + Default> SubTreeRead for &'a Smt { Ics23Proof::Exist(ep) => Ok(CommitmentProof { proof: Some(Ics23Proof::Exist(ExistenceProof { key: key.to_string().as_bytes().to_vec(), - value, + value: value.to_vec(), leaf: Some(ics23_specs::leaf_spec::()), ..ep })), @@ -84,7 +84,7 @@ impl<'a, H: StorageHasher + Default> SubTreeWrite for &'a mut Smt { fn subtree_update( &mut self, key: &Key, - value: &[u8], + value: StorageBytes, ) -> Result { let value = H::hash(value); self.update(H::hash(key.to_string()).into(), value.into()) @@ -139,7 +139,7 @@ impl<'a, H: StorageHasher + Default> SubTreeWrite for &'a mut Amt { fn subtree_update( &mut self, key: &Key, - value: &[u8], + value: StorageBytes, ) -> Result { let key = StringKey::try_from_bytes(key.to_string().as_bytes())?; let value = TreeBytes::from(value.as_ref().to_owned()); @@ -170,9 +170,7 @@ impl<'a> SubTreeRead for &'a BridgePoolTree { ) -> Result { let values = values .iter() - .filter_map(|val| { - PendingTransfer::try_from_slice(val.as_slice()).ok() - }) + .filter_map(|val| PendingTransfer::try_from_slice(val).ok()) .collect(); self.get_membership_proof(values) .map(Into::into) @@ -181,7 +179,11 @@ impl<'a> SubTreeRead for &'a BridgePoolTree { } impl<'a> SubTreeWrite for &'a mut BridgePoolTree { - fn subtree_update(&mut self, key: &Key, _: &[u8]) -> Result { + fn subtree_update( + &mut self, + key: &Key, + _: StorageBytes, + ) -> Result { self.insert_key(key) .map_err(|err| Error::MerkleTree(err.to_string())) } diff --git a/shared/src/types/token.rs b/shared/src/types/token.rs index 7bc914b6ba..3109fee837 100644 --- a/shared/src/types/token.rs +++ b/shared/src/types/token.rs @@ -234,7 +234,7 @@ impl FromStr for Amount { match rust_decimal::Decimal::from_str(s) { Ok(decimal) => { let scale = decimal.scale(); - if scale > 6 { + if scale > MAX_DECIMAL_PLACES { return Err(AmountParseError::ScaleTooLarge(scale)); } let whole = @@ -518,4 +518,12 @@ pub mod testing { pub fn arb_amount_ceiled(max: u64) -> impl Strategy { (0..=max).prop_map(Amount::from) } + + /// Generate an arbitrary non-zero token amount up to and including given + /// `max` value + pub fn arb_amount_non_zero_ceiled( + max: u64, + ) -> impl Strategy { + (1..=max).prop_map(Amount::from) + } } diff --git a/tests/src/e2e/eth_bridge_tests.rs b/tests/src/e2e/eth_bridge_tests.rs index 57d9fa4793..9d39c6ce03 100644 --- a/tests/src/e2e/eth_bridge_tests.rs +++ b/tests/src/e2e/eth_bridge_tests.rs @@ -1,11 +1,16 @@ use color_eyre::eyre::Result; +use namada::ledger::eth_bridge::parameters::{ + Contracts, EthereumBridgeConfig, UpgradeableContract, +}; +use namada::types::address::wnam; +use namada::types::ethereum_events::EthAddress; use namada_apps::config::ethereum_bridge; use super::setup::set_ethereum_bridge_mode; use crate::e2e::helpers::get_actor_rpc; use crate::e2e::setup; use crate::e2e::setup::constants::{ - wasm_abs_path, ALBERT, TX_WRITE_STORAGE_KEY_WASM, + wasm_abs_path, ALBERT, BERTHA, NAM, TX_WRITE_STORAGE_KEY_WASM, }; use crate::e2e::setup::{Bin, Who}; use crate::{run, run_as}; @@ -125,3 +130,108 @@ fn run_ledger_with_ethereum_events_endpoint() -> Result<()> { Ok(()) } + +/// In this test, we check the following: +/// 1. We can successfully add tranfers to the bridge pool. +/// 2. We can query the bridge pool and it is non-empty. +#[test] +fn test_add_to_bridge_pool() { + const LEDGER_STARTUP_TIMEOUT_SECONDS: u64 = 40; + const CLIENT_COMMAND_TIMEOUT_SECONDS: u64 = 60; + const QUERY_TIMEOUT_SECONDS: u64 = 40; + const SOLE_VALIDATOR: Who = Who::Validator(0); + const RECEIVER: &str = "0x6B175474E89094C55Da98b954EedeAC495271d0F"; + let wnam_address = wnam().to_canonical(); + let test = setup::network( + |mut genesis| { + genesis.ethereum_bridge_params = Some(EthereumBridgeConfig { + min_confirmations: Default::default(), + contracts: Contracts { + native_erc20: wnam(), + bridge: UpgradeableContract { + address: EthAddress([0; 20]), + version: Default::default(), + }, + governance: UpgradeableContract { + address: EthAddress([1; 20]), + version: Default::default(), + }, + }, + }); + genesis + }, + None, + ) + .unwrap(); + set_ethereum_bridge_mode( + &test, + &test.net.chain_id, + &Who::Validator(0), + ethereum_bridge::ledger::Mode::EventsEndpoint, + ); + + let mut namadan_ledger = run_as!( + test, + SOLE_VALIDATOR, + Bin::Node, + &["ledger"], + Some(LEDGER_STARTUP_TIMEOUT_SECONDS) + ) + .unwrap(); + namadan_ledger + .exp_string("Anoma ledger node started") + .unwrap(); + namadan_ledger + .exp_string("Tendermint node started") + .unwrap(); + namadan_ledger.exp_string("Committed block hash").unwrap(); + let _bg_ledger = namadan_ledger.background(); + + let ledger_addr = get_actor_rpc(&test, &SOLE_VALIDATOR); + let tx_args = vec![ + "add-erc20-transfer", + "--address", + BERTHA, + "--signer", + BERTHA, + "--amount", + "100", + "--erc20", + &wnam_address, + "--ethereum-address", + RECEIVER, + "--fee-amount", + "10", + "--fee-payer", + BERTHA, + "--gas-amount", + "0", + "--gas-limit", + "0", + "--gas-token", + NAM, + "--ledger-address", + &ledger_addr, + ]; + + let mut namadac_tx = run!( + test, + Bin::Client, + tx_args, + Some(CLIENT_COMMAND_TIMEOUT_SECONDS) + ) + .unwrap(); + namadac_tx.exp_string("Transaction accepted").unwrap(); + namadac_tx.exp_string("Transaction applied").unwrap(); + namadac_tx.exp_string("Transaction is valid").unwrap(); + drop(namadac_tx); + + let mut namadar = run!( + test, + Bin::BridgePool, + ["query", "--ledger-address", &ledger_addr,], + Some(QUERY_TIMEOUT_SECONDS), + ) + .unwrap(); + namadar.exp_string(r#""bridge_pool_contents":"#).unwrap(); +} diff --git a/tests/src/e2e/setup.rs b/tests/src/e2e/setup.rs index 773a93069c..cf02c3e790 100644 --- a/tests/src/e2e/setup.rs +++ b/tests/src/e2e/setup.rs @@ -253,6 +253,7 @@ pub enum Bin { Node, Client, Wallet, + BridgePool, } #[derive(Debug)] @@ -711,6 +712,7 @@ where Bin::Node => ("namadan", "info"), Bin::Client => ("namadac", "tendermint_rpc=debug"), Bin::Wallet => ("namadaw", "info"), + Bin::BridgePool => ("namadar", "info"), }; let mut run_cmd = generate_bin_command( diff --git a/wasm/wasm_source/proptest-regressions/tx_bond.txt b/wasm/wasm_source/proptest-regressions/tx_bond.txt index 3a88756618..8c589d1abd 100644 --- a/wasm/wasm_source/proptest-regressions/tx_bond.txt +++ b/wasm/wasm_source/proptest-regressions/tx_bond.txt @@ -1 +1 @@ -cc e54347c5114ef29538127ba9ad68d1572af839ec63c015318fc0827818853a22 +cc f22e874350910b197cb02a4a07ec5bef18e16c0d1a39eaabaee43d1fc05ce11d diff --git a/wasm/wasm_source/src/tx_bond.rs b/wasm/wasm_source/src/tx_bond.rs index 8def153df3..ca53c11635 100644 --- a/wasm/wasm_source/src/tx_bond.rs +++ b/wasm/wasm_source/src/tx_bond.rs @@ -353,7 +353,7 @@ mod tests { ( arb_established_address(), prop::option::of(arb_non_internal_address()), - token::testing::arb_amount_ceiled(max_amount), + token::testing::arb_amount_non_zero_ceiled(max_amount), ) .prop_map(|(validator, source, amount)| { transaction::pos::Bond { diff --git a/wasm/wasm_source/src/tx_bridge_pool.rs b/wasm/wasm_source/src/tx_bridge_pool.rs index 812e95b25f..e246bdb930 100644 --- a/wasm/wasm_source/src/tx_bridge_pool.rs +++ b/wasm/wasm_source/src/tx_bridge_pool.rs @@ -7,9 +7,13 @@ use namada_tx_prelude::*; #[transaction] fn apply_tx(ctx: &mut Ctx, tx_data: Vec) -> TxResult { - let signed = SignedTxData::try_from_slice(&tx_data[..]).unwrap(); - let transfer = - PendingTransfer::try_from_slice(&signed.data.unwrap()[..]).unwrap(); + let signed = SignedTxData::try_from_slice(&tx_data[..]) + .map_err(|e| Error::wrap("Error deserializing SignedTxData", e))?; + let transfer = PendingTransfer::try_from_slice(&signed.data.unwrap()[..]) + .map_err(|e| { + Error::wrap("Error deserializing PendingTransfer", e) + })?; + log_string("Received transfer to add to pool."); // pay the gas fees let GasFee { amount, ref payer } = transfer.gas_fee; token::transfer( @@ -22,14 +26,15 @@ fn apply_tx(ctx: &mut Ctx, tx_data: Vec) -> TxResult { &None, &None, )?; + log_string("Token transfer succeeded."); let TransferToEthereum { - ref asset, + asset, ref sender, amount, .. } = transfer.transfer; // if minting wNam, escrow the correct amount - if *asset == native_erc20_address(ctx) { + if asset == native_erc20_address(ctx)? { token::transfer( ctx, sender, @@ -42,7 +47,7 @@ fn apply_tx(ctx: &mut Ctx, tx_data: Vec) -> TxResult { )?; } else { // Otherwise we escrow ERC20 tokens. - let sub_prefix = wrapped_erc20s::sub_prefix(asset); + let sub_prefix = wrapped_erc20s::sub_prefix(&asset); token::transfer( ctx, sender, @@ -54,14 +59,20 @@ fn apply_tx(ctx: &mut Ctx, tx_data: Vec) -> TxResult { &None, )?; } + log_string("Escrow succeeded"); // add transfer into the pool let pending_key = bridge_pool::get_pending_key(&transfer); ctx.write_bytes(&pending_key, transfer.try_to_vec().unwrap()) - .unwrap(); + .wrap_err("Could not write transfer to bridge pool")?; Ok(()) } -fn native_erc20_address(ctx: &mut Ctx) -> EthAddress { - let addr = ctx.read_bytes(&native_erc20_key()).unwrap().unwrap(); - BorshDeserialize::try_from_slice(addr.as_slice()).unwrap() +fn native_erc20_address(ctx: &mut Ctx) -> EnvResult { + log_string("Trying to get wnam key"); + let addr = ctx + .read_bytes(&native_erc20_key()) + .map_err(|e| Error::wrap("Could not read wNam key from storage", e))? + .unwrap(); + log_string("Got wnam key"); + Ok(BorshDeserialize::try_from_slice(addr.as_slice()).unwrap()) } diff --git a/wasm/wasm_source/src/tx_unbond.rs b/wasm/wasm_source/src/tx_unbond.rs index 0f249e73f3..46db9ec99c 100644 --- a/wasm/wasm_source/src/tx_unbond.rs +++ b/wasm/wasm_source/src/tx_unbond.rs @@ -225,24 +225,24 @@ mod tests { epoch {epoch}" ); } - let start_epoch = match &unbond.source { - Some(_) => { - // This bond was a delegation - namada_tx_prelude::proof_of_stake::types::Epoch::from( - pos_params.pipeline_len, - ) - } - None => { - // This bond was a genesis validator self-bond - namada_tx_prelude::proof_of_stake::types::Epoch::default() - } + let start_epoch = if is_delegation { + // This bond was a delegation + namada_tx_prelude::proof_of_stake::types::Epoch::from( + pos_params.pipeline_len, + ) + } else { + // This bond was a genesis validator self-bond + namada_tx_prelude::proof_of_stake::types::Epoch::default() }; let end_epoch = namada_tx_prelude::proof_of_stake::types::Epoch::from( pos_params.unbonding_len - 1, ); - let expected_unbond = - HashMap::from_iter([((start_epoch, end_epoch), unbond.amount)]); + let expected_unbond = if unbond.amount == token::Amount::default() { + HashMap::new() + } else { + HashMap::from_iter([((start_epoch, end_epoch), unbond.amount)]) + }; let actual_unbond: Unbond = unbonds_post.get(pos_params.unbonding_len).unwrap(); assert_eq!( @@ -390,12 +390,14 @@ mod tests { fn arb_initial_stake_and_unbond() -> impl Strategy { // Generate initial stake - token::testing::arb_amount().prop_flat_map(|initial_stake| { - // Use the initial stake to limit the bond amount - let unbond = arb_unbond(u64::from(initial_stake)); - // Use the generated initial stake too too - (Just(initial_stake), unbond) - }) + token::testing::arb_amount_ceiled((i64::MAX / 8) as u64).prop_flat_map( + |initial_stake| { + // Use the initial stake to limit the bond amount + let unbond = arb_unbond(u64::from(initial_stake)); + // Use the generated initial stake too too + (Just(initial_stake), unbond) + }, + ) } /// Generates an initial validator stake and a unbond, while making sure @@ -406,7 +408,7 @@ mod tests { ( address::testing::arb_established_address(), prop::option::of(address::testing::arb_non_internal_address()), - token::testing::arb_amount_ceiled(max_amount), + token::testing::arb_amount_non_zero_ceiled(max_amount), ) .prop_map(|(validator, source, amount)| { let validator = Address::Established(validator);