Skip to content

Commit 27f6696

Browse files
authored
Merge pull request #3809 from dusk-network/mempool_nonce
2 parents 7238e64 + e5ee2a8 commit 27f6696

File tree

3 files changed

+52
-24
lines changed

3 files changed

+52
-24
lines changed

rusk/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1010
### Added
1111

1212
- Add support for `TransactionData::Blob`
13+
- Add `mempool_nonce` field to `/on/account/status` response
1314

1415
## [1.3.0] - 2025-04-17
1516

rusk/src/lib/http/chain.rs

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,16 @@ pub mod graphql;
1010
use std::collections::HashMap;
1111
use std::sync::Arc;
1212

13+
use dusk_bytes::DeserializableSlice;
14+
use dusk_core::signatures::bls::PublicKey as BlsPublicKey;
1315
use dusk_core::transfer::data::{BlobData, BlobSidecar};
1416
use dusk_core::transfer::Transaction as ProtocolTransaction;
1517
use dusk_vm::execute;
1618
use node::database::rocksdb::MD_HASH_KEY;
1719
use node::database::{self, Ledger, LightBlock, Mempool, Metadata, DB};
1820
use node::mempool::MempoolSrv;
1921
use node::vm::VMExecution;
20-
use node_data::ledger::Transaction;
22+
use node_data::ledger::{SpendingId, Transaction};
2123

2224
use async_graphql::{
2325
EmptyMutation, EmptySubscription, Name, Schema, Variables,
@@ -62,6 +64,7 @@ impl HandleRequest for RuskNode {
6264
("network", _, "peers") => true,
6365
("network", _, "peers_location") => true,
6466
("node", _, "info") => true,
67+
("account", Some(_), "status") => true,
6568
("blocks", _, "gas-price") => true,
6669
("blobs", Some(_), "commitment") => true,
6770
("blobs", Some(_), "hash") => true,
@@ -93,6 +96,7 @@ impl HandleRequest for RuskNode {
9396

9497
("network", _, "peers_location") => self.peers_location().await,
9598
("node", _, "info") => self.get_info().await,
99+
("account", Some(pk), "status") => self.get_account(pk).await,
96100
("blocks", _, "gas-price") => {
97101
let max_transactions = request
98102
.data
@@ -333,6 +337,51 @@ impl RuskNode {
333337
};
334338
Ok(response)
335339
}
340+
341+
async fn get_account(&self, pk: &str) -> anyhow::Result<ResponseData> {
342+
let pk = bs58::decode(pk)
343+
.into_vec()
344+
.map_err(|_| anyhow::anyhow!("Invalid bs58 account"))?;
345+
let pk = BlsPublicKey::from_slice(&pk)
346+
.map_err(|_| anyhow::anyhow!("Invalid bls account"))?;
347+
348+
let db = self.inner().database();
349+
let vm = self.inner().vm_handler();
350+
351+
let account = vm
352+
.read()
353+
.await
354+
.account(&pk)
355+
.map_err(|e| anyhow::anyhow!("Cannot query the state {e:?}"))?;
356+
357+
// Determine the next available nonce not already used in the mempool.
358+
// This ensures that any in-flight transactions using sequential nonces
359+
// are accounted for.
360+
// If the account has no transactions in the mempool, the mempool_nonce
361+
// will be `None`, indicating that the next nonce is the same as the
362+
// account's current nonce.
363+
let mempool_nonce = db
364+
.read()
365+
.await
366+
.view(|t| {
367+
let mut next_nonce = account.nonce;
368+
loop {
369+
let id = SpendingId::AccountNonce(pk, next_nonce);
370+
if t.mempool_txs_by_spendable_ids(&[id]).is_empty() {
371+
break;
372+
}
373+
next_nonce += 1;
374+
}
375+
anyhow::Ok((next_nonce > account.nonce).then_some(next_nonce))
376+
})
377+
.map_err(|e| anyhow::anyhow!("Cannot check the mempool {e}"))?;
378+
379+
Ok(ResponseData::new(json!({
380+
"balance": account.balance,
381+
"nonce": account.nonce,
382+
"mempool_nonce": mempool_nonce,
383+
})))
384+
}
336385
}
337386

338387
async fn load_tip<DB: database::DB>(

rusk/src/lib/http/rusk.rs

Lines changed: 1 addition & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,14 @@
77
use super::*;
88

99
use anyhow::anyhow;
10-
use dusk_bytes::{DeserializableSlice, Serializable};
10+
use dusk_bytes::Serializable;
1111
use dusk_core::abi::ContractId;
12-
use dusk_core::signatures::bls::PublicKey as BlsPublicKey;
1312
use dusk_core::stake::{StakeFundOwner, STAKE_CONTRACT};
1413
use dusk_core::transfer::TRANSFER_CONTRACT;
1514
use dusk_data_driver::ConvertibleContract;
1615
use event::RequestData;
1716
use rusk_profile::CRS_17_HASH;
1817
use serde::Serialize;
19-
use serde_json::json;
2018
use std::sync::mpsc;
2119
use std::thread;
2220

@@ -32,7 +30,6 @@ impl HandleRequest for Rusk {
3230
("contracts", Some(_), _) => true,
3331
("driver", Some(_), _) => true,
3432
("node", _, "provisioners") => true,
35-
("account", Some(_), "status") => true,
3633
("node", _, "crs") => true,
3734
_ => false,
3835
}
@@ -57,7 +54,6 @@ impl HandleRequest for Rusk {
5754
}
5855
("node", _, "provisioners") => self.get_provisioners(),
5956

60-
("account", Some(pk), "status") => self.get_account(pk),
6157
("node", _, "crs") => self.get_crs(),
6258
_ => Err(anyhow::anyhow!("Unsupported")),
6359
}
@@ -228,24 +224,6 @@ impl Rusk {
228224
Ok(ResponseData::new(serde_json::to_value(prov)?))
229225
}
230226

231-
fn get_account(&self, pk: &str) -> anyhow::Result<ResponseData> {
232-
let pk = bs58::decode(pk)
233-
.into_vec()
234-
.map_err(|_| anyhow::anyhow!("Invalid bs58 account"))?;
235-
let pk = BlsPublicKey::from_slice(&pk)
236-
.map_err(|_| anyhow::anyhow!("Invalid bls account"))?;
237-
let account = self
238-
.account(&pk)
239-
.map(|account| {
240-
json!({
241-
"balance": account.balance,
242-
"nonce": account.nonce,
243-
})
244-
})
245-
.map_err(|e| anyhow::anyhow!("Cannot query the state {e:?}"))?;
246-
Ok(ResponseData::new(account))
247-
}
248-
249227
fn get_crs(&self) -> anyhow::Result<ResponseData> {
250228
let crs = rusk_profile::get_common_reference_string()?;
251229
Ok(ResponseData::new(crs).with_header("crs-hash", CRS_17_HASH))

0 commit comments

Comments
 (0)