Files
serai/coordinator/tributary/src/blockchain.rs

119 lines
3.2 KiB
Rust
Raw Normal View History

use std::collections::HashMap;
2023-04-12 11:13:48 -04:00
use ciphersuite::{Ciphersuite, Ristretto};
use crate::{
Signed, TransactionKind, Transaction, verify_transaction, ProvidedTransactions, BlockError,
Block, Mempool,
};
2023-04-12 11:13:48 -04:00
#[derive(Clone, PartialEq, Eq, Debug)]
pub(crate) struct Blockchain<T: Transaction> {
2023-04-12 11:13:48 -04:00
genesis: [u8; 32],
// TODO: db
block_number: u64,
2023-04-12 11:13:48 -04:00
tip: [u8; 32],
next_nonces: HashMap<<Ristretto as Ciphersuite>::G, u32>,
2023-04-13 09:47:14 -04:00
provided: ProvidedTransactions<T>,
mempool: Mempool<T>,
2023-04-12 11:13:48 -04:00
}
impl<T: Transaction> Blockchain<T> {
pub(crate) fn new(genesis: [u8; 32], participants: &[<Ristretto as Ciphersuite>::G]) -> Self {
// TODO: Reload block_number/tip/next_nonces/provided/mempool
let mut next_nonces = HashMap::new();
for participant in participants {
next_nonces.insert(*participant, 0);
}
2023-04-13 09:47:14 -04:00
Self {
genesis,
block_number: 0,
2023-04-13 09:47:14 -04:00
tip: genesis,
next_nonces,
provided: ProvidedTransactions::new(),
mempool: Mempool::new(genesis),
}
2023-04-12 11:13:48 -04:00
}
pub(crate) fn tip(&self) -> [u8; 32] {
2023-04-12 11:13:48 -04:00
self.tip
}
pub(crate) fn block_number(&self) -> u64 {
self.block_number
}
pub(crate) fn add_transaction(&mut self, internal: bool, tx: T) -> bool {
self.mempool.add(&self.next_nonces, internal, tx)
2023-04-13 09:47:14 -04:00
}
pub(crate) fn provide_transaction(&mut self, tx: T) -> bool {
// TODO: Should this check be internal to ProvidedTransactions?
if verify_transaction(&tx, self.genesis, &mut HashMap::new()).is_err() {
return false;
}
self.provided.provide(tx);
true
2023-04-12 11:13:48 -04:00
}
/// Returns the next nonce for signing, or None if they aren't a participant.
pub(crate) fn next_nonce(&self, key: <Ristretto as Ciphersuite>::G) -> Option<u32> {
Some(self.next_nonces.get(&key).cloned()?.max(self.mempool.next_nonce(&key).unwrap_or(0)))
2023-04-12 11:13:48 -04:00
}
pub(crate) fn build_block(&mut self) -> Block<T> {
let block = Block::new(
self.tip,
self.provided.transactions.iter().cloned().collect(),
self.mempool.block(&self.next_nonces),
);
2023-04-12 11:13:48 -04:00
// build_block should not return invalid blocks
self.verify_block(&block).unwrap();
block
}
pub(crate) fn verify_block(&self, block: &Block<T>) -> Result<(), BlockError> {
block.verify(
self.genesis,
self.tip,
&self.provided.transactions.iter().map(Transaction::hash).collect::<Vec<_>>(),
self.next_nonces.clone(),
)
2023-04-12 11:13:48 -04:00
}
/// Add a block.
pub(crate) fn add_block(&mut self, block: &Block<T>) -> Result<(), BlockError> {
self.verify_block(block)?;
// None of the following assertions should be reachable since we verified the block
2023-04-12 11:13:48 -04:00
self.tip = block.hash();
self.block_number += 1;
2023-04-12 11:13:48 -04:00
for tx in &block.transactions {
match tx.kind() {
TransactionKind::Provided => {
self.provided.complete(tx.hash());
2023-04-12 11:13:48 -04:00
}
TransactionKind::Unsigned => {}
TransactionKind::Signed(Signed { signer, nonce, .. }) => {
let prev = self
.next_nonces
.insert(*signer, nonce + 1)
.expect("block had signed transaction from non-participant");
if prev != *nonce {
panic!("verified block had an invalid nonce");
2023-04-12 11:13:48 -04:00
}
self.mempool.remove(&tx.hash());
2023-04-12 11:13:48 -04:00
}
}
}
Ok(())
2023-04-12 11:13:48 -04:00
}
}