From 61d920a2bccb431c89588e2db69991a3819eed3a Mon Sep 17 00:00:00 2001 From: Lu Zhang <8418040+longbowlu@users.noreply.github.com> Date: Tue, 27 Sep 2022 11:15:49 -0700 Subject: [PATCH] deterministically wait for fullnode to sync txns (#4810) --- crates/sui-cluster-test/src/faucet.rs | 101 ++--------------- crates/sui-cluster-test/src/helper.rs | 27 ++--- crates/sui-cluster-test/src/lib.rs | 103 ++++++++++++++++-- .../src/test_case/call_contract_test.rs | 9 +- .../src/test_case/coin_merge_split_test.rs | 21 ++-- .../fullnode_execute_transaction_test.rs | 6 +- .../src/test_case/native_transfer_test.rs | 3 +- .../src/test_case/shared_object_test.rs | 16 ++- crates/sui-faucet/src/faucet/mod.rs | 25 +---- crates/sui-faucet/src/faucet/simple_faucet.rs | 3 +- crates/sui-sdk/src/lib.rs | 16 ++- crates/sui-test-validator/src/main.rs | 23 ++-- crates/test-utils/src/transaction.rs | 2 + 13 files changed, 181 insertions(+), 174 deletions(-) diff --git a/crates/sui-cluster-test/src/faucet.rs b/crates/sui-cluster-test/src/faucet.rs index 9e36fd2393d27..d259bba722ec7 100644 --- a/crates/sui-cluster-test/src/faucet.rs +++ b/crates/sui-cluster-test/src/faucet.rs @@ -1,23 +1,12 @@ // Copyright (c) 2022, Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 -use super::{ - cluster::{new_wallet_context_from_cluster, Cluster}, - helper::ObjectChecker, - wallet_client::WalletClient, -}; -use anyhow::bail; +use super::cluster::{new_wallet_context_from_cluster, Cluster}; use async_trait::async_trait; -use clap::*; use std::collections::HashMap; use std::sync::Arc; -use sui_faucet::{CoinInfo, Faucet, FaucetResponse, SimpleFaucet}; +use sui_faucet::{Faucet, FaucetResponse, SimpleFaucet}; +use sui_types::base_types::{encode_bytes_hex, SuiAddress}; use sui_types::crypto::KeypairTraits; -use sui_types::{ - base_types::{encode_bytes_hex, SuiAddress}, - gas_coin::GasCoin, - object::Owner, -}; -use tokio::time::{sleep, Duration}; use tracing::{debug, info, info_span, Instrument}; use uuid::Uuid; @@ -52,12 +41,7 @@ impl FaucetClientFactory { /// Faucet Client abstraction #[async_trait] pub trait FaucetClient { - async fn request_sui_coins( - &self, - client: &WalletClient, - minimum_coins: Option, - request_address: Option, - ) -> Result, anyhow::Error>; + async fn request_sui_coins(&self, request_address: SuiAddress) -> FaucetResponse; } /// Client for a remote faucet that is accessible by POST requests @@ -76,16 +60,10 @@ impl RemoteFaucetClient { impl FaucetClient for RemoteFaucetClient { /// Request test SUI coins from faucet. /// It also verifies the effects are observed by gateway/fullnode. - async fn request_sui_coins( - &self, - client: &WalletClient, - minimum_coins: Option, - request_address: Option, - ) -> Result, anyhow::Error> { + async fn request_sui_coins(&self, request_address: SuiAddress) -> FaucetResponse { let gas_url = format!("{}/gas", self.remote_url); debug!("Getting coin from remote faucet {}", gas_url); - let address = request_address.unwrap_or_else(|| client.get_wallet_address()); - let data = HashMap::from([("recipient", encode_bytes_hex(&address))]); + let data = HashMap::from([("recipient", encode_bytes_hex(&request_address))]); let map = HashMap::from([("FixedAmountRequest", data)]); let response = reqwest::Client::new() @@ -101,27 +79,9 @@ impl FaucetClient for RemoteFaucetClient { if let Some(error) = faucet_response.error { panic!("Failed to get gas tokens with error: {}", error) - } - - sleep(Duration::from_secs(2)).await; - - let gas_coins = into_gas_coin_with_owner_check( - faucet_response.transferred_gas_objects, - address, - client, - ) - .await; - - let minimum_coins = minimum_coins.unwrap_or(5); + }; - if gas_coins.len() < minimum_coins { - bail!( - "Expect to get at least {minimum_coins} Sui Coins for address {address}, but only got {}", - gas_coins.len() - ) - } - - Ok(gas_coins) + faucet_response } } @@ -138,52 +98,13 @@ impl LocalFaucetClient { } #[async_trait] impl FaucetClient for LocalFaucetClient { - async fn request_sui_coins( - &self, - client: &WalletClient, - minimum_coins: Option, - request_address: Option, - ) -> Result, anyhow::Error> { - let address = request_address.unwrap_or_else(|| client.get_wallet_address()); + async fn request_sui_coins(&self, request_address: SuiAddress) -> FaucetResponse { let receipt = self .simple_faucet - .send(Uuid::new_v4(), address, &[50000; 5]) + .send(Uuid::new_v4(), request_address, &[50000; 5]) .await .unwrap_or_else(|err| panic!("Failed to get gas tokens with error: {}", err)); - sleep(Duration::from_secs(2)).await; - - let gas_coins = into_gas_coin_with_owner_check(receipt.sent, address, client).await; - - let minimum_coins = minimum_coins.unwrap_or(5); - - if gas_coins.len() < minimum_coins { - bail!( - "Expect to get at least {minimum_coins} Sui Coins for address {address}, but only got {}. Try minting more coins on genesis.", - gas_coins.len() - ) - } - - Ok(gas_coins) + receipt.into() } } - -async fn into_gas_coin_with_owner_check( - coin_info: Vec, - owner: SuiAddress, - client: &WalletClient, -) -> Vec { - futures::future::join_all( - coin_info - .iter() - .map(|coin_info| { - ObjectChecker::new(coin_info.id) - .owner(Owner::AddressOwner(owner)) - .check_into_gas_coin(client.get_fullnode()) - }) - .collect::>(), - ) - .await - .into_iter() - .collect::>() -} diff --git a/crates/sui-cluster-test/src/helper.rs b/crates/sui-cluster-test/src/helper.rs index f702d63684c8f..82293a51d4844 100644 --- a/crates/sui-cluster-test/src/helper.rs +++ b/crates/sui-cluster-test/src/helper.rs @@ -17,8 +17,8 @@ use tracing::debug; /// Use builder style to construct the conditions. /// When optionals fields are not set, related checks are omitted. /// Consuming functions such as `check` perform the check and panics if -/// verification results are unexpected. `check_into_sui_object` and -/// `check_info_gas_object` expect to get a `SuiObject` and `GasObject` +/// verification results are unexpected. `check_into_object` and +/// `check_into_gas_coin` expect to get a `SuiRawObject` and `GasCoin` /// respectfully. /// ``` #[derive(Debug)] @@ -65,8 +65,8 @@ impl ObjectChecker { .into_gas_coin() } - pub async fn check_into_sui_object(self, client: &SuiClient) -> SuiRawObject { - self.check(client).await.unwrap().into_sui_object() + pub async fn check_into_object(self, client: &SuiClient) -> SuiRawObject { + self.check(client).await.unwrap().into_object() } pub async fn check(self, client: &SuiClient) -> Result { @@ -83,7 +83,11 @@ impl ObjectChecker { match object_info { GetRawObjectDataResponse::NotExists(_) => { - panic!("Node can't find gas object {}", object_id) + panic!( + "Node can't find gas object {} with client {:?}", + object_id, + client.read_api() + ) } GetRawObjectDataResponse::Deleted(_) => { if !self.is_deleted { @@ -119,21 +123,18 @@ impl ObjectChecker { pub struct CheckerResultObject { gas_coin: Option, - sui_object: Option, + object: Option, } impl CheckerResultObject { - pub fn new(gas_coin: Option, sui_object: Option) -> Self { - Self { - gas_coin, - sui_object, - } + pub fn new(gas_coin: Option, object: Option) -> Self { + Self { gas_coin, object } } pub fn into_gas_coin(self) -> GasCoin { self.gas_coin.unwrap() } - pub fn into_sui_object(self) -> SuiRawObject { - self.sui_object.unwrap() + pub fn into_object(self) -> SuiRawObject { + self.object.unwrap() } } diff --git a/crates/sui-cluster-test/src/lib.rs b/crates/sui-cluster-test/src/lib.rs index 82b8905d84ca8..6703c4d92b969 100644 --- a/crates/sui-cluster-test/src/lib.rs +++ b/crates/sui-cluster-test/src/lib.rs @@ -5,9 +5,14 @@ use async_trait::async_trait; use clap::*; use cluster::{Cluster, ClusterFactory}; use config::ClusterTestOpt; +use futures::{stream::FuturesUnordered, StreamExt}; +use helper::ObjectChecker; use std::sync::Arc; use sui::client_commands::WalletContext; +use sui_faucet::CoinInfo; use sui_json_rpc_types::SuiTransactionResponse; +use sui_types::base_types::TransactionDigest; +use sui_types::object::Owner; use test_utils::messages::make_transactions_with_wallet_context; use sui_sdk::SuiClient; @@ -21,7 +26,7 @@ use test_case::{ fullnode_execute_transaction_test::FullNodeExecuteTransactionTest, native_transfer_test::NativeTransferTest, shared_object_test::SharedCounterTest, }; -use tokio::time::{sleep, Duration}; +use tokio::time::{self, Duration}; use tracing::{error, info}; use wallet_client::WalletClient; @@ -45,10 +50,30 @@ pub struct TestContext { impl TestContext { async fn get_sui_from_faucet(&self, minimum_coins: Option) -> Vec { - self.faucet - .request_sui_coins(self.get_context(), minimum_coins, None) - .await - .unwrap_or_else(|e| panic!("Failed to get test SUI coins from faucet, {e}")) + let addr = self.get_wallet_address(); + let faucet_response = self.faucet.request_sui_coins(addr).await; + + let coin_info = faucet_response + .transferred_gas_objects + .iter() + .map(|coin_info| coin_info.transfer_tx_digest) + .collect::>(); + self.let_fullnode_sync(coin_info, 5).await; + + let gas_coins = self + .check_owner_and_into_gas_coin(faucet_response.transferred_gas_objects, addr) + .await; + + let minimum_coins = minimum_coins.unwrap_or(5); + + if gas_coins.len() < minimum_coins { + panic!( + "Expect to get at least {minimum_coins} Sui Coins for address {addr}, but only got {}", + gas_coins.len() + ) + } + + gas_coins } fn get_context(&self) -> &WalletClient { @@ -108,9 +133,71 @@ impl TestContext { // TODO: figure out a more efficient way to test a local cluster // A potential way to do this is to subscribe to txns from fullnode // when the feature is ready - pub async fn let_fullnode_sync(&self) { - let duration = Duration::from_secs(5); - sleep(duration).await; + pub async fn let_fullnode_sync(&self, digests: Vec, timeout_sec: u64) { + let mut futures = FuturesUnordered::new(); + for digest in digests.clone() { + let task = self.get_tx_with_retry_times(digest, 1); + futures.push(Box::pin(task)); + } + let mut sleep = Box::pin(time::sleep(Duration::from_secs(timeout_sec))); + + loop { + tokio::select! { + _ = &mut sleep => { + panic!("Fullnode does not know all of {:?} after {} secs.", digests, timeout_sec); + } + res = futures.next() => { + match res { + Some((true, _, _)) => {}, + Some((false, digest, retry_times)) => { + let task = self.get_tx_with_retry_times(digest, retry_times); + futures.push(Box::pin(task)); + }, + None => break, // all txns appear on fullnode, mission completed + } + } + } + } + } + + async fn get_tx_with_retry_times( + &self, + digest: TransactionDigest, + retry_times: u64, + ) -> (bool, TransactionDigest, u64) { + match self + .client + .get_fullnode() + .read_api() + .get_transaction(digest) + .await + { + Ok(_) => (true, digest, retry_times), + Err(_) => { + time::sleep(Duration::from_millis(300 * retry_times)).await; + (false, digest, retry_times + 1) + } + } + } + + async fn check_owner_and_into_gas_coin( + &self, + coin_info: Vec, + owner: SuiAddress, + ) -> Vec { + futures::future::join_all( + coin_info + .iter() + .map(|coin_info| { + ObjectChecker::new(coin_info.id) + .owner(Owner::AddressOwner(owner)) + .check_into_gas_coin(self.get_fullnode()) + }) + .collect::>(), + ) + .await + .into_iter() + .collect::>() } } diff --git a/crates/sui-cluster-test/src/test_case/call_contract_test.rs b/crates/sui-cluster-test/src/test_case/call_contract_test.rs index a730ea65f7edb..fdeaf6496ff17 100644 --- a/crates/sui-cluster-test/src/test_case/call_contract_test.rs +++ b/crates/sui-cluster-test/src/test_case/call_contract_test.rs @@ -106,15 +106,16 @@ impl TestCaseImpl for CallContractTest { )).unwrap_or_else(|| panic!("Expect such a MoveEvent in events {:?}", events)); // Verify fullnode observes the txn - ctx.let_fullnode_sync().await; + ctx.let_fullnode_sync(vec![effects.transaction_digest], 5) + .await; - let sui_object = ObjectChecker::new(nft_id) + let object = ObjectChecker::new(nft_id) .owner(Owner::AddressOwner(signer)) - .check_into_sui_object(ctx.get_fullnode()) + .check_into_object(ctx.get_fullnode()) .await; assert_eq!( - sui_object.reference.version, + object.reference.version, SequenceNumber::from_u64(1), "Expect sequence number to be 1" ); diff --git a/crates/sui-cluster-test/src/test_case/coin_merge_split_test.rs b/crates/sui-cluster-test/src/test_case/coin_merge_split_test.rs index aa41bc53138b9..466ac1c5273ad 100644 --- a/crates/sui-cluster-test/src/test_case/coin_merge_split_test.rs +++ b/crates/sui-cluster-test/src/test_case/coin_merge_split_test.rs @@ -4,6 +4,7 @@ use crate::{helper::ObjectChecker, TestCaseImpl, TestContext}; use anyhow::bail; use async_trait::async_trait; +use sui_json_rpc_types::SuiTransactionResponse; use sui_types::base_types::{ObjectID, SuiAddress}; use sui_types::object::Owner; use tracing::{debug, info}; @@ -46,16 +47,17 @@ impl TestCaseImpl for CoinMergeSplitTest { .await .or_else(|e| bail!("Failed to get transaction data for coin split: {}", e))?; - let split_response = ctx - .sign_and_execute(data, "coin split") - .await + let response = ctx.sign_and_execute(data, "coin split").await; + + let split_response = response .parsed_data .unwrap() .to_split_coin_response() .or_else(|e| bail!("Failed to execute SplitCoin: {e}"))?; // Verify fullnode observes the txn - ctx.let_fullnode_sync().await; + ctx.let_fullnode_sync(vec![response.certificate.transaction_digest], 5) + .await; let _ = futures::future::join_all( split_response @@ -73,6 +75,7 @@ impl TestCaseImpl for CoinMergeSplitTest { // Merge info!("Testing coin merge."); let mut coins_merged = Vec::new(); + let mut txes = Vec::new(); // We on purpose linearize the merge operations, otherwise the primary coin may be locked for new_coin in &split_response.new_coins { let coin_to_merge = new_coin.reference.object_id; @@ -80,13 +83,15 @@ impl TestCaseImpl for CoinMergeSplitTest { "Merging coin {} back to {}.", coin_to_merge, primary_coin_id ); - Self::merge_coin(ctx, signer, primary_coin_id, coin_to_merge, *gas_obj.id()).await; + let response = + Self::merge_coin(ctx, signer, primary_coin_id, coin_to_merge, *gas_obj.id()).await; debug!("Verifying the merged coin {} is deleted.", coin_to_merge); coins_merged.push(coin_to_merge); + txes.push(response.certificate.transaction_digest); } // Verify fullnode observes the txn - ctx.let_fullnode_sync().await; + ctx.let_fullnode_sync(txes, 5).await; let _ = futures::future::join_all( coins_merged @@ -130,13 +135,13 @@ impl CoinMergeSplitTest { primary_coin: ObjectID, coin_to_merge: ObjectID, gas_obj_id: ObjectID, - ) { + ) -> SuiTransactionResponse { let data = ctx .get_gateway() .transaction_builder() .merge_coins(signer, primary_coin, coin_to_merge, Some(gas_obj_id), 5000) .await .expect("Failed to get transaction data for coin merge"); - ctx.sign_and_execute(data, "coin merge").await; + ctx.sign_and_execute(data, "coin merge").await } } diff --git a/crates/sui-cluster-test/src/test_case/fullnode_execute_transaction_test.rs b/crates/sui-cluster-test/src/test_case/fullnode_execute_transaction_test.rs index 673f5137c5296..bca384682ee58 100644 --- a/crates/sui-cluster-test/src/test_case/fullnode_execute_transaction_test.rs +++ b/crates/sui-cluster-test/src/test_case/fullnode_execute_transaction_test.rs @@ -46,7 +46,7 @@ impl TestCaseImpl for FullNodeExecuteTransactionTest { assert_eq!(txn_digest, tx_digest); // Verify fullnode observes the txn - ctx.let_fullnode_sync().await; + ctx.let_fullnode_sync(vec![tx_digest], 5).await; fullnode .read_api() @@ -76,7 +76,7 @@ impl TestCaseImpl for FullNodeExecuteTransactionTest { assert_eq!(txn_digest, certificate.transaction_digest); // Verify fullnode observes the txn - ctx.let_fullnode_sync().await; + ctx.let_fullnode_sync(vec![txn_digest], 5).await; fullnode .read_api() @@ -113,7 +113,7 @@ impl TestCaseImpl for FullNodeExecuteTransactionTest { ) } // Verify fullnode observes the txn - ctx.let_fullnode_sync().await; + ctx.let_fullnode_sync(vec![txn_digest], 5).await; fullnode .read_api() diff --git a/crates/sui-cluster-test/src/test_case/native_transfer_test.rs b/crates/sui-cluster-test/src/test_case/native_transfer_test.rs index ac41ac3f1efb6..e0167d98b55ad 100644 --- a/crates/sui-cluster-test/src/test_case/native_transfer_test.rs +++ b/crates/sui-cluster-test/src/test_case/native_transfer_test.rs @@ -77,7 +77,8 @@ impl TestCaseImpl for NativeTransferTest { .check(&event); // Verify fullnode observes the txn - ctx.let_fullnode_sync().await; + ctx.let_fullnode_sync(vec![response.certificate.transaction_digest], 5) + .await; let _ = ObjectChecker::new(*obj_to_transfer.id()) .owner(Owner::AddressOwner(recipient_addr)) diff --git a/crates/sui-cluster-test/src/test_case/shared_object_test.rs b/crates/sui-cluster-test/src/test_case/shared_object_test.rs index 4bcadda934f1b..b0a7306e100e7 100644 --- a/crates/sui-cluster-test/src/test_case/shared_object_test.rs +++ b/crates/sui-cluster-test/src/test_case/shared_object_test.rs @@ -25,14 +25,17 @@ impl TestCaseImpl for SharedCounterTest { async fn run(&self, ctx: &mut TestContext) -> Result<(), anyhow::Error> { info!("Testing shared object transactions."); + let sui_objs = ctx.get_sui_from_faucet(Some(1)).await; + assert!(!sui_objs.is_empty()); + let wallet_context: &WalletContext = ctx.get_wallet(); let address = ctx.get_wallet_address(); let (package_ref, counter_id) = publish_basics_package_and_make_counter(wallet_context, address).await; - let effects: SuiTransactionResponse = + let response: SuiTransactionResponse = increment_counter(wallet_context, address, None, package_ref, counter_id).await; - let effects = effects.effects; + let effects = response.effects; assert_eq!( effects.status, SuiExecutionStatus::Success, @@ -46,15 +49,16 @@ impl TestCaseImpl for SharedCounterTest { .expect("Expect obj {counter_id} in shared_objects"); // Verify fullnode observes the txn - ctx.let_fullnode_sync().await; + ctx.let_fullnode_sync(vec![response.certificate.transaction_digest], 5) + .await; - let sui_object = ObjectChecker::new(counter_id) + let counter_object = ObjectChecker::new(counter_id) .owner(Owner::Shared) - .check_into_sui_object(ctx.get_fullnode()) + .check_into_object(ctx.get_fullnode()) .await; assert_eq!( - sui_object.reference.version, + counter_object.reference.version, SequenceNumber::from_u64(2), "Expect sequence number to be 2" ); diff --git a/crates/sui-faucet/src/faucet/mod.rs b/crates/sui-faucet/src/faucet/mod.rs index a6f25838d4ac0..2b1f5390f7fff 100644 --- a/crates/sui-faucet/src/faucet/mod.rs +++ b/crates/sui-faucet/src/faucet/mod.rs @@ -3,11 +3,7 @@ use crate::FaucetError; use async_trait::async_trait; use serde::{Deserialize, Serialize}; -use sui_json_rpc_types::SuiParsedObject; -use sui_types::{ - base_types::{ObjectID, SuiAddress}, - gas_coin::GasCoin, -}; +use sui_types::base_types::{ObjectID, SuiAddress, TransactionDigest}; use uuid::Uuid; mod simple_faucet; @@ -22,6 +18,7 @@ pub struct FaucetReceipt { pub struct CoinInfo { pub amount: u64, pub id: ObjectID, + pub transfer_tx_digest: TransactionDigest, } #[async_trait] @@ -34,21 +31,3 @@ pub trait Faucet { amounts: &[u64], ) -> Result; } - -impl<'a> FromIterator<&'a SuiParsedObject> for FaucetReceipt { - fn from_iter>(iter: T) -> Self { - FaucetReceipt { - sent: iter.into_iter().map(|o| o.into()).collect(), - } - } -} - -impl From<&SuiParsedObject> for CoinInfo { - fn from(v: &SuiParsedObject) -> Self { - let gas_coin = GasCoin::try_from(v).unwrap(); - Self { - amount: gas_coin.value(), - id: *gas_coin.id(), - } - } -} diff --git a/crates/sui-faucet/src/faucet/simple_faucet.rs b/crates/sui-faucet/src/faucet/simple_faucet.rs index 5b6c4e1b28399..10efafa4acfcc 100644 --- a/crates/sui-faucet/src/faucet/simple_faucet.rs +++ b/crates/sui-faucet/src/faucet/simple_faucet.rs @@ -334,7 +334,8 @@ impl Faucet for SimpleFaucet { Ok(FaucetReceipt { sent: results .iter() - .map(|(_digest, obj_id, amount, _gas_id)| CoinInfo { + .map(|(digest, obj_id, amount, _gas_id)| CoinInfo { + transfer_tx_digest: *digest, amount: *amount, id: *obj_id, }) diff --git a/crates/sui-sdk/src/lib.rs b/crates/sui-sdk/src/lib.rs index 365a06705ce4c..b9b6fb9e8082e 100644 --- a/crates/sui-sdk/src/lib.rs +++ b/crates/sui-sdk/src/lib.rs @@ -1,7 +1,7 @@ // Copyright (c) 2022, Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 -use std::fmt::Write; +use std::fmt::{Debug, Write}; use std::fmt::{Display, Formatter}; use std::sync::Arc; @@ -60,6 +60,19 @@ enum SuiClientApi { Embedded(GatewayClient), } +impl Debug for SuiClientApi { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match self { + SuiClientApi::Rpc(rpc_client) => write!( + f, + "RPC client. Http: {:?}, Websocket: {:?}", + rpc_client.http, rpc_client.ws + ), + SuiClientApi::Embedded(_) => write!(f, "Embedded Gateway client."), + } + } +} + struct RpcClient { http: HttpClient, ws: Option, @@ -200,6 +213,7 @@ impl SuiClient { } } +#[derive(Debug)] pub struct ReadApi { api: Arc, } diff --git a/crates/sui-test-validator/src/main.rs b/crates/sui-test-validator/src/main.rs index 43c2add604a68..7ef79d740921e 100644 --- a/crates/sui-test-validator/src/main.rs +++ b/crates/sui-test-validator/src/main.rs @@ -15,7 +15,6 @@ use sui_cluster_test::{ cluster::{Cluster, LocalNewCluster}, config::{ClusterTestOpt, Env}, faucet::{FaucetClient, FaucetClientFactory}, - wallet_client::WalletClient, }; use sui_types::base_types::SuiAddress; use tower::ServiceBuilder; @@ -73,17 +72,12 @@ async fn main() -> Result<()> { struct AppState { faucet: Arc, - wallet_client: WalletClient, } async fn start_faucet(cluster: &LocalNewCluster, port: u16) -> Result<()> { - let wallet_client = WalletClient::new_from_cluster(cluster).await; let faucet = FaucetClientFactory::new_from_cluster(cluster).await; - let app_state = Arc::new(AppState { - faucet, - wallet_client, - }); + let app_state = Arc::new(AppState { faucet }); let cors = CorsLayer::new() .allow_methods(vec![Method::GET, Method::POST]) @@ -130,16 +124,13 @@ async fn faucet_request( Json(payload): Json, Extension(state): Extension>, ) -> impl IntoResponse { - let result = state - .faucet - .request_sui_coins(&state.wallet_client, Some(1), Some(payload.recipient)) - .await; - - match result { - Ok(_) => (StatusCode::OK, Json(FaucetResponse { ok: true })), - Err(_) => ( + let result = state.faucet.request_sui_coins(payload.recipient).await; + if !result.transferred_gas_objects.is_empty() { + (StatusCode::OK, Json(FaucetResponse { ok: true })) + } else { + ( StatusCode::INTERNAL_SERVER_ERROR, Json(FaucetResponse { ok: false }), - ), + ) } } diff --git a/crates/test-utils/src/transaction.rs b/crates/test-utils/src/transaction.rs index c22cc61e04ffc..9c67fa55c691d 100644 --- a/crates/test-utils/src/transaction.rs +++ b/crates/test-utils/src/transaction.rs @@ -138,6 +138,8 @@ pub async fn submit_move_transaction( let signature = context.keystore.sign(&sender, &data.to_bytes()).unwrap(); let tx = Transaction::new(data, signature); + let tx_digest = tx.digest(); + debug!(?tx_digest, "submitting move transaction"); context .client