From b30a877460fc69d24303a0e744ef2322a882f013 Mon Sep 17 00:00:00 2001 From: Tiago Carvalho Date: Thu, 3 Aug 2023 16:12:00 +0100 Subject: [PATCH 1/4] Update ethbridge-rs to v0.22.0 --- Cargo.lock | 24 ++++++++++++------------ Cargo.toml | 12 ++++++------ wasm/Cargo.lock | 20 ++++++++++---------- wasm_for_tests/wasm_source/Cargo.lock | 20 ++++++++++---------- 4 files changed, 38 insertions(+), 38 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d8131f58e9b..18e78ed5040 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1999,8 +1999,8 @@ dependencies = [ [[package]] name = "ethbridge-bridge-contract" -version = "0.21.0" -source = "git+https://github.com/heliaxdev/ethbridge-rs?tag=v0.21.0#781782307aac9c4529fe4c6600ea671ec98353d9" +version = "0.22.0" +source = "git+https://github.com/heliaxdev/ethbridge-rs?tag=v0.22.0#1c1028a823a7c2148b3efacea800bfc6c8969c20" dependencies = [ "ethbridge-bridge-events", "ethbridge-structs", @@ -2010,8 +2010,8 @@ dependencies = [ [[package]] name = "ethbridge-bridge-events" -version = "0.21.0" -source = "git+https://github.com/heliaxdev/ethbridge-rs?tag=v0.21.0#781782307aac9c4529fe4c6600ea671ec98353d9" +version = "0.22.0" +source = "git+https://github.com/heliaxdev/ethbridge-rs?tag=v0.22.0#1c1028a823a7c2148b3efacea800bfc6c8969c20" dependencies = [ "ethabi", "ethbridge-structs", @@ -2021,8 +2021,8 @@ dependencies = [ [[package]] name = "ethbridge-events" -version = "0.21.0" -source = "git+https://github.com/heliaxdev/ethbridge-rs?tag=v0.21.0#781782307aac9c4529fe4c6600ea671ec98353d9" +version = "0.22.0" +source = "git+https://github.com/heliaxdev/ethbridge-rs?tag=v0.22.0#1c1028a823a7c2148b3efacea800bfc6c8969c20" dependencies = [ "ethbridge-bridge-events", "ethbridge-governance-events", @@ -2032,8 +2032,8 @@ dependencies = [ [[package]] name = "ethbridge-governance-contract" -version = "0.21.0" -source = "git+https://github.com/heliaxdev/ethbridge-rs?tag=v0.21.0#781782307aac9c4529fe4c6600ea671ec98353d9" +version = "0.22.0" +source = "git+https://github.com/heliaxdev/ethbridge-rs?tag=v0.22.0#1c1028a823a7c2148b3efacea800bfc6c8969c20" dependencies = [ "ethbridge-governance-events", "ethbridge-structs", @@ -2043,8 +2043,8 @@ dependencies = [ [[package]] name = "ethbridge-governance-events" -version = "0.21.0" -source = "git+https://github.com/heliaxdev/ethbridge-rs?tag=v0.21.0#781782307aac9c4529fe4c6600ea671ec98353d9" +version = "0.22.0" +source = "git+https://github.com/heliaxdev/ethbridge-rs?tag=v0.22.0#1c1028a823a7c2148b3efacea800bfc6c8969c20" dependencies = [ "ethabi", "ethbridge-structs", @@ -2054,8 +2054,8 @@ dependencies = [ [[package]] name = "ethbridge-structs" -version = "0.21.0" -source = "git+https://github.com/heliaxdev/ethbridge-rs?tag=v0.21.0#781782307aac9c4529fe4c6600ea671ec98353d9" +version = "0.22.0" +source = "git+https://github.com/heliaxdev/ethbridge-rs?tag=v0.22.0#1c1028a823a7c2148b3efacea800bfc6c8969c20" dependencies = [ "ethabi", "ethers", diff --git a/Cargo.toml b/Cargo.toml index d524d0c7a24..7375bb01e1e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -66,12 +66,12 @@ directories = "4.0.1" ed25519-consensus = "1.2.0" escargot = "0.5.7" ethabi = "18.0.0" -ethbridge-bridge-contract = {git = "https://github.com/heliaxdev/ethbridge-rs", tag = "v0.21.0"} -ethbridge-bridge-events = {git = "https://github.com/heliaxdev/ethbridge-rs", tag = "v0.21.0"} -ethbridge-events = {git = "https://github.com/heliaxdev/ethbridge-rs", tag = "v0.21.0"} -ethbridge-governance-contract = {git = "https://github.com/heliaxdev/ethbridge-rs", tag = "v0.21.0"} -ethbridge-governance-events = {git = "https://github.com/heliaxdev/ethbridge-rs", tag = "v0.21.0"} -ethbridge-structs = { git = "https://github.com/heliaxdev/ethbridge-rs", tag = "v0.21.0" } +ethbridge-bridge-contract = {git = "https://github.com/heliaxdev/ethbridge-rs", tag = "v0.22.0"} +ethbridge-bridge-events = {git = "https://github.com/heliaxdev/ethbridge-rs", tag = "v0.22.0"} +ethbridge-events = {git = "https://github.com/heliaxdev/ethbridge-rs", tag = "v0.22.0"} +ethbridge-governance-contract = {git = "https://github.com/heliaxdev/ethbridge-rs", tag = "v0.22.0"} +ethbridge-governance-events = {git = "https://github.com/heliaxdev/ethbridge-rs", tag = "v0.22.0"} +ethbridge-structs = { git = "https://github.com/heliaxdev/ethbridge-rs", tag = "v0.22.0" } ethers = "2.0.0" expectrl = "0.7.0" eyre = "0.6.5" diff --git a/wasm/Cargo.lock b/wasm/Cargo.lock index a273155cd25..0f4f1f4a390 100644 --- a/wasm/Cargo.lock +++ b/wasm/Cargo.lock @@ -1677,8 +1677,8 @@ dependencies = [ [[package]] name = "ethbridge-bridge-contract" -version = "0.21.0" -source = "git+https://github.com/heliaxdev/ethbridge-rs?tag=v0.21.0#781782307aac9c4529fe4c6600ea671ec98353d9" +version = "0.22.0" +source = "git+https://github.com/heliaxdev/ethbridge-rs?tag=v0.22.0#1c1028a823a7c2148b3efacea800bfc6c8969c20" dependencies = [ "ethbridge-bridge-events", "ethbridge-structs", @@ -1688,8 +1688,8 @@ dependencies = [ [[package]] name = "ethbridge-bridge-events" -version = "0.21.0" -source = "git+https://github.com/heliaxdev/ethbridge-rs?tag=v0.21.0#781782307aac9c4529fe4c6600ea671ec98353d9" +version = "0.22.0" +source = "git+https://github.com/heliaxdev/ethbridge-rs?tag=v0.22.0#1c1028a823a7c2148b3efacea800bfc6c8969c20" dependencies = [ "ethabi", "ethbridge-structs", @@ -1699,8 +1699,8 @@ dependencies = [ [[package]] name = "ethbridge-governance-contract" -version = "0.21.0" -source = "git+https://github.com/heliaxdev/ethbridge-rs?tag=v0.21.0#781782307aac9c4529fe4c6600ea671ec98353d9" +version = "0.22.0" +source = "git+https://github.com/heliaxdev/ethbridge-rs?tag=v0.22.0#1c1028a823a7c2148b3efacea800bfc6c8969c20" dependencies = [ "ethbridge-governance-events", "ethbridge-structs", @@ -1710,8 +1710,8 @@ dependencies = [ [[package]] name = "ethbridge-governance-events" -version = "0.21.0" -source = "git+https://github.com/heliaxdev/ethbridge-rs?tag=v0.21.0#781782307aac9c4529fe4c6600ea671ec98353d9" +version = "0.22.0" +source = "git+https://github.com/heliaxdev/ethbridge-rs?tag=v0.22.0#1c1028a823a7c2148b3efacea800bfc6c8969c20" dependencies = [ "ethabi", "ethbridge-structs", @@ -1721,8 +1721,8 @@ dependencies = [ [[package]] name = "ethbridge-structs" -version = "0.21.0" -source = "git+https://github.com/heliaxdev/ethbridge-rs?tag=v0.21.0#781782307aac9c4529fe4c6600ea671ec98353d9" +version = "0.22.0" +source = "git+https://github.com/heliaxdev/ethbridge-rs?tag=v0.22.0#1c1028a823a7c2148b3efacea800bfc6c8969c20" dependencies = [ "ethabi", "ethers", diff --git a/wasm_for_tests/wasm_source/Cargo.lock b/wasm_for_tests/wasm_source/Cargo.lock index 6d5a227eb47..6405e7100ec 100644 --- a/wasm_for_tests/wasm_source/Cargo.lock +++ b/wasm_for_tests/wasm_source/Cargo.lock @@ -1677,8 +1677,8 @@ dependencies = [ [[package]] name = "ethbridge-bridge-contract" -version = "0.21.0" -source = "git+https://github.com/heliaxdev/ethbridge-rs?tag=v0.21.0#781782307aac9c4529fe4c6600ea671ec98353d9" +version = "0.22.0" +source = "git+https://github.com/heliaxdev/ethbridge-rs?tag=v0.22.0#1c1028a823a7c2148b3efacea800bfc6c8969c20" dependencies = [ "ethbridge-bridge-events", "ethbridge-structs", @@ -1688,8 +1688,8 @@ dependencies = [ [[package]] name = "ethbridge-bridge-events" -version = "0.21.0" -source = "git+https://github.com/heliaxdev/ethbridge-rs?tag=v0.21.0#781782307aac9c4529fe4c6600ea671ec98353d9" +version = "0.22.0" +source = "git+https://github.com/heliaxdev/ethbridge-rs?tag=v0.22.0#1c1028a823a7c2148b3efacea800bfc6c8969c20" dependencies = [ "ethabi", "ethbridge-structs", @@ -1699,8 +1699,8 @@ dependencies = [ [[package]] name = "ethbridge-governance-contract" -version = "0.21.0" -source = "git+https://github.com/heliaxdev/ethbridge-rs?tag=v0.21.0#781782307aac9c4529fe4c6600ea671ec98353d9" +version = "0.22.0" +source = "git+https://github.com/heliaxdev/ethbridge-rs?tag=v0.22.0#1c1028a823a7c2148b3efacea800bfc6c8969c20" dependencies = [ "ethbridge-governance-events", "ethbridge-structs", @@ -1710,8 +1710,8 @@ dependencies = [ [[package]] name = "ethbridge-governance-events" -version = "0.21.0" -source = "git+https://github.com/heliaxdev/ethbridge-rs?tag=v0.21.0#781782307aac9c4529fe4c6600ea671ec98353d9" +version = "0.22.0" +source = "git+https://github.com/heliaxdev/ethbridge-rs?tag=v0.22.0#1c1028a823a7c2148b3efacea800bfc6c8969c20" dependencies = [ "ethabi", "ethbridge-structs", @@ -1721,8 +1721,8 @@ dependencies = [ [[package]] name = "ethbridge-structs" -version = "0.21.0" -source = "git+https://github.com/heliaxdev/ethbridge-rs?tag=v0.21.0#781782307aac9c4529fe4c6600ea671ec98353d9" +version = "0.22.0" +source = "git+https://github.com/heliaxdev/ethbridge-rs?tag=v0.22.0#1c1028a823a7c2148b3efacea800bfc6c8969c20" dependencies = [ "ethabi", "ethers", From 1a63f01d71bff78b6064c266f2f2bee7b6da80ba Mon Sep 17 00:00:00 2001 From: Tiago Carvalho Date: Thu, 3 Aug 2023 17:02:50 +0100 Subject: [PATCH 2/4] Fix compilation errors to support new `ethbridge-rs` --- .../lib/node/ledger/ethereum_oracle/events.rs | 80 ++++---- .../lib/node/ledger/ethereum_oracle/mod.rs | 15 +- .../lib/node/ledger/shell/finalize_block.rs | 31 +-- .../shell/vote_extensions/eth_events.rs | 39 +--- core/src/types/eth_bridge_pool.rs | 183 +++++++++++++++--- core/src/types/ethereum_events.rs | 91 +++------ .../transactions/ethereum_events/events.rs | 62 +++--- .../src/storage/eth_bridge_queries.rs | 32 ++- shared/src/ledger/eth_bridge/bridge_pool.rs | 66 ++++--- shared/src/ledger/queries/mod.rs | 3 + shared/src/ledger/queries/shell.rs | 2 +- shared/src/ledger/queries/shell/eth_bridge.rs | 156 ++++++++++----- 12 files changed, 456 insertions(+), 304 deletions(-) diff --git a/apps/src/lib/node/ledger/ethereum_oracle/events.rs b/apps/src/lib/node/ledger/ethereum_oracle/events.rs index 645b87d1c41..3472fcb6013 100644 --- a/apps/src/lib/node/ledger/ethereum_oracle/events.rs +++ b/apps/src/lib/node/ledger/ethereum_oracle/events.rs @@ -14,9 +14,9 @@ pub mod eth_events { use namada::eth_bridge::ethers::contract::EthEvent; use namada::types::address::Address; use namada::types::ethereum_events::{ - EthAddress, EthereumEvent, TransferToEthereum, TransferToEthereumKind, - TransferToNamada, Uint, + EthAddress, EthereumEvent, TransferToEthereum, TransferToNamada, Uint, }; + use namada::types::hash::Hash; use namada::types::keccak::KeccakHash; use namada::types::token::Amount; use num256::Uint256; @@ -153,32 +153,33 @@ pub mod eth_events { }; } - /// Trait to add parsing methods to foreign types. - trait Parse: Sized { - parse_method! { parse_eth_transfer_kind -> TransferToEthereumKind } - parse_method! { parse_eth_address -> EthAddress } - parse_method! { parse_address -> Address } - parse_method! { parse_amount -> Amount } - parse_method! { parse_u32 -> u32 } - parse_method! { parse_uint256 -> Uint } - parse_method! { parse_bool -> bool } - parse_method! { parse_string -> String } - parse_method! { parse_keccak -> KeccakHash } - parse_method! { parse_amount_array -> Vec } - parse_method! { parse_eth_address_array -> Vec } - parse_method! { parse_address_array -> Vec
} - parse_method! { parse_string_array -> Vec } - parse_method! { parse_transfer_to_namada_array -> Vec } - parse_method! { parse_transfer_to_namada -> TransferToNamada } - parse_method! { parse_transfer_to_eth_array -> Vec } - parse_method! { parse_transfer_to_eth -> TransferToEthereum } + macro_rules! trait_parse_def { + ($($name:ident -> $type:ty;)*) => { + /// Trait to add parsing methods to foreign types. + trait Parse: Sized { + $( parse_method!($name -> $type); )* + } + } } - impl Parse for u8 { - fn parse_eth_transfer_kind(self) -> Result { - self.try_into() - .map_err(|err| Error::Decode(format!("{:?}", err))) - } + trait_parse_def! { + parse_address -> Address; + parse_address_array -> Vec
; + parse_amount -> Amount; + parse_amount_array -> Vec; + parse_bool -> bool; + parse_eth_address -> EthAddress; + parse_eth_address_array -> Vec; + parse_hash -> Hash; + parse_keccak -> KeccakHash; + parse_string -> String; + parse_string_array -> Vec; + parse_transfer_to_eth -> TransferToEthereum; + parse_transfer_to_eth_array -> Vec; + parse_transfer_to_namada -> TransferToNamada; + parse_transfer_to_namada_array -> Vec; + parse_u32 -> u32; + parse_uint256 -> Uint; } impl Parse for ethabi::Address { @@ -200,7 +201,13 @@ pub mod eth_events { impl Parse for ethabi::Uint { fn parse_amount(self) -> Result { - Ok(Amount::from(self.as_u64())) + let uint = { + use namada::core::types::uint::Uint as NamadaUint; + let mut num_buf = [0; 32]; + self.to_little_endian(&mut num_buf); + NamadaUint::from_little_endian(&num_buf) + }; + Amount::from_uint(uint, 0).map_err(|e| Error::Decode(e.to_string())) } fn parse_u32(self) -> Result { @@ -222,6 +229,10 @@ pub mod eth_events { fn parse_keccak(self) -> Result { Ok(KeccakHash(self)) } + + fn parse_hash(self) -> Result { + Ok(Hash(self)) + } } impl Parse for Vec { @@ -279,21 +290,15 @@ pub mod eth_events { impl Parse for ethereum_structs::Erc20Transfer { fn parse_transfer_to_eth(self) -> Result { - let kind = self.kind.parse_eth_transfer_kind()?; let asset = self.from.parse_eth_address()?; let receiver = self.to.parse_eth_address()?; - let sender = self.sender.parse_address()?; let amount = self.amount.parse_amount()?; - let gas_payer = self.fee_from.parse_address()?; - let gas_amount = self.fee.parse_amount()?; + let checksum = self.namada_data_digest.parse_hash()?; Ok(TransferToEthereum { - kind, asset, amount, - sender, receiver, - gas_amount, - gas_payer, + checksum, }) } } @@ -509,13 +514,10 @@ pub mod eth_events { let eth_transfers = TransferToErcFilter { transfers: vec![ ethereum_structs::Erc20Transfer { - kind: TransferToEthereumKind::Erc20 as u8, from: H160([1; 20]), to: H160([2; 20]), - sender: address.clone(), amount: 0u64.into(), - fee_from: address.clone(), - fee: 0u64.into(), + namada_data_digest: [0; 32], }; 2 ], diff --git a/apps/src/lib/node/ledger/ethereum_oracle/mod.rs b/apps/src/lib/node/ledger/ethereum_oracle/mod.rs index 05486aa11e7..fc9ae9f0d15 100644 --- a/apps/src/lib/node/ledger/ethereum_oracle/mod.rs +++ b/apps/src/lib/node/ledger/ethereum_oracle/mod.rs @@ -569,9 +569,8 @@ mod test_oracle { use namada::eth_bridge::ethers::types::H160; use namada::eth_bridge::structs::Erc20Transfer; use namada::types::address::testing::gen_established_address; - use namada::types::ethereum_events::{ - EthAddress, TransferToEthereum, TransferToEthereumKind, - }; + use namada::types::ethereum_events::{EthAddress, TransferToEthereum}; + use namada::types::hash::Hash; use tokio::sync::oneshot::channel; use tokio::time::timeout; @@ -828,13 +827,10 @@ mod test_oracle { let gas_payer = gen_established_address(); let second_event = TransferToErcFilter { transfers: vec![Erc20Transfer { - kind: TransferToEthereumKind::Erc20 as u8, amount: 0.into(), from: H160([0; 20]), - sender: gas_payer.to_string(), to: H160([1; 20]), - fee: 0.into(), - fee_from: gas_payer.to_string(), + namada_data_digest: [0; 32], }], valid_map: vec![true], relayer_address: gas_payer.to_string(), @@ -898,13 +894,10 @@ mod test_oracle { assert_eq!( transfer, TransferToEthereum { - kind: TransferToEthereumKind::Erc20, amount: Default::default(), asset: EthAddress([0; 20]), - sender: gas_payer.clone(), receiver: EthAddress([1; 20]), - gas_amount: Default::default(), - gas_payer: gas_payer.clone(), + checksum: Hash::default(), } ); } else { diff --git a/apps/src/lib/node/ledger/shell/finalize_block.rs b/apps/src/lib/node/ledger/shell/finalize_block.rs index f8fb4fb0a45..f79c5cf421c 100644 --- a/apps/src/lib/node/ledger/shell/finalize_block.rs +++ b/apps/src/lib/node/ledger/shell/finalize_block.rs @@ -1038,9 +1038,7 @@ mod test_finalize_block { }; use namada::proto::{Code, Data, Section, Signature}; use namada::types::dec::POS_DECIMAL_PRECISION; - use namada::types::ethereum_events::{ - EthAddress, TransferToEthereum, TransferToEthereumKind, Uint as ethUint, - }; + use namada::types::ethereum_events::{EthAddress, Uint as ethUint}; use namada::types::hash::Hash; use namada::types::keccak::KeccakHash; use namada::types::key::tm_consensus_key_raw_hash; @@ -1695,17 +1693,24 @@ mod test_finalize_block { } // write transfer to storage let transfer = { - use namada::core::types::eth_bridge_pool::PendingTransfer; - let transfer = TransferToEthereum { - kind: TransferToEthereumKind::Erc20, - amount: 10u64.into(), - asset, - receiver, - gas_amount: 10u64.into(), - sender: bertha.clone(), - gas_payer: bertha.clone(), + use namada::core::types::eth_bridge_pool::{ + GasFee, PendingTransfer, TransferToEthereum, + TransferToEthereumKind, + }; + let pending = PendingTransfer { + transfer: TransferToEthereum { + kind: TransferToEthereumKind::Erc20, + amount: 10u64.into(), + asset, + recipient: receiver, + sender: bertha.clone(), + }, + gas_fee: GasFee { + amount: 10u64.into(), + payer: bertha.clone(), + }, }; - let pending = PendingTransfer::from(&transfer); + let transfer = (&pending).into(); shell .wl_storage .write(&bridge_pool::get_pending_key(&pending), pending) diff --git a/apps/src/lib/node/ledger/shell/vote_extensions/eth_events.rs b/apps/src/lib/node/ledger/shell/vote_extensions/eth_events.rs index 5282864e6d0..cec4158940e 100644 --- a/apps/src/lib/node/ledger/shell/vote_extensions/eth_events.rs +++ b/apps/src/lib/node/ledger/shell/vote_extensions/eth_events.rs @@ -461,9 +461,9 @@ mod test_vote_extensions { #[cfg(feature = "abcipp")] use namada::types::eth_abi::Encode; use namada::types::ethereum_events::{ - EthAddress, EthereumEvent, TransferToEthereum, TransferToEthereumKind, - Uint, + EthAddress, EthereumEvent, TransferToEthereum, Uint, }; + use namada::types::hash::Hash; #[cfg(feature = "abcipp")] use namada::types::keccak::keccak_hash; #[cfg(feature = "abcipp")] @@ -594,13 +594,10 @@ mod test_vote_extensions { let event_1 = EthereumEvent::TransfersToEthereum { nonce: 0.into(), transfers: vec![TransferToEthereum { - kind: TransferToEthereumKind::Erc20, amount: 100.into(), asset: EthAddress([1; 20]), - sender: gen_established_address(), receiver: EthAddress([2; 20]), - gas_amount: 10.into(), - gas_payer: gen_established_address(), + checksum: Hash::default(), }], valid_transfers_map: vec![true], relayer: gen_established_address(), @@ -608,13 +605,10 @@ mod test_vote_extensions { let event_2 = EthereumEvent::TransfersToEthereum { nonce: 1.into(), transfers: vec![TransferToEthereum { - kind: TransferToEthereumKind::Erc20, amount: 100.into(), asset: EthAddress([1; 20]), - sender: gen_established_address(), receiver: EthAddress([2; 20]), - gas_amount: 10.into(), - gas_payer: gen_established_address(), + checksum: Hash::default(), }], valid_transfers_map: vec![true], relayer: gen_established_address(), @@ -660,13 +654,10 @@ mod test_vote_extensions { let event_1 = EthereumEvent::TransfersToEthereum { nonce: 0.into(), transfers: vec![TransferToEthereum { - kind: TransferToEthereumKind::Erc20, amount: 100.into(), asset: EthAddress([1; 20]), - sender: gen_established_address(), receiver: EthAddress([2; 20]), - gas_amount: 10.into(), - gas_payer: gen_established_address(), + checksum: Hash::default(), }], valid_transfers_map: vec![true], relayer: gen_established_address(), @@ -723,13 +714,10 @@ mod test_vote_extensions { ethereum_events: vec![EthereumEvent::TransfersToEthereum { nonce: 0.into(), transfers: vec![TransferToEthereum { - kind: TransferToEthereumKind::Erc20, amount: 100.into(), - sender: gen_established_address(), asset: EthAddress([1; 20]), receiver: EthAddress([2; 20]), - gas_amount: 10.into(), - gas_payer: gen_established_address(), + checksum: Hash::default(), }], valid_transfers_map: vec![true], relayer: gen_established_address(), @@ -818,13 +806,10 @@ mod test_vote_extensions { ethereum_events: vec![EthereumEvent::TransfersToEthereum { nonce: 0.into(), transfers: vec![TransferToEthereum { - kind: TransferToEthereumKind::Erc20, amount: 100.into(), - sender: gen_established_address(), asset: EthAddress([1; 20]), receiver: EthAddress([2; 20]), - gas_amount: 10.into(), - gas_payer: gen_established_address(), + checksum: Hash::default(), }], valid_transfers_map: vec![true], relayer: gen_established_address(), @@ -896,13 +881,10 @@ mod test_vote_extensions { ethereum_events: vec![EthereumEvent::TransfersToEthereum { nonce: 0.into(), transfers: vec![TransferToEthereum { - kind: TransferToEthereumKind::Erc20, amount: 100.into(), - sender: gen_established_address(), asset: EthAddress([1; 20]), receiver: EthAddress([2; 20]), - gas_amount: 10.into(), - gas_payer: gen_established_address(), + checksum: Hash::default(), }], valid_transfers_map: vec![true], relayer: gen_established_address(), @@ -979,13 +961,10 @@ mod test_vote_extensions { ethereum_events: vec![EthereumEvent::TransfersToEthereum { nonce: 0.into(), transfers: vec![TransferToEthereum { - kind: TransferToEthereumKind::Erc20, amount: 100.into(), - sender: gen_established_address(), asset: EthAddress([1; 20]), receiver: EthAddress([2; 20]), - gas_amount: 10.into(), - gas_payer: gen_established_address(), + checksum: Hash::default(), }], valid_transfers_map: vec![true], relayer: gen_established_address(), diff --git a/core/src/types/eth_bridge_pool.rs b/core/src/types/eth_bridge_pool.rs index c7394af167f..4f12eec6197 100644 --- a/core/src/types/eth_bridge_pool.rs +++ b/core/src/types/eth_bridge_pool.rs @@ -1,6 +1,8 @@ //! The necessary type definitions for the contents of the //! Ethereum bridge pool +use std::borrow::Cow; + use borsh::{BorshDeserialize, BorshSchema, BorshSerialize}; use ethabi::token::Token; use serde::{Deserialize, Serialize}; @@ -8,16 +10,108 @@ use serde::{Deserialize, Serialize}; use crate::ledger::eth_bridge::storage::wrapped_erc20s; use crate::types::address::Address; use crate::types::eth_abi::Encode; -pub use crate::types::ethereum_events::TransferToEthereumKind; use crate::types::ethereum_events::{ EthAddress, TransferToEthereum as TransferToEthereumEvent, }; +use crate::types::hash::Hash as HashDigest; use crate::types::storage::{DbKeySeg, Key}; use crate::types::token::Amount; +/// A version used in our Ethereuem smart contracts +const VERSION: u8 = 1; + /// A namespace used in our Ethereuem smart contracts const NAMESPACE: &str = "transfer"; +/// Transfer to Ethereum kinds. +#[derive( + Copy, + Clone, + Debug, + PartialEq, + Eq, + Hash, + PartialOrd, + Ord, + BorshSerialize, + BorshDeserialize, + BorshSchema, + Serialize, + Deserialize, +)] +pub enum TransferToEthereumKind { + /// Transfer ERC20 assets from Namada to Ethereum. + /// + /// These transfers burn wrapped ERC20 assets in Namada, once + /// they have been confirmed. + Erc20, + /// Refund non-usable tokens. + /// + /// These Bridge pool transfers should be crafted for assets + /// that have been transferred to Namada, that had either not + /// been whitelisted or whose token caps had been exceeded in + /// Namada at the time of the transfer. + Nut, +} + +/// Additional data appended to a [`TransferToEthereumEvent`] to +/// construct a [`PendingTransfer`]. +#[derive( + Debug, + Clone, + Hash, + PartialOrd, + PartialEq, + Ord, + Eq, + Serialize, + Deserialize, + BorshSerialize, + BorshDeserialize, + BorshSchema, +)] +pub struct PendingTransferAppendix<'transfer> { + /// The kind of the pending transfer to Ethereum. + pub kind: Cow<'transfer, TransferToEthereumKind>, + /// The sender of the transfer. + pub sender: Cow<'transfer, Address>, + /// The amount of gas fees (in NAM) + /// paid by the user sending this transfer + pub gas_fee: Cow<'transfer, GasFee>, +} + +impl From for PendingTransferAppendix<'static> { + #[inline] + fn from(pending: PendingTransfer) -> Self { + Self { + kind: Cow::Owned(pending.transfer.kind), + sender: Cow::Owned(pending.transfer.sender), + gas_fee: Cow::Owned(pending.gas_fee), + } + } +} + +impl<'t> From<&'t PendingTransfer> for PendingTransferAppendix<'t> { + #[inline] + fn from(pending: &'t PendingTransfer) -> Self { + Self { + kind: Cow::Borrowed(&pending.transfer.kind), + sender: Cow::Borrowed(&pending.transfer.sender), + gas_fee: Cow::Borrowed(&pending.gas_fee), + } + } +} + +impl<'transfer> PendingTransferAppendix<'transfer> { + /// Calculate the checksum of this [`PendingTransferAppendix`]. + pub fn checksum(&self) -> HashDigest { + let serialized = self + .try_to_vec() + .expect("Serializing a PendingTransferAppendix should not fail"); + HashDigest::sha256(serialized) + } +} + /// A transfer message to be submitted to Ethereum /// to move assets from Namada across the bridge. #[derive( @@ -84,51 +178,84 @@ impl PendingTransfer { } } } + + /// Retrieve a reference to the appendix of this [`PendingTransfer`]. + #[inline] + pub fn appendix(&self) -> PendingTransferAppendix<'_> { + self.into() + } + + /// Retrieve the owned appendix of this [`PendingTransfer`]. + #[inline] + pub fn into_appendix(self) -> PendingTransferAppendix<'static> { + self.into() + } + + /// Craft a [`PendingTransfer`] from its constituents. + pub fn from_parts( + event: &TransferToEthereumEvent, + appendix: PendingTransferAppendix<'_>, + ) -> Self { + let transfer = TransferToEthereum { + kind: *appendix.kind, + asset: event.asset, + recipient: event.receiver, + sender: (*appendix.sender).clone(), + amount: event.amount, + }; + let gas_fee = (*appendix.gas_fee).clone(); + Self { transfer, gas_fee } + } } -impl From for ethbridge_structs::Erc20Transfer { - fn from(pending: PendingTransfer) -> Self { +impl From<&PendingTransfer> for ethbridge_structs::Erc20Transfer { + fn from(pending: &PendingTransfer) -> Self { + let HashDigest(namada_data_digest) = pending.appendix().checksum(); Self { - kind: pending.transfer.kind as u8, from: pending.transfer.asset.0.into(), to: pending.transfer.recipient.0.into(), amount: pending.transfer.amount.into(), - fee_from: pending.gas_fee.payer.to_string(), - fee: pending.gas_fee.amount.into(), - sender: pending.transfer.sender.to_string(), + namada_data_digest, + } + } +} + +impl From<&PendingTransfer> for TransferToEthereumEvent { + fn from(pending: &PendingTransfer) -> Self { + Self { + amount: pending.transfer.amount, + asset: pending.transfer.asset, + receiver: pending.transfer.recipient, + checksum: pending.appendix().checksum(), } } } -impl Encode<8> for PendingTransfer { - fn tokenize(&self) -> [Token; 8] { +impl Encode<6> for PendingTransfer { + fn tokenize(&self) -> [Token; 6] { // TODO: This version should be looked up from storage - let version = Token::Uint(1.into()); + let version = Token::Uint(VERSION.into()); let namespace = Token::String(NAMESPACE.into()); let from = Token::Address(self.transfer.asset.0.into()); - let fee = Token::Uint(self.gas_fee.amount.into()); let to = Token::Address(self.transfer.recipient.0.into()); let amount = Token::Uint(self.transfer.amount.into()); - let fee_from = Token::String(self.gas_fee.payer.to_string()); - let sender = Token::String(self.transfer.sender.to_string()); - [version, namespace, from, to, amount, fee_from, fee, sender] + let checksum = Token::FixedBytes(self.appendix().checksum().0.into()); + [version, namespace, from, to, amount, checksum] } } -impl From<&TransferToEthereumEvent> for PendingTransfer { - fn from(event: &TransferToEthereumEvent) -> Self { - let transfer = TransferToEthereum { - kind: event.kind, - asset: event.asset, - recipient: event.receiver, - sender: event.sender.clone(), - amount: event.amount, - }; - let gas_fee = GasFee { - amount: event.gas_amount, - payer: event.gas_payer.clone(), - }; - Self { transfer, gas_fee } +// TODO: test that encode for `PendingTransfer` and +// `TransferToEthereumEvent` yield the same keccak hash +impl Encode<6> for TransferToEthereumEvent { + fn tokenize(&self) -> [Token; 6] { + // TODO: This version should be looked up from storage + let version = Token::Uint(VERSION.into()); + let namespace = Token::String(NAMESPACE.into()); + let from = Token::Address(self.asset.0.into()); + let to = Token::Address(self.receiver.0.into()); + let amount = Token::Uint(self.amount.into()); + let checksum = Token::FixedBytes(self.checksum.0.into()); + [version, namespace, from, to, amount, checksum] } } diff --git a/core/src/types/ethereum_events.rs b/core/src/types/ethereum_events.rs index b896674a0cd..f7097e97496 100644 --- a/core/src/types/ethereum_events.rs +++ b/core/src/types/ethereum_events.rs @@ -13,6 +13,7 @@ use serde::{Deserialize, Serialize}; use crate::types::address::Address; use crate::types::eth_abi::Encode; +use crate::types::ethereum_structs::Erc20Transfer; use crate::types::hash::Hash; use crate::types::keccak::KeccakHash; use crate::types::storage::{DbKeySeg, KeySeg}; @@ -366,60 +367,6 @@ pub struct TransferToNamada { pub receiver: Address, } -/// Transfer to Ethereum kinds. -#[derive( - Copy, - Clone, - Debug, - PartialEq, - Eq, - Hash, - PartialOrd, - Ord, - BorshSerialize, - BorshDeserialize, - BorshSchema, - Serialize, - Deserialize, -)] -#[repr(u8)] -pub enum TransferToEthereumKind { - /// Transfer ERC20 assets from Namada to Ethereum. - /// - /// These transfers burn wrapped ERC20 assets in Namada, once - /// they have been confirmed. - Erc20 = Self::KIND_ERC20, - /// Refund non-usable tokens. - /// - /// These Bridge pool transfers should be crafted for assets - /// that have been transferred to Namada, that had either not - /// been whitelisted or whose token caps had been exceeded in - /// Namada at the time of the transfer. - Nut = Self::KIND_NUT, -} - -// XXX: keep these values in sync with the smart contracts -impl TransferToEthereumKind { - const KIND_ERC20: u8 = 0; - const KIND_NUT: u8 = 1; -} - -impl TryFrom for TransferToEthereumKind { - type Error = eyre::Error; - - fn try_from(kind: u8) -> Result { - match kind { - Self::KIND_ERC20 => Ok(Self::Erc20), - Self::KIND_NUT => Ok(Self::Nut), - _ => Err(eyre!( - "Only valid kinds are {} (ERC20) and {} (NUT)", - Self::KIND_ERC20, - Self::KIND_NUT - )), - } - } -} - /// An event transferring some kind of value from Namada to Ethereum #[derive( Clone, @@ -436,20 +383,40 @@ impl TryFrom for TransferToEthereumKind { Deserialize, )] pub struct TransferToEthereum { - /// The kind of transfer to Ethereum. - pub kind: TransferToEthereumKind, /// Quantity of wrapped Asset in the transfer pub amount: Amount, /// Address of the smart contract issuing the token pub asset: EthAddress, /// The address receiving assets on Ethereum pub receiver: EthAddress, - /// The amount of fees (in NAM) - pub gas_amount: Amount, - /// The address sending assets to Ethereum. - pub sender: Address, - /// The account of fee payer. - pub gas_payer: Address, + /// Checksum of all Namada specific fields, including, + /// but not limited to, whether it is a NUT transfer, + /// the address of the sender, etc + /// + /// It serves to uniquely identify an event stored under + /// the Bridge pool, in Namada + pub checksum: Hash, +} + +impl From for TransferToEthereum { + #[inline] + fn from(transfer: Erc20Transfer) -> Self { + Self { + amount: { + let uint = { + use crate::types::uint::Uint as NamadaUint; + let mut num_buf = [0; 32]; + transfer.amount.to_little_endian(&mut num_buf); + NamadaUint::from_little_endian(&num_buf) + }; + // this is infallible for a denom of 0 + Amount::from_uint(uint, 0).unwrap() + }, + asset: EthAddress(transfer.from.0), + receiver: EthAddress(transfer.to.0), + checksum: Hash(transfer.namada_data_digest), + } + } } #[cfg(test)] diff --git a/ethereum_bridge/src/protocol/transactions/ethereum_events/events.rs b/ethereum_bridge/src/protocol/transactions/ethereum_events/events.rs index d6408cc7cf7..65c8f832ca5 100644 --- a/ethereum_bridge/src/protocol/transactions/ethereum_events/events.rs +++ b/ethereum_bridge/src/protocol/transactions/ethereum_events/events.rs @@ -7,8 +7,7 @@ use borsh::BorshDeserialize; use eyre::{Result, WrapErr}; use namada_core::hints; use namada_core::ledger::eth_bridge::storage::bridge_pool::{ - get_nonce_key, get_pending_key, is_pending_transfer_key, - BRIDGE_POOL_ADDRESS, + get_nonce_key, is_pending_transfer_key, BRIDGE_POOL_ADDRESS, }; use namada_core::ledger::eth_bridge::storage::{ self as bridge_storage, wrapped_erc20s, @@ -370,11 +369,14 @@ where for (event, is_valid) in transfers.iter().zip(valid_transfers.iter().copied()) { - let pending_transfer = event.into(); - let key = get_pending_key(&pending_transfer); - if hints::unlikely(!wl_storage.has_key(&key)?) { + let (pending_transfer, key) = if let Some((pending, key)) = + wl_storage.ethbridge_queries().lookup_transfer_to_eth(event) + { + (pending, key) + } else { + hints::cold(); unreachable!("The transfer should exist in the bridge pool"); - } + }; if hints::likely(is_valid) { tracing::debug!( ?pending_transfer, @@ -612,6 +614,7 @@ mod tests { use assert_matches::assert_matches; use borsh::BorshSerialize; use eyre::Result; + use namada_core::ledger::eth_bridge::storage::bridge_pool::get_pending_key; use namada_core::ledger::parameters::{ update_epoch_parameter, EpochDuration, }; @@ -982,19 +985,10 @@ mod tests { let pending_keys: HashSet = pending_transfers.iter().map(get_pending_key).collect(); let relayer = gen_established_address("random"); - let mut transfers = vec![]; - for transfer in pending_transfers { - let transfer_to_eth = TransferToEthereum { - kind: transfer.transfer.kind, - amount: transfer.transfer.amount, - asset: transfer.transfer.asset, - receiver: transfer.transfer.recipient, - gas_amount: transfer.gas_fee.amount, - gas_payer: transfer.gas_fee.payer, - sender: transfer.transfer.sender, - }; - transfers.push(transfer_to_eth); - } + let transfers: Vec<_> = pending_transfers + .iter() + .map(TransferToEthereum::from) + .collect(); let event = EthereumEvent::TransfersToEthereum { nonce: arbitrary_nonce(), valid_transfers_map: transfers.iter().map(|_| true).collect(), @@ -1307,16 +1301,8 @@ mod tests { init_balance(&mut wl_storage, &pending_transfers); let (transfers, valid_transfers_map) = pending_transfers .into_iter() - .map(|transfer| { - let transfer_to_eth = TransferToEthereum { - kind: transfer.transfer.kind, - amount: transfer.transfer.amount, - asset: transfer.transfer.asset, - receiver: transfer.transfer.recipient, - gas_amount: transfer.gas_fee.amount, - gas_payer: transfer.gas_fee.payer, - sender: transfer.transfer.sender, - }; + .map(|ref transfer| { + let transfer_to_eth: TransferToEthereum = transfer.into(); (transfer_to_eth, true) }) .unzip(); @@ -1353,16 +1339,18 @@ mod tests { read_native_erc20_address(wl_storage).expect("Test failed"); let deltas = transfers .filter_map( - |TransferToEthereum { - kind, - asset, - amount, - .. - }| { + |event @ TransferToEthereum { asset, amount, .. }| { if asset == &native_erc20 { return None; } - let erc20_token = match kind { + let kind = { + let (pending, _) = wl_storage + .ethbridge_queries() + .lookup_transfer_to_eth(event) + .expect("Test failed"); + pending.transfer.kind + }; + let erc20_token = match &kind { eth_bridge_pool::TransferToEthereumKind::Erc20 => { wrapped_erc20s::token(asset) } @@ -1380,7 +1368,7 @@ mod tests { .read(&minted_balance_key(&erc20_token)) .expect("Test failed"); Some(Delta { - kind: *kind, + kind, asset: *asset, sent_amount: *amount, prev_balance, diff --git a/ethereum_bridge/src/storage/eth_bridge_queries.rs b/ethereum_bridge/src/storage/eth_bridge_queries.rs index 08fdcab2fa1..c30de9a536b 100644 --- a/ethereum_bridge/src/storage/eth_bridge_queries.rs +++ b/ethereum_bridge/src/storage/eth_bridge_queries.rs @@ -1,16 +1,19 @@ use borsh::{BorshDeserialize, BorshSerialize}; use namada_core::hints; -use namada_core::ledger::eth_bridge::storage::bridge_pool::{ - get_nonce_key, get_signed_root_key, +use namada_core::ledger::eth_bridge::storage::{ + active_key, bridge_pool, whitelist, }; -use namada_core::ledger::eth_bridge::storage::{active_key, whitelist}; use namada_core::ledger::storage; use namada_core::ledger::storage::{StoreType, WlStorage}; use namada_core::ledger::storage_api::StorageRead; use namada_core::types::address::Address; -use namada_core::types::ethereum_events::{EthAddress, GetEventNonce, Uint}; +use namada_core::types::eth_abi::Encode; +use namada_core::types::eth_bridge_pool::PendingTransfer; +use namada_core::types::ethereum_events::{ + EthAddress, GetEventNonce, TransferToEthereum, Uint, +}; use namada_core::types::keccak::KeccakHash; -use namada_core::types::storage::{BlockHeight, Epoch}; +use namada_core::types::storage::{BlockHeight, Epoch, Key as StorageKey}; use namada_core::types::token; use namada_core::types::vote_extensions::validator_set_update::{ EthAddrBook, ValidatorSetArgs, VotingPowersMap, VotingPowersMapExt, @@ -175,7 +178,7 @@ where &self .wl_storage .storage - .read(&get_nonce_key()) + .read(&bridge_pool::get_nonce_key()) .expect("Reading Bridge pool nonce shouldn't fail.") .0 .expect("Reading Bridge pool nonce shouldn't fail."), @@ -191,7 +194,7 @@ where .storage .db .read_subspace_val_with_height( - &get_nonce_key(), + &bridge_pool::get_nonce_key(), height, self.wl_storage.storage.get_last_block_height(), ) @@ -225,7 +228,7 @@ where self, ) -> Option<(BridgePoolRootProof, BlockHeight)> { self.wl_storage - .read_bytes(&get_signed_root_key()) + .read_bytes(&bridge_pool::get_signed_root_key()) .expect("Reading signed Bridge pool root shouldn't fail.") .map(|bytes| { BorshDeserialize::try_from_slice(&bytes).expect( @@ -488,6 +491,19 @@ where nut_amount: token::Amount::zero(), } } + + /// Given a [`TransferToEthereum`] event, look-up the corresponding + /// [`PendingTransfer`]. + pub fn lookup_transfer_to_eth( + self, + transfer: &TransferToEthereum, + ) -> Option<(PendingTransfer, StorageKey)> { + let pending_key = bridge_pool::get_key_from_hash(&transfer.keccak256()); + self.wl_storage + .read(&pending_key) + .expect("Reading from storage should not fail") + .zip(Some(pending_key)) + } } /// Number of tokens to mint after receiving a "transfer diff --git a/shared/src/ledger/eth_bridge/bridge_pool.rs b/shared/src/ledger/eth_bridge/bridge_pool.rs index 940524d34f8..9576b70baf4 100644 --- a/shared/src/ledger/eth_bridge/bridge_pool.rs +++ b/shared/src/ledger/eth_bridge/bridge_pool.rs @@ -1,5 +1,6 @@ //! Bridge pool SDK functionality. +use std::borrow::Cow; use std::cmp::Ordering; use std::collections::HashMap; use std::io::Write; @@ -17,7 +18,9 @@ use super::{block_on_eth_sync, eth_sync_or_exit, BlockOnEthSync}; use crate::eth_bridge::ethers::abi::AbiDecode; use crate::eth_bridge::structs::RelayProof; use crate::ledger::args; -use crate::ledger::queries::{Client, RPC}; +use crate::ledger::queries::{ + Client, GenBridgePoolProofReq, GenBridgePoolProofRsp, RPC, +}; use crate::ledger::rpc::{query_wasm_code_hash, validate_amount}; use crate::ledger::tx::{prepare_tx, Error}; use crate::proto::Tx; @@ -179,9 +182,8 @@ where /// bridge pool. async fn construct_bridge_pool_proof( client: &C, - transfers: &[KeccakHash], - relayer: Address, -) -> Halt> + args: GenBridgePoolProofReq<'_, '_>, +) -> Halt where C: Client + Sync, { @@ -196,8 +198,8 @@ where .into_iter() .filter_map(|(ref transfer, voting_power)| { if voting_power > FractionalVotingPower::ONE_THIRD { - let hash = PendingTransfer::from(transfer).keccak256(); - transfers.contains(&hash).then_some(hash) + let hash = transfer.keccak256(); + args.transfers.contains(&hash).then_some(hash) } else { None } @@ -234,7 +236,7 @@ where } } - let data = (transfers, relayer).try_to_vec().unwrap(); + let data = args.try_to_vec().unwrap(); let response = RPC .shell() .eth_bridge() @@ -242,7 +244,7 @@ where .await; response.map(|response| response.data).try_halt(|e| { - println!("Encountered error constructing proof:\n{:?}", e); + println!("Encountered error constructing proof:\n{e}"); }) } @@ -265,25 +267,29 @@ pub async fn construct_proof( where C: Client + Sync, { - let bp_proof_bytes = construct_bridge_pool_proof( + let GenBridgePoolProofRsp { + abi_encoded_proof: bp_proof_bytes, + appendices, + } = construct_bridge_pool_proof( client, - &args.transfers, - args.relayer.clone(), + GenBridgePoolProofReq { + transfers: args.transfers.as_slice().into(), + relayer: Cow::Borrowed(&args.relayer), + with_appendix: true, + }, ) .await?; - let bp_proof: RelayProof = - AbiDecode::decode(&bp_proof_bytes).try_halt(|error| { - println!("Unable to decode the generated proof: {:?}", error); - })?; let resp = BridgePoolProofResponse { hashes: args.transfers, relayer_address: args.relayer, - total_fees: bp_proof - .transfers - .iter() - .map(|t| t.fee.as_u64()) - .sum::() - .into(), + total_fees: appendices + .map(|appendices| { + appendices + .into_iter() + .map(|app| app.gas_fee.amount) + .sum::() + }) + .unwrap_or(Amount::zero()), abi_encoded_proof: bp_proof_bytes, }; println!("{}", serde_json::to_string(&resp).unwrap()); @@ -316,9 +322,18 @@ where eth_sync_or_exit(&*eth_client).await?; } - let bp_proof = - construct_bridge_pool_proof(nam_client, &args.transfers, args.relayer) - .await?; + let GenBridgePoolProofRsp { + abi_encoded_proof: bp_proof, + .. + } = construct_bridge_pool_proof( + nam_client, + GenBridgePoolProofReq { + transfers: Cow::Owned(args.transfers), + relayer: Cow::Owned(args.relayer), + with_appendix: false, + }, + ) + .await?; let bridge = match RPC .shell() .eth_bridge() @@ -468,8 +483,7 @@ mod recommendations { .transfer_to_ethereum_progress(client) .await .unwrap() - .keys() - .map(PendingTransfer::from) + .into_keys() .collect::>(); // get the signed bridge pool root so we can analyze the signatures diff --git a/shared/src/ledger/queries/mod.rs b/shared/src/ledger/queries/mod.rs index ce689e6325a..bcd9fc8c271 100644 --- a/shared/src/ledger/queries/mod.rs +++ b/shared/src/ledger/queries/mod.rs @@ -12,6 +12,9 @@ pub use types::{ }; use vp::{Vp, VP}; +pub use self::shell::eth_bridge::{ + Erc20FlowControl, GenBridgePoolProofReq, GenBridgePoolProofRsp, +}; use super::storage::traits::StorageHasher; use super::storage::{DBIter, DB}; use super::storage_api; diff --git a/shared/src/ledger/queries/shell.rs b/shared/src/ledger/queries/shell.rs index 94412f1a154..b1e243f74b4 100644 --- a/shared/src/ledger/queries/shell.rs +++ b/shared/src/ledger/queries/shell.rs @@ -1,4 +1,4 @@ -mod eth_bridge; +pub(super) mod eth_bridge; use borsh::{BorshDeserialize, BorshSerialize}; use masp_primitives::asset_type::AssetType; diff --git a/shared/src/ledger/queries/shell/eth_bridge.rs b/shared/src/ledger/queries/shell/eth_bridge.rs index 44ad306789d..0dbd76b3760 100644 --- a/shared/src/ledger/queries/shell/eth_bridge.rs +++ b/shared/src/ledger/queries/shell/eth_bridge.rs @@ -1,5 +1,6 @@ //! Ethereum bridge related shell queries. +use std::borrow::Cow; use std::collections::HashMap; use std::str::FromStr; @@ -11,6 +12,7 @@ use namada_core::ledger::storage_api::{ self, CustomError, ResultExt, StorageRead, }; use namada_core::types::address::Address; +use namada_core::types::eth_bridge_pool::PendingTransferAppendix; use namada_core::types::ethereum_events::{ EthAddress, EthereumEvent, TransferToEthereum, }; @@ -34,6 +36,7 @@ use namada_ethereum_bridge::storage::{ }; use namada_proof_of_stake::pos_queries::PosQueries; +use crate::eth_bridge::ethers::abi::AbiDecode; use crate::ledger::queries::{EncodedResponseQuery, RequestCtx, RequestQuery}; use crate::types::eth_abi::{Encode, EncodeCell}; use crate::types::eth_bridge_pool::PendingTransfer; @@ -55,7 +58,39 @@ pub struct Erc20FlowControl { cap: Amount, } -pub type RelayProofBytes = Vec; +/// Request data to pass to `generate_bridge_pool_proof`. +#[derive(Debug, Clone, Eq, PartialEq, BorshSerialize, BorshDeserialize)] +pub struct GenBridgePoolProofReq<'transfers, 'relayer> { + /// The hashes of the transfers to be relayed. + pub transfers: Cow<'transfers, [KeccakHash]>, + /// The address of the relayer to compensate. + pub relayer: Cow<'relayer, Address>, + /// Whether to return the appendix of a [`PendingTransfer`]. + pub with_appendix: bool, +} + +/// Response data returned by `generate_bridge_pool_proof`. +#[derive(Debug, Clone, Eq, PartialEq, BorshSerialize, BorshDeserialize)] +pub struct GenBridgePoolProofRsp { + /// Ethereum ABI encoded [`RelayProof`]. + pub abi_encoded_proof: Vec, + /// Appendix data of all requested pending transfers. + pub appendices: Option>>, +} + +impl GenBridgePoolProofRsp { + /// Retrieve all [`PendingTransfer`] instances returned from the RPC server. + pub fn pending_transfers(self) -> impl Iterator { + RelayProof::decode(&self.abi_encoded_proof) + .into_iter() + .flat_map(|proof| proof.transfers) + .zip(self.appendices.into_iter().flatten()) + .map(|(event, appendix)| { + let event: TransferToEthereum = event.into(); + PendingTransfer::from_parts(&event, appendix) + }) + } +} router! {ETH_BRIDGE, // Get the current contents of the Ethereum bridge pool @@ -70,12 +105,12 @@ router! {ETH_BRIDGE, // Generate a merkle proof for the inclusion of requested // transfers in the Ethereum bridge pool ( "pool" / "proof" ) - -> RelayProofBytes = (with_options generate_bridge_pool_proof), + -> GenBridgePoolProofRsp = (with_options generate_bridge_pool_proof), // Iterates over all ethereum events and returns the amount of // voting power backing each `TransferToEthereum` event. ( "pool" / "transfer_to_eth_progress" ) - -> HashMap + -> HashMap = transfer_to_ethereum_progress, // Request a proof of a validator set signed off for @@ -290,8 +325,11 @@ where D: 'static + DB + for<'iter> DBIter<'iter> + Sync, H: 'static + StorageHasher + Sync, { - if let Ok((transfer_hashes, relayer)) = - <(Vec, Address)>::try_from_slice(request.data.as_slice()) + if let Ok(GenBridgePoolProofReq { + transfers: transfer_hashes, + relayer, + with_appendix, + }) = BorshDeserialize::try_from_slice(request.data.as_slice()) { // get the latest signed merkle root of the Ethereum bridge pool let (signed_root, height) = ctx @@ -335,14 +373,19 @@ where .into(), ))); } - let transfers = values - .iter() - .map(|bytes| { - PendingTransfer::try_from_slice(bytes) - .expect("Deserializing storage shouldn't fail") - .into() - }) - .collect(); + let (transfers, appendices) = values.iter().fold( + (vec![], vec![]), + |(mut transfers, mut appendices), bytes| { + let pending = PendingTransfer::try_from_slice(bytes) + .expect("Deserializing storage shouldn't fail"); + let eth_transfer = (&pending).into(); + if with_appendix { + appendices.push(pending.into_appendix()); + } + transfers.push(eth_transfer); + (transfers, appendices) + }, + ); // get the membership proof match tree.get_sub_tree_existence_proof( &keys, @@ -353,7 +396,7 @@ where .wl_storage .ethbridge_queries() .get_validator_set_args(None); - let data = RelayProof { + let relay_proof = RelayProof { validator_set_args: validator_args.into(), signatures: sort_sigs( &voting_powers, @@ -366,9 +409,13 @@ where batch_nonce: signed_root.data.1.into(), relayer_address: relayer.to_string(), }; - let data = ethers::abi::AbiEncode::encode(data) - .try_to_vec() - .expect("Serializing a relay proof should not fail."); + let rsp = GenBridgePoolProofRsp { + abi_encoded_proof: ethers::abi::AbiEncode::encode( + relay_proof, + ), + appendices: with_appendix.then_some(appendices), + }; + let data = rsp.try_to_vec().into_storage_result()?; Ok(EncodedResponseQuery { data, ..Default::default() @@ -389,7 +436,7 @@ where /// backing each `TransferToEthereum` event. fn transfer_to_ethereum_progress( ctx: RequestCtx<'_, D, H>, -) -> storage_api::Result> +) -> storage_api::Result> where D: 'static + DB + for<'iter> DBIter<'iter> + Sync, H: 'static + StorageHasher + Sync, @@ -444,6 +491,12 @@ where ) .average_voting_power(ctx.wl_storage); for transfer in transfers { + let key = get_key_from_hash(&transfer.keccak256()); + let transfer = ctx + .wl_storage + .read::(&key) + .into_storage_result()? + .expect("The transfer must be present in storage"); pending_events.insert(transfer, voting_power); } } @@ -974,9 +1027,13 @@ mod test_ethbridge_router { .generate_bridge_pool_proof( &client, Some( - (vec![transfer.keccak256()], bertha_address()) - .try_to_vec() - .expect("Test failed"), + GenBridgePoolProofReq { + transfers: vec![transfer.keccak256()].into(), + relayer: Cow::Owned(bertha_address()), + with_appendix: false, + } + .try_to_vec() + .expect("Test failed"), ), None, false, @@ -999,7 +1056,7 @@ mod test_ethbridge_router { let data = RelayProof { validator_set_args: validator_args.into(), signatures: sort_sigs(&voting_powers, &signed_root.signatures), - transfers: vec![transfer.into()], + transfers: vec![(&transfer).into()], pool_root: signed_root.data.0.0, proof: proof.proof.into_iter().map(|hash| hash.0).collect(), proof_flags: proof.flags, @@ -1007,12 +1064,11 @@ mod test_ethbridge_router { relayer_address: bertha_address().to_string(), }; let proof = ethers::abi::AbiEncode::encode(data); - assert_eq!(proof, resp.data); + assert_eq!(proof, resp.data.abi_encoded_proof); } - /// Test if the merkle tree including a transfer - /// has had its root signed, then we cannot generate - /// a proof. + /// Test if the merkle tree including a transfer has not had its + /// root signed, then we cannot generate a proof. #[tokio::test] async fn test_cannot_get_proof() { let mut client = TestClient::new(RPC); @@ -1090,9 +1146,13 @@ mod test_ethbridge_router { .generate_bridge_pool_proof( &client, Some( - (vec![transfer2.keccak256()], bertha_address()) - .try_to_vec() - .expect("Test failed"), + GenBridgePoolProofReq { + transfers: vec![transfer2.keccak256()].into(), + relayer: Cow::Owned(bertha_address()), + with_appendix: false, + } + .try_to_vec() + .expect("Test failed"), ), None, false, @@ -1204,16 +1264,8 @@ mod test_ethbridge_router { ) .expect("Test failed"); - let event_transfer = - namada_core::types::ethereum_events::TransferToEthereum { - kind: transfer.transfer.kind, - asset: transfer.transfer.asset, - receiver: transfer.transfer.recipient, - amount: transfer.transfer.amount, - gas_payer: transfer.gas_fee.payer.clone(), - gas_amount: transfer.gas_fee.amount, - sender: transfer.transfer.sender.clone(), - }; + let event_transfer: namada_core::types::ethereum_events::TransferToEthereum + = (&transfer).into(); let eth_event = EthereumEvent::TransfersToEthereum { nonce: Default::default(), transfers: vec![event_transfer.clone()], @@ -1274,10 +1326,8 @@ mod test_ethbridge_router { .transfer_to_ethereum_progress(&client) .await .unwrap(); - let expected: HashMap< - namada_core::types::ethereum_events::TransferToEthereum, - FractionalVotingPower, - > = [(event_transfer, voting_power)].into_iter().collect(); + let expected: HashMap = + [(transfer, voting_power)].into_iter().collect(); assert_eq!(expected, resp); } @@ -1354,9 +1404,13 @@ mod test_ethbridge_router { .generate_bridge_pool_proof( &client, Some( - vec![(transfer.keccak256(), bertha_address())] - .try_to_vec() - .expect("Test failed"), + GenBridgePoolProofReq { + transfers: vec![transfer.keccak256()].into(), + relayer: Cow::Owned(bertha_address()), + with_appendix: false, + } + .try_to_vec() + .expect("Test failed"), ), None, false, @@ -1377,9 +1431,13 @@ mod test_ethbridge_router { .generate_bridge_pool_proof( &client, Some( - vec![transfer.keccak256()] - .try_to_vec() - .expect("Test failed"), + GenBridgePoolProofReq { + transfers: vec![transfer.keccak256()].into(), + relayer: Cow::Owned(bertha_address()), + with_appendix: false, + } + .try_to_vec() + .expect("Test failed"), ), None, false, From 91b2fef6e48940d3e29a8618005ffe7cc7c88f47 Mon Sep 17 00:00:00 2001 From: Tiago Carvalho Date: Mon, 7 Aug 2023 14:32:08 +0100 Subject: [PATCH 3/4] Test pending transfers and events have the same ABI encoding --- core/src/types/eth_bridge_pool.rs | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/core/src/types/eth_bridge_pool.rs b/core/src/types/eth_bridge_pool.rs index 4f12eec6197..3a9b185e9fd 100644 --- a/core/src/types/eth_bridge_pool.rs +++ b/core/src/types/eth_bridge_pool.rs @@ -244,8 +244,6 @@ impl Encode<6> for PendingTransfer { } } -// TODO: test that encode for `PendingTransfer` and -// `TransferToEthereumEvent` yield the same keccak hash impl Encode<6> for TransferToEthereumEvent { fn tokenize(&self) -> [Token; 6] { // TODO: This version should be looked up from storage @@ -292,3 +290,30 @@ pub struct GasFee { /// The account of fee payer. pub payer: Address, } + +#[cfg(test)] +mod test_eth_bridge_pool_types { + use super::*; + use crate::types::address::testing::established_address_1; + + /// Test that [`PendingTransfer`] and [`TransferToEthereum`] + /// have the same keccak hash, after being ABI encoded. + #[test] + fn test_same_keccak_hash() { + let pending = PendingTransfer { + transfer: TransferToEthereum { + kind: TransferToEthereumKind::Erc20, + amount: 10u64.into(), + asset: EthAddress([0xaa; 20]), + recipient: EthAddress([0xbb; 20]), + sender: established_address_1(), + }, + gas_fee: GasFee { + amount: 10u64.into(), + payer: established_address_1(), + }, + }; + let event: TransferToEthereumEvent = (&pending).into(); + assert_eq!(pending.keccak256(), event.keccak256()); + } +} From 5678ee7cabd4d27ce94ef3ef62b1fa5e9a8445be Mon Sep 17 00:00:00 2001 From: Tiago Carvalho Date: Mon, 7 Aug 2023 14:48:17 +0100 Subject: [PATCH 4/4] Changelog for #1789 --- .changelog/unreleased/features/1789-update-ethbridge-rs.md | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .changelog/unreleased/features/1789-update-ethbridge-rs.md diff --git a/.changelog/unreleased/features/1789-update-ethbridge-rs.md b/.changelog/unreleased/features/1789-update-ethbridge-rs.md new file mode 100644 index 00000000000..4fe862e522f --- /dev/null +++ b/.changelog/unreleased/features/1789-update-ethbridge-rs.md @@ -0,0 +1,2 @@ +- Update ethbridge-rs to v0.22.0 + ([\#1789](https://github.com/anoma/namada/pull/1789)) \ No newline at end of file