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.
203 lines
5.0 KiB
Rust
203 lines
5.0 KiB
Rust
use core::future::Future;
|
|
use std::{
|
|
sync::Arc,
|
|
time::{UNIX_EPOCH, SystemTime, Duration},
|
|
};
|
|
|
|
use parity_scale_codec::{Encode, Decode};
|
|
|
|
use futures_util::sink::SinkExt;
|
|
use tokio::{sync::RwLock, time::sleep};
|
|
|
|
use serai_db::MemDb;
|
|
|
|
use tendermint_machine::{
|
|
ext::*, SignedMessageFor, SyncedBlockSender, SyncedBlockResultReceiver, MessageSender,
|
|
SlashEvent, TendermintMachine, TendermintHandle,
|
|
};
|
|
|
|
type TestValidatorId = u16;
|
|
type TestBlockId = [u8; 4];
|
|
|
|
struct TestSigner(u16);
|
|
impl Signer for TestSigner {
|
|
type ValidatorId = TestValidatorId;
|
|
type Signature = [u8; 32];
|
|
|
|
fn validator_id(&self) -> impl Send + Future<Output = Option<TestValidatorId>> {
|
|
async move { Some(self.0) }
|
|
}
|
|
|
|
fn sign(&self, msg: &[u8]) -> impl Send + Future<Output = [u8; 32]> {
|
|
async move {
|
|
let mut sig = [0; 32];
|
|
sig[.. 2].copy_from_slice(&self.0.to_le_bytes());
|
|
sig[2 .. (2 + 30.min(msg.len()))].copy_from_slice(&msg[.. 30.min(msg.len())]);
|
|
sig
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Clone)]
|
|
struct TestSignatureScheme;
|
|
impl SignatureScheme for TestSignatureScheme {
|
|
type ValidatorId = TestValidatorId;
|
|
type Signature = [u8; 32];
|
|
type AggregateSignature = Vec<[u8; 32]>;
|
|
type Signer = TestSigner;
|
|
|
|
#[must_use]
|
|
fn verify(&self, validator: u16, msg: &[u8], sig: &[u8; 32]) -> bool {
|
|
(sig[.. 2] == validator.to_le_bytes()) && (sig[2 ..] == [msg, &[0; 30]].concat()[.. 30])
|
|
}
|
|
|
|
fn aggregate(
|
|
&self,
|
|
_: &[Self::ValidatorId],
|
|
_: &[u8],
|
|
sigs: &[Self::Signature],
|
|
) -> Self::AggregateSignature {
|
|
sigs.to_vec()
|
|
}
|
|
|
|
#[must_use]
|
|
fn verify_aggregate(
|
|
&self,
|
|
signers: &[TestValidatorId],
|
|
msg: &[u8],
|
|
sigs: &Vec<[u8; 32]>,
|
|
) -> bool {
|
|
assert_eq!(signers.len(), sigs.len());
|
|
for sig in signers.iter().zip(sigs.iter()) {
|
|
assert!(self.verify(*sig.0, msg, sig.1));
|
|
}
|
|
true
|
|
}
|
|
}
|
|
|
|
struct TestWeights;
|
|
impl Weights for TestWeights {
|
|
type ValidatorId = TestValidatorId;
|
|
|
|
fn total_weight(&self) -> u64 {
|
|
4
|
|
}
|
|
fn weight(&self, id: TestValidatorId) -> u64 {
|
|
[1; 4][usize::from(id)]
|
|
}
|
|
|
|
fn proposer(&self, number: BlockNumber, round: RoundNumber) -> TestValidatorId {
|
|
TestValidatorId::try_from((number.0 + u64::from(round.0)) % 4).unwrap()
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, PartialEq, Eq, Debug, Encode, Decode)]
|
|
struct TestBlock {
|
|
id: TestBlockId,
|
|
valid: Result<(), BlockError>,
|
|
}
|
|
|
|
impl Block for TestBlock {
|
|
type Id = TestBlockId;
|
|
|
|
fn id(&self) -> TestBlockId {
|
|
self.id
|
|
}
|
|
}
|
|
|
|
#[allow(clippy::type_complexity)]
|
|
struct TestNetwork(
|
|
u16,
|
|
Arc<RwLock<Vec<(MessageSender<Self>, SyncedBlockSender<Self>, SyncedBlockResultReceiver)>>>,
|
|
);
|
|
|
|
impl Network for TestNetwork {
|
|
type Db = MemDb;
|
|
|
|
type ValidatorId = TestValidatorId;
|
|
type SignatureScheme = TestSignatureScheme;
|
|
type Weights = TestWeights;
|
|
type Block = TestBlock;
|
|
|
|
const BLOCK_PROCESSING_TIME: u32 = 2000;
|
|
const LATENCY_TIME: u32 = 1000;
|
|
|
|
fn signer(&self) -> TestSigner {
|
|
TestSigner(self.0)
|
|
}
|
|
|
|
fn signature_scheme(&self) -> TestSignatureScheme {
|
|
TestSignatureScheme
|
|
}
|
|
|
|
fn weights(&self) -> TestWeights {
|
|
TestWeights
|
|
}
|
|
|
|
async fn broadcast(&mut self, msg: SignedMessageFor<Self>) {
|
|
for (messages, _, _) in self.1.write().await.iter_mut() {
|
|
messages.send(msg.clone()).await.unwrap();
|
|
}
|
|
}
|
|
|
|
async fn slash(&mut self, id: TestValidatorId, event: SlashEvent) {
|
|
println!("Slash for {id} due to {event:?}");
|
|
}
|
|
|
|
async fn validate(&self, block: &TestBlock) -> Result<(), BlockError> {
|
|
block.valid
|
|
}
|
|
|
|
async fn add_block(
|
|
&mut self,
|
|
block: TestBlock,
|
|
commit: Commit<TestSignatureScheme>,
|
|
) -> Option<TestBlock> {
|
|
println!("Adding {:?}", &block);
|
|
assert!(block.valid.is_ok());
|
|
assert!(self.verify_commit(block.id(), &commit));
|
|
Some(TestBlock { id: (u32::from_le_bytes(block.id) + 1).to_le_bytes(), valid: Ok(()) })
|
|
}
|
|
}
|
|
|
|
impl TestNetwork {
|
|
async fn new(
|
|
validators: usize,
|
|
start_time: u64,
|
|
) -> Arc<RwLock<Vec<(MessageSender<Self>, SyncedBlockSender<Self>, SyncedBlockResultReceiver)>>>
|
|
{
|
|
let arc = Arc::new(RwLock::new(vec![]));
|
|
{
|
|
let mut write = arc.write().await;
|
|
for i in 0 .. validators {
|
|
let i = u16::try_from(i).unwrap();
|
|
let TendermintHandle { messages, synced_block, synced_block_result, machine } =
|
|
TendermintMachine::new(
|
|
MemDb::new(),
|
|
TestNetwork(i, arc.clone()),
|
|
[0; 32],
|
|
BlockNumber(1),
|
|
start_time,
|
|
TestBlock { id: 1u32.to_le_bytes(), valid: Ok(()) },
|
|
)
|
|
.await;
|
|
tokio::spawn(machine.run());
|
|
write.push((messages, synced_block, synced_block_result));
|
|
}
|
|
}
|
|
arc
|
|
}
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_machine() {
|
|
TestNetwork::new(4, SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs()).await;
|
|
sleep(Duration::from_secs(30)).await;
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_machine_with_historic_start_time() {
|
|
TestNetwork::new(4, SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs() - 60).await;
|
|
sleep(Duration::from_secs(30)).await;
|
|
}
|