From 5501de1f3af12589e6193bb881a1de49cccc7dab Mon Sep 17 00:00:00 2001 From: Luke Parker Date: Fri, 10 May 2024 13:33:56 -0400 Subject: [PATCH 1/7] Update to the latest alloy Also makes various tweaks as necessary. --- Cargo.lock | 63 ++++++++++--------- coins/ethereum/Cargo.toml | 15 +++-- .../alloy-simple-request-transport/Cargo.toml | 4 +- coins/ethereum/src/crypto.rs | 5 +- coins/ethereum/src/erc20.rs | 23 ++----- coins/ethereum/src/lib.rs | 8 ++- coins/ethereum/src/router.rs | 31 ++++++--- coins/ethereum/src/tests/mod.rs | 11 +++- coins/ethereum/src/tests/schnorr.rs | 4 +- 9 files changed, 91 insertions(+), 73 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 57e438de..66212c4a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -102,7 +102,7 @@ checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" [[package]] name = "alloy-consensus" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=037dd4b20ec8533d6b6d5cf5e9489bbb182c18c6#037dd4b20ec8533d6b6d5cf5e9489bbb182c18c6" +source = "git+https://github.com/alloy-rs/alloy?rev=b79db21734cffddc11753fe62ba571565c896f42#b79db21734cffddc11753fe62ba571565c896f42" dependencies = [ "alloy-eips", "alloy-primitives", @@ -110,7 +110,6 @@ dependencies = [ "alloy-serde", "c-kzg", "serde", - "sha2", ] [[package]] @@ -125,7 +124,7 @@ dependencies = [ [[package]] name = "alloy-eips" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=037dd4b20ec8533d6b6d5cf5e9489bbb182c18c6#037dd4b20ec8533d6b6d5cf5e9489bbb182c18c6" +source = "git+https://github.com/alloy-rs/alloy?rev=b79db21734cffddc11753fe62ba571565c896f42#b79db21734cffddc11753fe62ba571565c896f42" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -133,23 +132,25 @@ dependencies = [ "c-kzg", "once_cell", "serde", + "sha2", ] [[package]] name = "alloy-genesis" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=037dd4b20ec8533d6b6d5cf5e9489bbb182c18c6#037dd4b20ec8533d6b6d5cf5e9489bbb182c18c6" +source = "git+https://github.com/alloy-rs/alloy?rev=b79db21734cffddc11753fe62ba571565c896f42#b79db21734cffddc11753fe62ba571565c896f42" dependencies = [ "alloy-primitives", "alloy-serde", "serde", + "serde_json", ] [[package]] name = "alloy-json-abi" -version = "0.7.0" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83a35ddfd27576474322a5869e4c123e5f3e7b2177297c18e4e82ea501cb125b" +checksum = "786689872ec4e7d354810ab0dffd48bb40b838c047522eb031cbd47d15634849" dependencies = [ "alloy-primitives", "alloy-sol-type-parser", @@ -159,7 +160,7 @@ dependencies = [ [[package]] name = "alloy-json-rpc" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=037dd4b20ec8533d6b6d5cf5e9489bbb182c18c6#037dd4b20ec8533d6b6d5cf5e9489bbb182c18c6" +source = "git+https://github.com/alloy-rs/alloy?rev=b79db21734cffddc11753fe62ba571565c896f42#b79db21734cffddc11753fe62ba571565c896f42" dependencies = [ "alloy-primitives", "serde", @@ -171,7 +172,7 @@ dependencies = [ [[package]] name = "alloy-network" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=037dd4b20ec8533d6b6d5cf5e9489bbb182c18c6#037dd4b20ec8533d6b6d5cf5e9489bbb182c18c6" +source = "git+https://github.com/alloy-rs/alloy?rev=b79db21734cffddc11753fe62ba571565c896f42#b79db21734cffddc11753fe62ba571565c896f42" dependencies = [ "alloy-consensus", "alloy-eips", @@ -179,6 +180,7 @@ dependencies = [ "alloy-primitives", "alloy-rpc-types", "alloy-signer", + "alloy-sol-types", "async-trait", "futures-utils-wasm", "thiserror", @@ -187,7 +189,7 @@ dependencies = [ [[package]] name = "alloy-node-bindings" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=037dd4b20ec8533d6b6d5cf5e9489bbb182c18c6#037dd4b20ec8533d6b6d5cf5e9489bbb182c18c6" +source = "git+https://github.com/alloy-rs/alloy?rev=b79db21734cffddc11753fe62ba571565c896f42#b79db21734cffddc11753fe62ba571565c896f42" dependencies = [ "alloy-genesis", "alloy-primitives", @@ -201,9 +203,9 @@ dependencies = [ [[package]] name = "alloy-primitives" -version = "0.7.0" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99bbad0a6b588ef4aec1b5ddbbfdacd9ef04e00b979617765b03174318ee1f3a" +checksum = "525448f6afc1b70dd0f9d0a8145631bf2f5e434678ab23ab18409ca264cae6b3" dependencies = [ "alloy-rlp", "bytes", @@ -224,7 +226,7 @@ dependencies = [ [[package]] name = "alloy-provider" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=037dd4b20ec8533d6b6d5cf5e9489bbb182c18c6#037dd4b20ec8533d6b6d5cf5e9489bbb182c18c6" +source = "git+https://github.com/alloy-rs/alloy?rev=b79db21734cffddc11753fe62ba571565c896f42#b79db21734cffddc11753fe62ba571565c896f42" dependencies = [ "alloy-eips", "alloy-json-rpc", @@ -271,7 +273,7 @@ dependencies = [ [[package]] name = "alloy-rpc-client" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=037dd4b20ec8533d6b6d5cf5e9489bbb182c18c6#037dd4b20ec8533d6b6d5cf5e9489bbb182c18c6" +source = "git+https://github.com/alloy-rs/alloy?rev=b79db21734cffddc11753fe62ba571565c896f42#b79db21734cffddc11753fe62ba571565c896f42" dependencies = [ "alloy-json-rpc", "alloy-transport", @@ -289,7 +291,7 @@ dependencies = [ [[package]] name = "alloy-rpc-types" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=037dd4b20ec8533d6b6d5cf5e9489bbb182c18c6#037dd4b20ec8533d6b6d5cf5e9489bbb182c18c6" +source = "git+https://github.com/alloy-rs/alloy?rev=b79db21734cffddc11753fe62ba571565c896f42#b79db21734cffddc11753fe62ba571565c896f42" dependencies = [ "alloy-consensus", "alloy-eips", @@ -307,7 +309,7 @@ dependencies = [ [[package]] name = "alloy-rpc-types-trace" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=037dd4b20ec8533d6b6d5cf5e9489bbb182c18c6#037dd4b20ec8533d6b6d5cf5e9489bbb182c18c6" +source = "git+https://github.com/alloy-rs/alloy?rev=b79db21734cffddc11753fe62ba571565c896f42#b79db21734cffddc11753fe62ba571565c896f42" dependencies = [ "alloy-primitives", "alloy-rpc-types", @@ -319,7 +321,7 @@ dependencies = [ [[package]] name = "alloy-serde" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=037dd4b20ec8533d6b6d5cf5e9489bbb182c18c6#037dd4b20ec8533d6b6d5cf5e9489bbb182c18c6" +source = "git+https://github.com/alloy-rs/alloy?rev=b79db21734cffddc11753fe62ba571565c896f42#b79db21734cffddc11753fe62ba571565c896f42" dependencies = [ "alloy-primitives", "serde", @@ -329,7 +331,7 @@ dependencies = [ [[package]] name = "alloy-signer" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=037dd4b20ec8533d6b6d5cf5e9489bbb182c18c6#037dd4b20ec8533d6b6d5cf5e9489bbb182c18c6" +source = "git+https://github.com/alloy-rs/alloy?rev=b79db21734cffddc11753fe62ba571565c896f42#b79db21734cffddc11753fe62ba571565c896f42" dependencies = [ "alloy-primitives", "async-trait", @@ -352,9 +354,9 @@ dependencies = [ [[package]] name = "alloy-sol-macro" -version = "0.7.0" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "452d929748ac948a10481fff4123affead32c553cf362841c5103dd508bdfc16" +checksum = "89c80a2cb97e7aa48611cbb63950336f9824a174cdf670527cc6465078a26ea1" dependencies = [ "alloy-json-abi", "alloy-sol-macro-input", @@ -371,9 +373,9 @@ dependencies = [ [[package]] name = "alloy-sol-macro-input" -version = "0.7.0" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df64e094f6d2099339f9e82b5b38440b159757b6920878f28316243f8166c8d1" +checksum = "c58894b58ac50979eeac6249661991ac40b9d541830d9a725f7714cc9ef08c23" dependencies = [ "alloy-json-abi", "const-hex", @@ -388,18 +390,18 @@ dependencies = [ [[package]] name = "alloy-sol-type-parser" -version = "0.7.0" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "715f4d09a330cc181fc7c361b5c5c2766408fa59a0bac60349dcb7baabd404cc" +checksum = "7da8e71ea68e780cc203919e03f69f59e7afe92d2696fb1dcb6662f61e4031b6" dependencies = [ "winnow 0.6.6", ] [[package]] name = "alloy-sol-types" -version = "0.7.0" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43bc2d6dfc2a19fd56644494479510f98b1ee929e04cf0d4aa45e98baa3e545b" +checksum = "399287f68d1081ed8b1f4903c49687658b95b142207d7cb4ae2f4813915343ef" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -410,7 +412,7 @@ dependencies = [ [[package]] name = "alloy-transport" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=037dd4b20ec8533d6b6d5cf5e9489bbb182c18c6#037dd4b20ec8533d6b6d5cf5e9489bbb182c18c6" +source = "git+https://github.com/alloy-rs/alloy?rev=b79db21734cffddc11753fe62ba571565c896f42#b79db21734cffddc11753fe62ba571565c896f42" dependencies = [ "alloy-json-rpc", "base64 0.22.0", @@ -428,7 +430,7 @@ dependencies = [ [[package]] name = "alloy-transport-http" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=037dd4b20ec8533d6b6d5cf5e9489bbb182c18c6#037dd4b20ec8533d6b6d5cf5e9489bbb182c18c6" +source = "git+https://github.com/alloy-rs/alloy?rev=b79db21734cffddc11753fe62ba571565c896f42#b79db21734cffddc11753fe62ba571565c896f42" dependencies = [ "alloy-transport", "url", @@ -2331,6 +2333,7 @@ version = "0.1.0" dependencies = [ "alloy-consensus", "alloy-core", + "alloy-network", "alloy-node-bindings", "alloy-provider", "alloy-rpc-client", @@ -3790,7 +3793,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19" dependencies = [ "cfg-if", - "windows-targets 0.48.5", + "windows-targets 0.52.4", ] [[package]] @@ -9341,9 +9344,9 @@ dependencies = [ [[package]] name = "syn-solidity" -version = "0.7.0" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4497156948bd342b52038035a6fa514a89626e37af9d2c52a5e8d8ebcc7ee479" +checksum = "5aa0cefd02f532035d83cfec82647c6eb53140b0485220760e669f4bad489e36" dependencies = [ "paste", "proc-macro2", diff --git a/coins/ethereum/Cargo.toml b/coins/ethereum/Cargo.toml index 4bb92fe4..dc30764e 100644 --- a/coins/ethereum/Cargo.toml +++ b/coins/ethereum/Cargo.toml @@ -29,18 +29,21 @@ frost = { package = "modular-frost", path = "../../crypto/frost", default-featur alloy-core = { version = "0.7", default-features = false } alloy-sol-types = { version = "0.7", default-features = false, features = ["json"] } -alloy-consensus = { git = "https://github.com/alloy-rs/alloy", rev = "037dd4b20ec8533d6b6d5cf5e9489bbb182c18c6", default-features = false, features = ["k256"] } -alloy-rpc-types = { git = "https://github.com/alloy-rs/alloy", rev = "037dd4b20ec8533d6b6d5cf5e9489bbb182c18c6", default-features = false } -alloy-rpc-client = { git = "https://github.com/alloy-rs/alloy", rev = "037dd4b20ec8533d6b6d5cf5e9489bbb182c18c6", default-features = false } +alloy-consensus = { git = "https://github.com/alloy-rs/alloy", rev = "b79db21734cffddc11753fe62ba571565c896f42", default-features = false, features = ["k256"] } +alloy-network = { git = "https://github.com/alloy-rs/alloy", rev = "b79db21734cffddc11753fe62ba571565c896f42", default-features = false } +alloy-rpc-types = { git = "https://github.com/alloy-rs/alloy", rev = "b79db21734cffddc11753fe62ba571565c896f42", default-features = false } +alloy-rpc-client = { git = "https://github.com/alloy-rs/alloy", rev = "b79db21734cffddc11753fe62ba571565c896f42", default-features = false } alloy-simple-request-transport = { path = "./alloy-simple-request-transport", default-features = false } -alloy-provider = { git = "https://github.com/alloy-rs/alloy", rev = "037dd4b20ec8533d6b6d5cf5e9489bbb182c18c6", default-features = false } +alloy-provider = { git = "https://github.com/alloy-rs/alloy", rev = "b79db21734cffddc11753fe62ba571565c896f42", default-features = false } + +alloy-node-bindings = { git = "https://github.com/alloy-rs/alloy", rev = "b79db21734cffddc11753fe62ba571565c896f42", default-features = false, optional = true } [dev-dependencies] frost = { package = "modular-frost", path = "../../crypto/frost", default-features = false, features = ["tests"] } tokio = { version = "1", features = ["macros"] } -alloy-node-bindings = { git = "https://github.com/alloy-rs/alloy", rev = "037dd4b20ec8533d6b6d5cf5e9489bbb182c18c6", default-features = false } +alloy-node-bindings = { git = "https://github.com/alloy-rs/alloy", rev = "b79db21734cffddc11753fe62ba571565c896f42", default-features = false } [features] -tests = [] +tests = ["alloy-node-bindings"] diff --git a/coins/ethereum/alloy-simple-request-transport/Cargo.toml b/coins/ethereum/alloy-simple-request-transport/Cargo.toml index 115998e4..0d9ea6b8 100644 --- a/coins/ethereum/alloy-simple-request-transport/Cargo.toml +++ b/coins/ethereum/alloy-simple-request-transport/Cargo.toml @@ -21,8 +21,8 @@ tower = "0.4" serde_json = { version = "1", default-features = false } simple-request = { path = "../../../common/request", default-features = false } -alloy-json-rpc = { git = "https://github.com/alloy-rs/alloy", rev = "037dd4b20ec8533d6b6d5cf5e9489bbb182c18c6", default-features = false } -alloy-transport = { git = "https://github.com/alloy-rs/alloy", rev = "037dd4b20ec8533d6b6d5cf5e9489bbb182c18c6", default-features = false } +alloy-json-rpc = { git = "https://github.com/alloy-rs/alloy", rev = "b79db21734cffddc11753fe62ba571565c896f42", default-features = false } +alloy-transport = { git = "https://github.com/alloy-rs/alloy", rev = "b79db21734cffddc11753fe62ba571565c896f42", default-features = false } [features] default = ["tls"] diff --git a/coins/ethereum/src/crypto.rs b/coins/ethereum/src/crypto.rs index ca228eb5..6ea6a0b0 100644 --- a/coins/ethereum/src/crypto.rs +++ b/coins/ethereum/src/crypto.rs @@ -31,7 +31,10 @@ pub fn address(point: &ProjectivePoint) -> [u8; 20] { keccak256(&encoded_point.as_ref()[1 .. 65])[12 ..].try_into().unwrap() } -pub(crate) fn deterministically_sign(tx: &TxLegacy) -> Signed { +/// Deterministically sign a transaction. +/// +/// This function panics if passed a transaction with a non-None chain ID. +pub fn deterministically_sign(tx: &TxLegacy) -> Signed { assert!( tx.chain_id.is_none(), "chain ID was Some when deterministically signing a TX (causing a non-deterministic signer)" diff --git a/coins/ethereum/src/erc20.rs b/coins/ethereum/src/erc20.rs index 86bd1b2d..1d874403 100644 --- a/coins/ethereum/src/erc20.rs +++ b/coins/ethereum/src/erc20.rs @@ -4,7 +4,7 @@ use alloy_core::primitives::{Address, B256, U256}; use alloy_sol_types::{SolInterface, SolEvent}; -use alloy_rpc_types::{BlockNumberOrTag, Filter}; +use alloy_rpc_types::Filter; use alloy_simple_request_transport::SimpleRequest; use alloy_provider::{Provider, RootProvider}; @@ -25,22 +25,8 @@ pub struct TopLevelErc20Transfer { pub struct Erc20(Arc>, Address); impl Erc20 { /// Construct a new view of the specified ERC20 contract. - /// - /// This checks a contract is deployed at that address yet does not check the contract is - /// actually an ERC20. - pub async fn new( - provider: Arc>, - address: [u8; 20], - ) -> Result, Error> { - let code = provider - .get_code_at(address.into(), BlockNumberOrTag::Finalized.into()) - .await - .map_err(|_| Error::ConnectionError)?; - // Contract has yet to be deployed - if code.is_empty() { - return Ok(None); - } - Ok(Some(Self(provider.clone(), Address::from(&address)))) + pub fn new(provider: Arc>, address: [u8; 20]) -> Self { + Self(provider, Address::from(&address)) } pub async fn top_level_transfers( @@ -65,7 +51,8 @@ impl Erc20 { } let tx_id = log.transaction_hash.ok_or(Error::ConnectionError)?; - let tx = self.0.get_transaction_by_hash(tx_id).await.map_err(|_| Error::ConnectionError)?; + let tx = + self.0.get_transaction_by_hash(tx_id).await.ok().flatten().ok_or(Error::ConnectionError)?; // If this is a top-level call... if tx.to == Some(self.1) { diff --git a/coins/ethereum/src/lib.rs b/coins/ethereum/src/lib.rs index 8d4a5312..eda54c72 100644 --- a/coins/ethereum/src/lib.rs +++ b/coins/ethereum/src/lib.rs @@ -1,8 +1,10 @@ use thiserror::Error; pub use alloy_core; -pub use alloy_consensus; +pub use alloy_sol_types; +pub use alloy_consensus; +pub use alloy_network; pub use alloy_rpc_types; pub use alloy_simple_request_transport; pub use alloy_rpc_client; @@ -18,8 +20,8 @@ pub mod router; pub mod machine; -#[cfg(test)] -mod tests; +#[cfg(any(test, feature = "tests"))] +pub mod tests; #[derive(Clone, Copy, PartialEq, Eq, Debug, Error)] pub enum Error { diff --git a/coins/ethereum/src/router.rs b/coins/ethereum/src/router.rs index d2750a02..8d46b24f 100644 --- a/coins/ethereum/src/router.rs +++ b/coins/ethereum/src/router.rs @@ -159,11 +159,12 @@ impl Router { #[cfg(test)] pub async fn serai_key(&self, at: [u8; 32]) -> Result { let call = TransactionRequest::default() - .to(Some(self.1)) + .to(self.1) .input(TransactionInput::new(abi::seraiKeyCall::new(()).abi_encode().into())); let bytes = self .0 - .call(&call, Some(BlockId::Hash(B256::from(at).into()))) + .call(&call) + .block(BlockId::Hash(B256::from(at).into())) .await .map_err(|_| Error::ConnectionError)?; let res = @@ -197,11 +198,12 @@ impl Router { #[cfg(test)] pub async fn nonce(&self, at: [u8; 32]) -> Result { let call = TransactionRequest::default() - .to(Some(self.1)) + .to(self.1) .input(TransactionInput::new(abi::nonceCall::new(()).abi_encode().into())); let bytes = self .0 - .call(&call, Some(BlockId::Hash(B256::from(at).into()))) + .call(&call) + .block(BlockId::Hash(B256::from(at).into())) .await .map_err(|_| Error::ConnectionError)?; let res = @@ -229,10 +231,13 @@ impl Router { } } - pub async fn key_at_end_of_block(&self, block: u64) -> Result { + pub async fn key_at_end_of_block(&self, block: u64) -> Result, Error> { let filter = Filter::new().from_block(0).to_block(block).address(self.1); let filter = filter.event_signature(SeraiKeyUpdated::SIGNATURE_HASH); let all_keys = self.0.get_logs(&filter).await.map_err(|_| Error::ConnectionError)?; + if all_keys.is_empty() { + return Ok(None); + }; let last_key_x_coordinate_log = all_keys.last().ok_or(Error::ConnectionError)?; let last_key_x_coordinate = last_key_x_coordinate_log @@ -246,7 +251,9 @@ impl Router { compressed_point[0] = u8::from(sec1::Tag::CompressedEvenY); compressed_point[1 ..].copy_from_slice(last_key_x_coordinate.as_slice()); - Option::from(ProjectivePoint::from_bytes(&compressed_point)).ok_or(Error::ConnectionError) + let key = + Option::from(ProjectivePoint::from_bytes(&compressed_point)).ok_or(Error::ConnectionError)?; + Ok(Some(key)) } pub async fn in_instructions( @@ -254,7 +261,9 @@ impl Router { block: u64, allowed_tokens: &HashSet<[u8; 20]>, ) -> Result, Error> { - let key_at_end_of_block = self.key_at_end_of_block(block).await?; + let Some(key_at_end_of_block) = self.key_at_end_of_block(block).await? else { + return Ok(vec![]); + }; let filter = Filter::new().from_block(block).to_block(block).address(self.1); let filter = filter.event_signature(InInstructionEvent::SIGNATURE_HASH); @@ -274,7 +283,13 @@ impl Router { ); let tx_hash = log.transaction_hash.ok_or(Error::ConnectionError)?; - let tx = self.0.get_transaction_by_hash(tx_hash).await.map_err(|_| Error::ConnectionError)?; + let tx = self + .0 + .get_transaction_by_hash(tx_hash) + .await + .ok() + .flatten() + .ok_or(Error::ConnectionError)?; let log = log.log_decode::().map_err(|_| Error::ConnectionError)?.inner.data; diff --git a/coins/ethereum/src/tests/mod.rs b/coins/ethereum/src/tests/mod.rs index 3a381d42..085ef3a2 100644 --- a/coins/ethereum/src/tests/mod.rs +++ b/coins/ethereum/src/tests/mod.rs @@ -11,16 +11,20 @@ use alloy_core::{ }; use alloy_consensus::{SignableTransaction, TxLegacy}; -use alloy_rpc_types::TransactionReceipt; +use alloy_rpc_types::{BlockNumberOrTag, TransactionReceipt}; use alloy_simple_request_transport::SimpleRequest; use alloy_provider::{Provider, RootProvider}; use crate::crypto::{address, deterministically_sign, PublicKey}; +#[cfg(test)] mod crypto; +#[cfg(test)] mod abi; +#[cfg(test)] mod schnorr; +#[cfg(test)] mod router; pub fn key_gen() -> (HashMap>, PublicKey) { @@ -53,14 +57,15 @@ pub async fn send( // let chain_id = provider.get_chain_id().await.unwrap(); // tx.chain_id = Some(chain_id); tx.chain_id = None; - tx.nonce = provider.get_transaction_count(address, None).await.unwrap(); + tx.nonce = + provider.get_transaction_count(address, BlockNumberOrTag::Latest.into()).await.unwrap(); // 100 gwei tx.gas_price = 100_000_000_000u128; let sig = wallet.sign_prehash_recoverable(tx.signature_hash().as_ref()).unwrap(); assert_eq!(address, tx.clone().into_signed(sig.into()).recover_signer().unwrap()); assert!( - provider.get_balance(address, None).await.unwrap() > + provider.get_balance(address, BlockNumberOrTag::Latest.into()).await.unwrap() > ((U256::from(tx.gas_price) * U256::from(tx.gas_limit)) + tx.value) ); diff --git a/coins/ethereum/src/tests/schnorr.rs b/coins/ethereum/src/tests/schnorr.rs index 9311c292..21d8b45a 100644 --- a/coins/ethereum/src/tests/schnorr.rs +++ b/coins/ethereum/src/tests/schnorr.rs @@ -56,12 +56,12 @@ pub async fn call_verify( let px: [u8; 32] = public_key.px.to_repr().into(); let c_bytes: [u8; 32] = signature.c.to_repr().into(); let s_bytes: [u8; 32] = signature.s.to_repr().into(); - let call = TransactionRequest::default().to(Some(contract)).input(TransactionInput::new( + let call = TransactionRequest::default().to(contract).input(TransactionInput::new( abi::verifyCall::new((px.into(), message.to_vec().into(), c_bytes.into(), s_bytes.into())) .abi_encode() .into(), )); - let bytes = provider.call(&call, None).await.map_err(|_| Error::ConnectionError)?; + let bytes = provider.call(&call).await.map_err(|_| Error::ConnectionError)?; let res = abi::verifyCall::abi_decode_returns(&bytes, true).map_err(|_| Error::ConnectionError)?; From 0c9dd5048e01bf6d7405e5acea9efd2a82cac2ab Mon Sep 17 00:00:00 2001 From: Luke Parker Date: Fri, 10 May 2024 14:04:58 -0400 Subject: [PATCH 2/7] Processor scanner tests for Ethereum --- common/request/src/lib.rs | 6 +- orchestration/dev/coins/ethereum/run.sh | 2 +- processor/Cargo.toml | 2 +- processor/src/networks/ethereum.rs | 69 +++++---- processor/src/tests/addresses.rs | 11 +- processor/src/tests/literal/mod.rs | 186 ++++++++++++++++++++++-- processor/src/tests/mod.rs | 101 +++++++------ processor/src/tests/scanner.rs | 31 +++- processor/src/tests/signer.rs | 9 +- processor/src/tests/wallet.rs | 19 ++- 10 files changed, 329 insertions(+), 107 deletions(-) diff --git a/common/request/src/lib.rs b/common/request/src/lib.rs index ad452a0c..60e51019 100644 --- a/common/request/src/lib.rs +++ b/common/request/src/lib.rs @@ -55,6 +55,8 @@ impl Client { fn connector() -> Connector { let mut res = HttpConnector::new(); res.set_keepalive(Some(core::time::Duration::from_secs(60))); + res.set_nodelay(true); + res.set_reuse_address(true); #[cfg(feature = "tls")] let res = HttpsConnectorBuilder::new() .with_native_roots() @@ -68,7 +70,9 @@ impl Client { pub fn with_connection_pool() -> Client { Client { connection: Connection::ConnectionPool( - HyperClient::builder(TokioExecutor::new()).build(Self::connector()), + HyperClient::builder(TokioExecutor::new()) + .pool_idle_timeout(core::time::Duration::from_secs(60)) + .build(Self::connector()), ), } } diff --git a/orchestration/dev/coins/ethereum/run.sh b/orchestration/dev/coins/ethereum/run.sh index 4fee3e46..464f4c6e 100755 --- a/orchestration/dev/coins/ethereum/run.sh +++ b/orchestration/dev/coins/ethereum/run.sh @@ -1,3 +1,3 @@ #!/bin/sh -~/.foundry/bin/anvil --no-mining --slots-in-an-epoch 32 +~/.foundry/bin/anvil --host 0.0.0.0 --no-cors --no-mining --slots-in-an-epoch 32 --silent diff --git a/processor/Cargo.toml b/processor/Cargo.toml index cbc022a1..f90f6117 100644 --- a/processor/Cargo.toml +++ b/processor/Cargo.toml @@ -84,7 +84,7 @@ serai-docker-tests = { path = "../tests/docker" } secp256k1 = ["k256", "frost/secp256k1"] bitcoin = ["dep:secp256k1", "secp256k1", "bitcoin-serai", "serai-client/bitcoin"] -ethereum = ["secp256k1", "ethereum-serai"] +ethereum = ["secp256k1", "ethereum-serai/tests"] ed25519 = ["dalek-ff-group", "frost/ed25519"] monero = ["ed25519", "monero-serai", "serai-client/monero"] diff --git a/processor/src/networks/ethereum.rs b/processor/src/networks/ethereum.rs index 4de08837..3bb012ca 100644 --- a/processor/src/networks/ethereum.rs +++ b/processor/src/networks/ethereum.rs @@ -124,7 +124,7 @@ impl SignableTransaction for RouterCommand { } #[async_trait] -impl TransactionTrait> for Transaction { +impl TransactionTrait> for Transaction { type Id = [u8; 32]; fn id(&self) -> Self::Id { self.hash.0 @@ -157,7 +157,7 @@ impl Epoch { } #[async_trait] -impl Block> for Epoch { +impl Block> for Epoch { type Id = [u8; 32]; fn id(&self) -> [u8; 32] { self.end_hash @@ -170,7 +170,7 @@ impl Block> for Epoch { } } -impl Output> for EthereumInInstruction { +impl Output> for EthereumInInstruction { type Id = [u8; 32]; fn kind(&self) -> OutputType { @@ -282,8 +282,8 @@ impl EventualityTrait for Eventuality { } } -#[derive(Clone, Debug)] -pub struct Ethereum { +#[derive(Clone)] +pub struct Ethereum { // This DB is solely used to access the first key generated, as needed to determine the Router's // address. Accordingly, all methods present are consistent to a Serai chain with a finalized // first key (regardless of local state), and this is safe. @@ -292,20 +292,26 @@ pub struct Ethereum { deployer: Deployer, router: Arc>>, } -impl PartialEq for Ethereum { +impl PartialEq for Ethereum { fn eq(&self, _other: &Ethereum) -> bool { true } } -impl Ethereum { +impl fmt::Debug for Ethereum { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt + .debug_struct("Ethereum") + .field("deployer", &self.deployer) + .field("router", &self.router) + .finish_non_exhaustive() + } +} +impl Ethereum { pub async fn new(db: D, url: String) -> Self { let provider = Arc::new(RootProvider::new( ClientBuilder::default().transport(SimpleRequest::new(url), true), )); - #[cfg(test)] // TODO: Move to test code - provider.raw_request::<_, ()>("evm_setAutomine".into(), false).await.unwrap(); - let mut deployer = Deployer::new(provider.clone()).await; while !matches!(deployer, Ok(Some(_))) { log::error!("Deployer wasn't deployed yet or networking error"); @@ -362,7 +368,7 @@ impl Ethereum { } #[async_trait] -impl Network for Ethereum { +impl Network for Ethereum { type Curve = Secp256k1; type Transaction = Transaction; @@ -479,7 +485,8 @@ impl Network for Ethereum { // Grab the key at the end of the epoch let key_at_end_of_block = loop { match router.key_at_end_of_block(block.start + 31).await { - Ok(key) => break key, + Ok(Some(key)) => break key, + Ok(None) => return vec![], Err(e) => { log::error!("couldn't connect to router for the key at the end of the block: {e:?}"); sleep(Duration::from_secs(5)).await; @@ -491,17 +498,7 @@ impl Network for Ethereum { let mut all_events = vec![]; let mut top_level_txids = HashSet::new(); for erc20_addr in [DAI] { - let erc20 = loop { - let Ok(Some(erc20)) = Erc20::new(self.provider.clone(), erc20_addr).await else { - log::error!( - "couldn't connect to Ethereum node for an ERC20: {}", - hex::encode(erc20_addr) - ); - sleep(Duration::from_secs(5)).await; - continue; - }; - break erc20; - }; + let erc20 = Erc20::new(self.provider.clone(), erc20_addr); for block in block.start .. (block.start + 32) { let transfers = loop { @@ -821,6 +818,7 @@ impl Network for Ethereum { .provider .get_transaction_by_hash(log.clone().transaction_hash.unwrap()) .await + .unwrap() .unwrap(); }; @@ -830,20 +828,26 @@ impl Network for Ethereum { .to_block(((block + 1) * 32) - 1) .topic1(nonce); let logs = self.provider.get_logs(&filter).await.unwrap(); - self.provider.get_transaction_by_hash(logs[0].transaction_hash.unwrap()).await.unwrap() + self + .provider + .get_transaction_by_hash(logs[0].transaction_hash.unwrap()) + .await + .unwrap() + .unwrap() } } } #[cfg(test)] async fn mine_block(&self) { - self.provider.raw_request::<_, ()>("anvil_mine".into(), [32]).await.unwrap(); + self.provider.raw_request::<_, ()>("anvil_mine".into(), [96]).await.unwrap(); } #[cfg(test)] async fn test_send(&self, send_to: Self::Address) -> Self::Block { use rand_core::OsRng; use ciphersuite::group::ff::Field; + use ethereum_serai::alloy_sol_types::SolCall; let key = ::F::random(&mut OsRng); let address = ethereum_serai::crypto::address(&(Secp256k1::generator() * key)); @@ -858,15 +862,22 @@ impl Network for Ethereum { .await .unwrap(); + let value = U256::from_str_radix("1000000000000000000", 10).unwrap(); let tx = ethereum_serai::alloy_consensus::TxLegacy { chain_id: None, nonce: 0, - gas_price: 100_000_000_000u128, - gas_limit: 21_0000u128, + gas_price: 1_000_000_000u128, + gas_limit: 200_000u128, to: ethereum_serai::alloy_core::primitives::TxKind::Call(send_to.0.into()), // 1 ETH - value: U256::from_str_radix("1000000000000000000", 10).unwrap(), - input: vec![].into(), + value, + input: ethereum_serai::router::abi::inInstructionCall::new(( + [0; 20].into(), + value, + vec![].into(), + )) + .abi_encode() + .into(), }; use ethereum_serai::alloy_consensus::SignableTransaction; diff --git a/processor/src/tests/addresses.rs b/processor/src/tests/addresses.rs index 8f730dbd..3d4d6d4c 100644 --- a/processor/src/tests/addresses.rs +++ b/processor/src/tests/addresses.rs @@ -1,4 +1,4 @@ -use core::time::Duration; +use core::{time::Duration, pin::Pin, future::Future}; use std::collections::HashMap; use rand_core::OsRng; @@ -82,8 +82,9 @@ async fn spend( } } -pub async fn test_addresses(network: N) -where +pub async fn test_addresses( + new_network: impl Fn(MemDb) -> Pin>>, +) where >::Addendum: From<()>, { let mut keys = frost::tests::key_gen::<_, N::Curve>(&mut OsRng); @@ -92,12 +93,14 @@ where } let key = keys[&Participant::new(1).unwrap()].group_key(); + let mut db = MemDb::new(); + let network = new_network(db.clone()).await; + // Mine blocks so there's a confirmed block for _ in 0 .. N::CONFIRMATIONS { network.mine_block().await; } - let mut db = MemDb::new(); let (mut scanner, current_keys) = Scanner::new(network.clone(), db.clone()); assert!(current_keys.is_empty()); let mut txn = db.txn(); diff --git a/processor/src/tests/literal/mod.rs b/processor/src/tests/literal/mod.rs index e2bfdc8a..20aa1083 100644 --- a/processor/src/tests/literal/mod.rs +++ b/processor/src/tests/literal/mod.rs @@ -3,6 +3,8 @@ use dockertest::{ TestBodySpecification, DockerOperations, DockerTest, }; +use serai_db::MemDb; + #[cfg(feature = "bitcoin")] mod bitcoin { use std::sync::Arc; @@ -33,8 +35,6 @@ mod bitcoin { sync::Mutex, }; - use serai_db::MemDb; - use super::*; use crate::{ networks::{Network, Bitcoin, Output, OutputType, Block}, @@ -57,7 +57,7 @@ mod bitcoin { fn test_receive_data_from_input() { let docker = spawn_bitcoin(); docker.run(|ops| async move { - let btc = bitcoin(&ops).await; + let btc = bitcoin(&ops).await(MemDb::new()).await; // generate a multisig address to receive the coins let mut keys = frost::tests::key_gen::<_, ::Curve>(&mut OsRng) @@ -208,23 +208,26 @@ mod bitcoin { test } - async fn bitcoin(ops: &DockerOperations) -> Bitcoin { + async fn bitcoin( + ops: &DockerOperations, + ) -> impl Fn(MemDb) -> Pin>> { let handle = ops.handle("serai-dev-bitcoin").host_port(8332).unwrap(); - let bitcoin = Bitcoin::new(format!("http://serai:seraidex@{}:{}", handle.0, handle.1)).await; + let url = format!("http://serai:seraidex@{}:{}", handle.0, handle.1); + let bitcoin = Bitcoin::new(url.clone()).await; bitcoin.fresh_chain().await; - bitcoin + move |_db| Box::pin(Bitcoin::new(url.clone())) } - test_network!( + test_utxo_network!( Bitcoin, spawn_bitcoin, bitcoin, bitcoin_key_gen, bitcoin_scanner, + bitcoin_no_deadlock_in_multisig_completed, bitcoin_signer, bitcoin_wallet, bitcoin_addresses, - bitcoin_no_deadlock_in_multisig_completed, ); } @@ -252,24 +255,181 @@ mod monero { test } - async fn monero(ops: &DockerOperations) -> Monero { + async fn monero( + ops: &DockerOperations, + ) -> impl Fn(MemDb) -> Pin>> { let handle = ops.handle("serai-dev-monero").host_port(18081).unwrap(); - let monero = Monero::new(format!("http://serai:seraidex@{}:{}", handle.0, handle.1)).await; + let url = format!("http://serai:seraidex@{}:{}", handle.0, handle.1); + let monero = Monero::new(url.clone()).await; while monero.get_latest_block_number().await.unwrap() < 150 { monero.mine_block().await; } - monero + move |_db| Box::pin(Monero::new(url.clone())) } - test_network!( + test_utxo_network!( Monero, spawn_monero, monero, monero_key_gen, monero_scanner, + monero_no_deadlock_in_multisig_completed, monero_signer, monero_wallet, monero_addresses, - monero_no_deadlock_in_multisig_completed, + ); +} + +#[cfg(feature = "ethereum")] +mod ethereum { + use super::*; + + use ciphersuite::{Ciphersuite, Secp256k1}; + + use serai_client::validator_sets::primitives::Session; + + use crate::networks::Ethereum; + + fn spawn_ethereum() -> DockerTest { + serai_docker_tests::build("ethereum".to_string()); + + let composition = TestBodySpecification::with_image( + Image::with_repository("serai-dev-ethereum").pull_policy(PullPolicy::Never), + ) + .set_start_policy(StartPolicy::Strict) + .set_log_options(Some(LogOptions { + action: LogAction::Forward, + policy: LogPolicy::OnError, + source: LogSource::Both, + })) + .set_publish_all_ports(true); + + let mut test = DockerTest::new(); + test.provide_container(composition); + test + } + + async fn ethereum( + ops: &DockerOperations, + ) -> impl Fn(MemDb) -> Pin>>> { + use std::sync::Arc; + use ethereum_serai::{ + alloy_core::primitives::U256, + alloy_simple_request_transport::SimpleRequest, + alloy_rpc_client::ClientBuilder, + alloy_provider::{Provider, RootProvider}, + deployer::Deployer, + }; + + let handle = ops.handle("serai-dev-ethereum").host_port(8545).unwrap(); + let url = format!("http://{}:{}", handle.0, handle.1); + tokio::time::sleep(core::time::Duration::from_secs(15)).await; + + { + let provider = Arc::new(RootProvider::new( + ClientBuilder::default().transport(SimpleRequest::new(url.clone()), true), + )); + provider.raw_request::<_, ()>("evm_setAutomine".into(), [false]).await.unwrap(); + provider.raw_request::<_, ()>("anvil_mine".into(), [96]).await.unwrap(); + + // Perform deployment + { + // Make sure the Deployer constructor returns None, as it doesn't exist yet + assert!(Deployer::new(provider.clone()).await.unwrap().is_none()); + + // Deploy the Deployer + let tx = Deployer::deployment_tx(); + + provider + .raw_request::<_, ()>( + "anvil_setBalance".into(), + [ + tx.recover_signer().unwrap().to_string(), + (U256::from(tx.tx().gas_limit) * U256::from(tx.tx().gas_price)).to_string(), + ], + ) + .await + .unwrap(); + + let (tx, sig, _) = tx.into_parts(); + let mut bytes = vec![]; + tx.encode_with_signature_fields(&sig, &mut bytes); + + let pending_tx = provider.send_raw_transaction(&bytes).await.unwrap(); + provider.raw_request::<_, ()>("anvil_mine".into(), [96]).await.unwrap(); + //tokio::time::sleep(core::time::Duration::from_secs(15)).await; + let receipt = pending_tx.get_receipt().await.unwrap(); + assert!(receipt.status()); + + let _ = Deployer::new(provider.clone()) + .await + .expect("network error") + .expect("deployer wasn't deployed"); + } + } + + move |db| { + let url = url.clone(); + Box::pin(async move { + { + let db = db.clone(); + let url = url.clone(); + // Spawn a task to deploy the proper Router when the time comes + tokio::spawn(async move { + let key = loop { + let Some(key) = crate::key_gen::NetworkKeyDb::get(&db, Session(0)) else { + tokio::time::sleep(core::time::Duration::from_secs(1)).await; + continue; + }; + break ethereum_serai::crypto::PublicKey::new( + Secp256k1::read_G(&mut key.as_slice()).unwrap(), + ) + .unwrap(); + }; + let provider = Arc::new(RootProvider::new( + ClientBuilder::default().transport(SimpleRequest::new(url.clone()), true), + )); + let deployer = Deployer::new(provider.clone()).await.unwrap().unwrap(); + + let mut tx = deployer.deploy_router(&key); + tx.gas_limit = 1_000_000u64.into(); + tx.gas_price = 1_000_000_000u64.into(); + let tx = ethereum_serai::crypto::deterministically_sign(&tx); + + provider + .raw_request::<_, ()>( + "anvil_setBalance".into(), + [ + tx.recover_signer().unwrap().to_string(), + (U256::from(tx.tx().gas_limit) * U256::from(tx.tx().gas_price)).to_string(), + ], + ) + .await + .unwrap(); + + let (tx, sig, _) = tx.into_parts(); + let mut bytes = vec![]; + tx.encode_with_signature_fields(&sig, &mut bytes); + let pending_tx = provider.send_raw_transaction(&bytes).await.unwrap(); + provider.raw_request::<_, ()>("anvil_mine".into(), [96]).await.unwrap(); + let receipt = pending_tx.get_receipt().await.unwrap(); + assert!(receipt.status()); + + let _router = deployer.find_router(provider.clone(), &key).await.unwrap().unwrap(); + }); + } + + Ethereum::new(db, url.clone()).await + }) + } + } + + test_network!( + Ethereum, + spawn_ethereum, + ethereum, + ethereum_key_gen, + ethereum_scanner, + ethereum_no_deadlock_in_multisig_completed, ); } diff --git a/processor/src/tests/mod.rs b/processor/src/tests/mod.rs index 974be10b..26b49635 100644 --- a/processor/src/tests/mod.rs +++ b/processor/src/tests/mod.rs @@ -1,22 +1,18 @@ use std::sync::OnceLock; mod key_gen; -pub(crate) use key_gen::test_key_gen; mod scanner; -pub(crate) use scanner::{test_scanner, test_no_deadlock_in_multisig_completed}; mod signer; -pub(crate) use signer::{sign, test_signer}; +pub(crate) use signer::sign; mod cosigner; mod batch_signer; mod wallet; -pub(crate) use wallet::test_wallet; mod addresses; -pub(crate) use addresses::test_addresses; // Effective Once static INIT_LOGGER_CELL: OnceLock<()> = OnceLock::new(); @@ -27,22 +23,21 @@ fn init_logger() { #[macro_export] macro_rules! test_network { ( - $N: ident, + $N: ty, $docker: ident, $network: ident, $key_gen: ident, $scanner: ident, - $signer: ident, - $wallet: ident, - $addresses: ident, $no_deadlock_in_multisig_completed: ident, ) => { + use core::{pin::Pin, future::Future}; use $crate::tests::{ - init_logger, test_key_gen, test_scanner, test_no_deadlock_in_multisig_completed, test_signer, - test_wallet, test_addresses, + init_logger, + key_gen::test_key_gen, + scanner::{test_scanner, test_no_deadlock_in_multisig_completed}, }; - // This doesn't interact with a node and accordingly doesn't need to be run + // This doesn't interact with a node and accordingly doesn't need to be spawn one #[tokio::test] async fn $key_gen() { init_logger(); @@ -54,34 +49,8 @@ macro_rules! test_network { init_logger(); let docker = $docker(); docker.run(|ops| async move { - test_scanner($network(&ops).await).await; - }); - } - - #[test] - fn $signer() { - init_logger(); - let docker = $docker(); - docker.run(|ops| async move { - test_signer($network(&ops).await).await; - }); - } - - #[test] - fn $wallet() { - init_logger(); - let docker = $docker(); - docker.run(|ops| async move { - test_wallet($network(&ops).await).await; - }); - } - - #[test] - fn $addresses() { - init_logger(); - let docker = $docker(); - docker.run(|ops| async move { - test_addresses($network(&ops).await).await; + let new_network = $network(&ops).await; + test_scanner(new_network).await; }); } @@ -90,7 +59,57 @@ macro_rules! test_network { init_logger(); let docker = $docker(); docker.run(|ops| async move { - test_no_deadlock_in_multisig_completed($network(&ops).await).await; + let new_network = $network(&ops).await; + test_no_deadlock_in_multisig_completed(new_network).await; + }); + } + }; +} + +#[macro_export] +macro_rules! test_utxo_network { + ( + $N: ty, + $docker: ident, + $network: ident, + $key_gen: ident, + $scanner: ident, + $no_deadlock_in_multisig_completed: ident, + $signer: ident, + $wallet: ident, + $addresses: ident, + ) => { + use $crate::tests::{signer::test_signer, wallet::test_wallet, addresses::test_addresses}; + + test_network!($N, $docker, $network, $key_gen, $scanner, $no_deadlock_in_multisig_completed,); + + #[test] + fn $signer() { + init_logger(); + let docker = $docker(); + docker.run(|ops| async move { + let new_network = $network(&ops).await; + test_signer(new_network).await; + }); + } + + #[test] + fn $wallet() { + init_logger(); + let docker = $docker(); + docker.run(|ops| async move { + let new_network = $network(&ops).await; + test_wallet(new_network).await; + }); + } + + #[test] + fn $addresses() { + init_logger(); + let docker = $docker(); + docker.run(|ops| async move { + let new_network = $network(&ops).await; + test_addresses(new_network).await; }); } }; diff --git a/processor/src/tests/scanner.rs b/processor/src/tests/scanner.rs index 42756d8b..16885dab 100644 --- a/processor/src/tests/scanner.rs +++ b/processor/src/tests/scanner.rs @@ -1,21 +1,23 @@ -use core::time::Duration; +use core::{pin::Pin, time::Duration, future::Future}; use std::sync::Arc; -use ciphersuite::Ciphersuite; use rand_core::OsRng; +use ciphersuite::{group::GroupEncoding, Ciphersuite}; use frost::{Participant, tests::key_gen}; use tokio::{sync::Mutex, time::timeout}; use serai_db::{DbTxn, Db, MemDb}; +use serai_client::validator_sets::primitives::Session; use crate::{ - networks::{OutputType, Output, Block, UtxoNetwork}, + networks::{OutputType, Output, Block, Network}, + key_gen::NetworkKeyDb, multisigs::scanner::{ScannerEvent, Scanner, ScannerHandle}, }; -pub async fn new_scanner( +pub async fn new_scanner( network: &N, db: &D, group_key: ::G, @@ -40,18 +42,27 @@ pub async fn new_scanner( scanner } -pub async fn test_scanner(network: N) { +pub async fn test_scanner( + new_network: impl Fn(MemDb) -> Pin>>, +) { let mut keys = frost::tests::key_gen::<_, N::Curve>(&mut OsRng).remove(&Participant::new(1).unwrap()).unwrap(); N::tweak_keys(&mut keys); let group_key = keys.group_key(); + let mut db = MemDb::new(); + { + let mut txn = db.txn(); + NetworkKeyDb::set(&mut txn, Session(0), &group_key.to_bytes().as_ref().to_vec()); + txn.commit(); + } + let network = new_network(db.clone()).await; + // Mine blocks so there's a confirmed block for _ in 0 .. N::CONFIRMATIONS { network.mine_block().await; } - let db = MemDb::new(); let first = Arc::new(Mutex::new(true)); let scanner = new_scanner(&network, &db, group_key, &first).await; @@ -101,13 +112,17 @@ pub async fn test_scanner(network: N) { .is_err()); } -pub async fn test_no_deadlock_in_multisig_completed(network: N) { +pub async fn test_no_deadlock_in_multisig_completed( + new_network: impl Fn(MemDb) -> Pin>>, +) { + let mut db = MemDb::new(); + let network = new_network(db.clone()).await; + // Mine blocks so there's a confirmed block for _ in 0 .. N::CONFIRMATIONS { network.mine_block().await; } - let mut db = MemDb::new(); let (mut scanner, current_keys) = Scanner::new(network.clone(), db.clone()); assert!(current_keys.is_empty()); diff --git a/processor/src/tests/signer.rs b/processor/src/tests/signer.rs index 524c5d29..85444d63 100644 --- a/processor/src/tests/signer.rs +++ b/processor/src/tests/signer.rs @@ -1,3 +1,4 @@ +use core::{pin::Pin, future::Future}; use std::collections::HashMap; use rand_core::{RngCore, OsRng}; @@ -153,8 +154,9 @@ pub async fn sign( typed_claim } -pub async fn test_signer(network: N) -where +pub async fn test_signer( + new_network: impl Fn(MemDb) -> Pin>>, +) where >::Addendum: From<()>, { let mut keys = key_gen(&mut OsRng); @@ -163,6 +165,9 @@ where } let key = keys[&Participant::new(1).unwrap()].group_key(); + let db = MemDb::new(); + let network = new_network(db).await; + let outputs = network .get_outputs(&network.test_send(N::external_address(&network, key).await).await, key) .await; diff --git a/processor/src/tests/wallet.rs b/processor/src/tests/wallet.rs index 4600fcbe..acd3cb65 100644 --- a/processor/src/tests/wallet.rs +++ b/processor/src/tests/wallet.rs @@ -1,4 +1,5 @@ -use std::{time::Duration, collections::HashMap}; +use core::{time::Duration, pin::Pin, future::Future}; +use std::collections::HashMap; use rand_core::OsRng; @@ -24,12 +25,9 @@ use crate::{ }; // Tests the Scanner, Scheduler, and Signer together -pub async fn test_wallet(network: N) { - // Mine blocks so there's a confirmed block - for _ in 0 .. N::CONFIRMATIONS { - network.mine_block().await; - } - +pub async fn test_wallet( + new_network: impl Fn(MemDb) -> Pin>>, +) { let mut keys = key_gen(&mut OsRng); for keys in keys.values_mut() { N::tweak_keys(keys); @@ -37,6 +35,13 @@ pub async fn test_wallet(network: N) { let key = keys[&Participant::new(1).unwrap()].group_key(); let mut db = MemDb::new(); + let network = new_network(db.clone()).await; + + // Mine blocks so there's a confirmed block + for _ in 0 .. N::CONFIRMATIONS { + network.mine_block().await; + } + let (mut scanner, current_keys) = Scanner::new(network.clone(), db.clone()); assert!(current_keys.is_empty()); let (block_id, outputs) = { From 79a79db399b7cb96b710952f2f9faed8e5621d97 Mon Sep 17 00:00:00 2001 From: Luke Parker Date: Fri, 10 May 2024 15:50:07 -0400 Subject: [PATCH 3/7] Update dockertest specification --- Cargo.lock | 2 +- Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 66212c4a..719ed497 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2125,7 +2125,7 @@ dependencies = [ [[package]] name = "dockertest" version = "0.4.0" -source = "git+https://github.com/kayabaNerve/dockertest-rs?branch=arc#c0ea77997048f9edc9987984bbe20e43fac74e06" +source = "git+https://github.com/orcalabs/dockertest-rs?rev=4dd6ae24738aa6dc5c89444cc822ea4745517493#4dd6ae24738aa6dc5c89444cc822ea4745517493" dependencies = [ "anyhow", "async-trait", diff --git a/Cargo.toml b/Cargo.toml index 8a19d159..94b52ffb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -110,7 +110,7 @@ panic = "unwind" lazy_static = { git = "https://github.com/rust-lang-nursery/lazy-static.rs", rev = "5735630d46572f1e5377c8f2ba0f79d18f53b10c" } # Needed due to dockertest's usage of `Rc`s when we need `Arc`s -dockertest = { git = "https://github.com/kayabaNerve/dockertest-rs", branch = "arc" } +dockertest = { git = "https://github.com/orcalabs/dockertest-rs", rev = "4dd6ae24738aa6dc5c89444cc822ea4745517493" } # wasmtime pulls in an old version for this zstd = { path = "patches/zstd" } From 02c4417a464ef162636036942987e40d01b54bab Mon Sep 17 00:00:00 2001 From: Luke Parker Date: Fri, 10 May 2024 15:57:05 -0400 Subject: [PATCH 4/7] Update no_deadlock_in_multisig test to set the initial key in the DB --- processor/src/tests/scanner.rs | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/processor/src/tests/scanner.rs b/processor/src/tests/scanner.rs index 16885dab..078a07d5 100644 --- a/processor/src/tests/scanner.rs +++ b/processor/src/tests/scanner.rs @@ -126,25 +126,33 @@ pub async fn test_no_deadlock_in_multisig_completed( let (mut scanner, current_keys) = Scanner::new(network.clone(), db.clone()); assert!(current_keys.is_empty()); - let mut txn = db.txn(); // Register keys to cause Block events at CONFIRMATIONS (dropped since first keys), // CONFIRMATIONS + 1, and CONFIRMATIONS + 2 for i in 0 .. 3 { + let key = { + let mut keys = key_gen(&mut OsRng); + for keys in keys.values_mut() { + N::tweak_keys(keys); + } + let key = keys[&Participant::new(1).unwrap()].group_key(); + if i == 0 { + let mut txn = db.txn(); + NetworkKeyDb::set(&mut txn, Session(0), &key.to_bytes().as_ref().to_vec()); + txn.commit(); + } + key + }; + + let mut txn = db.txn(); scanner .register_key( &mut txn, network.get_latest_block_number().await.unwrap() + N::CONFIRMATIONS + i, - { - let mut keys = key_gen(&mut OsRng); - for keys in keys.values_mut() { - N::tweak_keys(keys); - } - keys[&Participant::new(1).unwrap()].group_key() - }, + key, ) .await; + txn.commit(); } - txn.commit(); for _ in 0 .. (3 * N::CONFIRMATIONS) { network.mine_block().await; From d27d93480aa8a849d84214ad4c71d83ce6fea0c1 Mon Sep 17 00:00:00 2001 From: Luke Parker Date: Sat, 11 May 2024 00:11:14 -0400 Subject: [PATCH 5/7] Get processor signer/wallet tests working for Ethereum They are handicapped by the fact Ethereum self-sends don't show up as outputs, yet that's fundamental (unless we add a *harmful* fallback function). --- deny.toml | 2 +- .../src/multisigs/scheduler/smart_contract.rs | 4 +- processor/src/networks/ethereum.rs | 108 +++++++++--------- processor/src/networks/mod.rs | 7 +- processor/src/tests/literal/mod.rs | 2 + processor/src/tests/mod.rs | 51 ++++++--- processor/src/tests/signer.rs | 96 +++++++++------- processor/src/tests/wallet.rs | 74 ++++++++---- 8 files changed, 203 insertions(+), 141 deletions(-) diff --git a/deny.toml b/deny.toml index 60331289..d6972d5e 100644 --- a/deny.toml +++ b/deny.toml @@ -101,5 +101,5 @@ allow-git = [ "https://github.com/serai-dex/substrate", "https://github.com/alloy-rs/alloy", "https://github.com/monero-rs/base58-monero", - "https://github.com/kayabaNerve/dockertest-rs", + "https://github.com/orcalabs/dockertest-rs", ] diff --git a/processor/src/multisigs/scheduler/smart_contract.rs b/processor/src/multisigs/scheduler/smart_contract.rs index 27268b82..4f48e391 100644 --- a/processor/src/multisigs/scheduler/smart_contract.rs +++ b/processor/src/multisigs/scheduler/smart_contract.rs @@ -116,7 +116,7 @@ impl> SchedulerTrait for Scheduler { assert!(self.coins.contains(&utxo.balance().coin)); } - let mut nonce = LastNonce::get(txn).map_or(0, |nonce| nonce + 1); + let mut nonce = LastNonce::get(txn).map_or(1, |nonce| nonce + 1); let mut plans = vec![]; for chunk in payments.as_slice().chunks(N::MAX_OUTPUTS) { // Once we rotate, all further payments should be scheduled via the new multisig @@ -179,7 +179,7 @@ impl> SchedulerTrait for Scheduler { .and_then(|key_bytes| ::read_G(&mut key_bytes.as_slice()).ok()) .unwrap_or(self.key); - let nonce = LastNonce::get(txn).map_or(0, |nonce| nonce + 1); + let nonce = LastNonce::get(txn).map_or(1, |nonce| nonce + 1); LastNonce::set(txn, &(nonce + 1)); Plan { key: current_key, diff --git a/processor/src/networks/ethereum.rs b/processor/src/networks/ethereum.rs index 3bb012ca..f3d562d7 100644 --- a/processor/src/networks/ethereum.rs +++ b/processor/src/networks/ethereum.rs @@ -719,22 +719,6 @@ impl Network for Ethereum { // Publish this using a dummy account we fund with magic RPC commands #[cfg(test)] { - use rand_core::OsRng; - use ciphersuite::group::ff::Field; - - let key = ::F::random(&mut OsRng); - let address = ethereum_serai::crypto::address(&(Secp256k1::generator() * key)); - - // Set a 1.1 ETH balance - self - .provider - .raw_request::<_, ()>( - "anvil_setBalance".into(), - [Address(address).to_string(), "1100000000000000000".into()], - ) - .await - .unwrap(); - let router = self.router().await; let router = router.as_ref().unwrap(); @@ -747,17 +731,30 @@ impl Network for Ethereum { completion.signature(), ), }; - tx.gas_price = 100_000_000_000u128; + tx.gas_limit = 1_000_000u64.into(); + tx.gas_price = 1_000_000_000u64.into(); + let tx = ethereum_serai::crypto::deterministically_sign(&tx); - use ethereum_serai::alloy_consensus::SignableTransaction; - let sig = - k256::ecdsa::SigningKey::from(k256::elliptic_curve::NonZeroScalar::new(key).unwrap()) - .sign_prehash_recoverable(tx.signature_hash().as_ref()) + if self.provider.get_transaction_by_hash(*tx.hash()).await.unwrap().is_none() { + self + .provider + .raw_request::<_, ()>( + "anvil_setBalance".into(), + [ + tx.recover_signer().unwrap().to_string(), + (U256::from(tx.tx().gas_limit) * U256::from(tx.tx().gas_price)).to_string(), + ], + ) + .await .unwrap(); - let mut bytes = vec![]; - tx.encode_with_signature_fields(&sig.into(), &mut bytes); - let _ = self.provider.send_raw_transaction(&bytes).await.ok().unwrap(); + let (tx, sig, _) = tx.into_parts(); + let mut bytes = vec![]; + tx.encode_with_signature_fields(&sig, &mut bytes); + let pending_tx = self.provider.send_raw_transaction(&bytes).await.unwrap(); + self.mine_block().await; + assert!(pending_tx.get_receipt().await.unwrap().status()); + } Ok(()) } @@ -801,41 +798,50 @@ impl Network for Ethereum { block: usize, eventuality: &Self::Eventuality, ) -> Self::Transaction { - match eventuality.1 { - RouterCommand::UpdateSeraiKey { nonce, .. } | RouterCommand::Execute { nonce, .. } => { - let router = self.router().await; - let router = router.as_ref().unwrap(); + // We mine 96 blocks to ensure the 32 blocks relevant are finalized + // Back-check the prior two epochs in response to this + // TODO: Review why this is sub(3) and not sub(2) + for block in block.saturating_sub(3) ..= block { + match eventuality.1 { + RouterCommand::UpdateSeraiKey { nonce, .. } | RouterCommand::Execute { nonce, .. } => { + let router = self.router().await; + let router = router.as_ref().unwrap(); - let block = u64::try_from(block).unwrap(); - let filter = router - .key_updated_filter() - .from_block(block * 32) - .to_block(((block + 1) * 32) - 1) - .topic1(nonce); - let logs = self.provider.get_logs(&filter).await.unwrap(); - if let Some(log) = logs.first() { + let block = u64::try_from(block).unwrap(); + let filter = router + .key_updated_filter() + .from_block(block * 32) + .to_block(((block + 1) * 32) - 1) + .topic1(nonce); + let logs = self.provider.get_logs(&filter).await.unwrap(); + if let Some(log) = logs.first() { + return self + .provider + .get_transaction_by_hash(log.clone().transaction_hash.unwrap()) + .await + .unwrap() + .unwrap(); + }; + + let filter = router + .executed_filter() + .from_block(block * 32) + .to_block(((block + 1) * 32) - 1) + .topic1(nonce); + let logs = self.provider.get_logs(&filter).await.unwrap(); + if logs.is_empty() { + continue; + } return self .provider - .get_transaction_by_hash(log.clone().transaction_hash.unwrap()) + .get_transaction_by_hash(logs[0].transaction_hash.unwrap()) .await .unwrap() .unwrap(); - }; - - let filter = router - .executed_filter() - .from_block(block * 32) - .to_block(((block + 1) * 32) - 1) - .topic1(nonce); - let logs = self.provider.get_logs(&filter).await.unwrap(); - self - .provider - .get_transaction_by_hash(logs[0].transaction_hash.unwrap()) - .await - .unwrap() - .unwrap() + } } } + panic!("couldn't find completion in any three of checked blocks"); } #[cfg(test)] diff --git a/processor/src/networks/mod.rs b/processor/src/networks/mod.rs index 803ed40a..ee3cd24a 100644 --- a/processor/src/networks/mod.rs +++ b/processor/src/networks/mod.rs @@ -432,9 +432,12 @@ pub trait Network: 'static + Send + Sync + Clone + PartialEq + Debug { let plan_id = plan.id(); let Plan { key, inputs, mut payments, change, scheduler_addendum } = plan; - let theoretical_change_amount = + let theoretical_change_amount = if change.is_some() { inputs.iter().map(|input| input.balance().amount.0).sum::() - - payments.iter().map(|payment| payment.balance.amount.0).sum::(); + payments.iter().map(|payment| payment.balance.amount.0).sum::() + } else { + 0 + }; let Some(tx_fee) = self.needed_fee(block_number, &inputs, &payments, &change).await? else { // This Plan is not fulfillable diff --git a/processor/src/tests/literal/mod.rs b/processor/src/tests/literal/mod.rs index 20aa1083..cecd5a3b 100644 --- a/processor/src/tests/literal/mod.rs +++ b/processor/src/tests/literal/mod.rs @@ -431,5 +431,7 @@ mod ethereum { ethereum_key_gen, ethereum_scanner, ethereum_no_deadlock_in_multisig_completed, + ethereum_signer, + ethereum_wallet, ); } diff --git a/processor/src/tests/mod.rs b/processor/src/tests/mod.rs index 26b49635..7ab57bde 100644 --- a/processor/src/tests/mod.rs +++ b/processor/src/tests/mod.rs @@ -29,12 +29,16 @@ macro_rules! test_network { $key_gen: ident, $scanner: ident, $no_deadlock_in_multisig_completed: ident, + $signer: ident, + $wallet: ident, ) => { use core::{pin::Pin, future::Future}; use $crate::tests::{ init_logger, key_gen::test_key_gen, scanner::{test_scanner, test_no_deadlock_in_multisig_completed}, + signer::test_signer, + wallet::test_wallet, }; // This doesn't interact with a node and accordingly doesn't need to be spawn one @@ -63,25 +67,6 @@ macro_rules! test_network { test_no_deadlock_in_multisig_completed(new_network).await; }); } - }; -} - -#[macro_export] -macro_rules! test_utxo_network { - ( - $N: ty, - $docker: ident, - $network: ident, - $key_gen: ident, - $scanner: ident, - $no_deadlock_in_multisig_completed: ident, - $signer: ident, - $wallet: ident, - $addresses: ident, - ) => { - use $crate::tests::{signer::test_signer, wallet::test_wallet, addresses::test_addresses}; - - test_network!($N, $docker, $network, $key_gen, $scanner, $no_deadlock_in_multisig_completed,); #[test] fn $signer() { @@ -102,6 +87,34 @@ macro_rules! test_utxo_network { test_wallet(new_network).await; }); } + }; +} + +#[macro_export] +macro_rules! test_utxo_network { + ( + $N: ty, + $docker: ident, + $network: ident, + $key_gen: ident, + $scanner: ident, + $no_deadlock_in_multisig_completed: ident, + $signer: ident, + $wallet: ident, + $addresses: ident, + ) => { + use $crate::tests::addresses::test_addresses; + + test_network!( + $N, + $docker, + $network, + $key_gen, + $scanner, + $no_deadlock_in_multisig_completed, + $signer, + $wallet, + ); #[test] fn $addresses() { diff --git a/processor/src/tests/signer.rs b/processor/src/tests/signer.rs index 85444d63..77307ef2 100644 --- a/processor/src/tests/signer.rs +++ b/processor/src/tests/signer.rs @@ -3,6 +3,7 @@ use std::collections::HashMap; use rand_core::{RngCore, OsRng}; +use ciphersuite::group::GroupEncoding; use frost::{ Participant, ThresholdKeys, dkg::tests::{key_gen, clone_without}, @@ -17,14 +18,15 @@ use serai_client::{ use messages::sign::*; use crate::{ - Payment, Plan, - networks::{Output, Transaction, Eventuality, UtxoNetwork}, + Payment, + networks::{Output, Transaction, Eventuality, Network}, + key_gen::NetworkKeyDb, multisigs::scheduler::Scheduler, signer::Signer, }; #[allow(clippy::type_complexity)] -pub async fn sign( +pub async fn sign( network: N, session: Session, mut keys_txs: HashMap< @@ -154,57 +156,55 @@ pub async fn sign( typed_claim } -pub async fn test_signer( +pub async fn test_signer( new_network: impl Fn(MemDb) -> Pin>>, -) where - >::Addendum: From<()>, -{ +) { let mut keys = key_gen(&mut OsRng); for keys in keys.values_mut() { N::tweak_keys(keys); } let key = keys[&Participant::new(1).unwrap()].group_key(); - let db = MemDb::new(); - let network = new_network(db).await; + let mut db = MemDb::new(); + { + let mut txn = db.txn(); + NetworkKeyDb::set(&mut txn, Session(0), &key.to_bytes().as_ref().to_vec()); + txn.commit(); + } + let network = new_network(db.clone()).await; let outputs = network .get_outputs(&network.test_send(N::external_address(&network, key).await).await, key) .await; let sync_block = network.get_latest_block_number().await.unwrap() - N::CONFIRMATIONS; - let amount = 2 * N::DUST; + let amount = (2 * N::DUST) + 1000; + let plan = { + let mut txn = db.txn(); + let mut scheduler = N::Scheduler::new::(&mut txn, key, N::NETWORK); + let payments = vec![Payment { + address: N::external_address(&network, key).await, + data: None, + balance: Balance { + coin: match N::NETWORK { + NetworkId::Serai => panic!("test_signer called with Serai"), + NetworkId::Bitcoin => Coin::Bitcoin, + NetworkId::Ethereum => Coin::Ether, + NetworkId::Monero => Coin::Monero, + }, + amount: Amount(amount), + }, + }]; + let mut plans = scheduler.schedule::(&mut txn, outputs.clone(), payments, key, false); + assert_eq!(plans.len(), 1); + plans.swap_remove(0) + }; + let mut keys_txs = HashMap::new(); let mut eventualities = vec![]; for (i, keys) in keys.drain() { - let (signable, eventuality) = network - .prepare_send( - sync_block, - Plan { - key, - inputs: outputs.clone(), - payments: vec![Payment { - address: N::external_address(&network, key).await, - data: None, - balance: Balance { - coin: match N::NETWORK { - NetworkId::Serai => panic!("test_signer called with Serai"), - NetworkId::Bitcoin => Coin::Bitcoin, - NetworkId::Ethereum => Coin::Ether, - NetworkId::Monero => Coin::Monero, - }, - amount: Amount(amount), - }, - }], - change: Some(N::change_address(key).unwrap()), - scheduler_addendum: ().into(), - }, - 0, - ) - .await - .unwrap() - .tx - .unwrap(); + let (signable, eventuality) = + network.prepare_send(sync_block, plan.clone(), 0).await.unwrap().tx.unwrap(); eventualities.push(eventuality.clone()); keys_txs.insert(i, (keys, (signable, eventuality))); @@ -222,11 +222,21 @@ pub async fn test_signer( key, ) .await; - assert_eq!(outputs.len(), 2); - // Adjust the amount for the fees - let amount = amount - tx.fee(&network).await; - // Check either output since Monero will randomize its output order - assert!((outputs[0].balance().amount.0 == amount) || (outputs[1].balance().amount.0 == amount)); + // Don't run if Ethereum as the received output will revert by the contract + // (and therefore not actually exist) + if N::NETWORK != NetworkId::Ethereum { + assert_eq!(outputs.len(), 1 + usize::from(u8::from(plan.change.is_some()))); + // Adjust the amount for the fees + let amount = amount - tx.fee(&network).await; + if plan.change.is_some() { + // Check either output since Monero will randomize its output order + assert!( + (outputs[0].balance().amount.0 == amount) || (outputs[1].balance().amount.0 == amount) + ); + } else { + assert!(outputs[0].balance().amount.0 == amount); + } + } // Check the eventualities pass for eventuality in eventualities { diff --git a/processor/src/tests/wallet.rs b/processor/src/tests/wallet.rs index acd3cb65..86a27349 100644 --- a/processor/src/tests/wallet.rs +++ b/processor/src/tests/wallet.rs @@ -3,6 +3,7 @@ use std::collections::HashMap; use rand_core::OsRng; +use ciphersuite::group::GroupEncoding; use frost::{Participant, dkg::tests::key_gen}; use tokio::time::timeout; @@ -16,16 +17,17 @@ use serai_client::{ use crate::{ Payment, Plan, - networks::{Output, Transaction, Eventuality, Block, UtxoNetwork}, + networks::{Output, Transaction, Eventuality, Block, Network}, + key_gen::NetworkKeyDb, multisigs::{ scanner::{ScannerEvent, Scanner}, - scheduler::Scheduler, + scheduler::{self, Scheduler}, }, tests::sign, }; // Tests the Scanner, Scheduler, and Signer together -pub async fn test_wallet( +pub async fn test_wallet( new_network: impl Fn(MemDb) -> Pin>>, ) { let mut keys = key_gen(&mut OsRng); @@ -35,6 +37,11 @@ pub async fn test_wallet( let key = keys[&Participant::new(1).unwrap()].group_key(); let mut db = MemDb::new(); + { + let mut txn = db.txn(); + NetworkKeyDb::set(&mut txn, Session(0), &key.to_bytes().as_ref().to_vec()); + txn.commit(); + } let network = new_network(db.clone()).await; // Mine blocks so there's a confirmed block @@ -98,7 +105,13 @@ pub async fn test_wallet( txn.commit(); assert_eq!(plans.len(), 1); assert_eq!(plans[0].key, key); - assert_eq!(plans[0].inputs, outputs); + if std::any::TypeId::of::() == + std::any::TypeId::of::>() + { + assert_eq!(plans[0].inputs, vec![]); + } else { + assert_eq!(plans[0].inputs, outputs); + } assert_eq!( plans[0].payments, vec![Payment { @@ -115,7 +128,7 @@ pub async fn test_wallet( } }] ); - assert_eq!(plans[0].change, Some(N::change_address(key).unwrap())); + assert_eq!(plans[0].change, N::change_address(key)); { let mut buf = vec![]; @@ -144,9 +157,22 @@ pub async fn test_wallet( let tx = network.get_transaction_by_eventuality(block_number, &eventualities[0]).await; let block = network.get_block(block_number).await.unwrap(); let outputs = network.get_outputs(&block, key).await; - assert_eq!(outputs.len(), 2); - let amount = amount - tx.fee(&network).await; - assert!((outputs[0].balance().amount.0 == amount) || (outputs[1].balance().amount.0 == amount)); + + // Don't run if Ethereum as the received output will revert by the contract + // (and therefore not actually exist) + if N::NETWORK != NetworkId::Ethereum { + assert_eq!(outputs.len(), 1 + usize::from(u8::from(plans[0].change.is_some()))); + // Adjust the amount for the fees + let amount = amount - tx.fee(&network).await; + if plans[0].change.is_some() { + // Check either output since Monero will randomize its output order + assert!( + (outputs[0].balance().amount.0 == amount) || (outputs[1].balance().amount.0 == amount) + ); + } else { + assert!(outputs[0].balance().amount.0 == amount); + } + } for eventuality in eventualities { let completion = network.confirm_completion(&eventuality, &claim).await.unwrap().unwrap(); @@ -157,21 +183,23 @@ pub async fn test_wallet( network.mine_block().await; } - match timeout(Duration::from_secs(30), scanner.events.recv()).await.unwrap().unwrap() { - ScannerEvent::Block { is_retirement_block, block: block_id, outputs: these_outputs } => { - scanner.multisig_completed.send(false).unwrap(); - assert!(!is_retirement_block); - assert_eq!(block_id, block.id()); - assert_eq!(these_outputs, outputs); + if N::NETWORK != NetworkId::Ethereum { + match timeout(Duration::from_secs(30), scanner.events.recv()).await.unwrap().unwrap() { + ScannerEvent::Block { is_retirement_block, block: block_id, outputs: these_outputs } => { + scanner.multisig_completed.send(false).unwrap(); + assert!(!is_retirement_block); + assert_eq!(block_id, block.id()); + assert_eq!(these_outputs, outputs); + } + ScannerEvent::Completed(_, _, _, _, _) => { + panic!("unexpectedly got eventuality completion"); + } } - ScannerEvent::Completed(_, _, _, _, _) => { - panic!("unexpectedly got eventuality completion"); - } - } - // Check the Scanner DB can reload the outputs - let mut txn = db.txn(); - assert_eq!(scanner.ack_block(&mut txn, block.id()).await.1, outputs); - scanner.release_lock().await; - txn.commit(); + // Check the Scanner DB can reload the outputs + let mut txn = db.txn(); + assert_eq!(scanner.ack_block(&mut txn, block.id()).await.1, outputs); + scanner.release_lock().await; + txn.commit(); + } } From af795864880a966e768cbdcb35f2d9870e69e514 Mon Sep 17 00:00:00 2001 From: Luke Parker Date: Tue, 14 May 2024 01:33:55 -0400 Subject: [PATCH 6/7] Fill out Ethereum functions in the processor Docker tests --- tests/docker/src/lib.rs | 2 +- tests/processor/Cargo.toml | 2 + tests/processor/src/lib.rs | 194 +++++++++++++++++++++++--------- tests/processor/src/networks.rs | 79 ++++++++++++- 4 files changed, 220 insertions(+), 57 deletions(-) diff --git a/tests/docker/src/lib.rs b/tests/docker/src/lib.rs index ee68b979..3493d502 100644 --- a/tests/docker/src/lib.rs +++ b/tests/docker/src/lib.rs @@ -124,7 +124,7 @@ pub fn build(name: String) { // Check any additionally specified paths let meta = |path: PathBuf| (path.clone(), fs::metadata(path)); let mut metadatas = match name.as_str() { - "bitcoin" | "monero" => vec![], + "bitcoin" | "ethereum" | "monero" => vec![], "message-queue" => vec![ meta(repo_path.join("common")), meta(repo_path.join("crypto")), diff --git a/tests/processor/Cargo.toml b/tests/processor/Cargo.toml index 686dbcea..be27e7de 100644 --- a/tests/processor/Cargo.toml +++ b/tests/processor/Cargo.toml @@ -27,12 +27,14 @@ ciphersuite = { path = "../../crypto/ciphersuite", default-features = false, fea dkg = { path = "../../crypto/dkg", default-features = false, features = ["tests"] } bitcoin-serai = { path = "../../coins/bitcoin" } +ethereum-serai = { path = "../../coins/ethereum" } monero-serai = { path = "../../coins/monero" } messages = { package = "serai-processor-messages", path = "../../processor/messages" } scale = { package = "parity-scale-codec", version = "3" } serai-client = { path = "../../substrate/client" } +serai-db = { path = "../../common/db", default-features = false } serai-message-queue = { path = "../../message-queue" } borsh = { version = "1", features = ["de_strict_order"] } diff --git a/tests/processor/src/lib.rs b/tests/processor/src/lib.rs index e400057a..66aa28c4 100644 --- a/tests/processor/src/lib.rs +++ b/tests/processor/src/lib.rs @@ -181,7 +181,28 @@ impl Coordinator { break; } } - NetworkId::Ethereum => todo!(), + NetworkId::Ethereum => { + use ethereum_serai::{ + alloy_simple_request_transport::SimpleRequest, + alloy_rpc_client::ClientBuilder, + alloy_provider::{Provider, RootProvider}, + alloy_network::Ethereum, + }; + + let provider = RootProvider::<_, Ethereum>::new( + ClientBuilder::default().transport(SimpleRequest::new(rpc_url.clone()), true), + ); + + loop { + if handle + .block_on(provider.raw_request::<_, ()>("evm_setAutomine".into(), [false])) + .is_ok() + { + break; + } + handle.block_on(tokio::time::sleep(core::time::Duration::from_secs(1))); + } + } NetworkId::Monero => { use monero_serai::rpc::HttpRpc; @@ -271,7 +292,45 @@ impl Coordinator { block.consensus_encode(&mut block_buf).unwrap(); (hash, block_buf) } - NetworkId::Ethereum => todo!(), + NetworkId::Ethereum => { + use ethereum_serai::{ + alloy_simple_request_transport::SimpleRequest, + alloy_rpc_types::BlockNumberOrTag, + alloy_rpc_client::ClientBuilder, + alloy_provider::{Provider, RootProvider}, + alloy_network::Ethereum, + }; + + let provider = RootProvider::<_, Ethereum>::new( + ClientBuilder::default().transport(SimpleRequest::new(rpc_url.clone()), true), + ); + let start = provider + .get_block(BlockNumberOrTag::Latest.into(), false) + .await + .unwrap() + .unwrap() + .header + .number + .unwrap(); + // We mine 96 blocks to mine one epoch, then cause its finalization + provider.raw_request::<_, ()>("anvil_mine".into(), [96]).await.unwrap(); + let end_of_epoch = start + 31; + let hash = provider + .get_block(BlockNumberOrTag::Number(end_of_epoch).into(), false) + .await + .unwrap() + .unwrap() + .header + .hash + .unwrap(); + + let state = provider + .raw_request::<_, String>("anvil_dumpState".into(), ()) + .await + .unwrap() + .into_bytes(); + (hash.into(), state) + } NetworkId::Monero => { use curve25519_dalek::{constants::ED25519_BASEPOINT_POINT, scalar::Scalar}; use monero_serai::{ @@ -303,39 +362,6 @@ impl Coordinator { } } - pub async fn broadcast_block(&self, ops: &DockerOperations, block: &[u8]) { - let rpc_url = network_rpc(self.network, ops, &self.network_handle); - match self.network { - NetworkId::Bitcoin => { - use bitcoin_serai::rpc::Rpc; - - let rpc = - Rpc::new(rpc_url).await.expect("couldn't connect to the coordinator's Bitcoin RPC"); - let res: Option = - rpc.rpc_call("submitblock", serde_json::json!([hex::encode(block)])).await.unwrap(); - if let Some(err) = res { - panic!("submitblock failed: {err}"); - } - } - NetworkId::Ethereum => todo!(), - NetworkId::Monero => { - use monero_serai::rpc::HttpRpc; - - let rpc = - HttpRpc::new(rpc_url).await.expect("couldn't connect to the coordinator's Monero RPC"); - let res: serde_json::Value = rpc - .json_rpc_call("submit_block", Some(serde_json::json!([hex::encode(block)]))) - .await - .unwrap(); - let err = res.get("error"); - if err.is_some() && (err.unwrap() != &serde_json::Value::Null) { - panic!("failed to submit Monero block: {res}"); - } - } - NetworkId::Serai => panic!("processor tests broadcasting block to Serai"), - } - } - pub async fn sync(&self, ops: &DockerOperations, others: &[Coordinator]) { let rpc_url = network_rpc(self.network, ops, &self.network_handle); match self.network { @@ -345,13 +371,8 @@ impl Coordinator { let rpc = Rpc::new(rpc_url).await.expect("couldn't connect to the Bitcoin RPC"); let to = rpc.get_latest_block_number().await.unwrap(); for coordinator in others { - let from = Rpc::new(network_rpc(self.network, ops, &coordinator.network_handle)) - .await - .expect("couldn't connect to the Bitcoin RPC") - .get_latest_block_number() - .await - .unwrap() + - 1; + let from = rpc.get_latest_block_number().await.unwrap() + 1; + for b in from ..= to { let mut buf = vec![]; rpc @@ -360,11 +381,40 @@ impl Coordinator { .unwrap() .consensus_encode(&mut buf) .unwrap(); - coordinator.broadcast_block(ops, &buf).await; + + let rpc_url = network_rpc(coordinator.network, ops, &coordinator.network_handle); + let rpc = + Rpc::new(rpc_url).await.expect("couldn't connect to the coordinator's Bitcoin RPC"); + + let res: Option = + rpc.rpc_call("submitblock", serde_json::json!([hex::encode(buf)])).await.unwrap(); + if let Some(err) = res { + panic!("submitblock failed: {err}"); + } } } } - NetworkId::Ethereum => todo!(), + NetworkId::Ethereum => { + use ethereum_serai::{ + alloy_simple_request_transport::SimpleRequest, + alloy_rpc_client::ClientBuilder, + alloy_provider::{Provider, RootProvider}, + alloy_network::Ethereum, + }; + + let provider = RootProvider::<_, Ethereum>::new( + ClientBuilder::default().transport(SimpleRequest::new(rpc_url.clone()), true), + ); + let state = provider.raw_request::<_, String>("anvil_dumpState".into(), ()).await.unwrap(); + + for coordinator in others { + let rpc_url = network_rpc(coordinator.network, ops, &coordinator.network_handle); + let provider = RootProvider::<_, Ethereum>::new( + ClientBuilder::default().transport(SimpleRequest::new(rpc_url.clone()), true), + ); + provider.raw_request::<_, ()>("anvil_loadState".into(), &state).await.unwrap(); + } + } NetworkId::Monero => { use monero_serai::rpc::HttpRpc; @@ -378,12 +428,21 @@ impl Coordinator { .await .unwrap(); for b in from .. to { - coordinator - .broadcast_block( - ops, - &rpc.get_block(rpc.get_block_hash(b).await.unwrap()).await.unwrap().serialize(), - ) - .await; + let block = + rpc.get_block(rpc.get_block_hash(b).await.unwrap()).await.unwrap().serialize(); + + let rpc_url = network_rpc(coordinator.network, ops, &coordinator.network_handle); + let rpc = HttpRpc::new(rpc_url) + .await + .expect("couldn't connect to the coordinator's Monero RPC"); + let res: serde_json::Value = rpc + .json_rpc_call("submit_block", Some(serde_json::json!([hex::encode(block)]))) + .await + .unwrap(); + let err = res.get("error"); + if err.is_some() && (err.unwrap() != &serde_json::Value::Null) { + panic!("failed to submit Monero block: {res}"); + } } } } @@ -404,7 +463,19 @@ impl Coordinator { Rpc::new(rpc_url).await.expect("couldn't connect to the coordinator's Bitcoin RPC"); rpc.send_raw_transaction(&Transaction::consensus_decode(&mut &*tx).unwrap()).await.unwrap(); } - NetworkId::Ethereum => todo!(), + NetworkId::Ethereum => { + use ethereum_serai::{ + alloy_simple_request_transport::SimpleRequest, + alloy_rpc_client::ClientBuilder, + alloy_provider::{Provider, RootProvider}, + alloy_network::Ethereum, + }; + + let provider = RootProvider::<_, Ethereum>::new( + ClientBuilder::default().transport(SimpleRequest::new(rpc_url.clone()), true), + ); + let _ = provider.send_raw_transaction(tx).await.unwrap(); + } NetworkId::Monero => { use monero_serai::{transaction::Transaction, rpc::HttpRpc}; @@ -445,7 +516,26 @@ impl Coordinator { None } } - NetworkId::Ethereum => todo!(), + NetworkId::Ethereum => { + use ethereum_serai::{ + alloy_simple_request_transport::SimpleRequest, + alloy_consensus::{TxLegacy, Signed}, + alloy_rpc_client::ClientBuilder, + alloy_provider::{Provider, RootProvider}, + alloy_network::Ethereum, + }; + + let provider = RootProvider::<_, Ethereum>::new( + ClientBuilder::default().transport(SimpleRequest::new(rpc_url.clone()), true), + ); + let mut hash = [0; 32]; + hash.copy_from_slice(tx); + let tx = provider.get_transaction_by_hash(hash.into()).await.unwrap()?; + let (tx, sig, _) = Signed::::try_from(tx).unwrap().into_parts(); + let mut bytes = vec![]; + tx.encode_with_signature_fields(&sig, &mut bytes); + Some(bytes) + } NetworkId::Monero => { use monero_serai::rpc::HttpRpc; diff --git a/tests/processor/src/networks.rs b/tests/processor/src/networks.rs index 882b9e89..7a81062a 100644 --- a/tests/processor/src/networks.rs +++ b/tests/processor/src/networks.rs @@ -19,6 +19,7 @@ pub const RPC_USER: &str = "serai"; pub const RPC_PASS: &str = "seraidex"; pub const BTC_PORT: u32 = 8332; +pub const ETH_PORT: u32 = 8545; pub const XMR_PORT: u32 = 18081; pub fn bitcoin_instance() -> (TestBodySpecification, u32) { @@ -31,6 +32,17 @@ pub fn bitcoin_instance() -> (TestBodySpecification, u32) { (composition, BTC_PORT) } +pub fn ethereum_instance() -> (TestBodySpecification, u32) { + serai_docker_tests::build("ethereum".to_string()); + + let composition = TestBodySpecification::with_image( + Image::with_repository("serai-dev-ethereum").pull_policy(PullPolicy::Never), + ) + .set_start_policy(StartPolicy::Strict) + .set_publish_all_ports(true); + (composition, ETH_PORT) +} + pub fn monero_instance() -> (TestBodySpecification, u32) { serai_docker_tests::build("monero".to_string()); @@ -45,7 +57,7 @@ pub fn monero_instance() -> (TestBodySpecification, u32) { pub fn network_instance(network: NetworkId) -> (TestBodySpecification, u32) { match network { NetworkId::Bitcoin => bitcoin_instance(), - NetworkId::Ethereum => todo!(), + NetworkId::Ethereum => ethereum_instance(), NetworkId::Monero => monero_instance(), NetworkId::Serai => { panic!("Serai is not a valid network to spawn an instance of for a processor") @@ -58,7 +70,7 @@ pub fn network_rpc(network: NetworkId, ops: &DockerOperations, handle: &str) -> .handle(handle) .host_port(match network { NetworkId::Bitcoin => BTC_PORT, - NetworkId::Ethereum => todo!(), + NetworkId::Ethereum => ETH_PORT, NetworkId::Monero => XMR_PORT, NetworkId::Serai => panic!("getting port for external network yet it was Serai"), }) @@ -70,7 +82,7 @@ pub fn confirmations(network: NetworkId) -> usize { use processor::networks::*; match network { NetworkId::Bitcoin => Bitcoin::CONFIRMATIONS, - NetworkId::Ethereum => todo!(), + NetworkId::Ethereum => Ethereum::::CONFIRMATIONS, NetworkId::Monero => Monero::CONFIRMATIONS, NetworkId::Serai => panic!("getting confirmations required for Serai"), } @@ -83,6 +95,10 @@ pub enum Wallet { public_key: bitcoin_serai::bitcoin::PublicKey, input_tx: bitcoin_serai::bitcoin::Transaction, }, + Ethereum { + key: ::F, + nonce: u64, + }, Monero { handle: String, spend_key: Zeroizing, @@ -138,7 +154,37 @@ impl Wallet { Wallet::Bitcoin { private_key, public_key, input_tx: funds } } - NetworkId::Ethereum => todo!(), + NetworkId::Ethereum => { + use ciphersuite::{group::ff::Field, Ciphersuite, Secp256k1}; + use ethereum_serai::{ + alloy_core::primitives::{U256, Address}, + alloy_simple_request_transport::SimpleRequest, + alloy_rpc_client::ClientBuilder, + alloy_provider::{Provider, RootProvider}, + alloy_network::Ethereum, + }; + + let key = ::F::random(&mut OsRng); + let address = + ethereum_serai::crypto::address(&(::generator() * key)); + + let provider = RootProvider::<_, Ethereum>::new( + ClientBuilder::default().transport(SimpleRequest::new(rpc_url.clone()), true), + ); + + provider + .raw_request::<_, ()>( + "anvil_setBalance".into(), + [Address(address.into()).to_string(), { + let nine_decimals = U256::from(1_000_000_000u64); + (U256::from(100u64) * nine_decimals * nine_decimals).to_string() + }], + ) + .await + .unwrap(); + + Wallet::Ethereum { key, nonce: 0 } + } NetworkId::Monero => { use curve25519_dalek::{constants::ED25519_BASEPOINT_POINT, scalar::Scalar}; @@ -282,6 +328,24 @@ impl Wallet { (buf, Balance { coin: Coin::Bitcoin, amount: Amount(AMOUNT) }) } + Wallet::Ethereum { key, ref mut nonce } => { + /* + use ethereum_serai::alloy_core::primitives::U256; + + let eight_decimals = U256::from(100_000_000u64); + let nine_decimals = eight_decimals * U256::from(10u64); + let eighteen_decimals = nine_decimals * nine_decimals; + + let tx = todo!("send to router"); + + *nonce += 1; + (tx, Balance { coin: Coin::Ether, amount: Amount(u64::try_from(eight_decimals).unwrap()) }) + */ + let _ = key; + let _ = nonce; + todo!() + } + Wallet::Monero { handle, ref spend_key, ref view_pair, ref mut inputs } => { use curve25519_dalek::constants::ED25519_BASEPOINT_POINT; use monero_serai::{ @@ -374,6 +438,13 @@ impl Wallet { ) .unwrap() } + Wallet::Ethereum { key, .. } => { + use ciphersuite::{Ciphersuite, Secp256k1}; + ExternalAddress::new( + ethereum_serai::crypto::address(&(Secp256k1::generator() * key)).into(), + ) + .unwrap() + } Wallet::Monero { view_pair, .. } => { use monero_serai::wallet::address::{Network, AddressSpec}; ExternalAddress::new( From ae8a27b8767ac4e5892af854b2e48c78d6851570 Mon Sep 17 00:00:00 2001 From: Luke Parker Date: Tue, 14 May 2024 01:42:18 -0400 Subject: [PATCH 7/7] Add our own alloy meta module to deduplicate alloy prefixes --- Cargo.lock | 2 ++ coins/ethereum/src/lib.rs | 19 ++++++----- processor/src/networks/ethereum.rs | 26 +++++++------- processor/src/tests/literal/mod.rs | 10 +++--- tests/processor/src/lib.rs | 54 +++++++++++++++--------------- tests/processor/src/networks.rs | 14 ++++---- 6 files changed, 67 insertions(+), 58 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 719ed497..75d6b7cc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8068,11 +8068,13 @@ dependencies = [ "curve25519-dalek", "dkg", "dockertest", + "ethereum-serai", "hex", "monero-serai", "parity-scale-codec", "rand_core", "serai-client", + "serai-db", "serai-docker-tests", "serai-message-queue", "serai-message-queue-tests", diff --git a/coins/ethereum/src/lib.rs b/coins/ethereum/src/lib.rs index eda54c72..322b5f83 100644 --- a/coins/ethereum/src/lib.rs +++ b/coins/ethereum/src/lib.rs @@ -1,14 +1,17 @@ use thiserror::Error; -pub use alloy_core; -pub use alloy_sol_types; +pub mod alloy { + pub use alloy_core::primitives; + pub use alloy_core as core; + pub use alloy_sol_types as sol_types; -pub use alloy_consensus; -pub use alloy_network; -pub use alloy_rpc_types; -pub use alloy_simple_request_transport; -pub use alloy_rpc_client; -pub use alloy_provider; + pub use alloy_consensus as consensus; + pub use alloy_network as network; + pub use alloy_rpc_types as rpc_types; + pub use alloy_simple_request_transport as simple_request_transport; + pub use alloy_rpc_client as rpc_client; + pub use alloy_provider as provider; +} pub mod crypto; diff --git a/processor/src/networks/ethereum.rs b/processor/src/networks/ethereum.rs index f3d562d7..7ffe7041 100644 --- a/processor/src/networks/ethereum.rs +++ b/processor/src/networks/ethereum.rs @@ -11,11 +11,13 @@ use ciphersuite::{group::GroupEncoding, Ciphersuite, Secp256k1}; use frost::ThresholdKeys; use ethereum_serai::{ - alloy_core::primitives::U256, - alloy_rpc_types::{BlockNumberOrTag, Transaction}, - alloy_simple_request_transport::SimpleRequest, - alloy_rpc_client::ClientBuilder, - alloy_provider::{Provider, RootProvider}, + alloy::{ + primitives::U256, + rpc_types::{BlockNumberOrTag, Transaction}, + simple_request_transport::SimpleRequest, + rpc_client::ClientBuilder, + provider::{Provider, RootProvider}, + }, crypto::{PublicKey, Signature}, erc20::Erc20, deployer::Deployer, @@ -23,7 +25,7 @@ use ethereum_serai::{ machine::*, }; #[cfg(test)] -use ethereum_serai::alloy_core::primitives::B256; +use ethereum_serai::alloy::primitives::B256; use tokio::{ time::sleep, @@ -112,7 +114,7 @@ impl TryInto> for Address { impl fmt::Display for Address { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - ethereum_serai::alloy_core::primitives::Address::from(self.0).fmt(f) + ethereum_serai::alloy::primitives::Address::from(self.0).fmt(f) } } @@ -181,7 +183,7 @@ impl Output> for EthereumInInstruction { let mut id = [0; 40]; id[.. 32].copy_from_slice(&self.id.0); id[32 ..].copy_from_slice(&self.id.1.to_le_bytes()); - *ethereum_serai::alloy_core::primitives::keccak256(id) + *ethereum_serai::alloy::primitives::keccak256(id) } fn tx_id(&self) -> [u8; 32] { self.id.0 @@ -853,7 +855,7 @@ impl Network for Ethereum { async fn test_send(&self, send_to: Self::Address) -> Self::Block { use rand_core::OsRng; use ciphersuite::group::ff::Field; - use ethereum_serai::alloy_sol_types::SolCall; + use ethereum_serai::alloy::sol_types::SolCall; let key = ::F::random(&mut OsRng); let address = ethereum_serai::crypto::address(&(Secp256k1::generator() * key)); @@ -869,12 +871,12 @@ impl Network for Ethereum { .unwrap(); let value = U256::from_str_radix("1000000000000000000", 10).unwrap(); - let tx = ethereum_serai::alloy_consensus::TxLegacy { + let tx = ethereum_serai::alloy::consensus::TxLegacy { chain_id: None, nonce: 0, gas_price: 1_000_000_000u128, gas_limit: 200_000u128, - to: ethereum_serai::alloy_core::primitives::TxKind::Call(send_to.0.into()), + to: ethereum_serai::alloy::primitives::TxKind::Call(send_to.0.into()), // 1 ETH value, input: ethereum_serai::router::abi::inInstructionCall::new(( @@ -886,7 +888,7 @@ impl Network for Ethereum { .into(), }; - use ethereum_serai::alloy_consensus::SignableTransaction; + use ethereum_serai::alloy::consensus::SignableTransaction; let sig = k256::ecdsa::SigningKey::from(k256::elliptic_curve::NonZeroScalar::new(key).unwrap()) .sign_prehash_recoverable(tx.signature_hash().as_ref()) .unwrap(); diff --git a/processor/src/tests/literal/mod.rs b/processor/src/tests/literal/mod.rs index cecd5a3b..5c5f3203 100644 --- a/processor/src/tests/literal/mod.rs +++ b/processor/src/tests/literal/mod.rs @@ -314,10 +314,12 @@ mod ethereum { ) -> impl Fn(MemDb) -> Pin>>> { use std::sync::Arc; use ethereum_serai::{ - alloy_core::primitives::U256, - alloy_simple_request_transport::SimpleRequest, - alloy_rpc_client::ClientBuilder, - alloy_provider::{Provider, RootProvider}, + alloy::{ + primitives::U256, + simple_request_transport::SimpleRequest, + rpc_client::ClientBuilder, + provider::{Provider, RootProvider}, + }, deployer::Deployer, }; diff --git a/tests/processor/src/lib.rs b/tests/processor/src/lib.rs index 66aa28c4..5e854272 100644 --- a/tests/processor/src/lib.rs +++ b/tests/processor/src/lib.rs @@ -182,11 +182,11 @@ impl Coordinator { } } NetworkId::Ethereum => { - use ethereum_serai::{ - alloy_simple_request_transport::SimpleRequest, - alloy_rpc_client::ClientBuilder, - alloy_provider::{Provider, RootProvider}, - alloy_network::Ethereum, + use ethereum_serai::alloy::{ + simple_request_transport::SimpleRequest, + rpc_client::ClientBuilder, + provider::{Provider, RootProvider}, + network::Ethereum, }; let provider = RootProvider::<_, Ethereum>::new( @@ -293,12 +293,12 @@ impl Coordinator { (hash, block_buf) } NetworkId::Ethereum => { - use ethereum_serai::{ - alloy_simple_request_transport::SimpleRequest, - alloy_rpc_types::BlockNumberOrTag, - alloy_rpc_client::ClientBuilder, - alloy_provider::{Provider, RootProvider}, - alloy_network::Ethereum, + use ethereum_serai::alloy::{ + simple_request_transport::SimpleRequest, + rpc_types::BlockNumberOrTag, + rpc_client::ClientBuilder, + provider::{Provider, RootProvider}, + network::Ethereum, }; let provider = RootProvider::<_, Ethereum>::new( @@ -395,11 +395,11 @@ impl Coordinator { } } NetworkId::Ethereum => { - use ethereum_serai::{ - alloy_simple_request_transport::SimpleRequest, - alloy_rpc_client::ClientBuilder, - alloy_provider::{Provider, RootProvider}, - alloy_network::Ethereum, + use ethereum_serai::alloy::{ + simple_request_transport::SimpleRequest, + rpc_client::ClientBuilder, + provider::{Provider, RootProvider}, + network::Ethereum, }; let provider = RootProvider::<_, Ethereum>::new( @@ -464,11 +464,11 @@ impl Coordinator { rpc.send_raw_transaction(&Transaction::consensus_decode(&mut &*tx).unwrap()).await.unwrap(); } NetworkId::Ethereum => { - use ethereum_serai::{ - alloy_simple_request_transport::SimpleRequest, - alloy_rpc_client::ClientBuilder, - alloy_provider::{Provider, RootProvider}, - alloy_network::Ethereum, + use ethereum_serai::alloy::{ + simple_request_transport::SimpleRequest, + rpc_client::ClientBuilder, + provider::{Provider, RootProvider}, + network::Ethereum, }; let provider = RootProvider::<_, Ethereum>::new( @@ -517,12 +517,12 @@ impl Coordinator { } } NetworkId::Ethereum => { - use ethereum_serai::{ - alloy_simple_request_transport::SimpleRequest, - alloy_consensus::{TxLegacy, Signed}, - alloy_rpc_client::ClientBuilder, - alloy_provider::{Provider, RootProvider}, - alloy_network::Ethereum, + use ethereum_serai::alloy::{ + consensus::{TxLegacy, Signed}, + simple_request_transport::SimpleRequest, + rpc_client::ClientBuilder, + provider::{Provider, RootProvider}, + network::Ethereum, }; let provider = RootProvider::<_, Ethereum>::new( diff --git a/tests/processor/src/networks.rs b/tests/processor/src/networks.rs index 7a81062a..5b54cc01 100644 --- a/tests/processor/src/networks.rs +++ b/tests/processor/src/networks.rs @@ -156,12 +156,12 @@ impl Wallet { NetworkId::Ethereum => { use ciphersuite::{group::ff::Field, Ciphersuite, Secp256k1}; - use ethereum_serai::{ - alloy_core::primitives::{U256, Address}, - alloy_simple_request_transport::SimpleRequest, - alloy_rpc_client::ClientBuilder, - alloy_provider::{Provider, RootProvider}, - alloy_network::Ethereum, + use ethereum_serai::alloy::{ + primitives::{U256, Address}, + simple_request_transport::SimpleRequest, + rpc_client::ClientBuilder, + provider::{Provider, RootProvider}, + network::Ethereum, }; let key = ::F::random(&mut OsRng); @@ -330,7 +330,7 @@ impl Wallet { Wallet::Ethereum { key, ref mut nonce } => { /* - use ethereum_serai::alloy_core::primitives::U256; + use ethereum_serai::alloy::primitives::U256; let eight_decimals = U256::from(100_000_000u64); let nine_decimals = eight_decimals * U256::from(10u64);