mirror of
https://github.com/logos-blockchain/lssa.git
synced 2026-01-03 22:03:06 +00:00
Merge pull request #97 from vacp2p/schouhy/prevent-replay-attacks-with-nonces
Prevent replay attacks with nonce mechanism
This commit is contained in:
commit
7f97340667
36
accounts/src/account_core/address.rs
Normal file
36
accounts/src/account_core/address.rs
Normal file
@ -0,0 +1,36 @@
|
||||
use common::transaction::SignaturePublicKey;
|
||||
use tiny_keccak::{Hasher, Keccak};
|
||||
|
||||
// TODO: Consider wrapping `AccountAddress` in a struct.
|
||||
|
||||
pub type AccountAddress = [u8; 32];
|
||||
|
||||
/// Returns the address associated with a public key
|
||||
pub fn from_public_key(public_key: &SignaturePublicKey) -> AccountAddress {
|
||||
let mut address = [0; 32];
|
||||
let mut keccak_hasher = Keccak::v256();
|
||||
keccak_hasher.update(&public_key.to_sec1_bytes());
|
||||
keccak_hasher.finalize(&mut address);
|
||||
address
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use common::transaction::SignaturePrivateKey;
|
||||
|
||||
use super::*;
|
||||
use crate::account_core::address;
|
||||
|
||||
#[test]
|
||||
fn test_address_key_equal_keccak_pub_sign_key() {
|
||||
let signing_key = SignaturePrivateKey::from_slice(&[1; 32]).unwrap();
|
||||
let public_key = signing_key.verifying_key();
|
||||
|
||||
let mut expected_address = [0; 32];
|
||||
let mut keccak_hasher = Keccak::v256();
|
||||
keccak_hasher.update(&public_key.to_sec1_bytes());
|
||||
keccak_hasher.finalize(&mut expected_address);
|
||||
|
||||
assert_eq!(expected_address, address::from_public_key(public_key));
|
||||
}
|
||||
}
|
||||
@ -7,14 +7,18 @@ use log::info;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use utxo::utxo_core::UTXO;
|
||||
|
||||
use crate::key_management::{
|
||||
constants_types::{CipherText, Nonce},
|
||||
ephemeral_key_holder::EphemeralKeyHolder,
|
||||
AddressKeyHolder,
|
||||
pub mod address;
|
||||
|
||||
use crate::{
|
||||
account_core::address::AccountAddress,
|
||||
key_management::{
|
||||
constants_types::{CipherText, Nonce},
|
||||
ephemeral_key_holder::EphemeralKeyHolder,
|
||||
AddressKeyHolder,
|
||||
},
|
||||
};
|
||||
|
||||
pub type PublicKey = AffinePoint;
|
||||
pub type AccountAddress = TreeHashType;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Account {
|
||||
@ -113,7 +117,8 @@ impl AccountPublicMask {
|
||||
impl Account {
|
||||
pub fn new() -> Self {
|
||||
let key_holder = AddressKeyHolder::new_os_random();
|
||||
let address = key_holder.address;
|
||||
let public_key = *key_holder.get_pub_account_signing_key().verifying_key();
|
||||
let address = address::from_public_key(&public_key);
|
||||
let balance = 0;
|
||||
let utxos = HashMap::new();
|
||||
|
||||
@ -127,7 +132,8 @@ impl Account {
|
||||
|
||||
pub fn new_with_balance(balance: u64) -> Self {
|
||||
let key_holder = AddressKeyHolder::new_os_random();
|
||||
let address = key_holder.address;
|
||||
let public_key = *key_holder.get_pub_account_signing_key().verifying_key();
|
||||
let address = address::from_public_key(&public_key);
|
||||
let utxos = HashMap::new();
|
||||
|
||||
Self {
|
||||
@ -228,7 +234,6 @@ mod tests {
|
||||
let account = Account::new();
|
||||
|
||||
assert_eq!(account.balance, 0);
|
||||
assert!(account.key_holder.address != [0u8; 32]); // Check if the address is not empty
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
use aes_gcm::{aead::Aead, Aes256Gcm, KeyInit};
|
||||
use common::merkle_tree_public::TreeHashType;
|
||||
use constants_types::{CipherText, Nonce};
|
||||
use elliptic_curve::point::AffineCoordinates;
|
||||
use k256::{ecdsa::SigningKey, AffinePoint, FieldBytes};
|
||||
@ -7,7 +6,6 @@ use log::info;
|
||||
use rand::{rngs::OsRng, RngCore};
|
||||
use secret_holders::{SeedHolder, TopSecretKeyHolder, UTXOSecretKeyHolder};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tiny_keccak::{Hasher, Keccak};
|
||||
|
||||
use crate::account_core::PublicKey;
|
||||
pub type PublicAccountSigningKey = [u8; 32];
|
||||
@ -19,12 +17,9 @@ pub mod secret_holders;
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
///Entrypoint to key management
|
||||
pub struct AddressKeyHolder {
|
||||
//Will be useful in future
|
||||
#[allow(dead_code)]
|
||||
top_secret_key_holder: TopSecretKeyHolder,
|
||||
pub utxo_secret_key_holder: UTXOSecretKeyHolder,
|
||||
pub_account_signing_key: PublicAccountSigningKey,
|
||||
pub address: TreeHashType,
|
||||
pub nullifer_public_key: PublicKey,
|
||||
pub viewing_public_key: PublicKey,
|
||||
}
|
||||
@ -47,21 +42,9 @@ impl AddressKeyHolder {
|
||||
bytes
|
||||
};
|
||||
|
||||
//Address is a Keccak(verification_key)
|
||||
let field_bytes = FieldBytes::from_slice(&pub_account_signing_key);
|
||||
let signing_key = SigningKey::from_bytes(field_bytes).unwrap();
|
||||
|
||||
let verifying_key = signing_key.verifying_key();
|
||||
|
||||
let mut address = [0; 32];
|
||||
let mut keccak_hasher = Keccak::v256();
|
||||
keccak_hasher.update(&verifying_key.to_sec1_bytes());
|
||||
keccak_hasher.finalize(&mut address);
|
||||
|
||||
Self {
|
||||
top_secret_key_holder,
|
||||
utxo_secret_key_holder,
|
||||
address,
|
||||
nullifer_public_key,
|
||||
viewing_public_key,
|
||||
pub_account_signing_key,
|
||||
@ -137,7 +120,7 @@ mod tests {
|
||||
use elliptic_curve::point::AffineCoordinates;
|
||||
use k256::{AffinePoint, ProjectivePoint, Scalar};
|
||||
|
||||
use crate::key_management::ephemeral_key_holder::EphemeralKeyHolder;
|
||||
use crate::{account_core::address, key_management::ephemeral_key_holder::EphemeralKeyHolder};
|
||||
|
||||
use super::*;
|
||||
|
||||
@ -214,7 +197,6 @@ mod tests {
|
||||
assert!(!Into::<bool>::into(
|
||||
address_key_holder.viewing_public_key.is_identity()
|
||||
));
|
||||
assert!(!address_key_holder.address.as_slice().is_empty()); // Assume TreeHashType has non-zero length for a valid address
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -343,21 +325,6 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_address_key_equal_keccak_pub_sign_key() {
|
||||
let address_key_holder = AddressKeyHolder::new_os_random();
|
||||
let signing_key = address_key_holder.get_pub_account_signing_key();
|
||||
|
||||
let verifying_key = signing_key.verifying_key();
|
||||
|
||||
let mut address = [0; 32];
|
||||
let mut keccak_hasher = Keccak::v256();
|
||||
keccak_hasher.update(&verifying_key.to_sec1_bytes());
|
||||
keccak_hasher.finalize(&mut address);
|
||||
|
||||
assert_eq!(address, address_key_holder.address);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn key_generation_test() {
|
||||
let seed_holder = SeedHolder::new_os_random();
|
||||
@ -380,10 +347,7 @@ mod tests {
|
||||
|
||||
let verifying_key = signing_key.verifying_key();
|
||||
|
||||
let mut address = [0; 32];
|
||||
let mut keccak_hasher = Keccak::v256();
|
||||
keccak_hasher.update(&verifying_key.to_sec1_bytes());
|
||||
keccak_hasher.finalize(&mut address);
|
||||
let address = address::from_public_key(verifying_key);
|
||||
|
||||
println!("======Prerequisites======");
|
||||
println!();
|
||||
|
||||
@ -5,6 +5,7 @@ use crate::merkle_tree_public::TreeHashType;
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct PublicNativeTokenSend {
|
||||
pub from: TreeHashType,
|
||||
pub nonce: u64,
|
||||
pub to: TreeHashType,
|
||||
pub balance_to_move: u64,
|
||||
}
|
||||
|
||||
@ -288,6 +288,10 @@ impl AuthenticatedTransaction {
|
||||
&self.transaction
|
||||
}
|
||||
|
||||
pub fn into_transaction(self) -> Transaction {
|
||||
self.transaction
|
||||
}
|
||||
|
||||
/// Returns the precomputed hash over the body of the transaction
|
||||
pub fn hash(&self) -> &TransactionHash {
|
||||
&self.hash
|
||||
@ -418,4 +422,11 @@ mod tests {
|
||||
let authenticated_tx = transaction.clone().into_authenticated().unwrap();
|
||||
assert_eq!(authenticated_tx.hash(), &transaction.body.hash());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_authenticated_transaction_into_transaction() {
|
||||
let transaction = test_transaction();
|
||||
let authenticated_tx = transaction.clone().into_authenticated().unwrap();
|
||||
assert_eq!(authenticated_tx.into_transaction(), transaction);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
use accounts::account_core::{Account, AccountAddress};
|
||||
use accounts::account_core::{address::AccountAddress, Account};
|
||||
use std::collections::HashMap;
|
||||
|
||||
pub struct NodeAccountsStore {
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
use std::collections::{BTreeMap, HashMap, HashSet};
|
||||
|
||||
use accounts::account_core::{Account, AccountAddress};
|
||||
use accounts::account_core::{address::AccountAddress, Account};
|
||||
use anyhow::Result;
|
||||
use block_store::NodeBlockStore;
|
||||
use common::{
|
||||
@ -342,40 +342,6 @@ mod tests {
|
||||
],
|
||||
"balance": 100,
|
||||
"key_holder": {
|
||||
"address": [
|
||||
244,
|
||||
55,
|
||||
238,
|
||||
205,
|
||||
74,
|
||||
115,
|
||||
179,
|
||||
192,
|
||||
65,
|
||||
186,
|
||||
166,
|
||||
169,
|
||||
221,
|
||||
45,
|
||||
6,
|
||||
57,
|
||||
200,
|
||||
65,
|
||||
195,
|
||||
70,
|
||||
118,
|
||||
252,
|
||||
206,
|
||||
100,
|
||||
215,
|
||||
250,
|
||||
72,
|
||||
230,
|
||||
19,
|
||||
71,
|
||||
217,
|
||||
249
|
||||
],
|
||||
"nullifer_public_key": "03A340BECA9FAAB444CED0140681D72EA1318B5C611704FEE017DA9836B17DB718",
|
||||
"pub_account_signing_key": [
|
||||
244,
|
||||
@ -460,40 +426,6 @@ mod tests {
|
||||
],
|
||||
"balance": 200,
|
||||
"key_holder": {
|
||||
"address": [
|
||||
72,
|
||||
169,
|
||||
70,
|
||||
237,
|
||||
1,
|
||||
96,
|
||||
35,
|
||||
157,
|
||||
25,
|
||||
15,
|
||||
83,
|
||||
18,
|
||||
52,
|
||||
206,
|
||||
202,
|
||||
63,
|
||||
48,
|
||||
59,
|
||||
173,
|
||||
76,
|
||||
78,
|
||||
7,
|
||||
254,
|
||||
229,
|
||||
28,
|
||||
45,
|
||||
194,
|
||||
79,
|
||||
6,
|
||||
89,
|
||||
58,
|
||||
85
|
||||
],
|
||||
"nullifer_public_key": "02172F50274DE67C4087C344F5D58E11DF761D90285B095060E0994FAA6BCDE271",
|
||||
"pub_account_signing_key": [
|
||||
136,
|
||||
|
||||
@ -8,7 +8,7 @@ use common::{
|
||||
};
|
||||
|
||||
use accounts::{
|
||||
account_core::{Account, AccountAddress},
|
||||
account_core::{address::AccountAddress, Account},
|
||||
key_management::ephemeral_key_holder::EphemeralKeyHolder,
|
||||
};
|
||||
use anyhow::Result;
|
||||
@ -956,6 +956,7 @@ impl NodeCore {
|
||||
pub async fn send_public_native_token_transfer(
|
||||
&self,
|
||||
from: AccountAddress,
|
||||
nonce: u64,
|
||||
to: AccountAddress,
|
||||
balance_to_move: u64,
|
||||
) -> Result<SendTxResponse, ExecutionFailureKind> {
|
||||
@ -983,6 +984,7 @@ impl NodeCore {
|
||||
sc_core::transaction_payloads_tools::create_public_transaction_payload(
|
||||
serde_json::to_vec(&PublicNativeTokenSend {
|
||||
from,
|
||||
nonce,
|
||||
to,
|
||||
balance_to_move,
|
||||
})
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
use std::collections::{BTreeMap, HashSet};
|
||||
|
||||
use accounts::account_core::{AccountAddress, AccountPublicMask};
|
||||
use accounts::account_core::{address::AccountAddress, AccountPublicMask};
|
||||
use common::merkle_tree_public::{merkle_tree::UTXOCommitmentsMerkleTree, TreeHashType};
|
||||
use serde::{ser::SerializeStruct, Serialize};
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
use std::fmt::Display;
|
||||
|
||||
use accounts::account_core::AccountAddress;
|
||||
use accounts::account_core::address::{self, AccountAddress};
|
||||
use anyhow::Result;
|
||||
use common::{
|
||||
block::{Block, HashableBlockData},
|
||||
@ -15,7 +15,6 @@ use mempool::MemPool;
|
||||
use mempool_transaction::MempoolTransaction;
|
||||
use sequencer_store::SequecerChainStore;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tiny_keccak::{Hasher, Keccak};
|
||||
|
||||
pub mod config;
|
||||
pub mod mempool_transaction;
|
||||
@ -41,6 +40,7 @@ pub enum TransactionMalformationErrorKind {
|
||||
InvalidSignature,
|
||||
IncorrectSender,
|
||||
BalanceMismatch { tx: TreeHashType },
|
||||
NonceMismatch { tx: TreeHashType },
|
||||
FailedToDecode { tx: TreeHashType },
|
||||
}
|
||||
|
||||
@ -146,13 +146,10 @@ impl SequencerCore {
|
||||
if let Ok(native_transfer_action) =
|
||||
serde_json::from_slice::<PublicNativeTokenSend>(execution_input)
|
||||
{
|
||||
let mut output = [0; 32];
|
||||
let mut keccak_hasher = Keccak::v256();
|
||||
keccak_hasher.update(&tx.transaction().public_key.to_sec1_bytes());
|
||||
keccak_hasher.finalize(&mut output);
|
||||
let signer_address = address::from_public_key(&tx.transaction().public_key);
|
||||
|
||||
//Correct sender check
|
||||
if native_transfer_action.from != output {
|
||||
if native_transfer_action.from != signer_address {
|
||||
return Err(TransactionMalformationErrorKind::IncorrectSender);
|
||||
}
|
||||
}
|
||||
@ -233,6 +230,15 @@ impl SequencerCore {
|
||||
if let Ok(native_transfer_action) =
|
||||
serde_json::from_slice::<PublicNativeTokenSend>(execution_input)
|
||||
{
|
||||
// Nonce check
|
||||
let signer_addres =
|
||||
address::from_public_key(&mempool_tx.auth_tx.transaction().public_key);
|
||||
if self.store.acc_store.get_account_nonce(&signer_addres)
|
||||
!= native_transfer_action.nonce
|
||||
{
|
||||
return Err(TransactionMalformationErrorKind::NonceMismatch { tx: tx_hash });
|
||||
}
|
||||
|
||||
let from_balance = self
|
||||
.store
|
||||
.acc_store
|
||||
@ -255,6 +261,8 @@ impl SequencerCore {
|
||||
&native_transfer_action.to,
|
||||
to_balance + native_transfer_action.balance_to_move,
|
||||
);
|
||||
|
||||
self.store.acc_store.increase_nonce(&signer_addres);
|
||||
}
|
||||
|
||||
for utxo_comm in utxo_commitments_created_hashes {
|
||||
@ -288,9 +296,16 @@ impl SequencerCore {
|
||||
.mempool
|
||||
.pop_size(self.sequencer_config.max_num_tx_in_block);
|
||||
|
||||
for tx in &transactions {
|
||||
self.execute_check_transaction_on_state(tx)?;
|
||||
}
|
||||
let valid_transactions = transactions
|
||||
.into_iter()
|
||||
.filter_map(|mempool_tx| {
|
||||
if self.execute_check_transaction_on_state(&mempool_tx).is_ok() {
|
||||
Some(mempool_tx.auth_tx.into_transaction())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
let prev_block_hash = self
|
||||
.store
|
||||
@ -301,10 +316,7 @@ impl SequencerCore {
|
||||
let hashable_data = HashableBlockData {
|
||||
block_id: new_block_height,
|
||||
prev_block_id: self.chain_height,
|
||||
transactions: transactions
|
||||
.into_iter()
|
||||
.map(|tx_mem| tx_mem.auth_tx.transaction().clone())
|
||||
.collect(),
|
||||
transactions: valid_transactions,
|
||||
data: vec![],
|
||||
prev_block_hash,
|
||||
};
|
||||
@ -313,9 +325,9 @@ impl SequencerCore {
|
||||
|
||||
self.store.block_store.put_block_at_id(block)?;
|
||||
|
||||
self.chain_height += 1;
|
||||
self.chain_height = new_block_height;
|
||||
|
||||
Ok(self.chain_height - 1)
|
||||
Ok(self.chain_height)
|
||||
}
|
||||
}
|
||||
|
||||
@ -324,24 +336,20 @@ mod tests {
|
||||
use crate::config::AccountInitialData;
|
||||
|
||||
use super::*;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use common::transaction::{SignaturePrivateKey, Transaction, TransactionBody, TxKind};
|
||||
use k256::{ecdsa::SigningKey, FieldBytes};
|
||||
use mempool_transaction::MempoolTransaction;
|
||||
use rand::Rng;
|
||||
use secp256k1_zkp::Tweak;
|
||||
|
||||
fn setup_sequencer_config_variable_initial_accounts(
|
||||
initial_accounts: Vec<AccountInitialData>,
|
||||
) -> SequencerConfig {
|
||||
let mut rng = rand::thread_rng();
|
||||
let random_u8: u8 = rng.gen();
|
||||
|
||||
let path_str = format!("/tmp/sequencer_{random_u8:?}");
|
||||
let tempdir = tempfile::tempdir().unwrap();
|
||||
let home = tempdir.path().to_path_buf();
|
||||
|
||||
SequencerConfig {
|
||||
home: PathBuf::from(path_str),
|
||||
home,
|
||||
override_rust_log: Some("info".to_string()),
|
||||
genesis_id: 1,
|
||||
is_genesis_random: false,
|
||||
@ -406,6 +414,7 @@ mod tests {
|
||||
|
||||
fn create_dummy_transaction_native_token_transfer(
|
||||
from: [u8; 32],
|
||||
nonce: u64,
|
||||
to: [u8; 32],
|
||||
balance_to_move: u64,
|
||||
signing_key: SigningKey,
|
||||
@ -414,6 +423,7 @@ mod tests {
|
||||
|
||||
let native_token_transfer = PublicNativeTokenSend {
|
||||
from,
|
||||
nonce,
|
||||
to,
|
||||
balance_to_move,
|
||||
};
|
||||
@ -596,7 +606,7 @@ mod tests {
|
||||
|
||||
let sign_key1 = create_signing_key_for_account1();
|
||||
|
||||
let tx = create_dummy_transaction_native_token_transfer(acc1, acc2, 10, sign_key1);
|
||||
let tx = create_dummy_transaction_native_token_transfer(acc1, 0, acc2, 10, sign_key1);
|
||||
let tx_roots = sequencer.get_tree_roots();
|
||||
let result = sequencer.transaction_pre_check(tx, tx_roots);
|
||||
|
||||
@ -621,7 +631,7 @@ mod tests {
|
||||
|
||||
let sign_key2 = create_signing_key_for_account2();
|
||||
|
||||
let tx = create_dummy_transaction_native_token_transfer(acc1, acc2, 10, sign_key2);
|
||||
let tx = create_dummy_transaction_native_token_transfer(acc1, 0, acc2, 10, sign_key2);
|
||||
let tx_roots = sequencer.get_tree_roots();
|
||||
let result = sequencer.transaction_pre_check(tx, tx_roots);
|
||||
|
||||
@ -649,7 +659,7 @@ mod tests {
|
||||
|
||||
let sign_key1 = create_signing_key_for_account1();
|
||||
|
||||
let tx = create_dummy_transaction_native_token_transfer(acc1, acc2, 10000000, sign_key1);
|
||||
let tx = create_dummy_transaction_native_token_transfer(acc1, 0, acc2, 10000000, sign_key1);
|
||||
let tx_roots = sequencer.get_tree_roots();
|
||||
let result = sequencer.transaction_pre_check(tx, tx_roots);
|
||||
|
||||
@ -683,7 +693,7 @@ mod tests {
|
||||
|
||||
let sign_key1 = create_signing_key_for_account1();
|
||||
|
||||
let tx = create_dummy_transaction_native_token_transfer(acc1, acc2, 100, sign_key1);
|
||||
let tx = create_dummy_transaction_native_token_transfer(acc1, 0, acc2, 100, sign_key1);
|
||||
|
||||
sequencer
|
||||
.execute_check_transaction_on_state(&tx.into_authenticated().unwrap().into())
|
||||
@ -742,6 +752,7 @@ mod tests {
|
||||
fn test_produce_new_block_with_mempool_transactions() {
|
||||
let config = setup_sequencer_config();
|
||||
let mut sequencer = SequencerCore::start_from_config(config);
|
||||
let genesis_height = sequencer.chain_height;
|
||||
|
||||
let tx = create_dummy_transaction(vec![[94; 32]], vec![[7; 32]], vec![[8; 32]]);
|
||||
let tx_mempool = MempoolTransaction {
|
||||
@ -751,6 +762,102 @@ mod tests {
|
||||
|
||||
let block_id = sequencer.produce_new_block_with_mempool_transactions();
|
||||
assert!(block_id.is_ok());
|
||||
assert_eq!(block_id.unwrap(), 1);
|
||||
assert_eq!(block_id.unwrap(), genesis_height + 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_replay_transactions_are_rejected_in_the_same_block() {
|
||||
let config = setup_sequencer_config();
|
||||
let mut sequencer = SequencerCore::start_from_config(config);
|
||||
|
||||
common_setup(&mut sequencer);
|
||||
|
||||
let acc1 = hex::decode(sequencer.sequencer_config.initial_accounts[0].addr.clone())
|
||||
.unwrap()
|
||||
.try_into()
|
||||
.unwrap();
|
||||
let acc2 = hex::decode(sequencer.sequencer_config.initial_accounts[1].addr.clone())
|
||||
.unwrap()
|
||||
.try_into()
|
||||
.unwrap();
|
||||
|
||||
let sign_key1 = create_signing_key_for_account1();
|
||||
|
||||
let tx = create_dummy_transaction_native_token_transfer(acc1, 0, acc2, 100, sign_key1);
|
||||
|
||||
let tx_mempool_original = MempoolTransaction {
|
||||
auth_tx: tx.clone().into_authenticated().unwrap(),
|
||||
};
|
||||
let tx_mempool_replay = MempoolTransaction {
|
||||
auth_tx: tx.clone().into_authenticated().unwrap(),
|
||||
};
|
||||
|
||||
// Pushing two copies of the same tx to the mempool
|
||||
sequencer.mempool.push_item(tx_mempool_original);
|
||||
sequencer.mempool.push_item(tx_mempool_replay);
|
||||
|
||||
// Create block
|
||||
let current_height = sequencer
|
||||
.produce_new_block_with_mempool_transactions()
|
||||
.unwrap();
|
||||
let block = sequencer
|
||||
.store
|
||||
.block_store
|
||||
.get_block_at_id(current_height)
|
||||
.unwrap();
|
||||
|
||||
// Only one should be included in the block
|
||||
assert_eq!(block.transactions, vec![tx.clone()]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_replay_transactions_are_rejected_in_different_blocks() {
|
||||
let config = setup_sequencer_config();
|
||||
let mut sequencer = SequencerCore::start_from_config(config);
|
||||
|
||||
common_setup(&mut sequencer);
|
||||
|
||||
let acc1 = hex::decode(sequencer.sequencer_config.initial_accounts[0].addr.clone())
|
||||
.unwrap()
|
||||
.try_into()
|
||||
.unwrap();
|
||||
let acc2 = hex::decode(sequencer.sequencer_config.initial_accounts[1].addr.clone())
|
||||
.unwrap()
|
||||
.try_into()
|
||||
.unwrap();
|
||||
|
||||
let sign_key1 = create_signing_key_for_account1();
|
||||
|
||||
let tx = create_dummy_transaction_native_token_transfer(acc1, 0, acc2, 100, sign_key1);
|
||||
|
||||
// The transaction should be included the first time
|
||||
let tx_mempool_original = MempoolTransaction {
|
||||
auth_tx: tx.clone().into_authenticated().unwrap(),
|
||||
};
|
||||
sequencer.mempool.push_item(tx_mempool_original);
|
||||
let current_height = sequencer
|
||||
.produce_new_block_with_mempool_transactions()
|
||||
.unwrap();
|
||||
let block = sequencer
|
||||
.store
|
||||
.block_store
|
||||
.get_block_at_id(current_height)
|
||||
.unwrap();
|
||||
assert_eq!(block.transactions, vec![tx.clone()]);
|
||||
|
||||
// Add same transaction should fail
|
||||
let tx_mempool_replay = MempoolTransaction {
|
||||
auth_tx: tx.into_authenticated().unwrap(),
|
||||
};
|
||||
sequencer.mempool.push_item(tx_mempool_replay);
|
||||
let current_height = sequencer
|
||||
.produce_new_block_with_mempool_transactions()
|
||||
.unwrap();
|
||||
let block = sequencer
|
||||
.store
|
||||
.block_store
|
||||
.get_block_at_id(current_height)
|
||||
.unwrap();
|
||||
assert!(block.transactions.is_empty());
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
use accounts::account_core::AccountAddress;
|
||||
use accounts::account_core::address::AccountAddress;
|
||||
use anyhow::Result;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
@ -7,18 +7,24 @@ use std::collections::HashMap;
|
||||
pub(crate) struct AccountPublicData {
|
||||
pub balance: u64,
|
||||
pub address: AccountAddress,
|
||||
nonce: u64,
|
||||
}
|
||||
|
||||
impl AccountPublicData {
|
||||
pub fn new(address: AccountAddress) -> Self {
|
||||
Self {
|
||||
balance: 0,
|
||||
nonce: 0,
|
||||
address,
|
||||
}
|
||||
}
|
||||
|
||||
fn new_with_balance(address: AccountAddress, balance: u64) -> Self {
|
||||
Self { balance, address }
|
||||
Self {
|
||||
balance,
|
||||
address,
|
||||
nonce: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -64,6 +70,13 @@ impl SequencerAccountsStore {
|
||||
.unwrap_or(0)
|
||||
}
|
||||
|
||||
pub fn get_account_nonce(&self, account_addr: &AccountAddress) -> u64 {
|
||||
self.accounts
|
||||
.get(account_addr)
|
||||
.map(|acc| acc.nonce)
|
||||
.unwrap_or(0)
|
||||
}
|
||||
|
||||
///Update `account_addr` balance,
|
||||
///
|
||||
/// returns 0, if account address not found, otherwise returns previous balance
|
||||
@ -85,6 +98,20 @@ impl SequencerAccountsStore {
|
||||
}
|
||||
}
|
||||
|
||||
///Update `account_addr` nonce,
|
||||
///
|
||||
/// Returns previous nonce
|
||||
pub fn increase_nonce(&mut self, account_addr: &AccountAddress) -> u64 {
|
||||
if let Some(acc_data) = self.accounts.get_mut(account_addr) {
|
||||
let old_nonce = acc_data.nonce;
|
||||
acc_data.nonce += 1;
|
||||
old_nonce
|
||||
} else {
|
||||
self.register_account(*account_addr);
|
||||
self.increase_nonce(account_addr)
|
||||
}
|
||||
}
|
||||
|
||||
///Remove account from storage
|
||||
///
|
||||
/// Fails, if `balance` is != 0
|
||||
@ -130,6 +157,13 @@ mod tests {
|
||||
assert_eq!(new_acc.address, [1; 32]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_zero_nonce_account_data_creation() {
|
||||
let new_acc = AccountPublicData::new([1; 32]);
|
||||
|
||||
assert_eq!(new_acc.nonce, 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_non_zero_balance_account_data_creation() {
|
||||
let new_acc = AccountPublicData::new_with_balance([1; 32], 10);
|
||||
@ -138,6 +172,13 @@ mod tests {
|
||||
assert_eq!(new_acc.address, [1; 32]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_zero_nonce_account_data_creation_with_balance() {
|
||||
let new_acc = AccountPublicData::new_with_balance([1; 32], 10);
|
||||
|
||||
assert_eq!(new_acc.nonce, 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn default_account_sequencer_store() {
|
||||
let seq_acc_store = SequencerAccountsStore::default();
|
||||
@ -256,4 +297,14 @@ mod tests {
|
||||
assert!(seq_acc_store.contains_account(&[1; 32]));
|
||||
assert_eq!(seq_acc_store.get_account_balance(&[1; 32]), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_increase_nonce() {
|
||||
let mut account_store = SequencerAccountsStore::default();
|
||||
let address = [1; 32];
|
||||
let first_nonce = account_store.increase_nonce(&address);
|
||||
assert_eq!(first_nonce, 0);
|
||||
let second_nonce = account_store.increase_nonce(&address);
|
||||
assert_eq!(second_nonce, 1);
|
||||
}
|
||||
}
|
||||
|
||||
@ -274,7 +274,7 @@ mod tests {
|
||||
let initial_accounts = sequencer_core.sequencer_config.initial_accounts.clone();
|
||||
|
||||
let tx_body = TransactionBody {
|
||||
tx_kind: common::transaction::TxKind::Public,
|
||||
tx_kind: common::transaction::TxKind::Shielded,
|
||||
execution_input: Default::default(),
|
||||
execution_output: Default::default(),
|
||||
utxo_commitments_spent_hashes: Default::default(),
|
||||
@ -499,9 +499,10 @@ mod tests {
|
||||
let request = serde_json::json!({
|
||||
"jsonrpc": "2.0",
|
||||
"method": "get_transaction_by_hash",
|
||||
"params": { "hash": "ca8e38269c0137d27cbe7c55d240a834b46e86e236578b9a1a3a25b3dabc5709" },
|
||||
"params": { "hash": "a5210ef33912a448cfe6eda43878c144df81f7bdf51d19b5ddf97be11806a6ed"},
|
||||
"id": 1
|
||||
});
|
||||
|
||||
let expected_response = serde_json::json!({
|
||||
"id": 1,
|
||||
"jsonrpc": "2.0",
|
||||
@ -519,12 +520,12 @@ mod tests {
|
||||
"secret_r": vec![0; 32],
|
||||
"state_changes": [null, 0],
|
||||
"tweak": "0".repeat(64),
|
||||
"tx_kind": "Public",
|
||||
"tx_kind": "Shielded",
|
||||
"utxo_commitments_created_hashes": [],
|
||||
"utxo_commitments_spent_hashes": []
|
||||
"utxo_commitments_spent_hashes": [],
|
||||
},
|
||||
"public_key": "3056301006072A8648CE3D020106052B8104000A034200041B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F70BEAF8F588B541507FED6A642C5AB42DFDF8120A7F639DE5122D47A69A8E8D1",
|
||||
"signature": "28CB6CA744864340A3441CB48D5700690F90130DE0760EE5C640F85F4285C5FD2BD7D0E270EC2AC82E4124999E63659AA9C33CF378F959EDF4E50F2626EA3B99"
|
||||
"signature": "A4E0D6A25BE829B006124F0DFD766427967AA3BEA96C29219E79BB2CC871891F384748C27E28718A4450AA78709FBF1A57DB33BCD575A2C819D2A439C2D878E6"
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
use accounts::account_core::AccountAddress;
|
||||
use accounts::account_core::address::AccountAddress;
|
||||
use common::ExecutionFailureKind;
|
||||
use rand::{rngs::OsRng, RngCore};
|
||||
use risc0_zkvm::{default_executor, default_prover, sha::Digest, ExecutorEnv, Receipt};
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user