Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make eth_getTransactionCount respect the pending block number #861

Merged
merged 2 commits into from
May 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
13 changes: 1 addition & 12 deletions zilliqa/src/api/eth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -191,25 +191,14 @@ fn get_storage_at(params: Params, node: &Arc<Mutex<Node>>) -> Result<String> {
}

fn get_transaction_count(params: Params, node: &Arc<Mutex<Node>>) -> Result<String> {
trace!("get_transaction_count: params: {:?}", params);
let mut params = params.sequence();
let address: H160 = params.next()?;
let block_number: BlockNumber = params.next()?;

trace!(
"get_transaction_count resp: {:?}",
node.lock()
.unwrap()
.get_account(address, block_number)?
.nonce
.to_hex()
);

Ok(node
.lock()
.unwrap()
.get_account(address, block_number)?
.nonce
.get_transaction_count(address, block_number)?
.to_hex())
}

Expand Down
34 changes: 18 additions & 16 deletions zilliqa/src/consensus.rs
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ pub struct Consensus {
/// The persistence database
db: Arc<Db>,
/// Actions that act on newly created blocks
transaction_pool: TransactionPool,
pub transaction_pool: TransactionPool,
// PRNG - non-cryptographically secure, but we don't need that here
rng: SmallRng,
/// Flag indicating that block creation should be postponed due to empty mempool
Expand Down Expand Up @@ -773,7 +773,7 @@ impl Consensus {
}

pub fn get_txns_to_execute(&mut self) -> Vec<VerifiedTransaction> {
std::iter::from_fn(|| self.transaction_pool.best_transaction())
let txns: Vec<_> = std::iter::from_fn(|| self.transaction_pool.best_transaction())
.filter(|txn| {
let account_nonce = self.state.must_get_account(txn.signer).nonce;
// Ignore this transaction if it is no longer valid.
Expand All @@ -784,7 +784,17 @@ impl Consensus {
.map(|tx_nonce| tx_nonce >= account_nonce)
.unwrap_or(true)
})
.collect()
.collect();

// Reinsert these transactions into the pool. In case this proposal doesn't get mined, we want to retain the
// pending transactions for another try later.
for txn in txns.clone() {
let account_nonce = self.state.must_get_account(txn.signer).nonce;
// This insertion should always succeed, because we just took this transaction out of the pool.
assert!(self.transaction_pool.insert_transaction(txn, account_nonce));
}

txns
}

pub fn get_touched_transactions(&self, address: H160) -> Result<Vec<Hash>> {
Expand Down Expand Up @@ -1036,19 +1046,11 @@ impl Consensus {
);
// as a future improvement, process the proposal before broadcasting it
trace!(proposal_hash = ?proposal.hash(), ?proposal.header.view, ?proposal.header.number, "######### vote successful, we are proposing block");
// intershard transactions are not meant to be broadcast
let (broadcasted_transactions, opaque_transactions): (Vec<_>, Vec<_>) =
applied_transactions
.into_iter()
.partition(|tx| !matches!(tx.tx, SignedTransaction::Intershard { .. }));
// however, for the transactions that we are NOT broadcasting, we re-insert
// them into the pool - this is because upon broadcasting the proposal, we will
// have to re-execute it ourselves (in order to vote on it) and thus will
// need those transactions again
for tx in opaque_transactions {
let account_nonce = self.state.get_account(tx.signer)?.nonce;
self.transaction_pool.insert_transaction(tx, account_nonce);
}
let broadcasted_transactions = applied_transactions
.into_iter()
// intershard transactions are not meant to be broadcast
.filter(|tx| !matches!(tx.tx, SignedTransaction::Intershard { .. }))
.collect();
Ok(Some((proposal, broadcasted_transactions)))
}

Expand Down
19 changes: 19 additions & 0 deletions zilliqa/src/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -463,6 +463,25 @@ impl Node {
.get_account(address)
}

/// Return the `nonce` of an account. If the `block_number` is [BlockNumber::Pending], any pending transactions in
/// our pool are also added to the nonce.
pub fn get_transaction_count(
&self,
address: Address,
block_number: BlockNumber,
) -> Result<u64> {
let nonce = self.get_account(address, block_number)?.nonce;

JamesHinshelwood marked this conversation as resolved.
Show resolved Hide resolved
if matches!(block_number, BlockNumber::Pending) {
Ok(self
.consensus
.transaction_pool
.pending_nonce(address, nonce))
} else {
Ok(nonce)
}
}

pub fn get_account_storage(
&self,
address: Address,
Expand Down
15 changes: 15 additions & 0 deletions zilliqa/src/pool.rs
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,21 @@ impl TransactionPool {
std::mem::take(&mut self.transactions).into_values()
}

/// Calculate the effective nonce of the sender, if all the currently pending transactions in the pool were mined
/// in order. Effectively, this calculates the current nonce plus the number of transactions with sequential nonces
/// in the pool.
pub fn pending_nonce(&self, sender: Address, mut current_nonce: u64) -> u64 {
loop {
let index = TxIndex::Nonced(sender, current_nonce);
JamesHinshelwood marked this conversation as resolved.
Show resolved Hide resolved
if self.transactions.get(&index).is_none() {
break;
}
current_nonce += 1;
}

current_nonce
}

pub fn size(&self) -> usize {
self.transactions.len()
}
Expand Down
33 changes: 32 additions & 1 deletion zilliqa/tests/it/eth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use futures::{future::join_all, StreamExt};
use primitive_types::{H160, H256};
use serde::Serialize;

use crate::{deploy_contract, LocalRpcClient, Network};
use crate::{deploy_contract, LocalRpcClient, Network, Wallet};

#[zilliqa_macros::test]
async fn call_block_number(mut network: Network) {
Expand Down Expand Up @@ -1056,3 +1056,34 @@ async fn block_subscription(mut network: Network) {

assert!(block_stream.unsubscribe().await.unwrap());
}

#[zilliqa_macros::test]
async fn get_transaction_count_pending(mut network: Network) {
async fn pending_count(wallet: &Wallet) -> u64 {
wallet
.get_transaction_count(wallet.address(), Some(BlockNumber::Pending.into()))
.await
.unwrap()
.as_u64()
}

let wallet = network.genesis_wallet().await;

assert_eq!(pending_count(&wallet).await, 0);

wallet
.send_transaction(TransactionRequest::pay(H160::random(), 10), None)
.await
.unwrap()
.tx_hash();

assert_eq!(pending_count(&wallet).await, 1);

wallet
.send_transaction(TransactionRequest::pay(H160::random(), 10).nonce(1), None)
.await
.unwrap()
.tx_hash();

assert_eq!(pending_count(&wallet).await, 2);
}