mirror of
https://github.com/serai-dex/serai.git
synced 2025-12-12 14:09:25 +00:00
coordinator/tributary was tributary-chain. This crate has been renamed tributary-sdk and moved to coordinator/tributary-sdk. coordinator/src/tributary was our instantion of a Tributary, the Transaction type and scan task. This has been moved to coordinator/tributary. The main reason for this was due to coordinator/main.rs becoming untidy. There is now a collection of clean, independent APIs present in the codebase. coordinator/main.rs is to compose them. Sometimes, these compositions are a bit silly (reading from a channel just to forward the message to a distinct channel). That's more than fine as the code is still readable and the value from the cleanliness of the APIs composed far exceeds the nits from having these odd compositions. This breaks down a bit as we now define a global database, and have some APIs interact with multiple other APIs. coordinator/src/tributary was a self-contained, clean API. The recently added task present in coordinator/tributary/mod.rs, which bound it to the rest of the Coordinator, wasn't. Now, coordinator/src is solely the API compositions, and all self-contained APIs are their own crates.
200 lines
5.4 KiB
Rust
200 lines
5.4 KiB
Rust
use std::{sync::Arc, collections::HashMap};
|
|
|
|
use zeroize::Zeroizing;
|
|
use rand::{RngCore, rngs::OsRng};
|
|
|
|
use ciphersuite::{group::ff::Field, Ciphersuite, Ristretto};
|
|
|
|
use tendermint::ext::Commit;
|
|
|
|
use serai_db::MemDb;
|
|
|
|
use crate::{
|
|
transaction::{TransactionError, Transaction as TransactionTrait},
|
|
tendermint::{TendermintBlock, Validators, Signer, TendermintNetwork},
|
|
ACCOUNT_MEMPOOL_LIMIT, Transaction, Mempool,
|
|
tests::{SignedTransaction, signed_transaction, p2p::DummyP2p, random_evidence_tx},
|
|
};
|
|
|
|
type N = TendermintNetwork<MemDb, SignedTransaction, DummyP2p>;
|
|
|
|
fn new_mempool<T: TransactionTrait>() -> ([u8; 32], MemDb, Mempool<MemDb, T>) {
|
|
let mut genesis = [0; 32];
|
|
OsRng.fill_bytes(&mut genesis);
|
|
let db = MemDb::new();
|
|
(genesis, db.clone(), Mempool::new(db, genesis))
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn mempool_addition() {
|
|
let (genesis, db, mut mempool) = new_mempool::<SignedTransaction>();
|
|
let commit = |_: u64| -> Option<Commit<Arc<Validators>>> {
|
|
Some(Commit::<Arc<Validators>> { end_time: 0, validators: vec![], signature: vec![] })
|
|
};
|
|
let unsigned_in_chain = |_: [u8; 32]| false;
|
|
let key = Zeroizing::new(<Ristretto as Ciphersuite>::F::random(&mut OsRng));
|
|
|
|
let first_tx = signed_transaction(&mut OsRng, genesis, &key, 0);
|
|
let signer = first_tx.1.signer;
|
|
assert_eq!(mempool.next_nonce_in_mempool(&signer, vec![]), None);
|
|
|
|
// validators
|
|
let validators = Arc::new(Validators::new(genesis, vec![(signer, 1)]).unwrap());
|
|
|
|
// Add TX 0
|
|
assert!(mempool
|
|
.add::<N, _>(
|
|
&|_, _| Some(0),
|
|
true,
|
|
Transaction::Application(first_tx.clone()),
|
|
&validators,
|
|
unsigned_in_chain,
|
|
commit,
|
|
)
|
|
.unwrap());
|
|
assert_eq!(mempool.next_nonce_in_mempool(&signer, vec![]), Some(1));
|
|
|
|
// add a tendermint evidence tx
|
|
let evidence_tx =
|
|
random_evidence_tx::<N>(Signer::new(genesis, key.clone()).into(), TendermintBlock(vec![]))
|
|
.await;
|
|
assert!(mempool
|
|
.add::<N, _>(
|
|
&|_, _| None,
|
|
true,
|
|
Transaction::Tendermint(evidence_tx.clone()),
|
|
&validators,
|
|
unsigned_in_chain,
|
|
commit,
|
|
)
|
|
.unwrap());
|
|
|
|
// Test reloading works
|
|
assert_eq!(mempool, Mempool::new(db, genesis));
|
|
|
|
// Adding them again should fail
|
|
assert_eq!(
|
|
mempool.add::<N, _>(
|
|
&|_, _| Some(0),
|
|
true,
|
|
Transaction::Application(first_tx.clone()),
|
|
&validators,
|
|
unsigned_in_chain,
|
|
commit,
|
|
),
|
|
Err(TransactionError::InvalidNonce)
|
|
);
|
|
assert_eq!(
|
|
mempool.add::<N, _>(
|
|
&|_, _| None,
|
|
true,
|
|
Transaction::Tendermint(evidence_tx.clone()),
|
|
&validators,
|
|
unsigned_in_chain,
|
|
commit,
|
|
),
|
|
Ok(false)
|
|
);
|
|
|
|
// Do the same with the next nonce
|
|
let second_tx = signed_transaction(&mut OsRng, genesis, &key, 1);
|
|
assert_eq!(
|
|
mempool.add::<N, _>(
|
|
&|_, _| Some(0),
|
|
true,
|
|
Transaction::Application(second_tx.clone()),
|
|
&validators,
|
|
unsigned_in_chain,
|
|
commit,
|
|
),
|
|
Ok(true)
|
|
);
|
|
assert_eq!(mempool.next_nonce_in_mempool(&signer, vec![]), Some(2));
|
|
assert_eq!(
|
|
mempool.add::<N, _>(
|
|
&|_, _| Some(0),
|
|
true,
|
|
Transaction::Application(second_tx.clone()),
|
|
&validators,
|
|
unsigned_in_chain,
|
|
commit,
|
|
),
|
|
Err(TransactionError::InvalidNonce)
|
|
);
|
|
|
|
// If the mempool doesn't have a nonce for an account, it should successfully use the
|
|
// blockchain's
|
|
let second_key = Zeroizing::new(<Ristretto as Ciphersuite>::F::random(&mut OsRng));
|
|
let tx = signed_transaction(&mut OsRng, genesis, &second_key, 2);
|
|
let second_signer = tx.1.signer;
|
|
assert_eq!(mempool.next_nonce_in_mempool(&second_signer, vec![]), None);
|
|
assert!(mempool
|
|
.add::<N, _>(
|
|
&|_, _| Some(2),
|
|
true,
|
|
Transaction::Application(tx.clone()),
|
|
&validators,
|
|
unsigned_in_chain,
|
|
commit
|
|
)
|
|
.unwrap());
|
|
assert_eq!(mempool.next_nonce_in_mempool(&second_signer, vec![]), Some(3));
|
|
|
|
// Getting a block should work
|
|
assert_eq!(mempool.block().len(), 4);
|
|
|
|
// Removing should successfully prune
|
|
mempool.remove(&tx.hash());
|
|
|
|
assert_eq!(
|
|
mempool.txs(),
|
|
&HashMap::from([
|
|
(first_tx.hash(), Transaction::Application(first_tx)),
|
|
(second_tx.hash(), Transaction::Application(second_tx)),
|
|
(evidence_tx.hash(), Transaction::Tendermint(evidence_tx))
|
|
])
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn too_many_mempool() {
|
|
let (genesis, _, mut mempool) = new_mempool::<SignedTransaction>();
|
|
let validators = Arc::new(Validators::new(genesis, vec![]).unwrap());
|
|
let commit = |_: u64| -> Option<Commit<Arc<Validators>>> {
|
|
Some(Commit::<Arc<Validators>> { end_time: 0, validators: vec![], signature: vec![] })
|
|
};
|
|
let unsigned_in_chain = |_: [u8; 32]| false;
|
|
let key = Zeroizing::new(<Ristretto as Ciphersuite>::F::random(&mut OsRng));
|
|
|
|
// We should be able to add transactions up to the limit
|
|
for i in 0 .. ACCOUNT_MEMPOOL_LIMIT {
|
|
assert!(mempool
|
|
.add::<N, _>(
|
|
&|_, _| Some(0),
|
|
false,
|
|
Transaction::Application(signed_transaction(&mut OsRng, genesis, &key, i)),
|
|
&validators,
|
|
unsigned_in_chain,
|
|
commit,
|
|
)
|
|
.unwrap());
|
|
}
|
|
// Yet adding more should fail
|
|
assert_eq!(
|
|
mempool.add::<N, _>(
|
|
&|_, _| Some(0),
|
|
false,
|
|
Transaction::Application(signed_transaction(
|
|
&mut OsRng,
|
|
genesis,
|
|
&key,
|
|
ACCOUNT_MEMPOOL_LIMIT
|
|
)),
|
|
&validators,
|
|
unsigned_in_chain,
|
|
commit,
|
|
),
|
|
Err(TransactionError::TooManyInMempool)
|
|
);
|
|
}
|