2023-04-13 22:43:03 +00:00
|
|
|
use core::fmt::Debug;
|
|
|
|
use std::{
|
|
|
|
sync::{Arc, RwLock},
|
|
|
|
io,
|
|
|
|
collections::HashMap,
|
|
|
|
};
|
|
|
|
|
|
|
|
use async_trait::async_trait;
|
|
|
|
|
|
|
|
use zeroize::Zeroizing;
|
|
|
|
|
|
|
|
use ciphersuite::{Ciphersuite, Ristretto};
|
|
|
|
|
|
|
|
use scale::Decode;
|
|
|
|
use futures::SinkExt;
|
|
|
|
use ::tendermint::{
|
2023-04-14 18:11:19 +00:00
|
|
|
ext::{BlockNumber, Commit, Block as BlockTrait, Network},
|
2023-04-13 22:43:03 +00:00
|
|
|
SignedMessageFor, SyncedBlock, SyncedBlockSender, MessageSender, TendermintMachine,
|
|
|
|
TendermintHandle,
|
|
|
|
};
|
2023-04-11 14:18:31 +00:00
|
|
|
|
2023-04-14 18:11:19 +00:00
|
|
|
use serai_db::Db;
|
|
|
|
|
2023-04-11 17:42:18 +00:00
|
|
|
mod merkle;
|
|
|
|
pub(crate) use merkle::*;
|
|
|
|
|
|
|
|
mod transaction;
|
|
|
|
pub use transaction::*;
|
|
|
|
|
2023-04-12 00:24:27 +00:00
|
|
|
mod provided;
|
2023-04-12 20:06:14 +00:00
|
|
|
pub(crate) use provided::*;
|
2023-04-12 00:24:27 +00:00
|
|
|
|
2023-04-11 17:42:18 +00:00
|
|
|
mod block;
|
|
|
|
pub use block::*;
|
|
|
|
|
2023-04-12 15:13:48 +00:00
|
|
|
mod blockchain;
|
2023-04-14 00:35:55 +00:00
|
|
|
pub(crate) use blockchain::*;
|
2023-04-12 15:13:48 +00:00
|
|
|
|
2023-04-12 16:15:38 +00:00
|
|
|
mod mempool;
|
2023-04-14 00:35:55 +00:00
|
|
|
pub(crate) use mempool::*;
|
2023-04-12 16:15:38 +00:00
|
|
|
|
2023-04-12 20:06:14 +00:00
|
|
|
mod tendermint;
|
2023-04-13 22:43:03 +00:00
|
|
|
pub(crate) use crate::tendermint::*;
|
2023-04-12 20:06:14 +00:00
|
|
|
|
2023-04-11 23:04:53 +00:00
|
|
|
#[cfg(any(test, feature = "tests"))]
|
|
|
|
pub mod tests;
|
|
|
|
|
2023-04-14 00:35:55 +00:00
|
|
|
/// Size limit for an individual transaction.
|
|
|
|
pub const TRANSACTION_SIZE_LIMIT: usize = 50_000;
|
|
|
|
/// Amount of transactions a single account may have in the mempool.
|
|
|
|
pub const ACCOUNT_MEMPOOL_LIMIT: u32 = 50;
|
|
|
|
/// Block size limit.
|
|
|
|
// This targets a growth limit of roughly 5 GB a day, under load, in order to prevent a malicious
|
|
|
|
// participant from flooding disks and causing out of space errors in order processes.
|
|
|
|
pub const BLOCK_SIZE_LIMIT: usize = 350_000;
|
|
|
|
|
2023-04-13 22:43:03 +00:00
|
|
|
pub(crate) const TRANSACTION_MESSAGE: u8 = 0;
|
|
|
|
pub(crate) const TENDERMINT_MESSAGE: u8 = 1;
|
|
|
|
|
2023-04-11 17:42:18 +00:00
|
|
|
/// An item which can be read and written.
|
|
|
|
pub trait ReadWrite: Sized {
|
|
|
|
fn read<R: io::Read>(reader: &mut R) -> io::Result<Self>;
|
|
|
|
fn write<W: io::Write>(&self, writer: &mut W) -> io::Result<()>;
|
|
|
|
|
|
|
|
fn serialize(&self) -> Vec<u8> {
|
|
|
|
// BlockHeader is 64 bytes and likely the smallest item in this system
|
|
|
|
let mut buf = Vec::with_capacity(64);
|
|
|
|
self.write(&mut buf).unwrap();
|
|
|
|
buf
|
|
|
|
}
|
|
|
|
}
|
2023-04-13 22:43:03 +00:00
|
|
|
|
|
|
|
#[async_trait]
|
|
|
|
pub trait P2p: 'static + Send + Sync + Clone + Debug {
|
|
|
|
async fn broadcast(&self, msg: Vec<u8>);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[async_trait]
|
|
|
|
impl<P: P2p> P2p for Arc<P> {
|
|
|
|
async fn broadcast(&self, msg: Vec<u8>) {
|
|
|
|
(*self).broadcast(msg).await
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-04-14 18:11:19 +00:00
|
|
|
pub struct Tributary<D: Db, T: Transaction, P: P2p> {
|
|
|
|
network: TendermintNetwork<D, T, P>,
|
2023-04-13 22:43:03 +00:00
|
|
|
|
2023-04-14 18:11:19 +00:00
|
|
|
synced_block: SyncedBlockSender<TendermintNetwork<D, T, P>>,
|
|
|
|
messages: MessageSender<TendermintNetwork<D, T, P>>,
|
2023-04-13 22:43:03 +00:00
|
|
|
}
|
|
|
|
|
2023-04-14 18:11:19 +00:00
|
|
|
impl<D: Db, T: Transaction, P: P2p> Tributary<D, T, P> {
|
2023-04-13 22:43:03 +00:00
|
|
|
pub async fn new(
|
2023-04-14 18:11:19 +00:00
|
|
|
db: D,
|
2023-04-13 22:43:03 +00:00
|
|
|
genesis: [u8; 32],
|
|
|
|
start_time: u64,
|
|
|
|
key: Zeroizing<<Ristretto as Ciphersuite>::F>,
|
|
|
|
validators: HashMap<<Ristretto as Ciphersuite>::G, u64>,
|
|
|
|
p2p: P,
|
|
|
|
) -> Self {
|
|
|
|
let validators_vec = validators.keys().cloned().collect::<Vec<_>>();
|
|
|
|
|
|
|
|
let signer = Arc::new(Signer::new(genesis, key));
|
|
|
|
let validators = Arc::new(Validators::new(genesis, validators));
|
|
|
|
|
2023-04-14 18:11:19 +00:00
|
|
|
let mut blockchain = Blockchain::new(db, genesis, &validators_vec);
|
2023-04-13 22:43:03 +00:00
|
|
|
let block_number = blockchain.block_number();
|
2023-04-14 18:11:19 +00:00
|
|
|
|
|
|
|
let start_time = if let Some(commit) = blockchain.commit(&blockchain.tip()) {
|
|
|
|
Commit::<Validators>::decode(&mut commit.as_ref()).unwrap().end_time
|
|
|
|
} else {
|
|
|
|
start_time
|
|
|
|
};
|
2023-04-13 22:43:03 +00:00
|
|
|
let proposal = TendermintBlock(blockchain.build_block().serialize());
|
|
|
|
let blockchain = Arc::new(RwLock::new(blockchain));
|
|
|
|
|
2023-04-14 18:11:19 +00:00
|
|
|
let network = TendermintNetwork { genesis, signer, validators, blockchain, p2p };
|
2023-04-13 22:43:03 +00:00
|
|
|
|
|
|
|
// The genesis block is 0, so we're working on block #1
|
2023-04-14 18:11:19 +00:00
|
|
|
let block_number = BlockNumber((block_number + 1).into());
|
2023-04-13 22:43:03 +00:00
|
|
|
let TendermintHandle { synced_block, messages, machine } =
|
|
|
|
TendermintMachine::new(network.clone(), block_number, start_time, proposal).await;
|
|
|
|
tokio::task::spawn(machine.run());
|
|
|
|
|
|
|
|
Self { network, synced_block, messages }
|
|
|
|
}
|
|
|
|
|
2023-04-14 18:11:19 +00:00
|
|
|
// TODO: Is there a race condition with providing these? Since the same provided TX provided
|
|
|
|
// twice counts as two transactions
|
2023-04-14 00:35:55 +00:00
|
|
|
pub fn provide_transaction(&self, tx: T) -> bool {
|
2023-04-13 22:43:03 +00:00
|
|
|
self.network.blockchain.write().unwrap().provide_transaction(tx)
|
|
|
|
}
|
|
|
|
|
2023-04-14 00:35:55 +00:00
|
|
|
pub fn next_nonce(&self, signer: <Ristretto as Ciphersuite>::G) -> Option<u32> {
|
|
|
|
self.network.blockchain.read().unwrap().next_nonce(signer)
|
|
|
|
}
|
|
|
|
|
2023-04-13 22:43:03 +00:00
|
|
|
// Returns if the transaction was valid.
|
2023-04-14 00:35:55 +00:00
|
|
|
pub async fn add_transaction(&mut self, tx: T) -> bool {
|
2023-04-13 22:43:03 +00:00
|
|
|
let mut to_broadcast = vec![TRANSACTION_MESSAGE];
|
|
|
|
tx.write(&mut to_broadcast).unwrap();
|
2023-04-14 00:35:55 +00:00
|
|
|
let res = self.network.blockchain.write().unwrap().add_transaction(true, tx);
|
2023-04-13 22:43:03 +00:00
|
|
|
if res {
|
|
|
|
self.network.p2p.broadcast(to_broadcast).await;
|
|
|
|
}
|
|
|
|
res
|
|
|
|
}
|
|
|
|
|
|
|
|
// Sync a block.
|
|
|
|
// TODO: Since we have a static validator set, we should only need the tail commit?
|
|
|
|
pub async fn sync_block(&mut self, block: Block<T>, commit: Vec<u8>) -> bool {
|
|
|
|
let (tip, block_number) = {
|
|
|
|
let blockchain = self.network.blockchain.read().unwrap();
|
|
|
|
(blockchain.tip(), blockchain.block_number())
|
|
|
|
};
|
|
|
|
|
|
|
|
if block.header.parent != tip {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
let block = TendermintBlock(block.serialize());
|
|
|
|
let Ok(commit) = Commit::<Arc<Validators>>::decode(&mut commit.as_ref()) else {
|
|
|
|
return false;
|
|
|
|
};
|
|
|
|
if !self.network.verify_commit(block.id(), &commit) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2023-04-14 18:11:19 +00:00
|
|
|
let number = BlockNumber((block_number + 1).into());
|
2023-04-13 22:43:03 +00:00
|
|
|
self.synced_block.send(SyncedBlock { number, block, commit }).await.unwrap();
|
|
|
|
true
|
|
|
|
}
|
|
|
|
|
|
|
|
// Return true if the message should be rebroadcasted.
|
|
|
|
pub async fn handle_message(&mut self, msg: Vec<u8>) -> bool {
|
|
|
|
match msg[0] {
|
|
|
|
TRANSACTION_MESSAGE => {
|
|
|
|
let Ok(tx) = T::read::<&[u8]>(&mut &msg[1 ..]) else {
|
|
|
|
return false;
|
|
|
|
};
|
|
|
|
|
|
|
|
// TODO: Sync mempools with fellow peers
|
|
|
|
// Can we just rebroadcast transactions not included for at least two blocks?
|
2023-04-14 00:35:55 +00:00
|
|
|
self.network.blockchain.write().unwrap().add_transaction(false, tx)
|
2023-04-13 22:43:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
TENDERMINT_MESSAGE => {
|
2023-04-14 18:11:19 +00:00
|
|
|
let Ok(msg) = SignedMessageFor::<TendermintNetwork<D, T, P>>::decode::<&[u8]>(
|
|
|
|
&mut &msg[1 ..]
|
|
|
|
) else {
|
2023-04-13 22:43:03 +00:00
|
|
|
return false;
|
|
|
|
};
|
|
|
|
|
|
|
|
// If this message isn't to form consensus on the next block, ignore it
|
2023-04-14 18:11:19 +00:00
|
|
|
if msg.block().0 != (self.network.blockchain.read().unwrap().block_number() + 1).into() {
|
2023-04-13 22:43:03 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if !msg.verify_signature(&self.network.validators) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
self.messages.send(msg).await.unwrap();
|
|
|
|
true
|
|
|
|
}
|
|
|
|
|
|
|
|
_ => false,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|