Have Tributary's add_transaction return a proper error

Modifies main.rs to properly handle the returned error.
This commit is contained in:
Luke Parker
2023-10-14 21:50:11 -04:00
parent 584943d1e9
commit 19e90b28b0
11 changed files with 163 additions and 133 deletions

View File

@@ -31,7 +31,9 @@ use tokio::{
time::sleep, time::sleep,
}; };
use ::tributary::{ProvidedError, TransactionKind, TransactionTrait, Block, Tributary}; use ::tributary::{
ProvidedError, TransactionKind, TransactionError, TransactionTrait, Block, Tributary,
};
mod tributary; mod tributary;
use crate::tributary::{ use crate::tributary::{
@@ -150,10 +152,16 @@ async fn publish_signed_transaction<D: Db, P: P2p>(
.await .await
.expect("we don't have a nonce, meaning we aren't a participant on this tributary"), .expect("we don't have a nonce, meaning we aren't a participant on this tributary"),
) { ) {
// TODO: Assert if we didn't create a valid transaction
// We need to return a proper error here to enable that, due to a race condition around // We need to return a proper error here to enable that, due to a race condition around
// multiple publications // multiple publications
tributary.add_transaction(tx).await; match tributary.add_transaction(tx.clone()).await {
Ok(_) => {}
// Some asynchonicity if InvalidNonce, assumed safe to deterministic nonces
Err(TransactionError::InvalidNonce) => {
log::warn!("publishing TX {tx:?} returned InvalidNonce. was it already added?")
}
Err(e) => panic!("created an invalid transaction: {e:?}"),
}
} }
} }
@@ -630,10 +638,10 @@ async fn handle_processor_message<D: Db, P: P2p>(
} }
TransactionKind::Unsigned => { TransactionKind::Unsigned => {
log::trace!("publishing unsigned transaction {}", hex::encode(tx.hash())); log::trace!("publishing unsigned transaction {}", hex::encode(tx.hash()));
// Ignores the result since we can't differentiate already in-mempool from match tributary.add_transaction(tx.clone()).await {
// already on-chain from invalid Ok(_) => {}
// TODO: Don't ignore the result Err(e) => panic!("created an invalid unsigned transaction: {e:?}"),
tributary.add_transaction(tx).await; }
} }
TransactionKind::Signed(_) => { TransactionKind::Signed(_) => {
log::trace!("getting next nonce for Tributary TX in response to processor message"); log::trace!("getting next nonce for Tributary TX in response to processor message");

View File

@@ -56,7 +56,7 @@ async fn dkg_test() {
// Publish all commitments but one // Publish all commitments but one
for (i, tx) in txs.iter().enumerate().skip(1) { for (i, tx) in txs.iter().enumerate().skip(1) {
assert!(tributaries[i].1.add_transaction(tx.clone()).await); assert_eq!(tributaries[i].1.add_transaction(tx.clone()).await, Ok(true));
} }
// Wait until these are included // Wait until these are included
@@ -104,7 +104,7 @@ async fn dkg_test() {
// Publish the last commitment // Publish the last commitment
let block_before_tx = tributaries[0].1.tip().await; let block_before_tx = tributaries[0].1.tip().await;
assert!(tributaries[0].1.add_transaction(txs[0].clone()).await); assert_eq!(tributaries[0].1.add_transaction(txs[0].clone()).await, Ok(true));
wait_for_tx_inclusion(&tributaries[0].1, block_before_tx, txs[0].hash()).await; wait_for_tx_inclusion(&tributaries[0].1, block_before_tx, txs[0].hash()).await;
sleep(Duration::from_secs(Tributary::<MemDb, Transaction, LocalP2p>::block_time().into())).await; sleep(Duration::from_secs(Tributary::<MemDb, Transaction, LocalP2p>::block_time().into())).await;
@@ -181,7 +181,7 @@ async fn dkg_test() {
let block_before_tx = tributaries[0].1.tip().await; let block_before_tx = tributaries[0].1.tip().await;
for (i, tx) in txs.iter().enumerate().skip(1) { for (i, tx) in txs.iter().enumerate().skip(1) {
assert!(tributaries[i].1.add_transaction(tx.clone()).await); assert_eq!(tributaries[i].1.add_transaction(tx.clone()).await, Ok(true));
} }
for tx in txs.iter().skip(1) { for tx in txs.iter().skip(1) {
wait_for_tx_inclusion(&tributaries[0].1, block_before_tx, tx.hash()).await; wait_for_tx_inclusion(&tributaries[0].1, block_before_tx, tx.hash()).await;
@@ -205,7 +205,7 @@ async fn dkg_test() {
// Publish the final set of shares // Publish the final set of shares
let block_before_tx = tributaries[0].1.tip().await; let block_before_tx = tributaries[0].1.tip().await;
assert!(tributaries[0].1.add_transaction(txs[0].clone()).await); assert_eq!(tributaries[0].1.add_transaction(txs[0].clone()).await, Ok(true));
wait_for_tx_inclusion(&tributaries[0].1, block_before_tx, txs[0].hash()).await; wait_for_tx_inclusion(&tributaries[0].1, block_before_tx, txs[0].hash()).await;
sleep(Duration::from_secs(Tributary::<MemDb, Transaction, LocalP2p>::block_time().into())).await; sleep(Duration::from_secs(Tributary::<MemDb, Transaction, LocalP2p>::block_time().into())).await;
@@ -296,7 +296,7 @@ async fn dkg_test() {
} }
let block_before_tx = tributaries[0].1.tip().await; let block_before_tx = tributaries[0].1.tip().await;
for (i, tx) in txs.iter().enumerate() { for (i, tx) in txs.iter().enumerate() {
assert!(tributaries[i].1.add_transaction(tx.clone()).await); assert_eq!(tributaries[i].1.add_transaction(tx.clone()).await, Ok(true));
} }
for tx in txs.iter() { for tx in txs.iter() {
wait_for_tx_inclusion(&tributaries[0].1, block_before_tx, tx.hash()).await; wait_for_tx_inclusion(&tributaries[0].1, block_before_tx, tx.hash()).await;

View File

@@ -43,7 +43,7 @@ async fn tx_test() {
Transaction::DkgCommitments(attempt, commitments.clone(), Transaction::empty_signed()); Transaction::DkgCommitments(attempt, commitments.clone(), Transaction::empty_signed());
tx.sign(&mut OsRng, spec.genesis(), &key, 0); tx.sign(&mut OsRng, spec.genesis(), &key, 0);
assert!(tributaries[sender].1.add_transaction(tx.clone()).await); assert_eq!(tributaries[sender].1.add_transaction(tx.clone()).await, Ok(true));
let included_in = wait_for_tx_inclusion(&tributaries[sender].1, block_before_tx, tx.hash()).await; let included_in = wait_for_tx_inclusion(&tributaries[sender].1, block_before_tx, tx.hash()).await;
// Also sleep for the block time to ensure the block is synced around before we run checks on it // Also sleep for the block time to ensure the block is synced around before we run checks on it
sleep(Duration::from_secs(Tributary::<MemDb, Transaction, LocalP2p>::block_time().into())).await; sleep(Duration::from_secs(Tributary::<MemDb, Transaction, LocalP2p>::block_time().into())).await;

View File

@@ -10,7 +10,7 @@ use tendermint::ext::{Network, Commit};
use crate::{ use crate::{
ReadWrite, ProvidedError, ProvidedTransactions, BlockError, Block, Mempool, Transaction, ReadWrite, ProvidedError, ProvidedTransactions, BlockError, Block, Mempool, Transaction,
transaction::{Signed, TransactionKind, Transaction as TransactionTrait}, transaction::{Signed, TransactionKind, TransactionError, Transaction as TransactionTrait},
}; };
#[derive(Debug)] #[derive(Debug)]
@@ -165,7 +165,7 @@ impl<D: Db, T: TransactionTrait> Blockchain<D, T> {
internal: bool, internal: bool,
tx: Transaction<T>, tx: Transaction<T>,
schema: N::SignatureScheme, schema: N::SignatureScheme,
) -> bool { ) -> Result<bool, TransactionError> {
let db = self.db.as_ref().unwrap(); let db = self.db.as_ref().unwrap();
let genesis = self.genesis; let genesis = self.genesis;

View File

@@ -256,10 +256,10 @@ impl<D: Db, T: TransactionTrait, P: P2p> Tributary<D, T, P> {
self.network.blockchain.read().await.next_nonce(signer) self.network.blockchain.read().await.next_nonce(signer)
} }
// Returns if the transaction was new and valid. // Returns Ok(true) if new, Ok(false) if an already present unsigned, or the error.
// Safe to be &self since the only meaningful usage of self is self.network.blockchain which // Safe to be &self since the only meaningful usage of self is self.network.blockchain which
// successfully acquires its own write lock // successfully acquires its own write lock
pub async fn add_transaction(&self, tx: T) -> bool { pub async fn add_transaction(&self, tx: T) -> Result<bool, TransactionError> {
let tx = Transaction::Application(tx); let tx = Transaction::Application(tx);
let mut to_broadcast = vec![TRANSACTION_MESSAGE]; let mut to_broadcast = vec![TRANSACTION_MESSAGE];
tx.write(&mut to_broadcast).unwrap(); tx.write(&mut to_broadcast).unwrap();
@@ -268,7 +268,7 @@ impl<D: Db, T: TransactionTrait, P: P2p> Tributary<D, T, P> {
tx, tx,
self.network.signature_scheme(), self.network.signature_scheme(),
); );
if res { if res == Ok(true) {
self.network.p2p.broadcast(self.genesis, to_broadcast).await; self.network.p2p.broadcast(self.genesis, to_broadcast).await;
} }
res res
@@ -339,8 +339,8 @@ impl<D: Db, T: TransactionTrait, P: P2p> Tributary<D, T, P> {
tx, tx,
self.network.signature_scheme(), self.network.signature_scheme(),
); );
log::debug!("received transaction message. valid new transaction: {res}"); log::debug!("received transaction message. valid new transaction: {res:?}");
res res == Ok(true)
} }
Some(&TENDERMINT_MESSAGE) => { Some(&TENDERMINT_MESSAGE) => {

View File

@@ -8,7 +8,9 @@ use tendermint::ext::{Network, Commit};
use crate::{ use crate::{
ACCOUNT_MEMPOOL_LIMIT, ReadWrite, ACCOUNT_MEMPOOL_LIMIT, ReadWrite,
transaction::{Signed, TransactionKind, Transaction as TransactionTrait, verify_transaction}, transaction::{
Signed, TransactionKind, TransactionError, Transaction as TransactionTrait, verify_transaction,
},
tendermint::tx::verify_tendermint_tx, tendermint::tx::verify_tendermint_tx,
Transaction, Transaction,
}; };
@@ -92,7 +94,7 @@ impl<D: Db, T: TransactionTrait> Mempool<D, T> {
res res
} }
/// Returns true if this is a valid, new transaction. // Returns Ok(true) if new, Ok(false) if an already present unsigned, or the error.
pub(crate) fn add<N: Network>( pub(crate) fn add<N: Network>(
&mut self, &mut self,
blockchain_next_nonces: &HashMap<<Ristretto as Ciphersuite>::G, u32>, blockchain_next_nonces: &HashMap<<Ristretto as Ciphersuite>::G, u32>,
@@ -101,7 +103,7 @@ impl<D: Db, T: TransactionTrait> Mempool<D, T> {
schema: N::SignatureScheme, schema: N::SignatureScheme,
unsigned_in_chain: impl Fn([u8; 32]) -> bool, unsigned_in_chain: impl Fn([u8; 32]) -> bool,
commit: impl Fn(u32) -> Option<Commit<N::SignatureScheme>>, commit: impl Fn(u32) -> Option<Commit<N::SignatureScheme>>,
) -> bool { ) -> Result<bool, TransactionError> {
match &tx { match &tx {
Transaction::Tendermint(tendermint_tx) => { Transaction::Tendermint(tendermint_tx) => {
// All Tendermint transactions should be unsigned // All Tendermint transactions should be unsigned
@@ -109,13 +111,11 @@ impl<D: Db, T: TransactionTrait> Mempool<D, T> {
// check we have the tx in the pool/chain // check we have the tx in the pool/chain
if self.unsigned_already_exist(tx.hash(), unsigned_in_chain) { if self.unsigned_already_exist(tx.hash(), unsigned_in_chain) {
return false; return Ok(false);
} }
// verify the tx // verify the tx
if verify_tendermint_tx::<N>(tendermint_tx, schema, commit).is_err() { verify_tendermint_tx::<N>(tendermint_tx, schema, commit)?;
return false;
}
} }
Transaction::Application(app_tx) => { Transaction::Application(app_tx) => {
match app_tx.kind() { match app_tx.kind() {
@@ -123,7 +123,7 @@ impl<D: Db, T: TransactionTrait> Mempool<D, T> {
// Get the nonce from the blockchain // Get the nonce from the blockchain
let Some(blockchain_next_nonce) = blockchain_next_nonces.get(signer).cloned() else { let Some(blockchain_next_nonce) = blockchain_next_nonces.get(signer).cloned() else {
// Not a participant // Not a participant
return false; Err(TransactionError::InvalidSigner)?
}; };
// If the blockchain's nonce is greater than the mempool's, use it // If the blockchain's nonce is greater than the mempool's, use it
@@ -140,32 +140,28 @@ impl<D: Db, T: TransactionTrait> Mempool<D, T> {
// If we have too many transactions from this sender, don't add this yet UNLESS we are // If we have too many transactions from this sender, don't add this yet UNLESS we are
// this sender // this sender
if !internal && (nonce >= &(blockchain_next_nonce + ACCOUNT_MEMPOOL_LIMIT)) { if !internal && (nonce >= &(blockchain_next_nonce + ACCOUNT_MEMPOOL_LIMIT)) {
return false; Err(TransactionError::TooManyInMempool)?;
} }
if verify_transaction(app_tx, self.genesis, &mut self.next_nonces).is_err() { verify_transaction(app_tx, self.genesis, &mut self.next_nonces)?;
return false;
}
debug_assert_eq!(self.next_nonces[signer], nonce + 1); debug_assert_eq!(self.next_nonces[signer], nonce + 1);
} }
TransactionKind::Unsigned => { TransactionKind::Unsigned => {
// check we have the tx in the pool/chain // check we have the tx in the pool/chain
if self.unsigned_already_exist(tx.hash(), unsigned_in_chain) { if self.unsigned_already_exist(tx.hash(), unsigned_in_chain) {
return false; return Ok(false);
} }
if app_tx.verify().is_err() { app_tx.verify()?;
return false;
}
} }
TransactionKind::Provided(_) => return false, TransactionKind::Provided(_) => Err(TransactionError::ProvidedAddedToMempool)?,
} }
} }
} }
// Save the TX to the pool // Save the TX to the pool
self.save_tx(tx); self.save_tx(tx);
true Ok(true)
} }
// Returns None if the mempool doesn't have a nonce tracked. // Returns None if the mempool doesn't have a nonce tracked.

View File

@@ -349,7 +349,8 @@ impl<D: Db, T: TransactionTrait, P: P2p> Network for TendermintNetwork<D, T, P>
true, true,
Transaction::Tendermint(tx), Transaction::Tendermint(tx),
self.signature_scheme(), self.signature_scheme(),
) { ) == Ok(true)
{
self.p2p.broadcast(signer.genesis, to_broadcast).await; self.p2p.broadcast(signer.genesis, to_broadcast).await;
} }
} }

View File

@@ -105,11 +105,9 @@ fn invalid_block() {
{ {
// Add a valid transaction // Add a valid transaction
let (_, mut blockchain) = new_blockchain(genesis, &[tx.1.signer]); let (_, mut blockchain) = new_blockchain(genesis, &[tx.1.signer]);
assert!(blockchain.add_transaction::<N>( blockchain
true, .add_transaction::<N>(true, Transaction::Application(tx.clone()), validators.clone())
Transaction::Application(tx.clone()), .unwrap();
validators.clone()
));
let mut block = blockchain.build_block::<N>(validators.clone()); let mut block = blockchain.build_block::<N>(validators.clone());
assert_eq!(block.header.transactions, merkle(&[tx.hash()])); assert_eq!(block.header.transactions, merkle(&[tx.hash()]));
blockchain.verify_block::<N>(&block, validators.clone(), false).unwrap(); blockchain.verify_block::<N>(&block, validators.clone(), false).unwrap();
@@ -130,11 +128,9 @@ fn invalid_block() {
{ {
// Invalid signature // Invalid signature
let (_, mut blockchain) = new_blockchain(genesis, &[tx.1.signer]); let (_, mut blockchain) = new_blockchain(genesis, &[tx.1.signer]);
assert!(blockchain.add_transaction::<N>( blockchain
true, .add_transaction::<N>(true, Transaction::Application(tx), validators.clone())
Transaction::Application(tx), .unwrap();
validators.clone()
));
let mut block = blockchain.build_block::<N>(validators.clone()); let mut block = blockchain.build_block::<N>(validators.clone());
blockchain.verify_block::<N>(&block, validators.clone(), false).unwrap(); blockchain.verify_block::<N>(&block, validators.clone(), false).unwrap();
match &mut block.transactions[0] { match &mut block.transactions[0] {
@@ -170,11 +166,9 @@ fn signed_transaction() {
panic!("tendermint tx found"); panic!("tendermint tx found");
}; };
let next_nonce = blockchain.next_nonce(signer).unwrap(); let next_nonce = blockchain.next_nonce(signer).unwrap();
assert!(blockchain.add_transaction::<N>( blockchain
true, .add_transaction::<N>(true, Transaction::Application(tx), validators.clone())
Transaction::Application(tx), .unwrap();
validators.clone()
));
assert_eq!(next_nonce + 1, blockchain.next_nonce(signer).unwrap()); assert_eq!(next_nonce + 1, blockchain.next_nonce(signer).unwrap());
} }
let block = blockchain.build_block::<N>(validators.clone()); let block = blockchain.build_block::<N>(validators.clone());
@@ -363,11 +357,9 @@ async fn tendermint_evidence_tx() {
let Transaction::Tendermint(tx) = tx else { let Transaction::Tendermint(tx) = tx else {
panic!("non-tendermint tx found"); panic!("non-tendermint tx found");
}; };
assert!(blockchain.add_transaction::<N>( blockchain
true, .add_transaction::<N>(true, Transaction::Tendermint(tx), validators.clone())
Transaction::Tendermint(tx), .unwrap();
validators.clone()
));
} }
let block = blockchain.build_block::<N>(validators.clone()); let block = blockchain.build_block::<N>(validators.clone());
assert_eq!(blockchain.tip(), tip); assert_eq!(blockchain.tip(), tip);
@@ -475,7 +467,7 @@ async fn block_tx_ordering() {
let signed_tx = Transaction::Application(SignedTx::Signed(Box::new( let signed_tx = Transaction::Application(SignedTx::Signed(Box::new(
crate::tests::signed_transaction(&mut OsRng, genesis, &key, i), crate::tests::signed_transaction(&mut OsRng, genesis, &key, i),
))); )));
assert!(blockchain.add_transaction::<N>(true, signed_tx.clone(), validators.clone())); blockchain.add_transaction::<N>(true, signed_tx.clone(), validators.clone()).unwrap();
mempool.push(signed_tx); mempool.push(signed_tx);
let unsigned_tx = Transaction::Tendermint( let unsigned_tx = Transaction::Tendermint(
@@ -485,7 +477,7 @@ async fn block_tx_ordering() {
) )
.await, .await,
); );
assert!(blockchain.add_transaction::<N>(true, unsigned_tx.clone(), validators.clone())); blockchain.add_transaction::<N>(true, unsigned_tx.clone(), validators.clone()).unwrap();
mempool.push(unsigned_tx); mempool.push(unsigned_tx);
let provided_tx = let provided_tx =

View File

@@ -10,7 +10,7 @@ use tendermint::ext::Commit;
use serai_db::MemDb; use serai_db::MemDb;
use crate::{ use crate::{
transaction::Transaction as TransactionTrait, transaction::{TransactionError, Transaction as TransactionTrait},
tendermint::{TendermintBlock, Validators, Signer, TendermintNetwork}, tendermint::{TendermintBlock, Validators, Signer, TendermintNetwork},
ACCOUNT_MEMPOOL_LIMIT, Transaction, Mempool, ACCOUNT_MEMPOOL_LIMIT, Transaction, Mempool,
tests::{SignedTransaction, signed_transaction, p2p::DummyP2p, random_evidence_tx}, tests::{SignedTransaction, signed_transaction, p2p::DummyP2p, random_evidence_tx},
@@ -43,69 +43,85 @@ async fn mempool_addition() {
// Add TX 0 // Add TX 0
let mut blockchain_next_nonces = HashMap::from([(signer, 0)]); let mut blockchain_next_nonces = HashMap::from([(signer, 0)]);
assert!(mempool.add::<N>( assert!(mempool
&blockchain_next_nonces, .add::<N>(
true, &blockchain_next_nonces,
Transaction::Application(first_tx.clone()), true,
validators.clone(), Transaction::Application(first_tx.clone()),
unsigned_in_chain, validators.clone(),
commit, unsigned_in_chain,
)); commit,
)
.unwrap());
assert_eq!(mempool.next_nonce(&signer), Some(1)); assert_eq!(mempool.next_nonce(&signer), Some(1));
// add a tendermint evidence tx // add a tendermint evidence tx
let evidence_tx = let evidence_tx =
random_evidence_tx::<N>(Signer::new(genesis, key.clone()).into(), TendermintBlock(vec![])) random_evidence_tx::<N>(Signer::new(genesis, key.clone()).into(), TendermintBlock(vec![]))
.await; .await;
assert!(mempool.add::<N>( assert!(mempool
&blockchain_next_nonces, .add::<N>(
true, &blockchain_next_nonces,
Transaction::Tendermint(evidence_tx.clone()), true,
validators.clone(), Transaction::Tendermint(evidence_tx.clone()),
unsigned_in_chain, validators.clone(),
commit, unsigned_in_chain,
)); commit,
)
.unwrap());
// Test reloading works // Test reloading works
assert_eq!(mempool, Mempool::new(db, genesis)); assert_eq!(mempool, Mempool::new(db, genesis));
// Adding it again should fail // Adding them again should fail
assert!(!mempool.add::<N>( assert_eq!(
&blockchain_next_nonces, mempool.add::<N>(
true, &blockchain_next_nonces,
Transaction::Application(first_tx.clone()), true,
validators.clone(), Transaction::Application(first_tx.clone()),
unsigned_in_chain, validators.clone(),
commit, unsigned_in_chain,
)); commit,
assert!(!mempool.add::<N>( ),
&blockchain_next_nonces, Err(TransactionError::InvalidNonce)
true, );
Transaction::Tendermint(evidence_tx.clone()), assert_eq!(
validators.clone(), mempool.add::<N>(
unsigned_in_chain, &blockchain_next_nonces,
commit, true,
)); Transaction::Tendermint(evidence_tx.clone()),
validators.clone(),
unsigned_in_chain,
commit,
),
Ok(false)
);
// Do the same with the next nonce // Do the same with the next nonce
let second_tx = signed_transaction(&mut OsRng, genesis, &key, 1); let second_tx = signed_transaction(&mut OsRng, genesis, &key, 1);
assert!(mempool.add::<N>( assert_eq!(
&blockchain_next_nonces, mempool.add::<N>(
true, &blockchain_next_nonces,
Transaction::Application(second_tx.clone()), true,
validators.clone(), Transaction::Application(second_tx.clone()),
unsigned_in_chain, validators.clone(),
commit, unsigned_in_chain,
)); commit,
),
Ok(true)
);
assert_eq!(mempool.next_nonce(&signer), Some(2)); assert_eq!(mempool.next_nonce(&signer), Some(2));
assert!(!mempool.add::<N>( assert_eq!(
&blockchain_next_nonces, mempool.add::<N>(
true, &blockchain_next_nonces,
Transaction::Application(second_tx.clone()), true,
validators.clone(), Transaction::Application(second_tx.clone()),
unsigned_in_chain, validators.clone(),
commit, unsigned_in_chain,
)); commit,
),
Err(TransactionError::InvalidNonce)
);
// If the mempool doesn't have a nonce for an account, it should successfully use the // If the mempool doesn't have a nonce for an account, it should successfully use the
// blockchain's // blockchain's
@@ -114,14 +130,16 @@ async fn mempool_addition() {
let second_signer = tx.1.signer; let second_signer = tx.1.signer;
assert_eq!(mempool.next_nonce(&second_signer), None); assert_eq!(mempool.next_nonce(&second_signer), None);
blockchain_next_nonces.insert(second_signer, 2); blockchain_next_nonces.insert(second_signer, 2);
assert!(mempool.add::<N>( assert!(mempool
&blockchain_next_nonces, .add::<N>(
true, &blockchain_next_nonces,
Transaction::Application(tx.clone()), true,
validators.clone(), Transaction::Application(tx.clone()),
unsigned_in_chain, validators.clone(),
commit unsigned_in_chain,
)); commit
)
.unwrap());
assert_eq!(mempool.next_nonce(&second_signer), Some(3)); assert_eq!(mempool.next_nonce(&second_signer), Some(3));
// Getting a block should work // Getting a block should work
@@ -159,22 +177,32 @@ fn too_many_mempool() {
// We should be able to add transactions up to the limit // We should be able to add transactions up to the limit
for i in 0 .. ACCOUNT_MEMPOOL_LIMIT { for i in 0 .. ACCOUNT_MEMPOOL_LIMIT {
assert!(mempool.add::<N>( assert!(mempool
.add::<N>(
&HashMap::from([(signer, 0)]),
false,
Transaction::Application(signed_transaction(&mut OsRng, genesis, &key, i)),
validators.clone(),
unsigned_in_chain,
commit,
)
.unwrap());
}
// Yet adding more should fail
assert_eq!(
mempool.add::<N>(
&HashMap::from([(signer, 0)]), &HashMap::from([(signer, 0)]),
false, false,
Transaction::Application(signed_transaction(&mut OsRng, genesis, &key, i)), Transaction::Application(signed_transaction(
&mut OsRng,
genesis,
&key,
ACCOUNT_MEMPOOL_LIMIT
)),
validators.clone(), validators.clone(),
unsigned_in_chain, unsigned_in_chain,
commit, commit,
)); ),
} Err(TransactionError::TooManyInMempool)
// Yet adding more should fail );
assert!(!mempool.add::<N>(
&HashMap::from([(signer, 0)]),
false,
Transaction::Application(signed_transaction(&mut OsRng, genesis, &key, ACCOUNT_MEMPOOL_LIMIT)),
validators.clone(),
unsigned_in_chain,
commit,
));
} }

View File

@@ -31,6 +31,12 @@ pub enum TransactionError {
/// Transaction's content is invalid. /// Transaction's content is invalid.
#[error("transaction content is invalid")] #[error("transaction content is invalid")]
InvalidContent, InvalidContent,
/// Transaction's signer has too many transactions in the mempool.
#[error("signer has too many transactions in the mempool")]
TooManyInMempool,
/// Provided Transaction added to mempool.
#[error("provided transaction added to mempool")]
ProvidedAddedToMempool,
} }
/// Data for a signed transaction. /// Data for a signed transaction.

View File

@@ -282,7 +282,6 @@ pub trait Network: Sized + Send + Sync {
/// Trigger a slash for the validator in question who was definitively malicious. /// Trigger a slash for the validator in question who was definitively malicious.
/// ///
/// The exact process of triggering a slash is undefined and left to the network as a whole. /// The exact process of triggering a slash is undefined and left to the network as a whole.
// TODO: We need to provide some evidence for this.
async fn slash(&mut self, validator: Self::ValidatorId, slash_event: SlashEvent<Self>); async fn slash(&mut self, validator: Self::ValidatorId, slash_event: SlashEvent<Self>);
/// Validate a block. /// Validate a block.