From 31b15dde1758a6ba7d7029ecbd74804180f4800c Mon Sep 17 00:00:00 2001 From: Lu Zhang <8418040+longbowlu@users.noreply.github.com> Date: Wed, 16 Oct 2024 08:51:49 -0700 Subject: [PATCH] [bridge] remove test-cluster's dependency on sui-bridge crate (#19840) ## Description as title. It also removes the transitive dependency of other crates on sui-bridge. ## Test plan tests --- ## Release notes Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required. For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates. - [ ] Protocol: - [ ] Nodes (Validators and Full nodes): - [ ] Indexer: - [ ] JSON-RPC: - [ ] GraphQL: - [ ] CLI: - [ ] Rust SDK: - [ ] REST API: --- Cargo.lock | 1 - crates/sui-bridge/src/e2e_tests/basic.rs | 107 ++++- crates/sui-bridge/src/e2e_tests/test_utils.rs | 414 ++++++++++++++++-- crates/sui-bridge/src/node.rs | 2 + crates/sui-bridge/src/sui_client.rs | 21 +- .../sui-bridge/src/sui_transaction_builder.rs | 69 +-- crates/sui-e2e-tests/tests/bridge_tests.rs | 105 ----- crates/test-cluster/Cargo.toml | 1 - crates/test-cluster/src/lib.rs | 282 +----------- 9 files changed, 543 insertions(+), 459 deletions(-) delete mode 100644 crates/sui-e2e-tests/tests/bridge_tests.rs diff --git a/Cargo.lock b/Cargo.lock index 36d43fe998665..30614cbf7a27b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -15687,7 +15687,6 @@ dependencies = [ "move-binary-format", "prometheus", "rand 0.8.5", - "sui-bridge", "sui-config", "sui-core", "sui-framework", diff --git a/crates/sui-bridge/src/e2e_tests/basic.rs b/crates/sui-bridge/src/e2e_tests/basic.rs index dff912fd39970..abdde652e9c13 100644 --- a/crates/sui-bridge/src/e2e_tests/basic.rs +++ b/crates/sui-bridge/src/e2e_tests/basic.rs @@ -3,10 +3,11 @@ use crate::abi::{eth_sui_bridge, EthBridgeEvent, EthERC20, EthSuiBridge}; use crate::client::bridge_authority_aggregator::BridgeAuthorityAggregator; -use crate::e2e_tests::test_utils::BridgeTestCluster; +use crate::crypto::BridgeAuthorityKeyPair; use crate::e2e_tests::test_utils::{ get_signatures, send_eth_tx_and_get_tx_receipt, BridgeTestClusterBuilder, }; +use crate::e2e_tests::test_utils::{BridgeTestCluster, TestClusterWrapperBuilder}; use crate::eth_transaction_builder::build_eth_transaction; use crate::events::{ SuiBridgeEvent, SuiToEthTokenBridgeV1, TokenTransferApproved, TokenTransferClaimed, @@ -16,11 +17,15 @@ use crate::sui_transaction_builder::build_add_tokens_on_sui_transaction; use crate::types::{AddTokensOnEvmAction, BridgeAction, BridgeActionStatus, SuiToEthBridgeAction}; use crate::utils::publish_and_register_coins_return_add_coins_on_sui_action; use crate::utils::EthSigner; +use crate::BRIDGE_ENABLE_PROTOCOL_VERSION; use eth_sui_bridge::EthSuiBridgeEvents; use ethers::prelude::*; use ethers::types::Address as EthAddress; use move_core_types::ident_str; use std::collections::{HashMap, HashSet}; +use sui_json_rpc_api::BridgeReadApiClient; +use sui_types::crypto::get_key_pair; +use test_cluster::TestClusterBuilder; use std::path::Path; @@ -32,10 +37,12 @@ use sui_json_rpc_types::{ use sui_sdk::wallet_context::WalletContext; use sui_sdk::SuiClient; use sui_types::base_types::{ObjectRef, SuiAddress}; -use sui_types::bridge::{BridgeChainId, BridgeTokenMetadata, BRIDGE_MODULE_NAME, TOKEN_ID_ETH}; +use sui_types::bridge::{ + get_bridge, BridgeChainId, BridgeTokenMetadata, BridgeTrait, BRIDGE_MODULE_NAME, TOKEN_ID_ETH, +}; use sui_types::programmable_transaction_builder::ProgrammableTransactionBuilder; use sui_types::transaction::{ObjectArg, TransactionData}; -use sui_types::{TypeTag, BRIDGE_PACKAGE_ID}; +use sui_types::{TypeTag, BRIDGE_PACKAGE_ID, SUI_BRIDGE_OBJECT_ID}; use tap::TapFallible; use tracing::info; @@ -327,6 +334,100 @@ async fn test_add_new_coins_on_sui_and_eth() { .unwrap(); } +#[tokio::test(flavor = "multi_thread", worker_threads = 8)] +async fn test_create_bridge_state_object() { + let test_cluster = TestClusterBuilder::new() + .with_protocol_version((BRIDGE_ENABLE_PROTOCOL_VERSION - 1).into()) + .with_epoch_duration_ms(20000) + .build() + .await; + + let handles = test_cluster.all_node_handles(); + + // no node has the bridge state object yet + for h in &handles { + h.with(|node| { + assert!(node + .state() + .get_object_cache_reader() + .get_latest_object_ref_or_tombstone(SUI_BRIDGE_OBJECT_ID) + .unwrap() + .is_none()); + }); + } + + // wait until feature is enabled + test_cluster + .wait_for_protocol_version(BRIDGE_ENABLE_PROTOCOL_VERSION.into()) + .await; + // wait until next epoch - authenticator state object is created at the end of the first epoch + // in which it is supported. + test_cluster.wait_for_epoch_all_nodes(2).await; // protocol upgrade completes in epoch 1 + + for h in &handles { + h.with(|node| { + node.state() + .get_object_cache_reader() + .get_latest_object_ref_or_tombstone(SUI_BRIDGE_OBJECT_ID) + .unwrap() + .expect("auth state object should exist"); + }); + } +} + +#[tokio::test] +async fn test_committee_registration() { + telemetry_subscribers::init_for_testing(); + let mut bridge_keys = vec![]; + for _ in 0..=3 { + let (_, kp): (_, BridgeAuthorityKeyPair) = get_key_pair(); + bridge_keys.push(kp); + } + let test_cluster = TestClusterWrapperBuilder::new() + .with_bridge_authority_keys(bridge_keys) + .build() + .await; + + let bridge = get_bridge( + test_cluster + .inner + .fullnode_handle + .sui_node + .state() + .get_object_store(), + ) + .unwrap(); + + // Member should be empty before end of epoch + assert!(bridge.committee().members.contents.is_empty()); + assert_eq!( + test_cluster.inner.swarm.active_validators().count(), + bridge.committee().member_registrations.contents.len() + ); + + test_cluster + .trigger_reconfiguration_if_not_yet_and_assert_bridge_committee_initialized() + .await; +} + +#[tokio::test] +async fn test_bridge_api_compatibility() { + let test_cluster: test_cluster::TestCluster = TestClusterBuilder::new() + .with_protocol_version(BRIDGE_ENABLE_PROTOCOL_VERSION.into()) + .build() + .await; + + test_cluster.trigger_reconfiguration().await; + let client = test_cluster.rpc_client(); + client.get_latest_bridge().await.unwrap(); + // TODO: assert fields in summary + + client + .get_bridge_object_initial_shared_version() + .await + .unwrap(); +} + pub(crate) async fn deposit_native_eth_to_sol_contract( signer: &EthSigner, contract_address: EthAddress, diff --git a/crates/sui-bridge/src/e2e_tests/test_utils.rs b/crates/sui-bridge/src/e2e_tests/test_utils.rs index 95948097e5e6d..187f28d5eb9b0 100644 --- a/crates/sui-bridge/src/e2e_tests/test_utils.rs +++ b/crates/sui-bridge/src/e2e_tests/test_utils.rs @@ -6,13 +6,23 @@ use crate::abi::EthBridgeConfig; use crate::config::default_ed25519_key_pair; use crate::crypto::BridgeAuthorityKeyPair; use crate::crypto::BridgeAuthorityPublicKeyBytes; +use crate::crypto::BridgeAuthoritySignInfo; use crate::events::*; use crate::metrics::BridgeMetrics; use crate::server::BridgeNodePublicMetadata; +use crate::sui_transaction_builder::build_add_tokens_on_sui_transaction; +use crate::sui_transaction_builder::build_committee_register_transaction; use crate::types::BridgeAction; +use crate::types::BridgeCommitteeValiditySignInfo; +use crate::types::CertifiedBridgeAction; +use crate::types::VerifiedCertifiedBridgeAction; use crate::utils::get_eth_signer_client; +use crate::utils::publish_and_register_coins_return_add_coins_on_sui_action; +use crate::utils::wait_for_server_to_be_up; use crate::utils::EthSigner; use ethers::types::Address as EthAddress; +use futures::future::join_all; +use futures::Future; use move_core_types::language_storage::StructTag; use prometheus::Registry; use rand::rngs::SmallRng; @@ -28,21 +38,33 @@ use std::path::PathBuf; use std::process::Command; use std::str::FromStr; use std::sync::Arc; +use sui_json_rpc_api::BridgeReadApiClient; use sui_json_rpc_types::SuiEvent; +use sui_json_rpc_types::SuiExecutionStatus; +use sui_json_rpc_types::SuiTransactionBlockEffectsAPI; use sui_json_rpc_types::SuiTransactionBlockResponse; use sui_json_rpc_types::SuiTransactionBlockResponseOptions; use sui_json_rpc_types::SuiTransactionBlockResponseQuery; use sui_json_rpc_types::TransactionFilter; use sui_sdk::wallet_context::WalletContext; use sui_test_transaction_builder::TestTransactionBuilder; +use sui_types::base_types::ObjectID; +use sui_types::bridge::get_bridge; +use sui_types::bridge::get_bridge_obj_initial_shared_version; use sui_types::bridge::BridgeChainId; +use sui_types::bridge::BridgeSummary; +use sui_types::bridge::BridgeTrait; +use sui_types::bridge::{TOKEN_ID_BTC, TOKEN_ID_ETH, TOKEN_ID_USDC, TOKEN_ID_USDT}; use sui_types::committee::TOTAL_VOTING_POWER; use sui_types::crypto::get_key_pair; +use sui_types::crypto::ToFromBytes; use sui_types::digests::TransactionDigest; -use sui_types::transaction::{ObjectArg, TransactionData}; +use sui_types::object::Object; +use sui_types::transaction::{ObjectArg, Transaction, TransactionData}; use sui_types::SUI_BRIDGE_OBJECT_ID; use tokio::join; use tokio::task::JoinHandle; +use tokio::time::Instant; use tracing::error; use tracing::info; @@ -79,7 +101,7 @@ pub const TEST_PK: &str = "0x4bbbf85ce3377467afe5d46f804f221813b2bb87f24d81f60f1 /// structs that are needed for testing. pub struct BridgeTestCluster { pub num_validators: usize, - pub test_cluster: TestCluster, + pub test_cluster: TestClusterWrapper, bridge_client: SuiBridgeClient, eth_environment: EthBridgeEnvironment, bridge_node_handles: Option>>, @@ -161,8 +183,7 @@ impl BridgeTestClusterBuilder { bridge_keys.push(kp.copy()); bridge_keys_copy.push(kp); } - let start_cluster_task = - tokio::task::spawn(Self::start_test_cluster(bridge_keys, self.num_validators)); + let start_cluster_task = tokio::task::spawn(Self::start_test_cluster(bridge_keys)); let start_eth_env_task = tokio::task::spawn(Self::start_eth_env(bridge_keys_copy)); let (start_cluster_res, start_eth_env_res) = join!(start_cluster_task, start_eth_env_task); let test_cluster = start_cluster_res.unwrap(); @@ -179,9 +200,10 @@ impl BridgeTestClusterBuilder { .await, ); } - let bridge_client = SuiBridgeClient::new(&test_cluster.fullnode_handle.rpc_url, metrics) - .await - .unwrap(); + let bridge_client = + SuiBridgeClient::new(&test_cluster.inner.fullnode_handle.rpc_url, metrics) + .await + .unwrap(); info!( "Bridge committee: {:?}", bridge_client @@ -203,15 +225,11 @@ impl BridgeTestClusterBuilder { } } - async fn start_test_cluster( - bridge_keys: Vec, - num_validators: usize, - ) -> TestCluster { - assert_eq!(bridge_keys.len(), num_validators); - let test_cluster: test_cluster::TestCluster = TestClusterBuilder::new() - .with_num_validators(num_validators) - .with_protocol_version(BRIDGE_ENABLE_PROTOCOL_VERSION.into()) - .build_with_bridge(bridge_keys, true) + async fn start_test_cluster(bridge_keys: Vec) -> TestClusterWrapper { + let test_cluster = TestClusterWrapperBuilder::new() + .with_bridge_authority_keys(bridge_keys) + .with_deploy_tokens(true) + .build() .await; info!("Test cluster built"); test_cluster @@ -261,11 +279,11 @@ impl BridgeTestCluster { } pub fn sui_client(&self) -> &SuiClient { - &self.test_cluster.fullnode_handle.sui_client + &self.test_cluster.inner.fullnode_handle.sui_client } pub fn sui_user_address(&self) -> SuiAddress { - self.test_cluster.get_address_0() + self.test_cluster.inner.get_address_0() } pub fn sui_chain_id(&self) -> BridgeChainId { @@ -289,19 +307,19 @@ impl BridgeTestCluster { } pub fn wallet_mut(&mut self) -> &mut WalletContext { - self.test_cluster.wallet_mut() + self.test_cluster.inner.wallet_mut() } pub fn wallet(&self) -> &WalletContext { - &self.test_cluster.wallet + &self.test_cluster.inner.wallet } pub fn bridge_authority_key(&self, index: usize) -> BridgeAuthorityKeyPair { - self.test_cluster.bridge_authority_keys.as_ref().unwrap()[index].copy() + self.test_cluster.bridge_authority_keys[index].copy() } pub fn sui_rpc_url(&self) -> String { - self.test_cluster.fullnode_handle.rpc_url.clone() + self.test_cluster.inner.fullnode_handle.rpc_url.clone() } pub fn eth_rpc_url(&self) -> String { @@ -317,6 +335,7 @@ impl BridgeTestCluster { sender: SuiAddress, ) -> TestTransactionBuilder { self.test_cluster + .inner .test_transaction_builder_with_sender(sender) .await } @@ -332,6 +351,7 @@ impl BridgeTestCluster { tx_data: &TransactionData, ) -> SuiTransactionBlockResponse { self.test_cluster + .inner .sign_and_execute_transaction(tx_data) .await } @@ -723,18 +743,16 @@ impl Drop for EthBridgeEnvironment { } pub(crate) async fn start_bridge_cluster( - test_cluster: &TestCluster, + test_cluster: &TestClusterWrapper, eth_environment: &EthBridgeEnvironment, approved_governance_actions: Vec>, ) -> Vec> { let bridge_authority_keys = test_cluster .bridge_authority_keys - .as_ref() - .unwrap() .iter() .map(|k| k.copy()) .collect::>(); - let bridge_server_ports = test_cluster.bridge_server_ports.as_ref().unwrap(); + let bridge_server_ports = test_cluster.bridge_server_ports.clone(); assert_eq!(bridge_authority_keys.len(), bridge_server_ports.len()); assert_eq!( bridge_authority_keys.len(), @@ -778,7 +796,7 @@ pub(crate) async fn start_bridge_cluster( eth_contracts_start_block_override: None, }, sui: SuiConfig { - sui_rpc_url: test_cluster.fullnode_handle.rpc_url.clone(), + sui_rpc_url: test_cluster.inner.fullnode_handle.rpc_url.clone(), sui_bridge_chain_id: BridgeChainId::SuiCustom as u8, bridge_client_key_path: None, bridge_client_gas_object: None, @@ -850,3 +868,345 @@ impl Drop for TempDir { } } } + +pub struct TestClusterWrapperBuilder { + protocol_version: u64, + bridge_authority_keys: Vec, + deploy_tokens: bool, +} + +impl TestClusterWrapperBuilder { + pub fn new() -> Self { + Self { + protocol_version: BRIDGE_ENABLE_PROTOCOL_VERSION, + bridge_authority_keys: vec![], + deploy_tokens: false, + } + } + + pub fn with_protocol_version(mut self, version: u64) -> Self { + self.protocol_version = version; + self + } + + pub fn with_bridge_authority_keys(mut self, keys: Vec) -> Self { + self.bridge_authority_keys = keys; + self + } + + pub fn with_deploy_tokens(mut self, deploy_tokens: bool) -> Self { + self.deploy_tokens = deploy_tokens; + self + } + + pub async fn build(self) -> TestClusterWrapper { + assert_ne!(self.bridge_authority_keys.len(), 0); + let num_validators = self.bridge_authority_keys.len(); + let builder = TestClusterBuilder::new().with_protocol_version(self.protocol_version.into()); + + let timer = Instant::now(); + let gas_objects_for_authority_keys = self + .bridge_authority_keys + .iter() + .map(|k| { + let address = SuiAddress::from(k.public()); + Object::with_id_owner_for_testing(ObjectID::random(), address) + }) + .collect::>(); + let mut test_cluster = builder + .with_num_validators(num_validators) + .with_objects(gas_objects_for_authority_keys) + .build() + .await; + info!( + "TestCluster build took {:?} secs", + timer.elapsed().as_secs() + ); + let ref_gas_price = test_cluster.get_reference_gas_price().await; + let bridge_arg = get_mut_bridge_arg(&test_cluster).await.unwrap(); + assert_eq!( + self.bridge_authority_keys.len(), + test_cluster.swarm.active_validators().count() + ); + + // Committee registers themselves + let mut server_ports = vec![]; + let mut tasks = vec![]; + let quorum_driver_api = test_cluster.quorum_driver_api().clone(); + // Reorder the nodes so that the last node has the largest stake. + let validator_with_max_stake = test_cluster + .sui_client() + .governance_api() + .get_committee_info(None) + .await + .unwrap() + .validators + .iter() + .max_by(|a, b| a.0.cmp(&b.0)) + .unwrap() + .0; + let node_with_max_stake = test_cluster + .swarm + .active_validators() + .find(|v| v.config().protocol_public_key() == validator_with_max_stake) + .unwrap(); + let other_nodes = test_cluster + .swarm + .active_validators() + .filter(|v| v.config().protocol_public_key() != validator_with_max_stake) + .collect::>(); + let reordered_nodes = other_nodes + .iter() + .chain(std::iter::once(&node_with_max_stake)); + for (node, kp) in reordered_nodes.zip(self.bridge_authority_keys.iter()) { + let validator_address = node.config().sui_address(); + // create committee registration tx + let gas = test_cluster + .wallet + .get_one_gas_object_owned_by_address(validator_address) + .await + .unwrap() + .unwrap(); + + let server_port = get_available_port("127.0.0.1"); + let server_url = format!("http://127.0.0.1:{}", server_port); + server_ports.push(server_port); + let data = build_committee_register_transaction( + validator_address, + &gas, + bridge_arg, + kp.public().as_bytes().to_vec(), + &server_url, + ref_gas_price, + 1000000000, + ) + .unwrap(); + + let tx = Transaction::from_data_and_signer( + data, + vec![node.config().account_key_pair.keypair()], + ); + let api_clone = quorum_driver_api.clone(); + tasks.push(async move { + api_clone + .execute_transaction_block( + tx, + SuiTransactionBlockResponseOptions::new().with_effects(), + None, + ) + .await + }); + } + + if self.deploy_tokens { + let timer = Instant::now(); + let token_ids = vec![TOKEN_ID_BTC, TOKEN_ID_ETH, TOKEN_ID_USDC, TOKEN_ID_USDT]; + let token_prices = vec![500_000_000u64, 30_000_000u64, 1_000u64, 1_000u64]; + let action = publish_and_register_coins_return_add_coins_on_sui_action( + test_cluster.wallet(), + bridge_arg, + vec![ + Path::new("../../bridge/move/tokens/btc").into(), + Path::new("../../bridge/move/tokens/eth").into(), + Path::new("../../bridge/move/tokens/usdc").into(), + Path::new("../../bridge/move/tokens/usdt").into(), + ], + token_ids, + token_prices, + 0, + ); + let action = action.await; + info!("register tokens took {:?} secs", timer.elapsed().as_secs()); + let sig_map = self + .bridge_authority_keys + .iter() + .map(|key| { + ( + key.public().into(), + BridgeAuthoritySignInfo::new(&action, key).signature, + ) + }) + .collect::>(); + let certified_action = CertifiedBridgeAction::new_from_data_and_sig( + action, + BridgeCommitteeValiditySignInfo { + signatures: sig_map.clone(), + }, + ); + let verifired_action_cert = + VerifiedCertifiedBridgeAction::new_from_verified(certified_action); + let sender_address = test_cluster.get_address_0(); + + await_committee_register_tasks(&test_cluster, tasks).await; + + // Wait until committee is set up + trigger_reconfiguration_if_not_yet_and_assert_bridge_committee_initialized( + &test_cluster, + ) + .await; + + let tx = build_add_tokens_on_sui_transaction( + sender_address, + &test_cluster + .wallet + .get_one_gas_object_owned_by_address(sender_address) + .await + .unwrap() + .unwrap(), + verifired_action_cert, + bridge_arg, + ref_gas_price, + ) + .unwrap(); + + let response = test_cluster.sign_and_execute_transaction(&tx).await; + assert_eq!( + response.effects.unwrap().status(), + &SuiExecutionStatus::Success + ); + info!("Deploy tokens took {:?} secs", timer.elapsed().as_secs()); + } else { + await_committee_register_tasks(&test_cluster, tasks).await; + } + async fn await_committee_register_tasks( + test_cluster: &TestCluster, + tasks: Vec< + impl Future>, + >, + ) { + // The tx may fail if a member tries to register when the committee is already finalized. + // In that case, we just need to check the committee members is not empty since once + // the committee is finalized, it should not be empty. + let responses = join_all(tasks).await; + let mut has_failure = false; + for response in responses { + if response.unwrap().effects.unwrap().status() != &SuiExecutionStatus::Success { + has_failure = true; + } + } + if has_failure { + let bridge_summary = get_bridge_summary(test_cluster).await; + assert_ne!(bridge_summary.committee.members.len(), 0); + } + } + + info!( + "TestCluster build_with_bridge took {:?} secs", + timer.elapsed().as_secs() + ); + TestClusterWrapper { + inner: test_cluster, + bridge_authority_keys: self.bridge_authority_keys, + bridge_server_ports: server_ports, + } + } +} + +impl Default for TestClusterWrapperBuilder { + fn default() -> Self { + Self::new() + } +} +pub struct TestClusterWrapper { + pub inner: TestCluster, + pub bridge_authority_keys: Vec, + pub bridge_server_ports: Vec, +} + +impl TestClusterWrapper { + pub fn authority_keys_clone(&self) -> Vec { + self.bridge_authority_keys + .iter() + .map(|k| k.copy()) + .collect() + } + + pub async fn trigger_reconfiguration_if_not_yet_and_assert_bridge_committee_initialized(&self) { + trigger_reconfiguration_if_not_yet_and_assert_bridge_committee_initialized(&self.inner) + .await + } + + // Wait for bridge node in the cluster to be up and running. + pub async fn wait_for_bridge_cluster_to_be_up(&self, timeout_sec: u64) { + let bridge_ports = self.bridge_server_ports.clone(); + let mut tasks = vec![]; + for port in bridge_ports.iter() { + let server_url = format!("http://127.0.0.1:{}", port); + tasks.push(wait_for_server_to_be_up(server_url, timeout_sec)); + } + join_all(tasks) + .await + .into_iter() + .collect::>>() + .unwrap(); + } + + pub async fn get_mut_bridge_arg(&self) -> Option { + get_mut_bridge_arg(&self.inner).await + } + + pub async fn get_bridge_summary(&self) -> BridgeSummary { + get_bridge_summary(&self.inner).await + } +} + +async fn get_bridge_summary(test_cluster: &TestCluster) -> BridgeSummary { + test_cluster + .sui_client() + .http() + .get_latest_bridge() + .await + .unwrap() +} + +async fn get_mut_bridge_arg(test_cluster: &TestCluster) -> Option { + get_bridge_obj_initial_shared_version( + test_cluster + .fullnode_handle + .sui_node + .state() + .get_object_store(), + ) + .unwrap() + .map(|seq| ObjectArg::SharedObject { + id: SUI_BRIDGE_OBJECT_ID, + initial_shared_version: seq, + mutable: true, + }) +} + +async fn trigger_reconfiguration_if_not_yet_and_assert_bridge_committee_initialized( + test_cluster: &TestCluster, +) { + let mut bridge = get_bridge( + test_cluster + .fullnode_handle + .sui_node + .state() + .get_object_store(), + ) + .unwrap(); + if !bridge.committee().members.contents.is_empty() { + assert_eq!( + test_cluster.swarm.active_validators().count(), + bridge.committee().members.contents.len() + ); + return; + } + // wait for next epoch + test_cluster.trigger_reconfiguration().await; + bridge = get_bridge( + test_cluster + .fullnode_handle + .sui_node + .state() + .get_object_store(), + ) + .unwrap(); + // Committee should be initiated + assert!(bridge.committee().member_registrations.contents.is_empty()); + assert_eq!( + test_cluster.swarm.active_validators().count(), + bridge.committee().members.contents.len() + ); +} diff --git a/crates/sui-bridge/src/node.rs b/crates/sui-bridge/src/node.rs index 5022d3069f72f..97b0b2caf22e9 100644 --- a/crates/sui-bridge/src/node.rs +++ b/crates/sui-bridge/src/node.rs @@ -524,6 +524,7 @@ mod tests { // send some gas to this address bridge_test_cluster .test_cluster + .inner .transfer_sui_must_exceed(sender_address, client_sui_address, 1000000000) .await; @@ -600,6 +601,7 @@ mod tests { // send some gas to this address let gas_obj = bridge_test_cluster .test_cluster + .inner .transfer_sui_must_exceed(sender_address, client_sui_address, 1000000000) .await; diff --git a/crates/sui-bridge/src/sui_client.rs b/crates/sui-bridge/src/sui_client.rs index 6e0698dbe974f..7319b2f4b77ca 100644 --- a/crates/sui-bridge/src/sui_client.rs +++ b/crates/sui-bridge/src/sui_client.rs @@ -644,7 +644,7 @@ where #[cfg(test)] mod tests { use crate::crypto::BridgeAuthorityKeyPair; - use crate::BRIDGE_ENABLE_PROTOCOL_VERSION; + use crate::e2e_tests::test_utils::TestClusterWrapperBuilder; use crate::{ events::{EmittedSuiToEthTokenBridgeV1, MoveTokenDepositedEvent}, sui_mock_client::SuiMockClient, @@ -660,7 +660,6 @@ mod tests { use std::str::FromStr; use sui_types::bridge::{BridgeChainId, TOKEN_ID_SUI, TOKEN_ID_USDC}; use sui_types::crypto::get_key_pair; - use test_cluster::TestClusterBuilder; use super::*; use crate::events::{init_all_struct_tags, SuiToEthTokenBridgeV1}; @@ -790,22 +789,24 @@ mod tests { let (_, kp): (_, BridgeAuthorityKeyPair) = get_key_pair(); bridge_keys.push(kp); } - let mut test_cluster: test_cluster::TestCluster = TestClusterBuilder::new() - .with_protocol_version((BRIDGE_ENABLE_PROTOCOL_VERSION).into()) - .build_with_bridge(bridge_keys, true) + let mut test_cluster = TestClusterWrapperBuilder::new() + .with_bridge_authority_keys(bridge_keys) + .with_deploy_tokens(true) + .build() .await; let bridge_metrics = Arc::new(BridgeMetrics::new_for_testing()); - let sui_client = SuiClient::new(&test_cluster.fullnode_handle.rpc_url, bridge_metrics) - .await - .unwrap(); - let bridge_authority_keys = test_cluster.bridge_authority_keys.take().unwrap(); + let sui_client = + SuiClient::new(&test_cluster.inner.fullnode_handle.rpc_url, bridge_metrics) + .await + .unwrap(); + let bridge_authority_keys = test_cluster.authority_keys_clone(); // Wait until committee is set up test_cluster .trigger_reconfiguration_if_not_yet_and_assert_bridge_committee_initialized() .await; - let context = &mut test_cluster.wallet; + let context = &mut test_cluster.inner.wallet; let sender = context.active_address().unwrap(); let usdc_amount = 5000000; let bridge_object_arg = sui_client diff --git a/crates/sui-bridge/src/sui_transaction_builder.rs b/crates/sui-bridge/src/sui_transaction_builder.rs index ff188d55927d8..9aabf39b179b9 100644 --- a/crates/sui-bridge/src/sui_transaction_builder.rs +++ b/crates/sui-bridge/src/sui_transaction_builder.rs @@ -617,6 +617,7 @@ pub fn build_committee_update_url_transaction( #[cfg(test)] mod tests { use crate::crypto::BridgeAuthorityKeyPair; + use crate::e2e_tests::test_utils::TestClusterWrapperBuilder; use crate::metrics::BridgeMetrics; use crate::sui_client::SuiClient; use crate::types::BridgeAction; @@ -629,7 +630,6 @@ mod tests { approve_action_with_validator_secrets, bridge_token, get_test_eth_to_sui_bridge_action, get_test_sui_to_eth_bridge_action, }, - BRIDGE_ENABLE_PROTOCOL_VERSION, }; use ethers::types::Address as EthAddress; use std::collections::HashMap; @@ -637,7 +637,6 @@ mod tests { use sui_types::bridge::{BridgeChainId, TOKEN_ID_BTC, TOKEN_ID_USDC}; use sui_types::crypto::get_key_pair; use sui_types::crypto::ToFromBytes; - use test_cluster::TestClusterBuilder; #[tokio::test(flavor = "multi_thread", worker_threads = 8)] async fn test_build_sui_transaction_for_token_transfer() { @@ -647,23 +646,24 @@ mod tests { let (_, kp): (_, BridgeAuthorityKeyPair) = get_key_pair(); bridge_keys.push(kp); } - let mut test_cluster: test_cluster::TestCluster = TestClusterBuilder::new() - .with_protocol_version((BRIDGE_ENABLE_PROTOCOL_VERSION).into()) - .build_with_bridge(bridge_keys, true) + let mut test_cluster = TestClusterWrapperBuilder::new() + .with_bridge_authority_keys(bridge_keys) + .with_deploy_tokens(true) + .build() .await; let metrics = Arc::new(BridgeMetrics::new_for_testing()); - let sui_client = SuiClient::new(&test_cluster.fullnode_handle.rpc_url, metrics) + let sui_client = SuiClient::new(&test_cluster.inner.fullnode_handle.rpc_url, metrics) .await .unwrap(); - let bridge_authority_keys = test_cluster.bridge_authority_keys.take().unwrap(); + let bridge_authority_keys = test_cluster.authority_keys_clone(); // Note: We don't call `sui_client.get_bridge_committee` here because it will err if the committee // is not initialized during the construction of `BridgeCommittee`. test_cluster .trigger_reconfiguration_if_not_yet_and_assert_bridge_committee_initialized() .await; - let context = &mut test_cluster.wallet; + let context = &mut test_cluster.inner.wallet; let sender = context.active_address().unwrap(); let usdc_amount = 5000000; let bridge_object_arg = sui_client @@ -725,16 +725,16 @@ mod tests { let (_, kp): (_, BridgeAuthorityKeyPair) = get_key_pair(); bridge_keys.push(kp); } - let mut test_cluster: test_cluster::TestCluster = TestClusterBuilder::new() - .with_protocol_version((BRIDGE_ENABLE_PROTOCOL_VERSION).into()) - .with_num_validators(num_valdiator) - .build_with_bridge(bridge_keys, true) + let mut test_cluster = TestClusterWrapperBuilder::new() + .with_bridge_authority_keys(bridge_keys) + .with_deploy_tokens(true) + .build() .await; let metrics = Arc::new(BridgeMetrics::new_for_testing()); - let sui_client = SuiClient::new(&test_cluster.fullnode_handle.rpc_url, metrics) + let sui_client = SuiClient::new(&test_cluster.inner.fullnode_handle.rpc_url, metrics) .await .unwrap(); - let bridge_authority_keys = test_cluster.bridge_authority_keys.take().unwrap(); + let bridge_authority_keys = test_cluster.authority_keys_clone(); // Wait until committee is set up test_cluster @@ -743,7 +743,7 @@ mod tests { let summary = sui_client.get_bridge_summary().await.unwrap(); assert!(!summary.is_frozen); - let context = &mut test_cluster.wallet; + let context = &mut test_cluster.inner.wallet; let bridge_object_arg = sui_client .get_mutable_bridge_object_arg_must_succeed() .await; @@ -796,15 +796,16 @@ mod tests { let (_, kp): (_, BridgeAuthorityKeyPair) = get_key_pair(); bridge_keys.push(kp); } - let mut test_cluster: test_cluster::TestCluster = TestClusterBuilder::new() - .with_protocol_version((BRIDGE_ENABLE_PROTOCOL_VERSION).into()) - .build_with_bridge(bridge_keys, true) + let mut test_cluster = TestClusterWrapperBuilder::new() + .with_bridge_authority_keys(bridge_keys) + .with_deploy_tokens(true) + .build() .await; let metrics = Arc::new(BridgeMetrics::new_for_testing()); - let sui_client = SuiClient::new(&test_cluster.fullnode_handle.rpc_url, metrics) + let sui_client = SuiClient::new(&test_cluster.inner.fullnode_handle.rpc_url, metrics) .await .unwrap(); - let bridge_authority_keys = test_cluster.bridge_authority_keys.take().unwrap(); + let bridge_authority_keys = test_cluster.authority_keys_clone(); // Wait until committee is set up test_cluster @@ -816,7 +817,7 @@ mod tests { assert!(!member.1.blocklisted); } - let context = &mut test_cluster.wallet; + let context = &mut test_cluster.inner.wallet; let bridge_object_arg = sui_client .get_mutable_bridge_object_arg_must_succeed() .await; @@ -885,15 +886,16 @@ mod tests { let (_, kp): (_, BridgeAuthorityKeyPair) = get_key_pair(); bridge_keys.push(kp); } - let mut test_cluster: test_cluster::TestCluster = TestClusterBuilder::new() - .with_protocol_version((BRIDGE_ENABLE_PROTOCOL_VERSION).into()) - .build_with_bridge(bridge_keys, true) + let mut test_cluster = TestClusterWrapperBuilder::new() + .with_bridge_authority_keys(bridge_keys) + .with_deploy_tokens(true) + .build() .await; let metrics = Arc::new(BridgeMetrics::new_for_testing()); - let sui_client = SuiClient::new(&test_cluster.fullnode_handle.rpc_url, metrics) + let sui_client = SuiClient::new(&test_cluster.inner.fullnode_handle.rpc_url, metrics) .await .unwrap(); - let bridge_authority_keys = test_cluster.bridge_authority_keys.take().unwrap(); + let bridge_authority_keys = test_cluster.authority_keys_clone(); // Wait until committee is set up test_cluster @@ -909,7 +911,7 @@ mod tests { .map(|(s, d, l)| ((s, d), l)) .collect::>(); - let context = &mut test_cluster.wallet; + let context = &mut test_cluster.inner.wallet; let bridge_object_arg = sui_client .get_mutable_bridge_object_arg_must_succeed() .await; @@ -955,15 +957,16 @@ mod tests { let (_, kp): (_, BridgeAuthorityKeyPair) = get_key_pair(); bridge_keys.push(kp); } - let mut test_cluster: test_cluster::TestCluster = TestClusterBuilder::new() - .with_protocol_version((BRIDGE_ENABLE_PROTOCOL_VERSION).into()) - .build_with_bridge(bridge_keys, true) + let mut test_cluster = TestClusterWrapperBuilder::new() + .with_bridge_authority_keys(bridge_keys) + .with_deploy_tokens(true) + .build() .await; let metrics = Arc::new(BridgeMetrics::new_for_testing()); - let sui_client = SuiClient::new(&test_cluster.fullnode_handle.rpc_url, metrics) + let sui_client = SuiClient::new(&test_cluster.inner.fullnode_handle.rpc_url, metrics) .await .unwrap(); - let bridge_authority_keys = test_cluster.bridge_authority_keys.take().unwrap(); + let bridge_authority_keys = test_cluster.authority_keys_clone(); // Note: We don't call `sui_client.get_bridge_committee` here because it will err if the committee // is not initialized during the construction of `BridgeCommittee`. @@ -973,7 +976,7 @@ mod tests { let notional_values = sui_client.get_notional_values().await.unwrap(); assert_ne!(notional_values[&TOKEN_ID_USDC], 69_000 * USD_MULTIPLIER); - let context = &mut test_cluster.wallet; + let context = &mut test_cluster.inner.wallet; let bridge_object_arg = sui_client .get_mutable_bridge_object_arg_must_succeed() .await; diff --git a/crates/sui-e2e-tests/tests/bridge_tests.rs b/crates/sui-e2e-tests/tests/bridge_tests.rs deleted file mode 100644 index 350b896c962df..0000000000000 --- a/crates/sui-e2e-tests/tests/bridge_tests.rs +++ /dev/null @@ -1,105 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use sui_bridge::crypto::BridgeAuthorityKeyPair; -use sui_bridge::BRIDGE_ENABLE_PROTOCOL_VERSION; -use sui_json_rpc_api::BridgeReadApiClient; -use sui_macros::sim_test; -use sui_types::bridge::get_bridge; -use sui_types::bridge::BridgeTrait; -use sui_types::crypto::get_key_pair; -use sui_types::SUI_BRIDGE_OBJECT_ID; -use test_cluster::TestClusterBuilder; - -#[sim_test] -async fn test_create_bridge_state_object() { - let test_cluster = TestClusterBuilder::new() - .with_protocol_version((BRIDGE_ENABLE_PROTOCOL_VERSION - 1).into()) - .with_epoch_duration_ms(20000) - .build() - .await; - - let handles = test_cluster.all_node_handles(); - - // no node has the bridge state object yet - for h in &handles { - h.with(|node| { - assert!(node - .state() - .get_object_cache_reader() - .get_latest_object_ref_or_tombstone(SUI_BRIDGE_OBJECT_ID) - .unwrap() - .is_none()); - }); - } - - // wait until feature is enabled - test_cluster - .wait_for_protocol_version(BRIDGE_ENABLE_PROTOCOL_VERSION.into()) - .await; - // wait until next epoch - authenticator state object is created at the end of the first epoch - // in which it is supported. - test_cluster.wait_for_epoch_all_nodes(2).await; // protocol upgrade completes in epoch 1 - - for h in &handles { - h.with(|node| { - node.state() - .get_object_cache_reader() - .get_latest_object_ref_or_tombstone(SUI_BRIDGE_OBJECT_ID) - .unwrap() - .expect("auth state object should exist"); - }); - } -} - -#[tokio::test] -async fn test_committee_registration() { - telemetry_subscribers::init_for_testing(); - let mut bridge_keys = vec![]; - for _ in 0..=3 { - let (_, kp): (_, BridgeAuthorityKeyPair) = get_key_pair(); - bridge_keys.push(kp); - } - let test_cluster: test_cluster::TestCluster = TestClusterBuilder::new() - .with_protocol_version((BRIDGE_ENABLE_PROTOCOL_VERSION).into()) - .build_with_bridge(bridge_keys, false) - .await; - - let bridge = get_bridge( - test_cluster - .fullnode_handle - .sui_node - .state() - .get_object_store(), - ) - .unwrap(); - - // Member should be empty before end of epoch - assert!(bridge.committee().members.contents.is_empty()); - assert_eq!( - test_cluster.swarm.active_validators().count(), - bridge.committee().member_registrations.contents.len() - ); - - test_cluster - .trigger_reconfiguration_if_not_yet_and_assert_bridge_committee_initialized() - .await; -} - -#[tokio::test] -async fn test_bridge_api_compatibility() { - let test_cluster: test_cluster::TestCluster = TestClusterBuilder::new() - .with_protocol_version(BRIDGE_ENABLE_PROTOCOL_VERSION.into()) - .build() - .await; - - test_cluster.trigger_reconfiguration().await; - let client = test_cluster.rpc_client(); - client.get_latest_bridge().await.unwrap(); - // TODO: assert fields in summary - - client - .get_bridge_object_initial_shared_version() - .await - .unwrap(); -} diff --git a/crates/test-cluster/Cargo.toml b/crates/test-cluster/Cargo.toml index 6f967e93389b8..58ca4d37edd93 100644 --- a/crates/test-cluster/Cargo.toml +++ b/crates/test-cluster/Cargo.toml @@ -31,7 +31,6 @@ sui-swarm.workspace = true sui-types = { workspace = true, features = ["test-utils"] } prometheus.workspace = true sui-keys.workspace = true -sui-bridge.workspace = true sui-sdk.workspace = true sui-test-transaction-builder.workspace = true diff --git a/crates/test-cluster/src/lib.rs b/crates/test-cluster/src/lib.rs index bbb4ef021370e..05643d8716b01 100644 --- a/crates/test-cluster/src/lib.rs +++ b/crates/test-cluster/src/lib.rs @@ -1,35 +1,21 @@ // Copyright (c) Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 -use futures::Future; use futures::{future::join_all, StreamExt}; -use jsonrpsee::core::RpcResult; use jsonrpsee::http_client::{HttpClient, HttpClientBuilder}; use rand::{distributions::*, rngs::OsRng, seq::SliceRandom}; -use std::collections::BTreeMap; use std::collections::HashMap; use std::net::SocketAddr; use std::num::NonZeroUsize; -use std::path::{Path, PathBuf}; +use std::path::PathBuf; use std::sync::{Arc, Mutex}; use std::time::Duration; -use sui_bridge::crypto::{BridgeAuthorityKeyPair, BridgeAuthoritySignInfo}; -use sui_bridge::sui_transaction_builder::build_add_tokens_on_sui_transaction; -use sui_bridge::sui_transaction_builder::build_committee_register_transaction; -use sui_bridge::types::BridgeCommitteeValiditySignInfo; -use sui_bridge::types::CertifiedBridgeAction; -use sui_bridge::types::VerifiedCertifiedBridgeAction; -use sui_bridge::utils::publish_and_register_coins_return_add_coins_on_sui_action; -use sui_bridge::utils::wait_for_server_to_be_up; use sui_config::genesis::Genesis; -use sui_config::local_ip_utils::get_available_port; use sui_config::node::{AuthorityOverloadConfig, DBCheckpointConfig, RunWithRange}; use sui_config::{Config, SUI_CLIENT_CONFIG, SUI_NETWORK_CONFIG}; use sui_config::{NodeConfig, PersistedConfig, SUI_KEYSTORE_FILENAME}; use sui_core::authority_aggregator::AuthorityAggregator; use sui_core::authority_client::NetworkAuthorityClient; -use sui_json_rpc_api::BridgeReadApiClient; -use sui_json_rpc_types::SuiTransactionBlockResponseOptions; use sui_json_rpc_types::{ SuiExecutionStatus, SuiTransactionBlockEffectsAPI, SuiTransactionBlockResponse, TransactionFilter, @@ -54,12 +40,10 @@ use sui_swarm_config::node_config_builder::{FullnodeConfigBuilder, ValidatorConf use sui_test_transaction_builder::TestTransactionBuilder; use sui_types::base_types::ConciseableName; use sui_types::base_types::{AuthorityName, ObjectID, ObjectRef, SuiAddress}; -use sui_types::bridge::{get_bridge, TOKEN_ID_BTC, TOKEN_ID_ETH, TOKEN_ID_USDC, TOKEN_ID_USDT}; -use sui_types::bridge::{get_bridge_obj_initial_shared_version, BridgeSummary, BridgeTrait}; use sui_types::committee::CommitteeTrait; use sui_types::committee::{Committee, EpochId}; +use sui_types::crypto::KeypairTraits; use sui_types::crypto::SuiKeyPair; -use sui_types::crypto::{KeypairTraits, ToFromBytes}; use sui_types::effects::{TransactionEffects, TransactionEvents}; use sui_types::error::SuiResult; use sui_types::governance::MIN_VALIDATOR_JOINING_STAKE_MIST; @@ -71,10 +55,8 @@ use sui_types::sui_system_state::SuiSystemStateTrait; use sui_types::supported_protocol_versions::SupportedProtocolVersions; use sui_types::traffic_control::{PolicyConfig, RemoteFirewallConfig}; use sui_types::transaction::{ - CertifiedTransaction, ObjectArg, Transaction, TransactionData, TransactionDataAPI, - TransactionKind, + CertifiedTransaction, Transaction, TransactionData, TransactionDataAPI, TransactionKind, }; -use sui_types::SUI_BRIDGE_OBJECT_ID; use tokio::time::{timeout, Instant}; use tokio::{task::JoinHandle, time::sleep}; use tracing::{error, info}; @@ -108,9 +90,6 @@ pub struct TestCluster { pub swarm: Swarm, pub wallet: WalletContext, pub fullnode_handle: FullNodeHandle, - - pub bridge_authority_keys: Option>, - pub bridge_server_ports: Option>, } impl TestCluster { @@ -268,10 +247,6 @@ impl TestCluster { .compute_object_reference() } - pub async fn get_bridge_summary(&self) -> RpcResult { - self.sui_client().http().get_latest_bridge().await - } - pub async fn get_object_or_tombstone_from_fullnode_store( &self, object_id: ObjectID, @@ -516,54 +491,6 @@ impl TestCluster { } } - pub async fn trigger_reconfiguration_if_not_yet_and_assert_bridge_committee_initialized(&self) { - let mut bridge = - get_bridge(self.fullnode_handle.sui_node.state().get_object_store()).unwrap(); - if !bridge.committee().members.contents.is_empty() { - assert_eq!( - self.swarm.active_validators().count(), - bridge.committee().members.contents.len() - ); - return; - } - // wait for next epoch - self.trigger_reconfiguration().await; - bridge = get_bridge(self.fullnode_handle.sui_node.state().get_object_store()).unwrap(); - // Committee should be initiated - assert!(bridge.committee().member_registrations.contents.is_empty()); - assert_eq!( - self.swarm.active_validators().count(), - bridge.committee().members.contents.len() - ); - } - - // Wait for bridge node in the cluster to be up and running. - pub async fn wait_for_bridge_cluster_to_be_up(&self, timeout_sec: u64) { - let bridge_ports = self.bridge_server_ports.as_ref().unwrap(); - let mut tasks = vec![]; - for port in bridge_ports.iter() { - let server_url = format!("http://127.0.0.1:{}", port); - tasks.push(wait_for_server_to_be_up(server_url, timeout_sec)); - } - join_all(tasks) - .await - .into_iter() - .collect::>>() - .unwrap(); - } - - pub async fn get_mut_bridge_arg(&self) -> Option { - get_bridge_obj_initial_shared_version( - self.fullnode_handle.sui_node.state().get_object_store(), - ) - .unwrap() - .map(|seq| ObjectArg::SharedObject { - id: SUI_BRIDGE_OBJECT_ID, - initial_shared_version: seq, - mutable: true, - }) - } - pub async fn wait_for_authenticator_state_update(&self) { timeout( Duration::from_secs(60), @@ -1191,210 +1118,7 @@ impl TestClusterBuilder { swarm, wallet, fullnode_handle, - bridge_authority_keys: None, - bridge_server_ports: None, - } - } - - pub async fn build_with_bridge( - self, - // Note: caller should make sure to keep the authority with largest stake at the end of the list. - // This is because we try to set up eth env and sui cluster at the same time. Since we use evenly - // distributed stake, we want to keep the authority with the remainder consistent in eth contracts - // and sui cluster. - bridge_authority_keys: Vec, - deploy_tokens: bool, - ) -> TestCluster { - let timer = Instant::now(); - let gas_objects_for_authority_keys = bridge_authority_keys - .iter() - .map(|k| { - let address = SuiAddress::from(k.public()); - Object::with_id_owner_for_testing(ObjectID::random(), address) - }) - .collect::>(); - let mut test_cluster = self - .with_objects(gas_objects_for_authority_keys) - .build() - .await; - info!( - "TestCluster build took {:?} secs", - timer.elapsed().as_secs() - ); - let ref_gas_price = test_cluster.get_reference_gas_price().await; - let bridge_arg = test_cluster.get_mut_bridge_arg().await.unwrap(); - assert_eq!( - bridge_authority_keys.len(), - test_cluster.swarm.active_validators().count() - ); - - // Committee registers themselves - let mut server_ports = vec![]; - let mut tasks = vec![]; - let quorum_driver_api = test_cluster.quorum_driver_api().clone(); - // Reorder the nodes so that the last node has the largest stake. - let validator_with_max_stake = test_cluster - .sui_client() - .governance_api() - .get_committee_info(None) - .await - .unwrap() - .validators - .iter() - .max_by(|a, b| a.0.cmp(&b.0)) - .unwrap() - .0; - let node_with_max_stake = test_cluster - .swarm - .active_validators() - .find(|v| v.config().protocol_public_key() == validator_with_max_stake) - .unwrap(); - let other_nodes = test_cluster - .swarm - .active_validators() - .filter(|v| v.config().protocol_public_key() != validator_with_max_stake) - .collect::>(); - let reordered_nodes = other_nodes - .iter() - .chain(std::iter::once(&node_with_max_stake)); - for (node, kp) in reordered_nodes.zip(bridge_authority_keys.iter()) { - let validator_address = node.config().sui_address(); - // create committee registration tx - let gas = test_cluster - .wallet - .get_one_gas_object_owned_by_address(validator_address) - .await - .unwrap() - .unwrap(); - - let server_port = get_available_port("127.0.0.1"); - let server_url = format!("http://127.0.0.1:{}", server_port); - server_ports.push(server_port); - let data = build_committee_register_transaction( - validator_address, - &gas, - bridge_arg, - kp.public().as_bytes().to_vec(), - &server_url, - ref_gas_price, - 1000000000, - ) - .unwrap(); - - let tx = Transaction::from_data_and_signer( - data, - vec![node.config().account_key_pair.keypair()], - ); - let api_clone = quorum_driver_api.clone(); - tasks.push(async move { - api_clone - .execute_transaction_block( - tx, - SuiTransactionBlockResponseOptions::new().with_effects(), - None, - ) - .await - }); - } - - if deploy_tokens { - let timer = Instant::now(); - let token_ids = vec![TOKEN_ID_BTC, TOKEN_ID_ETH, TOKEN_ID_USDC, TOKEN_ID_USDT]; - let token_prices = vec![500_000_000u64, 30_000_000u64, 1_000u64, 1_000u64]; - let action = publish_and_register_coins_return_add_coins_on_sui_action( - test_cluster.wallet(), - bridge_arg, - vec![ - Path::new("../../bridge/move/tokens/btc").into(), - Path::new("../../bridge/move/tokens/eth").into(), - Path::new("../../bridge/move/tokens/usdc").into(), - Path::new("../../bridge/move/tokens/usdt").into(), - ], - token_ids, - token_prices, - 0, - ); - let action = action.await; - info!("register tokens took {:?} secs", timer.elapsed().as_secs()); - let sig_map = bridge_authority_keys - .iter() - .map(|key| { - ( - key.public().into(), - BridgeAuthoritySignInfo::new(&action, key).signature, - ) - }) - .collect::>(); - let certified_action = CertifiedBridgeAction::new_from_data_and_sig( - action, - BridgeCommitteeValiditySignInfo { - signatures: sig_map.clone(), - }, - ); - let verifired_action_cert = - VerifiedCertifiedBridgeAction::new_from_verified(certified_action); - let sender_address = test_cluster.get_address_0(); - - await_committee_register_tasks(&test_cluster, tasks).await; - - // Wait until committee is set up - test_cluster - .trigger_reconfiguration_if_not_yet_and_assert_bridge_committee_initialized() - .await; - - let tx = build_add_tokens_on_sui_transaction( - sender_address, - &test_cluster - .wallet - .get_one_gas_object_owned_by_address(sender_address) - .await - .unwrap() - .unwrap(), - verifired_action_cert, - bridge_arg, - ref_gas_price, - ) - .unwrap(); - - let response = test_cluster.sign_and_execute_transaction(&tx).await; - assert_eq!( - response.effects.unwrap().status(), - &SuiExecutionStatus::Success - ); - info!("Deploy tokens took {:?} secs", timer.elapsed().as_secs()); - } else { - await_committee_register_tasks(&test_cluster, tasks).await; } - - async fn await_committee_register_tasks( - test_cluster: &TestCluster, - tasks: Vec< - impl Future>, - >, - ) { - // The tx may fail if a member tries to register when the committee is already finalized. - // In that case, we just need to check the committee members is not empty since once - // the committee is finalized, it should not be empty. - let responses = join_all(tasks).await; - let mut has_failure = false; - for response in responses { - if response.unwrap().effects.unwrap().status() != &SuiExecutionStatus::Success { - has_failure = true; - } - } - if has_failure { - let bridge_summary = test_cluster.get_bridge_summary().await.unwrap(); - assert_ne!(bridge_summary.committee.members.len(), 0); - } - } - - info!( - "TestCluster build_with_bridge took {:?} secs", - timer.elapsed().as_secs() - ); - test_cluster.bridge_authority_keys = Some(bridge_authority_keys); - test_cluster.bridge_server_ports = Some(server_ports); - test_cluster } /// Start a Swarm and set up WalletConfig