Skip to content

Commit 0942e57

Browse files
authored
chore: implement get_balance via grpc (#2935)
* chore: implement get_balance via grpc
1 parent db0fd2a commit 0942e57

File tree

18 files changed

+390
-186
lines changed

18 files changed

+390
-186
lines changed

.github/workflows/code.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ jobs:
113113
runs-on: ubuntu-ghcloud
114114
strategy:
115115
matrix:
116-
grpc_migration_level: [0, 1]
116+
grpc_migration_level: [0, 100]
117117
env:
118118
RUST_LOG: walrus=info,error,sui_=off,consensus_core=off
119119
OPENSSL_STATIC: "1"
@@ -161,11 +161,11 @@ jobs:
161161
- name: Run tests (seed=2)
162162
run: MSIM_TEST_SEED=2 cargo simtest simtest --profile simtest
163163
- name: Run tests (seed=3, grpc-client-enabled)
164-
run: MSIM_TEST_SEED=3 WALRUS_GRPC_MIGRATION_LEVEL=1 cargo simtest simtest --profile simtest
164+
run: MSIM_TEST_SEED=3 WALRUS_GRPC_MIGRATION_LEVEL=100 cargo simtest simtest --profile simtest
165165
- name: Run tests (seed=4, grpc-client-enabled)
166-
run: MSIM_TEST_SEED=4 WALRUS_GRPC_MIGRATION_LEVEL=1 cargo simtest simtest --profile simtest
166+
run: MSIM_TEST_SEED=4 WALRUS_GRPC_MIGRATION_LEVEL=100 cargo simtest simtest --profile simtest
167167
- name: Run tests (seed=5, grpc-client-enabled)
168-
run: MSIM_TEST_SEED=5 WALRUS_GRPC_MIGRATION_LEVEL=1 cargo simtest simtest --profile simtest
168+
run: MSIM_TEST_SEED=5 WALRUS_GRPC_MIGRATION_LEVEL=100 cargo simtest simtest --profile simtest
169169

170170
simtests-build:
171171
name: Build all simtests

crates/walrus-e2e-tests/tests/test_client.rs

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ use walrus_sui::{
9898
SuiContractClient,
9999
retry_client::{RetriableSuiClient, retriable_sui_client::LazySuiClientBuilder},
100100
},
101+
coin::Coin,
101102
config::WalletConfig,
102103
test_utils::{self, fund_addresses, wallet_for_testing},
103104
types::{
@@ -2527,19 +2528,19 @@ pub async fn test_select_coins_max_objects() -> TestResult {
25272528
ExponentialBackoffConfig::default(),
25282529
)?;
25292530

2530-
let balance = retry_client.get_balance(address, None).await?;
2531-
assert_eq!(balance.total_balance, u128::from(sui(4)));
2531+
let balance = retry_client.get_total_balance(address, Coin::SUI).await?;
2532+
assert_eq!(balance, sui(4));
25322533

25332534
// The maximum number of coins that can be selected to reach the amount.
25342535
let max_num_coins = 2;
25352536

25362537
let result = retry_client
2537-
.select_coins(address, None, sui(1).into(), vec![], max_num_coins)
2538+
.select_coins(address, Coin::SUI, sui(1).into(), vec![], max_num_coins)
25382539
.await;
25392540
assert!(result.is_ok(), "1 SUI can be constructed with <= 2 coins");
25402541

25412542
let result = retry_client
2542-
.select_coins(address, None, sui(3).into(), vec![], max_num_coins)
2543+
.select_coins(address, Coin::SUI, sui(3).into(), vec![], max_num_coins)
25432544
.await;
25442545
if let Err(error) = result {
25452546
assert!(
@@ -2551,7 +2552,7 @@ pub async fn test_select_coins_max_objects() -> TestResult {
25512552
}
25522553

25532554
let result = retry_client
2554-
.select_coins(address, None, sui(5).into(), vec![], max_num_coins)
2555+
.select_coins(address, Coin::SUI, sui(5).into(), vec![], max_num_coins)
25552556
.await;
25562557
if let Err(error) = result {
25572558
assert!(
@@ -2796,10 +2797,9 @@ async fn test_store_with_upload_relay_with_tip() {
27962797

27972798
// Get initial balance of relay wallet to verify tip payment
27982799
let initial_relay_balance = retry_client
2799-
.get_balance(relay_address, None)
2800+
.get_total_balance(relay_address, Coin::SUI)
28002801
.await
2801-
.expect("get balance")
2802-
.total_balance;
2802+
.expect("get balance");
28032803

28042804
const BLOB_SIZE: usize = 40000;
28052805
match basic_store_and_read(
@@ -2819,10 +2819,9 @@ async fn test_store_with_upload_relay_with_tip() {
28192819

28202820
// Verify that the relay wallet received a tip
28212821
let final_relay_balance = retry_client
2822-
.get_balance(relay_address, None)
2822+
.get_total_balance(relay_address, Coin::SUI)
28232823
.await
2824-
.expect("get balance")
2825-
.total_balance;
2824+
.expect("get balance");
28262825

28272826
tracing::info!(
28282827
"Relay address balance - Initial: {initial_relay_balance}, Final: {final_relay_balance}",
@@ -2839,8 +2838,7 @@ async fn test_store_with_upload_relay_with_tip() {
28392838
encoded_blob_length_for_n_shards(n_shards, BLOB_SIZE as u64, EncodingType::RS2)
28402839
.expect("encoded blob size should be valid");
28412840

2842-
let expected_tip_lower_bound =
2843-
u128::from(TIP_BASE + encoded_blob_size.div_ceil(1024) * TIP_MULTIPLIER);
2841+
let expected_tip_lower_bound = TIP_BASE + encoded_blob_size.div_ceil(1024) * TIP_MULTIPLIER;
28442842
let actual_tip = final_relay_balance - initial_relay_balance;
28452843

28462844
tracing::info!(

crates/walrus-sdk/src/client/upload_relay_client.rs

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -42,12 +42,7 @@ use crate::{
4242
metadata::{BlobMetadataApi, VerifiedBlobMetadataWithId},
4343
},
4444
sui::{
45-
client::{
46-
BlobPersistence,
47-
CoinType,
48-
SuiContractClient,
49-
transaction_builder::WalrusPtbBuilder,
50-
},
45+
client::{BlobPersistence, SuiContractClient, transaction_builder::WalrusPtbBuilder},
5146
config::WalletConfig,
5247
types::{BlobEvent, BlobRegistered},
5348
wallet::Wallet,
@@ -221,6 +216,7 @@ impl UploadRelayClient {
221216
self.gas_budget,
222217
0, // No additional gas budget.
223218
tip_amount,
219+
None,
224220
)
225221
.await?;
226222

crates/walrus-service/src/client/multiplexer.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ use walrus_sui::{
4343
SuiReadClient,
4444
retry_client::RetriableSuiClient,
4545
},
46+
coin::Coin,
4647
config::load_wallet_context_from_path,
4748
types::move_structs::BlobWithAttribute,
4849
utils::create_wallet,
@@ -496,13 +497,13 @@ impl<'a> SubClientLoader<'a> {
496497
self.config.communication_config.sui_client_request_timeout,
497498
)?;
498499

499-
if should_refill(&sui_client, address, None, min_balance).await {
500+
if should_refill(&sui_client, address, Coin::SUI, min_balance).await {
500501
self.refiller.send_gas_request(address).await?;
501502
} else {
502503
tracing::debug!(%address, "sub-wallet has enough SUI, skipping refill");
503504
}
504505

505-
if should_refill(&sui_client, address, Some(wal_coin_type), min_balance).await {
506+
if should_refill(&sui_client, address, wal_coin_type, min_balance).await {
506507
self.refiller.send_wal_request(address).await?;
507508
} else {
508509
tracing::debug!(%address, "sub-wallet has enough WAL, skipping refill");

crates/walrus-service/src/client/refill.rs

Lines changed: 15 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,10 @@ use sui_sdk::types::base_types::SuiAddress;
1111
use tokio::{task::JoinHandle, time::MissedTickBehavior};
1212
use tracing::Instrument;
1313
use walrus_sdk::client::metrics::ClientMetrics;
14-
use walrus_sui::client::{SuiContractClient, retry_client::RetriableSuiClient};
14+
use walrus_sui::{
15+
client::{SuiContractClient, retry_client::RetriableSuiClient},
16+
coin::Coin,
17+
};
1518

1619
/// Refills gas and WAL for the clients.
1720
#[derive(Debug, Clone)]
@@ -77,7 +80,7 @@ impl Refiller {
7780
addresses,
7881
period,
7982
sui_client,
80-
None, // Use SUI
83+
Coin::SUI,
8184
self.min_balance,
8285
move |refiller, address| {
8386
let metrics = metrics.clone();
@@ -103,7 +106,7 @@ impl Refiller {
103106
addresses,
104107
period,
105108
sui_client,
106-
Some(self.wal_coin_type()),
109+
self.wal_coin_type(),
107110
self.min_balance,
108111
move |refiller, address| {
109112
let metrics = metrics.clone();
@@ -122,7 +125,7 @@ impl Refiller {
122125
addresses: Vec<SuiAddress>,
123126
period: Duration,
124127
sui_client: RetriableSuiClient,
125-
coin_type: Option<String>,
128+
coin_type: &str,
126129
min_coin_value: u64,
127130
inner_action: F,
128131
) -> JoinHandle<anyhow::Result<()>>
@@ -134,6 +137,7 @@ impl Refiller {
134137
interval.set_missed_tick_behavior(MissedTickBehavior::Delay);
135138

136139
let refiller = self.contract_client.clone();
140+
let coin_type = coin_type.to_owned();
137141
tokio::spawn(async move {
138142
loop {
139143
let span =
@@ -143,17 +147,10 @@ impl Refiller {
143147
interval.tick().await;
144148
let sui_client = &sui_client;
145149
let _ = try_join_all(addresses.iter().cloned().map(|address| {
146-
let coin_type_inner = coin_type.clone();
147150
let inner_fut = inner_action(refiller.clone(), address);
148-
151+
let coin_type = coin_type.clone();
149152
async move {
150-
if should_refill(
151-
sui_client,
152-
address,
153-
coin_type_inner.clone(),
154-
min_coin_value,
155-
)
156-
.await
153+
if should_refill(sui_client, address, &coin_type, min_coin_value).await
157154
{
158155
inner_fut.await
159156
} else {
@@ -182,11 +179,8 @@ impl Refiller {
182179
}
183180

184181
/// The WAL coin type.
185-
pub fn wal_coin_type(&self) -> String {
186-
self.contract_client
187-
.read_client()
188-
.wal_coin_type()
189-
.to_owned()
182+
pub fn wal_coin_type(&self) -> &str {
183+
self.contract_client.read_client().wal_coin_type()
190184
}
191185

192186
/// Sends SUI to the specified address.
@@ -223,13 +217,13 @@ pub struct RefillHandles {
223217
pub async fn should_refill(
224218
sui_client: &RetriableSuiClient,
225219
address: SuiAddress,
226-
coin_type: Option<String>,
220+
coin_type: &str,
227221
min_balance: u64,
228222
) -> bool {
229223
sui_client
230-
.get_balance(address, coin_type)
224+
.get_total_balance(address, coin_type)
231225
.await
232-
.map(|balance| balance.total_balance < u128::from(min_balance))
226+
.map(|balance| balance < min_balance)
233227
.inspect_err(|error| {
234228
tracing::debug!(
235229
?error,

crates/walrus-service/src/node/contract_service.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,14 @@ use walrus_core::{Epoch, PublicKey, messages::InvalidBlobCertificate};
2121
use walrus_sui::{
2222
client::{
2323
BlobObjectMetadata,
24-
CoinType,
2524
FixedSystemParameters,
2625
ReadClient,
2726
SuiClientError,
2827
SuiClientMetricSet,
2928
SuiContractClient,
3029
SuiReadClient,
3130
},
31+
coin::CoinType,
3232
types::{
3333
StorageNodeCap,
3434
UpdatePublicKeyParams,
@@ -346,8 +346,8 @@ async fn monitor_sui_balance(
346346
async {
347347
tracing::trace!("querying wallet SUI balance");
348348

349-
let balance_mist = match client.balance(CoinType::Sui).await {
350-
Ok(balance_mist) => balance_mist,
349+
let balance_mist = match client.total_balance(CoinType::Sui).await {
350+
Ok(balance) => balance,
351351
Err(error) => {
352352
tracing::warn!(?error, "failed to get SUI balance, skipping interval");
353353
return; // Exit the async closure, does not exit the outer function.

crates/walrus-stress/src/main.rs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@ use walrus_stress::single_client_workload::{
2828
single_client_workload_arg::SingleClientWorkloadArgs,
2929
};
3030
use walrus_sui::{
31-
client::{CoinType, MIN_STAKING_THRESHOLD, ReadClient, SuiContractClient},
31+
client::{MIN_STAKING_THRESHOLD, ReadClient, SuiContractClient},
32+
coin::CoinType,
3233
config::WalletConfig,
3334
types::StorageNode,
3435
utils::SuiNetwork,
@@ -181,8 +182,8 @@ async fn run_stress(
181182
.context("Failed to load wallet context")?;
182183
let contract_client = client_config.new_contract_client(wallet, None).await?;
183184

184-
let wal_balance = contract_client.balance(CoinType::Wal).await?;
185-
let sui_balance = contract_client.balance(CoinType::Sui).await?;
185+
let wal_balance = contract_client.total_balance(CoinType::Wal).await?;
186+
let sui_balance = contract_client.total_balance(CoinType::Sui).await?;
186187
tracing::info!(wal_balance, sui_balance, "initial balances");
187188

188189
let refiller = Refiller::new(
@@ -240,8 +241,8 @@ async fn run_staking(config: ClientConfig, _metrics: Arc<ClientMetrics>) -> anyh
240241
tokio::time::sleep(restaking_period).await;
241242
let mut committee = contract_client.read_client().current_committee().await?;
242243
let current_epoch = committee.epoch;
243-
let wal_balance = contract_client.balance(CoinType::Wal).await?;
244-
let sui_balance = contract_client.balance(CoinType::Sui).await?;
244+
let wal_balance = contract_client.total_balance(CoinType::Wal).await?;
245+
let sui_balance = contract_client.total_balance(CoinType::Sui).await?;
245246
tracing::info!(current_epoch, ?mode, wal_balance, sui_balance, "woke up");
246247

247248
match mode {

crates/walrus-sui/src/balance.rs

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
// Copyright (c) Walrus Foundation
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
//! A module defining the Balance struct.
5+
6+
use anyhow::Context;
7+
8+
use crate::coin::Coin;
9+
10+
/// An error type for balance retrieval errors.
11+
#[derive(Debug, thiserror::Error)]
12+
#[error("error retrieving balance information: {0}")]
13+
pub struct BalanceRetrievalError(anyhow::Error);
14+
15+
impl From<anyhow::Error> for BalanceRetrievalError {
16+
fn from(err: anyhow::Error) -> Self {
17+
BalanceRetrievalError(err)
18+
}
19+
}
20+
21+
/// A struct representing the balance of a specific coin type.
22+
#[derive(Debug, Clone)]
23+
pub struct Balance {
24+
/// The total balance across all coin objects.
25+
total_balance: u64,
26+
/// The number of coin objects or the actual coin objects.
27+
coin_balance: CoinBalance,
28+
}
29+
30+
/// An enum representing either the count of coin objects or the actual coin objects.
31+
#[derive(Debug, Clone)]
32+
pub enum CoinBalance {
33+
/// The count of coin objects.
34+
Count(usize),
35+
/// The actual coin objects.
36+
Coins(Vec<Coin>),
37+
}
38+
39+
impl Balance {
40+
/// Creates a new Balance from a coin type and a list of coins.
41+
pub fn try_from_coins(coins: Vec<Coin>) -> Result<Self, BalanceRetrievalError> {
42+
let total_balance: u128 = coins.iter().map(|coin| u128::from(coin.balance)).sum();
43+
Ok(Self {
44+
total_balance: u64::try_from(total_balance).context("total balance exceeds u64")?,
45+
coin_balance: CoinBalance::Coins(coins),
46+
})
47+
}
48+
49+
/// Returns the number of coin objects.
50+
pub fn coin_object_count(&self) -> usize {
51+
match self.coin_balance {
52+
CoinBalance::Count(count) => count,
53+
CoinBalance::Coins(ref coins) => coins.len(),
54+
}
55+
}
56+
57+
/// Returns the total balance.
58+
pub fn total_balance(&self) -> u64 {
59+
self.total_balance
60+
}
61+
62+
/// Take the coins if they are available.
63+
pub fn coins(self) -> Option<Vec<Coin>> {
64+
match self.coin_balance {
65+
CoinBalance::Count(_) => None,
66+
CoinBalance::Coins(coins) => Some(coins),
67+
}
68+
}
69+
}
70+
71+
impl TryFrom<sui_sdk::rpc_types::Balance> for Balance {
72+
type Error = BalanceRetrievalError;
73+
74+
fn try_from(balance: sui_sdk::rpc_types::Balance) -> Result<Self, BalanceRetrievalError> {
75+
Ok(Self {
76+
coin_balance: CoinBalance::Count(balance.coin_object_count),
77+
total_balance: u64::try_from(balance.total_balance)
78+
.context("total balance exceeds u64")?,
79+
})
80+
}
81+
}

0 commit comments

Comments
 (0)