mirror of
https://github.com/serai-dex/serai.git
synced 2025-12-08 12:19:24 +00:00
Have Tributary's add_transaction return a proper error
Modifies main.rs to properly handle the returned error.
This commit is contained in:
@@ -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");
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
|||||||
@@ -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) => {
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 =
|
||||||
|
|||||||
@@ -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,
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
Reference in New Issue
Block a user