mirror of
https://github.com/serai-dex/serai.git
synced 2025-12-08 12:19:24 +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.
141 lines
3.8 KiB
Rust
141 lines
3.8 KiB
Rust
use std::{sync::Arc, io, collections::HashMap, fmt::Debug};
|
|
|
|
use blake2::{Digest, Blake2s256};
|
|
use ciphersuite::{
|
|
group::{ff::Field, Group},
|
|
Ciphersuite, Ristretto,
|
|
};
|
|
use schnorr::SchnorrSignature;
|
|
|
|
use serai_db::MemDb;
|
|
use tendermint::ext::Commit;
|
|
|
|
use crate::{
|
|
ReadWrite, BlockError, Block, Transaction,
|
|
tests::p2p::DummyP2p,
|
|
transaction::{TransactionError, Signed, TransactionKind, Transaction as TransactionTrait},
|
|
tendermint::{TendermintNetwork, Validators},
|
|
};
|
|
|
|
type N = TendermintNetwork<MemDb, NonceTransaction, DummyP2p>;
|
|
|
|
// A transaction solely defined by its nonce and a distinguisher (to allow creating distinct TXs
|
|
// sharing a nonce).
|
|
#[derive(Clone, PartialEq, Eq, Debug)]
|
|
struct NonceTransaction(u32, u8, Signed);
|
|
|
|
impl NonceTransaction {
|
|
fn new(nonce: u32, distinguisher: u8) -> Self {
|
|
NonceTransaction(
|
|
nonce,
|
|
distinguisher,
|
|
Signed {
|
|
signer: <Ristretto as Ciphersuite>::G::identity(),
|
|
nonce,
|
|
signature: SchnorrSignature::<Ristretto> {
|
|
R: <Ristretto as Ciphersuite>::G::identity(),
|
|
s: <Ristretto as Ciphersuite>::F::ZERO,
|
|
},
|
|
},
|
|
)
|
|
}
|
|
}
|
|
|
|
impl ReadWrite for NonceTransaction {
|
|
fn read<R: io::Read>(reader: &mut R) -> io::Result<Self> {
|
|
let mut nonce = [0; 4];
|
|
reader.read_exact(&mut nonce)?;
|
|
let nonce = u32::from_le_bytes(nonce);
|
|
|
|
let mut distinguisher = [0];
|
|
reader.read_exact(&mut distinguisher)?;
|
|
|
|
Ok(NonceTransaction::new(nonce, distinguisher[0]))
|
|
}
|
|
|
|
fn write<W: io::Write>(&self, writer: &mut W) -> io::Result<()> {
|
|
writer.write_all(&self.0.to_le_bytes())?;
|
|
writer.write_all(&[self.1])
|
|
}
|
|
}
|
|
|
|
impl TransactionTrait for NonceTransaction {
|
|
fn kind(&self) -> TransactionKind {
|
|
TransactionKind::Signed(vec![], self.2.clone())
|
|
}
|
|
|
|
fn hash(&self) -> [u8; 32] {
|
|
Blake2s256::digest([self.0.to_le_bytes().as_ref(), &[self.1]].concat()).into()
|
|
}
|
|
|
|
fn verify(&self) -> Result<(), TransactionError> {
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn empty_block() {
|
|
const GENESIS: [u8; 32] = [0xff; 32];
|
|
const LAST: [u8; 32] = [0x01; 32];
|
|
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 provided_or_unsigned_in_chain = |_: [u8; 32]| false;
|
|
Block::<NonceTransaction>::new(LAST, vec![], vec![])
|
|
.verify::<N, _>(
|
|
GENESIS,
|
|
LAST,
|
|
HashMap::new(),
|
|
&mut |_, _| None,
|
|
&validators,
|
|
commit,
|
|
provided_or_unsigned_in_chain,
|
|
false,
|
|
)
|
|
.unwrap();
|
|
}
|
|
|
|
#[test]
|
|
fn duplicate_nonces() {
|
|
const GENESIS: [u8; 32] = [0xff; 32];
|
|
const LAST: [u8; 32] = [0x01; 32];
|
|
|
|
let validators = Arc::new(Validators::new(GENESIS, vec![]).unwrap());
|
|
|
|
// Run once without duplicating a nonce, and once with, so that's confirmed to be the faulty
|
|
// component
|
|
for i in [1, 0] {
|
|
let mut mempool = vec![];
|
|
let mut insert = |tx: NonceTransaction| mempool.push(Transaction::Application(tx));
|
|
insert(NonceTransaction::new(0, 0));
|
|
insert(NonceTransaction::new(i, 1));
|
|
|
|
let commit = |_: u64| -> Option<Commit<Arc<Validators>>> {
|
|
Some(Commit::<Arc<Validators>> { end_time: 0, validators: vec![], signature: vec![] })
|
|
};
|
|
let provided_or_unsigned_in_chain = |_: [u8; 32]| false;
|
|
|
|
let mut last_nonce = 0;
|
|
let res = Block::new(LAST, vec![], mempool).verify::<N, _>(
|
|
GENESIS,
|
|
LAST,
|
|
HashMap::new(),
|
|
&mut |_, _| {
|
|
let res = last_nonce;
|
|
last_nonce += 1;
|
|
Some(res)
|
|
},
|
|
&validators,
|
|
commit,
|
|
provided_or_unsigned_in_chain,
|
|
false,
|
|
);
|
|
if i == 1 {
|
|
res.unwrap();
|
|
} else {
|
|
assert_eq!(res, Err(BlockError::TransactionError(TransactionError::InvalidNonce)));
|
|
}
|
|
}
|
|
}
|