2023-04-12 11:13:48 -04:00
|
|
|
use zeroize::Zeroizing;
|
2023-04-13 18:43:03 -04:00
|
|
|
use rand::{RngCore, rngs::OsRng};
|
2023-04-12 11:13:48 -04:00
|
|
|
|
|
|
|
|
use blake2::{Digest, Blake2s256};
|
|
|
|
|
|
|
|
|
|
use ciphersuite::{group::ff::Field, Ciphersuite, Ristretto};
|
|
|
|
|
|
|
|
|
|
use crate::{
|
2023-04-13 20:35:55 -04:00
|
|
|
merkle, Transaction, ProvidedTransactions, Block, Blockchain,
|
2023-04-12 11:13:48 -04:00
|
|
|
tests::{ProvidedTransaction, SignedTransaction, random_provided_transaction},
|
|
|
|
|
};
|
|
|
|
|
|
2023-04-12 12:42:23 -04:00
|
|
|
fn new_genesis() -> [u8; 32] {
|
2023-04-12 11:13:48 -04:00
|
|
|
let mut genesis = [0; 32];
|
|
|
|
|
OsRng.fill_bytes(&mut genesis);
|
2023-04-12 12:42:23 -04:00
|
|
|
genesis
|
|
|
|
|
}
|
2023-04-12 11:13:48 -04:00
|
|
|
|
2023-04-12 12:42:23 -04:00
|
|
|
fn new_blockchain<T: Transaction>(
|
|
|
|
|
genesis: [u8; 32],
|
|
|
|
|
participants: &[<Ristretto as Ciphersuite>::G],
|
|
|
|
|
) -> Blockchain<T> {
|
|
|
|
|
let blockchain = Blockchain::new(genesis, participants);
|
2023-04-12 11:13:48 -04:00
|
|
|
assert_eq!(blockchain.tip(), genesis);
|
2023-04-13 18:43:03 -04:00
|
|
|
assert_eq!(blockchain.block_number(), 0);
|
2023-04-12 12:42:23 -04:00
|
|
|
blockchain
|
2023-04-12 11:13:48 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn block_addition() {
|
2023-04-12 12:42:23 -04:00
|
|
|
let genesis = new_genesis();
|
|
|
|
|
let mut blockchain = new_blockchain::<SignedTransaction>(genesis, &[]);
|
2023-04-13 09:47:14 -04:00
|
|
|
let block = blockchain.build_block();
|
2023-04-12 11:13:48 -04:00
|
|
|
assert_eq!(block.header.parent, genesis);
|
|
|
|
|
assert_eq!(block.header.transactions, [0; 32]);
|
|
|
|
|
blockchain.verify_block(&block).unwrap();
|
2023-04-12 18:04:28 -04:00
|
|
|
assert!(blockchain.add_block(&block).is_ok());
|
2023-04-12 11:13:48 -04:00
|
|
|
assert_eq!(blockchain.tip(), block.hash());
|
2023-04-13 18:43:03 -04:00
|
|
|
assert_eq!(blockchain.block_number(), 1);
|
2023-04-12 11:13:48 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn invalid_block() {
|
2023-04-12 12:42:23 -04:00
|
|
|
let genesis = new_genesis();
|
2023-04-13 09:47:14 -04:00
|
|
|
let mut blockchain = new_blockchain::<SignedTransaction>(genesis, &[]);
|
2023-04-12 11:13:48 -04:00
|
|
|
|
2023-04-13 09:47:14 -04:00
|
|
|
let block = blockchain.build_block();
|
2023-04-12 11:13:48 -04:00
|
|
|
|
|
|
|
|
// Mutate parent
|
|
|
|
|
{
|
|
|
|
|
#[allow(clippy::redundant_clone)] // False positive
|
|
|
|
|
let mut block = block.clone();
|
|
|
|
|
block.header.parent = Blake2s256::digest(block.header.parent).into();
|
|
|
|
|
assert!(blockchain.verify_block(&block).is_err());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Mutate tranactions merkle
|
|
|
|
|
{
|
|
|
|
|
let mut block = block;
|
|
|
|
|
block.header.transactions = Blake2s256::digest(block.header.transactions).into();
|
|
|
|
|
assert!(blockchain.verify_block(&block).is_err());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let key = Zeroizing::new(<Ristretto as Ciphersuite>::F::random(&mut OsRng));
|
2023-04-12 12:42:23 -04:00
|
|
|
let tx = crate::tests::signed_transaction(&mut OsRng, genesis, &key, 0);
|
|
|
|
|
|
|
|
|
|
// Not a participant
|
|
|
|
|
{
|
|
|
|
|
// Manually create the block to bypass build_block's checks
|
2023-04-13 20:35:55 -04:00
|
|
|
let block = Block::new(blockchain.tip(), vec![], vec![tx.clone()]);
|
2023-04-12 12:42:23 -04:00
|
|
|
assert_eq!(block.header.transactions, merkle(&[tx.hash()]));
|
|
|
|
|
assert!(blockchain.verify_block(&block).is_err());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Run the rest of the tests with them as a participant
|
|
|
|
|
let blockchain = new_blockchain(genesis, &[tx.1.signer]);
|
|
|
|
|
|
|
|
|
|
// Re-run the not a participant block to make sure it now works
|
|
|
|
|
{
|
2023-04-13 20:35:55 -04:00
|
|
|
let block = Block::new(blockchain.tip(), vec![], vec![tx.clone()]);
|
2023-04-12 12:42:23 -04:00
|
|
|
assert_eq!(block.header.transactions, merkle(&[tx.hash()]));
|
|
|
|
|
blockchain.verify_block(&block).unwrap();
|
|
|
|
|
}
|
2023-04-12 11:13:48 -04:00
|
|
|
|
|
|
|
|
{
|
|
|
|
|
// Add a valid transaction
|
2023-04-13 09:47:14 -04:00
|
|
|
let mut blockchain = blockchain.clone();
|
2023-04-13 20:35:55 -04:00
|
|
|
assert!(blockchain.add_transaction(true, tx.clone()));
|
2023-04-13 09:47:14 -04:00
|
|
|
let mut block = blockchain.build_block();
|
2023-04-12 11:13:48 -04:00
|
|
|
assert_eq!(block.header.transactions, merkle(&[tx.hash()]));
|
|
|
|
|
blockchain.verify_block(&block).unwrap();
|
|
|
|
|
|
|
|
|
|
// And verify mutating the transactions merkle now causes a failure
|
|
|
|
|
block.header.transactions = merkle(&[]);
|
|
|
|
|
assert!(blockchain.verify_block(&block).is_err());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
// Invalid nonce
|
|
|
|
|
let tx = crate::tests::signed_transaction(&mut OsRng, genesis, &key, 5);
|
|
|
|
|
// Manually create the block to bypass build_block's checks
|
2023-04-13 20:35:55 -04:00
|
|
|
let block = Block::new(blockchain.tip(), vec![], vec![tx]);
|
2023-04-12 11:13:48 -04:00
|
|
|
assert!(blockchain.verify_block(&block).is_err());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
// Invalid signature
|
2023-04-13 09:47:14 -04:00
|
|
|
let mut blockchain = blockchain;
|
2023-04-13 20:35:55 -04:00
|
|
|
assert!(blockchain.add_transaction(true, tx));
|
2023-04-13 09:47:14 -04:00
|
|
|
let mut block = blockchain.build_block();
|
2023-04-12 11:13:48 -04:00
|
|
|
blockchain.verify_block(&block).unwrap();
|
|
|
|
|
block.transactions[0].1.signature.s += <Ristretto as Ciphersuite>::F::ONE;
|
|
|
|
|
assert!(blockchain.verify_block(&block).is_err());
|
|
|
|
|
|
|
|
|
|
// Make sure this isn't because the merkle changed due to the transaction hash including the
|
|
|
|
|
// signature (which it explicitly isn't allowed to anyways)
|
|
|
|
|
assert_eq!(block.header.transactions, merkle(&[block.transactions[0].hash()]));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn signed_transaction() {
|
2023-04-12 12:42:23 -04:00
|
|
|
let genesis = new_genesis();
|
|
|
|
|
|
2023-04-12 11:13:48 -04:00
|
|
|
let key = Zeroizing::new(<Ristretto as Ciphersuite>::F::random(&mut OsRng));
|
|
|
|
|
let tx = crate::tests::signed_transaction(&mut OsRng, genesis, &key, 0);
|
|
|
|
|
let signer = tx.1.signer;
|
2023-04-12 12:42:23 -04:00
|
|
|
|
|
|
|
|
let mut blockchain = new_blockchain::<SignedTransaction>(genesis, &[signer]);
|
|
|
|
|
assert_eq!(blockchain.next_nonce(signer), Some(0));
|
2023-04-12 11:13:48 -04:00
|
|
|
|
2023-04-13 20:35:55 -04:00
|
|
|
let test = |blockchain: &mut Blockchain<SignedTransaction>, mempool: Vec<SignedTransaction>| {
|
2023-04-12 11:13:48 -04:00
|
|
|
let tip = blockchain.tip();
|
2023-04-13 20:35:55 -04:00
|
|
|
for tx in mempool.clone() {
|
|
|
|
|
let next_nonce = blockchain.next_nonce(signer).unwrap();
|
|
|
|
|
assert!(blockchain.add_transaction(true, tx));
|
|
|
|
|
assert_eq!(next_nonce + 1, blockchain.next_nonce(signer).unwrap());
|
|
|
|
|
}
|
2023-04-13 09:47:14 -04:00
|
|
|
let block = blockchain.build_block();
|
2023-04-13 20:35:55 -04:00
|
|
|
assert_eq!(block, Block::new(blockchain.tip(), vec![], mempool.clone()));
|
2023-04-12 11:13:48 -04:00
|
|
|
assert_eq!(blockchain.tip(), tip);
|
|
|
|
|
assert_eq!(block.header.parent, tip);
|
|
|
|
|
|
|
|
|
|
// Make sure all transactions were included
|
2023-04-13 20:35:55 -04:00
|
|
|
assert_eq!(block.transactions, mempool);
|
2023-04-12 11:13:48 -04:00
|
|
|
// Make sure the merkle was correct
|
2023-04-13 20:35:55 -04:00
|
|
|
assert_eq!(
|
|
|
|
|
block.header.transactions,
|
|
|
|
|
merkle(&mempool.iter().map(Transaction::hash).collect::<Vec<_>>())
|
|
|
|
|
);
|
2023-04-12 11:13:48 -04:00
|
|
|
|
|
|
|
|
// Verify and add the block
|
|
|
|
|
blockchain.verify_block(&block).unwrap();
|
2023-04-12 18:04:28 -04:00
|
|
|
assert!(blockchain.add_block(&block).is_ok());
|
2023-04-12 11:13:48 -04:00
|
|
|
assert_eq!(blockchain.tip(), block.hash());
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Test with a single nonce
|
2023-04-13 20:35:55 -04:00
|
|
|
test(&mut blockchain, vec![tx]);
|
2023-04-12 12:42:23 -04:00
|
|
|
assert_eq!(blockchain.next_nonce(signer), Some(1));
|
2023-04-12 11:13:48 -04:00
|
|
|
|
|
|
|
|
// Test with a flood of nonces
|
2023-04-13 20:35:55 -04:00
|
|
|
let mut mempool = vec![];
|
|
|
|
|
for nonce in 1 .. 64 {
|
|
|
|
|
mempool.push(crate::tests::signed_transaction(&mut OsRng, genesis, &key, nonce));
|
2023-04-12 11:13:48 -04:00
|
|
|
}
|
|
|
|
|
test(&mut blockchain, mempool);
|
2023-04-12 12:42:23 -04:00
|
|
|
assert_eq!(blockchain.next_nonce(signer), Some(64));
|
2023-04-12 11:13:48 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn provided_transaction() {
|
2023-04-12 12:42:23 -04:00
|
|
|
let mut blockchain = new_blockchain::<ProvidedTransaction>(new_genesis(), &[]);
|
2023-04-12 11:13:48 -04:00
|
|
|
|
|
|
|
|
let tx = random_provided_transaction(&mut OsRng);
|
2023-04-13 20:35:55 -04:00
|
|
|
|
|
|
|
|
// This should be provideable
|
2023-04-12 11:13:48 -04:00
|
|
|
let mut txs = ProvidedTransactions::new();
|
|
|
|
|
txs.provide(tx.clone());
|
2023-04-13 20:35:55 -04:00
|
|
|
txs.complete(tx.hash());
|
|
|
|
|
|
2023-04-12 11:13:48 -04:00
|
|
|
// Non-provided transactions should fail verification
|
2023-04-13 20:35:55 -04:00
|
|
|
let block = Block::new(blockchain.tip(), vec![tx.clone()], vec![]);
|
2023-04-12 11:13:48 -04:00
|
|
|
assert!(blockchain.verify_block(&block).is_err());
|
|
|
|
|
|
|
|
|
|
// Provided transactions should pass verification
|
2023-04-13 20:35:55 -04:00
|
|
|
blockchain.provide_transaction(tx.clone());
|
2023-04-12 11:13:48 -04:00
|
|
|
blockchain.verify_block(&block).unwrap();
|
|
|
|
|
|
|
|
|
|
// add_block should work for verified blocks
|
2023-04-12 18:04:28 -04:00
|
|
|
assert!(blockchain.add_block(&block).is_ok());
|
2023-04-12 11:13:48 -04:00
|
|
|
|
2023-04-13 20:35:55 -04:00
|
|
|
let block = Block::new(blockchain.tip(), vec![tx], vec![]);
|
2023-04-12 11:13:48 -04:00
|
|
|
// The provided transaction should no longer considered provided, causing this error
|
|
|
|
|
assert!(blockchain.verify_block(&block).is_err());
|
2023-04-12 16:18:42 -04:00
|
|
|
// add_block should fail for unverified provided transactions if told to add them
|
2023-04-12 18:04:28 -04:00
|
|
|
assert!(blockchain.add_block(&block).is_err());
|
2023-04-12 11:13:48 -04:00
|
|
|
}
|