Skip to content

Commit 059241d

Browse files
committed
rusk: add tests for sequential nonce ordering
1 parent e1952ae commit 059241d

File tree

5 files changed

+225
-6
lines changed

5 files changed

+225
-6
lines changed

rusk/tests/common/wallet/test_wallet/imp.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -743,6 +743,7 @@ where
743743
gas_limit,
744744
gas_price,
745745
data,
746+
None,
746747
)
747748
}
748749

@@ -757,23 +758,26 @@ where
757758
gas_limit: u64,
758759
gas_price: u64,
759760
data: Option<impl Into<TransactionData>>,
761+
nonce: Option<u64>,
760762
) -> Result<Transaction, Error<S, SC>> {
761763
let mut seed = self.store.get_seed().map_err(Error::from_store_err)?;
762764
let mut sender_sk = derive_bls_sk(&seed, sender_index);
763765
let sender_account = BlsPublicKey::from(&sender_sk);
764766

767+
println!("before fetch_account");
765768
let account = self
766769
.state
767770
.fetch_account(&sender_account)
768771
.map_err(Error::from_state_err)?;
772+
println!("after fetch_account");
769773

770774
// technically this check is not necessary, but it's nice to not spam
771775
// the network with transactions that are unspendable.
772776
let max_value = value + deposit + gas_limit * gas_price;
773777
if max_value > account.balance {
774778
return Err(ExecutionError::InsufficientBalance.into());
775779
}
776-
let nonce = account.nonce + 1;
780+
let nonce = nonce.unwrap_or(account.nonce + 1);
777781

778782
let chain_id =
779783
self.state.fetch_chain_id().map_err(Error::from_state_err)?;
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
[[moonlight_account]]
2+
address = "qe1FbZxf6YaCAeFNSvL1G82cBhG4Q4gBf4vKYo527Vws3b23jdbBuzKSFsdUHnZeBgsTnyNJLkApEpRyJw87sdzR9g9iESJrG5ZgpCs9jq88m6d4qMY5txGpaXskRQmkzE3"
3+
balance = 10_000_000_000
4+
5+
6+
[[moonlight_account]]
7+
address = "25omWWRyfcMjYNbQZVyc3rypYLi8UqZuthoJHqbCEriRX3z2EmnBaXWZLFL2NvzvDnkoYoHLGiSYQpmupNJj1sSdWNstqzfFEpiqvSNYw7gqvoEiU9FsEHUMG1ZyG3XgL8Rv"
8+
balance = 10_000_000_000
9+
10+
11+
[[moonlight_account]]
12+
address = "mqL8WTruQUzafU8Q4bSFqTLMhHmB97DAGow73KRpDkDik5vqMD9EpGvyZc2j3TFaEh8hc6g3LJ5X3ox18HDDoVDj8MKaHPzphvdgyG4XmsFEafAiym6kqRPmB14QNRvkEK4"
13+
balance = 10_000_000_000

rusk/tests/services/init.rs

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -48,10 +48,7 @@ const OWNER: [u8; 32] = [1; 32];
4848
const DEPLOYER_INDEX: u8 = 0;
4949
const SENDER_INDEX: u8 = 1;
5050

51-
async fn initial_state<P: AsRef<Path>>(
52-
dir: P,
53-
owner: impl AsRef<[u8]>,
54-
) -> Result<Rusk> {
51+
async fn initial_state<P: AsRef<Path>>(dir: P) -> Result<Rusk> {
5552
let dir = dir.as_ref();
5653

5754
let snapshot = toml::from_str(include_str!("../config/init.toml"))
@@ -181,7 +178,7 @@ pub async fn calling_init_via_tx_fails() -> Result<()> {
181178
logger();
182179

183180
let tmp = tempdir().expect("Should be able to create temporary directory");
184-
let rusk = initial_state(&tmp, OWNER).await?;
181+
let rusk = initial_state(&tmp).await?;
185182

186183
let cache = Arc::new(RwLock::new(HashMap::new()));
187184

rusk/tests/services/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,5 +16,6 @@ pub mod moonlight_stake;
1616
pub mod multi_transfer;
1717
pub mod owner_calls;
1818
pub mod phoenix_stake;
19+
pub mod sequential_nonce;
1920
pub mod transfer;
2021
pub mod unspendable;
Lines changed: 204 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,204 @@
1+
// This Source Code Form is subject to the terms of the Mozilla Public
2+
// License, v. 2.0. If a copy of the MPL was not distributed with this
3+
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
4+
//
5+
// Copyright (c) DUSK NETWORK. All rights reserved.
6+
7+
use std::collections::HashMap;
8+
use std::path::Path;
9+
use std::sync::{Arc, RwLock};
10+
11+
use dusk_bytes::Serializable;
12+
use dusk_core::transfer::data::TransactionData;
13+
use rand::prelude::*;
14+
use rand::rngs::StdRng;
15+
use rusk::node::RuskVmConfig;
16+
use rusk::{Result, Rusk};
17+
use tempfile::tempdir;
18+
use tracing::info;
19+
20+
use crate::common::logger;
21+
use crate::common::state::{generator_procedure, new_state, ExecuteResult};
22+
use crate::common::wallet::{
23+
test_wallet as wallet, TestStateClient, TestStore,
24+
};
25+
26+
const BLOCK_HEIGHT: u64 = 1;
27+
// This is purposefully chosen to be low to trigger the discarding of a
28+
// perfectly good transaction.
29+
const BLOCK_GAS_LIMIT: u64 = 24_000_000;
30+
31+
const GAS_LIMIT: u64 = 12_000_000; // Lowest value for a transfer
32+
const INITIAL_BALANCE: u64 = 10_000_000_000;
33+
34+
// Creates the Rusk initial state for the tests below
35+
async fn initial_state<P: AsRef<Path>>(dir: P) -> Result<Rusk> {
36+
let snapshot =
37+
toml::from_str(include_str!("../config/sequential_nonce.toml"))
38+
.expect("Cannot deserialize config");
39+
let vm_config = RuskVmConfig::new().with_block_gas_limit(BLOCK_GAS_LIMIT);
40+
41+
new_state(dir, &snapshot, vm_config).await
42+
}
43+
44+
/// Executes three different transactions in the same block, expecting only two
45+
/// to be included due to exceeding the block gas limit
46+
fn wallet_transfer(
47+
rusk: &Rusk,
48+
wallet: &wallet::Wallet<TestStore, TestStateClient>,
49+
amount: u64,
50+
) {
51+
for i in 0..3 {
52+
let account = wallet
53+
.account_public_key(i)
54+
.expect("Failed to get the account");
55+
info!(
56+
"Account {i}: {}",
57+
bs58::encode(account.to_bytes()).into_string()
58+
);
59+
}
60+
// Generate a receiver pk
61+
let receiver = wallet
62+
.account_public_key(3)
63+
.expect("Failed to get public key");
64+
65+
let initial_balance_0 = wallet
66+
.get_account(0)
67+
.expect("Failed to get the balance")
68+
.balance;
69+
let initial_balance_1 = wallet
70+
.get_account(1)
71+
.expect("Failed to get the balance")
72+
.balance;
73+
let initial_balance_2 = wallet
74+
.get_account(2)
75+
.expect("Failed to get the balance")
76+
.balance;
77+
78+
// Check the senders initial balance is correct
79+
assert_eq!(
80+
initial_balance_0, INITIAL_BALANCE,
81+
"Wrong initial balance for the sender"
82+
);
83+
assert_eq!(
84+
initial_balance_1, INITIAL_BALANCE,
85+
"Wrong initial balance for the sender"
86+
);
87+
assert_eq!(
88+
initial_balance_2, INITIAL_BALANCE,
89+
"Wrong initial balance for the sender"
90+
);
91+
92+
// Check the receiver initial balance is zero
93+
assert_eq!(
94+
wallet
95+
.get_balance(3)
96+
.expect("Failed to get the balance")
97+
.value,
98+
0,
99+
"Wrong initial balance for the receiver"
100+
);
101+
102+
const TOTAL_TX: u64 = 50;
103+
let mut idxs: Vec<u64> = (1..=TOTAL_TX).collect();
104+
let mut rng = thread_rng(); // Get a random number generator
105+
idxs.shuffle(&mut rng);
106+
107+
let mut txs = Vec::with_capacity(TOTAL_TX as usize);
108+
for i in idxs {
109+
let tx = wallet
110+
.moonlight_transaction(
111+
0,
112+
Some(receiver),
113+
amount,
114+
0,
115+
GAS_LIMIT,
116+
1,
117+
None::<TransactionData>,
118+
Some(i),
119+
)
120+
.expect("Failed to transfer");
121+
122+
txs.push(tx);
123+
}
124+
125+
let expected = ExecuteResult {
126+
discarded: 0,
127+
executed: TOTAL_TX as usize,
128+
};
129+
130+
generator_procedure(
131+
rusk,
132+
&txs[..],
133+
BLOCK_HEIGHT,
134+
BLOCK_GAS_LIMIT,
135+
vec![],
136+
Some(expected),
137+
)
138+
.expect("generator procedure to succeed");
139+
140+
// Check the receiver's balance is changed accordingly
141+
assert_eq!(
142+
wallet
143+
.get_account(3)
144+
.expect("Failed to get the balance")
145+
.balance,
146+
TOTAL_TX * amount,
147+
"Wrong resulting balance for the receiver"
148+
);
149+
150+
let final_balance_0 = wallet
151+
.get_account(0)
152+
.expect("Failed to get the balance")
153+
.balance;
154+
let gas_limit_0 = txs[0].gas_limit();
155+
let gas_price_0 = txs[0].gas_price();
156+
let fee_0 = gas_limit_0 * gas_price_0;
157+
158+
assert!(
159+
initial_balance_0 - (amount + fee_0) * TOTAL_TX <= final_balance_0,
160+
"Final sender balance {} should be greater or equal than {}",
161+
final_balance_0,
162+
initial_balance_0 - (amount + fee_0) * TOTAL_TX
163+
);
164+
165+
assert!(
166+
initial_balance_0 - amount * TOTAL_TX >= final_balance_0,
167+
"Final sender balance {} should be lesser or equal than {}",
168+
final_balance_0,
169+
initial_balance_0 - amount * TOTAL_TX
170+
);
171+
}
172+
173+
#[tokio::test(flavor = "multi_thread")]
174+
pub async fn multi_transfer() -> Result<()> {
175+
// Setup the logger
176+
logger();
177+
178+
let tmp = tempdir().expect("Should be able to create temporary directory");
179+
let rusk = initial_state(&tmp).await?;
180+
181+
let cache = Arc::new(RwLock::new(HashMap::new()));
182+
183+
// Create a wallet
184+
let wallet = wallet::Wallet::new(
185+
TestStore,
186+
TestStateClient {
187+
rusk: rusk.clone(),
188+
cache,
189+
},
190+
);
191+
192+
let original_root = rusk.state_root();
193+
194+
info!("Original Root: {}", hex::encode(original_root));
195+
196+
wallet_transfer(&rusk, &wallet, 1_000);
197+
198+
// Check the state's root is changed from the original one
199+
let new_root = rusk.state_root();
200+
info!("New root after the 1st transfer: {}", hex::encode(new_root));
201+
assert_ne!(original_root, new_root, "Root should have changed");
202+
203+
Ok(())
204+
}

0 commit comments

Comments
 (0)