2023-04-12 15:13:48 +00:00
|
|
|
use std::collections::{HashSet, HashMap};
|
|
|
|
|
|
|
|
use ciphersuite::{Ciphersuite, Ristretto};
|
|
|
|
|
|
|
|
use crate::{Signed, TransactionKind, Transaction, ProvidedTransactions, BlockError, Block};
|
|
|
|
|
|
|
|
#[derive(Clone, PartialEq, Eq, Debug)]
|
|
|
|
pub struct Blockchain<T: Transaction> {
|
|
|
|
genesis: [u8; 32],
|
|
|
|
// TODO: db
|
|
|
|
tip: [u8; 32],
|
|
|
|
provided: ProvidedTransactions<T>,
|
|
|
|
// TODO: Mempool
|
2023-04-12 16:42:23 +00:00
|
|
|
next_nonces: HashMap<<Ristretto as Ciphersuite>::G, u32>,
|
2023-04-12 15:13:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl<T: Transaction> Blockchain<T> {
|
2023-04-12 16:42:23 +00:00
|
|
|
pub fn new(genesis: [u8; 32], participants: &[<Ristretto as Ciphersuite>::G]) -> Self {
|
2023-04-12 15:13:48 +00:00
|
|
|
// TODO: Reload provided/nonces
|
2023-04-12 16:42:23 +00:00
|
|
|
|
|
|
|
let mut next_nonces = HashMap::new();
|
|
|
|
for participant in participants {
|
|
|
|
next_nonces.insert(*participant, 0);
|
|
|
|
}
|
|
|
|
Self { genesis, tip: genesis, provided: ProvidedTransactions::new(), next_nonces }
|
2023-04-12 15:13:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn tip(&self) -> [u8; 32] {
|
|
|
|
self.tip
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn provide_transaction(&mut self, tx: T) {
|
|
|
|
self.provided.provide(tx)
|
|
|
|
}
|
|
|
|
|
2023-04-12 16:42:23 +00:00
|
|
|
/// Returns the next nonce, or None if they aren't a participant.
|
|
|
|
pub fn next_nonce(&self, key: <Ristretto as Ciphersuite>::G) -> Option<u32> {
|
|
|
|
self.next_nonces.get(&key).cloned()
|
2023-04-12 15:13:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: Embed mempool
|
|
|
|
pub fn build_block(&self, txs: HashMap<[u8; 32], T>) -> Block<T> {
|
|
|
|
let block = Block::new(self.tip, &self.provided, txs);
|
|
|
|
// build_block should not return invalid blocks
|
|
|
|
self.verify_block(&block).unwrap();
|
|
|
|
block
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn verify_block(&self, block: &Block<T>) -> Result<(), BlockError> {
|
|
|
|
let mut locally_provided = HashSet::new();
|
|
|
|
for provided in self.provided.transactions.keys() {
|
|
|
|
locally_provided.insert(*provided);
|
|
|
|
}
|
2023-04-12 16:42:23 +00:00
|
|
|
block.verify(self.genesis, self.tip, locally_provided, self.next_nonces.clone())
|
2023-04-12 15:13:48 +00:00
|
|
|
}
|
|
|
|
|
2023-04-12 20:18:42 +00:00
|
|
|
/// Add a block.
|
|
|
|
#[must_use]
|
|
|
|
pub fn add_block(&mut self, block: &Block<T>) -> bool {
|
|
|
|
// TODO: Handle desyncs re: provided transactions properly
|
|
|
|
if self.verify_block(block).is_err() {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// None of the following assertions should be reachable since we verified the block
|
2023-04-12 15:13:48 +00:00
|
|
|
self.tip = block.hash();
|
|
|
|
for tx in &block.transactions {
|
|
|
|
match tx.kind() {
|
|
|
|
TransactionKind::Provided => {
|
2023-04-12 20:18:42 +00:00
|
|
|
assert!(
|
|
|
|
self.provided.withdraw(tx.hash()),
|
|
|
|
"verified block had a provided transaction we didn't have"
|
|
|
|
);
|
2023-04-12 15:13:48 +00:00
|
|
|
}
|
|
|
|
TransactionKind::Unsigned => {}
|
|
|
|
TransactionKind::Signed(Signed { signer, nonce, .. }) => {
|
2023-04-12 16:42:23 +00:00
|
|
|
let prev = self
|
|
|
|
.next_nonces
|
|
|
|
.insert(*signer, nonce + 1)
|
|
|
|
.expect("block had signed transaction from non-participant");
|
|
|
|
if prev != *nonce {
|
2023-04-12 20:18:42 +00:00
|
|
|
panic!("verified block had an invalid nonce");
|
2023-04-12 15:13:48 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2023-04-12 20:18:42 +00:00
|
|
|
|
|
|
|
true
|
2023-04-12 15:13:48 +00:00
|
|
|
}
|
|
|
|
}
|