diff --git a/Cargo.lock b/Cargo.lock index 75d6b7cc..bf98299d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3706,6 +3706,7 @@ dependencies = [ "elliptic-curve", "once_cell", "sha2", + "signature", ] [[package]] @@ -8070,6 +8071,7 @@ dependencies = [ "dockertest", "ethereum-serai", "hex", + "k256", "monero-serai", "parity-scale-codec", "rand_core", diff --git a/coins/ethereum/Cargo.toml b/coins/ethereum/Cargo.toml index dc30764e..f600c21d 100644 --- a/coins/ethereum/Cargo.toml +++ b/coins/ethereum/Cargo.toml @@ -46,4 +46,4 @@ tokio = { version = "1", features = ["macros"] } alloy-node-bindings = { git = "https://github.com/alloy-rs/alloy", rev = "b79db21734cffddc11753fe62ba571565c896f42", default-features = false } [features] -tests = ["alloy-node-bindings"] +tests = ["alloy-node-bindings", "frost/tests"] diff --git a/orchestration/src/coordinator.rs b/orchestration/src/coordinator.rs index 13fdff59..26058886 100644 --- a/orchestration/src/coordinator.rs +++ b/orchestration/src/coordinator.rs @@ -17,6 +17,7 @@ pub fn coordinator( let longer_reattempts = if network == Network::Dev { "longer-reattempts" } else { "" }; let setup = mimalloc(Os::Debian).to_string() + &build_serai_service( + "", network.release(), &format!("{db} {longer_reattempts}"), "serai-coordinator", diff --git a/orchestration/src/main.rs b/orchestration/src/main.rs index 0e6c7cb0..1925b94c 100644 --- a/orchestration/src/main.rs +++ b/orchestration/src/main.rs @@ -137,7 +137,7 @@ WORKDIR /home/{user} } } -fn build_serai_service(release: bool, features: &str, package: &str) -> String { +fn build_serai_service(prelude: &str, release: bool, features: &str, package: &str) -> String { let profile = if release { "release" } else { "debug" }; let profile_flag = if release { "--release" } else { "" }; @@ -159,6 +159,8 @@ RUN apt install -y make protobuf-compiler # Add the wasm toolchain RUN rustup target add wasm32-unknown-unknown +{prelude} + # Add files for build ADD patches /serai/patches ADD common /serai/common diff --git a/orchestration/src/message_queue.rs b/orchestration/src/message_queue.rs index eb662b67..ea97a619 100644 --- a/orchestration/src/message_queue.rs +++ b/orchestration/src/message_queue.rs @@ -13,7 +13,7 @@ pub fn message_queue( monero_key: ::G, ) { let setup = mimalloc(Os::Debian).to_string() + - &build_serai_service(network.release(), network.db(), "serai-message-queue"); + &build_serai_service("", network.release(), network.db(), "serai-message-queue"); let env_vars = [ ("COORDINATOR_KEY", hex::encode(coordinator_key.to_bytes())), diff --git a/orchestration/src/processor.rs b/orchestration/src/processor.rs index 8a2c8c77..85f7ec5f 100644 --- a/orchestration/src/processor.rs +++ b/orchestration/src/processor.rs @@ -17,6 +17,15 @@ pub fn processor( ) { let setup = mimalloc(Os::Debian).to_string() + &build_serai_service( + if coin == "ethereum" { + r#" +RUN cargo install svm-rs +RUN svm install 0.8.25 +RUN svm use 0.8.25 +"# + } else { + "" + }, network.release(), &format!("binaries {} {coin}", network.db()), "serai-processor", @@ -34,7 +43,7 @@ RUN apt install -y ca-certificates let hostname = format!("serai-{}-{coin}", network.label()); let port = match coin { "bitcoin" => 8332, - "ethereum" => return, // TODO + "ethereum" => 8545, "monero" => 18081, _ => panic!("unrecognized external network"), }; diff --git a/orchestration/src/serai.rs b/orchestration/src/serai.rs index 2e1e915c..e2f96f6a 100644 --- a/orchestration/src/serai.rs +++ b/orchestration/src/serai.rs @@ -11,9 +11,9 @@ pub fn serai( serai_key: &Zeroizing<::F>, ) { // Always builds in release for performance reasons - let setup = mimalloc(Os::Debian).to_string() + &build_serai_service(true, "", "serai-node"); + let setup = mimalloc(Os::Debian).to_string() + &build_serai_service("", true, "", "serai-node"); let setup_fast_epoch = - mimalloc(Os::Debian).to_string() + &build_serai_service(true, "fast-epoch", "serai-node"); + mimalloc(Os::Debian).to_string() + &build_serai_service("", true, "fast-epoch", "serai-node"); let env_vars = [("KEY", hex::encode(serai_key.to_repr()))]; let mut env_vars_str = String::new(); diff --git a/processor/src/key_gen.rs b/processor/src/key_gen.rs index f1a5b47c..6976e225 100644 --- a/processor/src/key_gen.rs +++ b/processor/src/key_gen.rs @@ -512,6 +512,7 @@ impl KeyGen { ProcessorMessage::GeneratedKeyPair { id, substrate_key: generated_substrate_key.unwrap().to_bytes(), + // TODO: This can be made more efficient since tweaked keys may be a subset of keys network_key: generated_network_key.unwrap().to_bytes().as_ref().to_vec(), } } diff --git a/processor/src/multisigs/mod.rs b/processor/src/multisigs/mod.rs index 75c91675..12f01715 100644 --- a/processor/src/multisigs/mod.rs +++ b/processor/src/multisigs/mod.rs @@ -63,9 +63,22 @@ fn instruction_from_output( return (presumed_origin, None); } - let Ok(shorthand) = Shorthand::decode(&mut data) else { return (presumed_origin, None) }; - let Ok(instruction) = RefundableInInstruction::try_from(shorthand) else { - return (presumed_origin, None); + let shorthand = match Shorthand::decode(&mut data) { + Ok(shorthand) => shorthand, + Err(e) => { + info!("data in output {} wasn't valid shorthand: {e:?}", hex::encode(output.id())); + return (presumed_origin, None); + } + }; + let instruction = match RefundableInInstruction::try_from(shorthand) { + Ok(instruction) => instruction, + Err(e) => { + info!( + "shorthand in output {} wasn't convertible to a RefundableInInstruction: {e:?}", + hex::encode(output.id()) + ); + return (presumed_origin, None); + } }; let mut balance = output.balance(); diff --git a/processor/src/multisigs/scanner.rs b/processor/src/multisigs/scanner.rs index 3d28f3e8..1b25e108 100644 --- a/processor/src/multisigs/scanner.rs +++ b/processor/src/multisigs/scanner.rs @@ -279,6 +279,8 @@ impl ScannerHandle { activation_number: usize, key: ::G, ) { + info!("Registering key {} in scanner at {activation_number}", hex::encode(key.to_bytes())); + let mut scanner_lock = self.scanner.write().await; let scanner = scanner_lock.as_mut().unwrap(); assert!( @@ -286,8 +288,6 @@ impl ScannerHandle { "activation block of new keys was already scanned", ); - info!("Registering key {} in scanner at {activation_number}", hex::encode(key.to_bytes())); - if scanner.keys.is_empty() { assert!(scanner.ram_scanned.is_none()); scanner.ram_scanned = Some(activation_number); diff --git a/processor/src/multisigs/scheduler/smart_contract.rs b/processor/src/multisigs/scheduler/smart_contract.rs index 4f48e391..3da8acf4 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(1, |nonce| nonce + 1); + let mut nonce = LastNonce::get(txn).unwrap_or(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 diff --git a/processor/src/multisigs/scheduler/utxo.rs b/processor/src/multisigs/scheduler/utxo.rs index e9aa3351..1865cab9 100644 --- a/processor/src/multisigs/scheduler/utxo.rs +++ b/processor/src/multisigs/scheduler/utxo.rs @@ -432,7 +432,7 @@ impl> Scheduler { } // If there's a UTXO to restore, restore it - // This is down now as if there is a to_restore output, and it was inserted into self.utxos + // This is done now as if there is a to_restore output, and it was inserted into self.utxos // earlier, self.utxos.len() may become `N::MAX_INPUTS + 1` // The prior block requires the len to be `<= N::MAX_INPUTS` if let Some(to_restore) = to_restore { @@ -442,9 +442,10 @@ impl> Scheduler { txn.put(scheduler_key::(&self.key), self.serialize()); log::info!( - "created {} plans containing {} payments to sign", + "created {} plans containing {} payments to sign, with {} payments pending scheduling", plans.len(), payments_at_start - self.payments.len(), + self.payments.len(), ); plans } @@ -589,7 +590,8 @@ impl> SchedulerTrait for Scheduler { output: N::Output, refund_to: N::Address, ) -> Plan { - Plan { + let output_id = output.id().as_ref().to_vec(); + let res = Plan { key: output.key(), // Uses a payment as this will still be successfully sent due to fee amortization, // and because change is currently always a Serai key @@ -597,7 +599,9 @@ impl> SchedulerTrait for Scheduler { inputs: vec![output], change: None, scheduler_addendum: (), - } + }; + log::info!("refund plan for {} has ID {}", hex::encode(output_id), hex::encode(res.id())); + res } fn shim_forward_plan(output: N::Output, to: ::G) -> Option> { diff --git a/processor/src/networks/ethereum.rs b/processor/src/networks/ethereum.rs index 7ffe7041..802ea68b 100644 --- a/processor/src/networks/ethereum.rs +++ b/processor/src/networks/ethereum.rs @@ -426,7 +426,7 @@ impl Network for Ethereum { .get_block(BlockNumberOrTag::Finalized.into(), false) .await .map_err(|_| NetworkError::ConnectionError)? - .expect("no blocks were finalized") + .ok_or(NetworkError::ConnectionError)? .header .number .unwrap(); diff --git a/tests/processor/Cargo.toml b/tests/processor/Cargo.toml index be27e7de..e46312c5 100644 --- a/tests/processor/Cargo.toml +++ b/tests/processor/Cargo.toml @@ -23,11 +23,14 @@ zeroize = { version = "1", default-features = false } rand_core = { version = "0.6", default-features = false, features = ["getrandom"] } curve25519-dalek = "4" -ciphersuite = { path = "../../crypto/ciphersuite", default-features = false, features = ["ristretto"] } +ciphersuite = { path = "../../crypto/ciphersuite", default-features = false, features = ["secp256k1", "ristretto"] } dkg = { path = "../../crypto/dkg", default-features = false, features = ["tests"] } bitcoin-serai = { path = "../../coins/bitcoin" } + +k256 = "0.13" ethereum-serai = { path = "../../coins/ethereum" } + monero-serai = { path = "../../coins/monero" } messages = { package = "serai-processor-messages", path = "../../processor/messages" } @@ -43,7 +46,7 @@ serde_json = { version = "1", default-features = false } tokio = { version = "1", features = ["time"] } -processor = { package = "serai-processor", path = "../../processor", features = ["bitcoin", "monero"] } +processor = { package = "serai-processor", path = "../../processor", features = ["bitcoin", "ethereum", "monero"] } dockertest = "0.4" serai-docker-tests = { path = "../docker" } diff --git a/tests/processor/src/lib.rs b/tests/processor/src/lib.rs index 5e854272..6e78e397 100644 --- a/tests/processor/src/lib.rs +++ b/tests/processor/src/lib.rs @@ -61,6 +61,7 @@ pub fn processor_instance( pub type Handles = (String, String, String); pub fn processor_stack( network: NetworkId, + network_hostname_override: Option, ) -> (Handles, ::F, Vec) { let (network_composition, network_rpc_port) = network_instance(network); @@ -113,7 +114,10 @@ pub fn processor_stack( } let processor_composition = compositions.last_mut().unwrap(); - processor_composition.inject_container_name(handles[0].clone(), "NETWORK_RPC_HOSTNAME"); + processor_composition.inject_container_name( + network_hostname_override.unwrap_or_else(|| handles[0].clone()), + "NETWORK_RPC_HOSTNAME", + ); processor_composition.inject_container_name(handles[1].clone(), "MESSAGE_QUEUE_RPC"); ((handles[0].clone(), handles[1].clone(), handles[2].clone()), coord_key, compositions) @@ -182,25 +186,52 @@ impl Coordinator { } } NetworkId::Ethereum => { - use ethereum_serai::alloy::{ - simple_request_transport::SimpleRequest, - rpc_client::ClientBuilder, - provider::{Provider, RootProvider}, - network::Ethereum, + use std::sync::Arc; + use ethereum_serai::{ + alloy::{ + simple_request_transport::SimpleRequest, + rpc_client::ClientBuilder, + provider::{Provider, RootProvider}, + network::Ethereum, + }, + deployer::Deployer, }; - let provider = RootProvider::<_, Ethereum>::new( + let provider = Arc::new(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))); + if handle + .block_on(provider.raw_request::<_, ()>("evm_setAutomine".into(), [false])) + .is_ok() + { + handle.block_on(async { + // Deploy the deployer + let tx = Deployer::deployment_tx(); + let signer = tx.recover_signer().unwrap(); + let (tx, sig, _) = tx.into_parts(); + + provider + .raw_request::<_, ()>( + "anvil_setBalance".into(), + [signer.to_string(), (tx.gas_limit * tx.gas_price).to_string()], + ) + .await + .unwrap(); + + let mut bytes = vec![]; + tx.encode_with_signature_fields(&sig, &mut bytes); + let _ = provider.send_raw_transaction(&bytes).await.unwrap(); + + provider.raw_request::<_, ()>("anvil_mine".into(), [96]).await.unwrap(); + + let _ = Deployer::new(provider.clone()).await.unwrap().unwrap(); + + // Sleep until the actual time is ahead of whatever time is in the epoch we just + // mined + tokio::time::sleep(core::time::Duration::from_secs(30)).await; + }); + break; } } NetworkId::Monero => { @@ -371,7 +402,10 @@ 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.get_latest_block_number().await.unwrap() + 1; + let other_rpc = Rpc::new(network_rpc(self.network, ops, &coordinator.network_handle)) + .await + .expect("couldn't connect to the Bitcoin RPC"); + let from = other_rpc.get_latest_block_number().await.unwrap() + 1; for b in from ..= to { let mut buf = vec![]; @@ -382,12 +416,10 @@ impl Coordinator { .consensus_encode(&mut buf) .unwrap(); - 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(); + let res: Option = other_rpc + .rpc_call("submitblock", serde_json::json!([hex::encode(buf)])) + .await + .unwrap(); if let Some(err) = res { panic!("submitblock failed: {err}"); } @@ -397,22 +429,52 @@ impl Coordinator { NetworkId::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( - ClientBuilder::default().transport(SimpleRequest::new(rpc_url.clone()), true), - ); - let state = provider.raw_request::<_, String>("anvil_dumpState".into(), ()).await.unwrap(); + let (expected_number, state) = { + let provider = RootProvider::<_, Ethereum>::new( + ClientBuilder::default().transport(SimpleRequest::new(rpc_url.clone()), true), + ); + + let expected_number = provider + .get_block(BlockNumberOrTag::Latest.into(), false) + .await + .unwrap() + .unwrap() + .header + .number; + ( + expected_number, + 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(); + assert!(provider + .raw_request::<_, bool>("anvil_loadState".into(), &[&state]) + .await + .unwrap()); + + let new_number = provider + .get_block(BlockNumberOrTag::Latest.into(), false) + .await + .unwrap() + .unwrap() + .header + .number; + + // TODO: https://github.com/foundry-rs/foundry/issues/7955 + let _ = expected_number; + let _ = new_number; + //assert_eq!(expected_number, new_number); } } NetworkId::Monero => { @@ -421,21 +483,17 @@ impl Coordinator { let rpc = HttpRpc::new(rpc_url).await.expect("couldn't connect to the Monero RPC"); let to = rpc.get_height().await.unwrap(); for coordinator in others { - let from = HttpRpc::new(network_rpc(self.network, ops, &coordinator.network_handle)) - .await - .expect("couldn't connect to the Monero RPC") - .get_height() - .await - .unwrap(); + let other_rpc = + HttpRpc::new(network_rpc(coordinator.network, ops, &coordinator.network_handle)) + .await + .expect("couldn't connect to the Monero RPC"); + + let from = other_rpc.get_height().await.unwrap(); for b in from .. to { 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 + let res: serde_json::Value = other_rpc .json_rpc_call("submit_block", Some(serde_json::json!([hex::encode(block)]))) .await .unwrap(); diff --git a/tests/processor/src/networks.rs b/tests/processor/src/networks.rs index 5b54cc01..61762f71 100644 --- a/tests/processor/src/networks.rs +++ b/tests/processor/src/networks.rs @@ -96,6 +96,7 @@ pub enum Wallet { input_tx: bitcoin_serai::bitcoin::Transaction, }, Ethereum { + rpc_url: String, key: ::F, nonce: u64, }, @@ -155,7 +156,7 @@ impl Wallet { } NetworkId::Ethereum => { - use ciphersuite::{group::ff::Field, Ciphersuite, Secp256k1}; + use ciphersuite::{group::ff::Field, Secp256k1}; use ethereum_serai::alloy::{ primitives::{U256, Address}, simple_request_transport::SimpleRequest, @@ -183,7 +184,7 @@ impl Wallet { .await .unwrap(); - Wallet::Ethereum { key, nonce: 0 } + Wallet::Ethereum { rpc_url: rpc_url.clone(), key, nonce: 0 } } NetworkId::Monero => { @@ -328,22 +329,107 @@ impl Wallet { (buf, Balance { coin: Coin::Bitcoin, amount: Amount(AMOUNT) }) } - Wallet::Ethereum { key, ref mut nonce } => { - /* - use ethereum_serai::alloy::primitives::U256; + Wallet::Ethereum { rpc_url, key, ref mut nonce } => { + use std::sync::Arc; + use ethereum_serai::{ + alloy::{ + primitives::{U256, TxKind}, + sol_types::SolCall, + simple_request_transport::SimpleRequest, + consensus::{TxLegacy, SignableTransaction}, + rpc_client::ClientBuilder, + provider::{Provider, RootProvider}, + network::Ethereum, + }, + crypto::PublicKey, + deployer::Deployer, + }; let eight_decimals = U256::from(100_000_000u64); let nine_decimals = eight_decimals * U256::from(10u64); let eighteen_decimals = nine_decimals * nine_decimals; + let one_eth = eighteen_decimals; - let tx = todo!("send to router"); + let provider = Arc::new(RootProvider::<_, Ethereum>::new( + ClientBuilder::default().transport(SimpleRequest::new(rpc_url.clone()), true), + )); + + let to_as_key = PublicKey::new( + ::read_G(&mut to.as_slice()).unwrap(), + ) + .unwrap(); + let router_addr = { + // Find the deployer + let deployer = Deployer::new(provider.clone()).await.unwrap().unwrap(); + + // Find the router, deploying if non-existent + let router = if let Some(router) = + deployer.find_router(provider.clone(), &to_as_key).await.unwrap() + { + router + } else { + let mut tx = deployer.deploy_router(&to_as_key); + tx.gas_price = 1_000_000_000u64.into(); + let tx = ethereum_serai::crypto::deterministically_sign(&tx); + let signer = tx.recover_signer().unwrap(); + let (tx, sig, _) = tx.into_parts(); + + provider + .raw_request::<_, ()>( + "anvil_setBalance".into(), + [signer.to_string(), (tx.gas_limit * tx.gas_price).to_string()], + ) + .await + .unwrap(); + + let mut bytes = vec![]; + tx.encode_with_signature_fields(&sig, &mut bytes); + let _ = provider.send_raw_transaction(&bytes).await.unwrap(); + + provider.raw_request::<_, ()>("anvil_mine".into(), [96]).await.unwrap(); + + deployer.find_router(provider.clone(), &to_as_key).await.unwrap().unwrap() + }; + + router.address() + }; + + let tx = TxLegacy { + chain_id: None, + nonce: *nonce, + gas_price: 1_000_000_000u128, + gas_limit: 200_000u128, + to: TxKind::Call(router_addr.into()), + // 1 ETH + value: one_eth, + input: ethereum_serai::router::abi::inInstructionCall::new(( + [0; 20].into(), + one_eth, + if let Some(instruction) = instruction { + Shorthand::Raw(RefundableInInstruction { origin: None, instruction }).encode().into() + } else { + vec![].into() + }, + )) + .abi_encode() + .into(), + }; *nonce += 1; - (tx, Balance { coin: Coin::Ether, amount: Amount(u64::try_from(eight_decimals).unwrap()) }) - */ - let _ = key; - let _ = nonce; - todo!() + + let sig = + k256::ecdsa::SigningKey::from(k256::elliptic_curve::NonZeroScalar::new(*key).unwrap()) + .sign_prehash_recoverable(tx.signature_hash().as_ref()) + .unwrap(); + + let mut bytes = vec![]; + tx.encode_with_signature_fields(&sig.into(), &mut bytes); + + // We drop the bottom 10 decimals + ( + bytes, + Balance { coin: Coin::Ether, amount: Amount(u64::try_from(eight_decimals).unwrap()) }, + ) } Wallet::Monero { handle, ref spend_key, ref view_pair, ref mut inputs } => { @@ -438,13 +524,10 @@ impl Wallet { ) .unwrap() } - Wallet::Ethereum { key, .. } => { - use ciphersuite::{Ciphersuite, Secp256k1}; - ExternalAddress::new( - ethereum_serai::crypto::address(&(Secp256k1::generator() * key)).into(), - ) - .unwrap() - } + Wallet::Ethereum { key, .. } => ExternalAddress::new( + ethereum_serai::crypto::address(&(ciphersuite::Secp256k1::generator() * key)).into(), + ) + .unwrap(), Wallet::Monero { view_pair, .. } => { use monero_serai::wallet::address::{Network, AddressSpec}; ExternalAddress::new( diff --git a/tests/processor/src/tests/batch.rs b/tests/processor/src/tests/batch.rs index 5729fd73..5397ad2d 100644 --- a/tests/processor/src/tests/batch.rs +++ b/tests/processor/src/tests/batch.rs @@ -17,7 +17,8 @@ use serai_client::{ validator_sets::primitives::Session, }; -use processor::networks::{Network, Bitcoin, Monero}; +use serai_db::MemDb; +use processor::networks::{Network, Bitcoin, Ethereum, Monero}; use crate::{*, tests::*}; @@ -188,7 +189,7 @@ pub(crate) async fn substrate_block( #[test] fn batch_test() { - for network in [NetworkId::Bitcoin, NetworkId::Monero] { + for network in [NetworkId::Bitcoin, NetworkId::Ethereum, NetworkId::Monero] { let (coordinators, test) = new_test(network); test.run(|ops| async move { @@ -245,6 +246,8 @@ fn batch_test() { // The scanner works on a 5s interval, so this leaves a few s for any processing/latency tokio::time::sleep(Duration::from_secs(10)).await; + println!("sent in transaction. with in instruction: {}", instruction.is_some()); + let expected_batch = Batch { network, id: i, @@ -256,10 +259,11 @@ fn batch_test() { coin: balance_sent.coin, amount: Amount( balance_sent.amount.0 - - (2 * if network == NetworkId::Bitcoin { - Bitcoin::COST_TO_AGGREGATE - } else { - Monero::COST_TO_AGGREGATE + (2 * match network { + NetworkId::Bitcoin => Bitcoin::COST_TO_AGGREGATE, + NetworkId::Ethereum => Ethereum::::COST_TO_AGGREGATE, + NetworkId::Monero => Monero::COST_TO_AGGREGATE, + NetworkId::Serai => panic!("minted for Serai?"), }), ), }, @@ -272,6 +276,8 @@ fn batch_test() { }, }; + println!("receiving batch preprocesses..."); + // Make sure the processors picked it up by checking they're trying to sign a batch for it let (mut id, mut preprocesses) = recv_batch_preprocesses(&mut coordinators, Session(0), &expected_batch, 0).await; @@ -291,6 +297,8 @@ fn batch_test() { recv_batch_preprocesses(&mut coordinators, Session(0), &expected_batch, attempt).await; } + println!("signing batch..."); + // Continue with signing the batch let batch = sign_batch(&mut coordinators, key_pair.0 .0, id, preprocesses).await; diff --git a/tests/processor/src/tests/key_gen.rs b/tests/processor/src/tests/key_gen.rs index d50c12b7..7dea0bfd 100644 --- a/tests/processor/src/tests/key_gen.rs +++ b/tests/processor/src/tests/key_gen.rs @@ -144,7 +144,7 @@ pub(crate) async fn key_gen(coordinators: &mut [Coordinator]) -> KeyPair { #[test] fn key_gen_test() { - for network in [NetworkId::Bitcoin, NetworkId::Monero] { + for network in [NetworkId::Bitcoin, NetworkId::Ethereum, NetworkId::Monero] { let (coordinators, test) = new_test(network); test.run(|ops| async move { diff --git a/tests/processor/src/tests/mod.rs b/tests/processor/src/tests/mod.rs index 54a17020..afda97d5 100644 --- a/tests/processor/src/tests/mod.rs +++ b/tests/processor/src/tests/mod.rs @@ -20,8 +20,14 @@ pub(crate) const THRESHOLD: usize = ((COORDINATORS * 2) / 3) + 1; fn new_test(network: NetworkId) -> (Vec<(Handles, ::F)>, DockerTest) { let mut coordinators = vec![]; let mut test = DockerTest::new().with_network(dockertest::Network::Isolated); + let mut eth_handle = None; for _ in 0 .. COORDINATORS { - let (handles, coord_key, compositions) = processor_stack(network); + let (handles, coord_key, compositions) = processor_stack(network, eth_handle.clone()); + // TODO: Remove this once https://github.com/foundry-rs/foundry/issues/7955 + // This has all processors share an Ethereum node until we can sync controlled nodes + if network == NetworkId::Ethereum { + eth_handle = eth_handle.or_else(|| Some(handles.0.clone())); + } coordinators.push((handles, coord_key)); for composition in compositions { test.provide_container(composition); diff --git a/tests/processor/src/tests/send.rs b/tests/processor/src/tests/send.rs index 4d0d3cd6..b764f306 100644 --- a/tests/processor/src/tests/send.rs +++ b/tests/processor/src/tests/send.rs @@ -8,12 +8,15 @@ use dkg::{Participant, tests::clone_without}; use messages::{sign::SignId, SubstrateContext}; use serai_client::{ - primitives::{BlockHash, NetworkId}, + primitives::{BlockHash, NetworkId, Amount, Balance, SeraiAddress}, coins::primitives::{OutInstruction, OutInstructionWithBalance}, - in_instructions::primitives::Batch, + in_instructions::primitives::{InInstruction, InInstructionWithBalance, Batch}, validator_sets::primitives::Session, }; +use serai_db::MemDb; +use processor::networks::{Network, Bitcoin, Ethereum, Monero}; + use crate::{*, tests::*}; #[allow(unused)] @@ -144,7 +147,7 @@ pub(crate) async fn sign_tx( #[test] fn send_test() { - for network in [NetworkId::Bitcoin, NetworkId::Monero] { + for network in [NetworkId::Bitcoin, /* TODO NetworkId::Ethereum, */ NetworkId::Monero] { let (coordinators, test) = new_test(network); test.run(|ops| async move { @@ -173,7 +176,11 @@ fn send_test() { coordinators[0].sync(&ops, &coordinators[1 ..]).await; // Send into the processor's wallet - let (tx, balance_sent) = wallet.send_to_address(&ops, &key_pair.1, None).await; + let mut serai_address = [0; 32]; + OsRng.fill_bytes(&mut serai_address); + let instruction = InInstruction::Transfer(SeraiAddress(serai_address)); + let (tx, balance_sent) = + wallet.send_to_address(&ops, &key_pair.1, Some(instruction.clone())).await; for coordinator in &mut coordinators { coordinator.publish_transacton(&ops, &tx).await; } @@ -192,8 +199,25 @@ fn send_test() { // The scanner works on a 5s interval, so this leaves a few s for any processing/latency tokio::time::sleep(Duration::from_secs(10)).await; - let expected_batch = - Batch { network, id: 0, block: BlockHash(block_with_tx.unwrap()), instructions: vec![] }; + let amount_minted = Amount( + balance_sent.amount.0 - + (2 * match network { + NetworkId::Bitcoin => Bitcoin::COST_TO_AGGREGATE, + NetworkId::Ethereum => Ethereum::::COST_TO_AGGREGATE, + NetworkId::Monero => Monero::COST_TO_AGGREGATE, + NetworkId::Serai => panic!("minted for Serai?"), + }), + ); + + let expected_batch = Batch { + network, + id: 0, + block: BlockHash(block_with_tx.unwrap()), + instructions: vec![InInstructionWithBalance { + instruction, + balance: Balance { coin: balance_sent.coin, amount: amount_minted }, + }], + }; // Make sure the proceessors picked it up by checking they're trying to sign a batch for it let (id, preprocesses) = @@ -221,7 +245,7 @@ fn send_test() { block: substrate_block_num, burns: vec![OutInstructionWithBalance { instruction: OutInstruction { address: wallet.address(), data: None }, - balance: balance_sent, + balance: Balance { coin: balance_sent.coin, amount: amount_minted }, }], batches: vec![batch.batch.id], },