unit tests for coinbase flags (output and tx kernel) ()

* add tests around flipping COINBASE_OUTPUT and COINBASE_KERNEL flags
* add serialize_deserialize test for a minimal block
This commit is contained in:
AntiochP 2017-08-22 14:26:13 -04:00 committed by Ignotus Peverell
parent 045f5bb4da
commit babb6110c4
2 changed files with 97 additions and 10 deletions

View file

@ -38,6 +38,7 @@ bitflags! {
}
/// Block header, fairly standard compared to other blockchains.
#[derive(Debug, PartialEq)]
pub struct BlockHeader {
/// Height of this block since the genesis block (height 0)
pub height: u64,
@ -136,6 +137,7 @@ impl Readable for BlockHeader {
/// non-explicit, assumed to be deducible from block height (similar to
/// bitcoin's schedule) and expressed as a global transaction fee (added v.H),
/// additive to the total of fees ever collected.
#[derive(Debug)]
pub struct Block {
/// The header with metadata and commitments to the rest of the data
pub header: BlockHeader,
@ -381,16 +383,20 @@ impl Block {
pub fn validate(&self, secp: &Secp256k1) -> Result<(), secp::Error> {
self.verify_coinbase(secp)?;
self.verify_kernels(secp)?;
// verify the transaction Merkle root
let tx_merkle = merkle_inputs_outputs(&self.inputs, &self.outputs);
if tx_merkle != self.header.tx_merkle {
// TODO more specific error
return Err(secp::Error::IncorrectCommitSum);
}
Ok(())
self.verify_merkle_inputs_outputs()?;
Ok(())
}
/// Verify the transaction Merkle root
pub fn verify_merkle_inputs_outputs(&self) -> Result<(), secp::Error> {
let tx_merkle = merkle_inputs_outputs(&self.inputs, &self.outputs);
if tx_merkle != self.header.tx_merkle {
// TODO more specific error
return Err(secp::Error::IncorrectCommitSum);
}
Ok(())
}
/// Validate the sum of input/output commitments match the sum in kernels
/// and
/// that all kernel signatures are valid.
@ -551,4 +557,85 @@ mod test {
assert_eq!(b3.inputs.len(), 3);
assert_eq!(b3.outputs.len(), 4);
}
#[test]
fn empty_block_with_coinbase_is_valid() {
let ref secp = new_secp();
let b = new_block(vec![], secp);
assert_eq!(b.inputs.len(), 0);
assert_eq!(b.outputs.len(), 1);
assert_eq!(b.kernels.len(), 1);
let coinbase_outputs = b.outputs
.iter()
.filter(|out| out.features.contains(COINBASE_OUTPUT))
.map(|o| o.clone())
.collect::<Vec<_>>();
assert_eq!(coinbase_outputs.len(), 1);
let coinbase_kernels = b.kernels
.iter()
.filter(|out| out.features.contains(COINBASE_KERNEL))
.map(|o| o.clone())
.collect::<Vec<_>>();
assert_eq!(coinbase_kernels.len(), 1);
// the block should be valid here (single coinbase output with corresponding txn kernel)
assert_eq!(b.validate(&secp), Ok(()));
}
#[test]
// test that flipping the COINBASE_OUTPUT flag on the output features
// invalidates the block and specifically it causes verify_coinbase to fail
// additionally verifying the merkle_inputs_outputs also fails
fn remove_coinbase_output_flag() {
let ref secp = new_secp();
let mut b = new_block(vec![], secp);
assert!(b.outputs[0].features.contains(COINBASE_OUTPUT));
b.outputs[0].features.remove(COINBASE_OUTPUT);
assert_eq!(b.verify_coinbase(&secp), Err(secp::Error::IncorrectCommitSum));
assert_eq!(b.verify_kernels(&secp), Ok(()));
assert_eq!(b.verify_merkle_inputs_outputs(), Err(secp::Error::IncorrectCommitSum));
assert_eq!(b.validate(&secp), Err(secp::Error::IncorrectCommitSum));
}
#[test]
// test that flipping the COINBASE_KERNEL flag on the kernel features
// invalidates the block and specifically it causes verify_coinbase to fail
fn remove_coinbase_kernel_flag() {
let ref secp = new_secp();
let mut b = new_block(vec![], secp);
assert!(b.kernels[0].features.contains(COINBASE_KERNEL));
b.kernels[0].features.remove(COINBASE_KERNEL);
assert_eq!(b.verify_coinbase(&secp), Err(secp::Error::IncorrectCommitSum));
assert_eq!(b.verify_kernels(&secp), Ok(()));
assert_eq!(b.verify_merkle_inputs_outputs(), Ok(()));
assert_eq!(b.validate(&secp), Err(secp::Error::IncorrectCommitSum));
}
#[test]
fn serialize_deserialize_block() {
let ref secp = new_secp();
let b = new_block(vec![], secp);
let mut vec = Vec::new();
ser::serialize(&mut vec, &b).expect("serialization failed");
let b2: Block = ser::deserialize(&mut &vec[..]).unwrap();
assert_eq!(b.inputs, b2.inputs);
assert_eq!(b.outputs, b2.outputs);
assert_eq!(b.kernels, b2.kernels);
// TODO - timestamps are not coming back equal here (UTC related?) -
// assert_eq!(b.header, b2.header);
// timestamp: Tm { tm_sec: 51, tm_min: 7, tm_hour: 23, tm_mday: 20, tm_mon: 7, tm_year: 117, tm_wday: 0, tm_yday: 231, tm_isdst: 1, tm_utcoff: -14400, tm_nsec: 780878000 },
// timestamp: Tm { tm_sec: 51, tm_min: 7, tm_hour: 3, tm_mday: 21, tm_mon: 7, tm_year: 117, tm_wday: 1, tm_yday: 232, tm_isdst: 0, tm_utcoff: 0, tm_nsec: 0 },
}
}

View file

@ -37,7 +37,7 @@ bitflags! {
/// Pedersen commitment and the signature, that guarantees that the commitments
/// amount to zero. The signature signs the fee, which is retained for
/// signature validation.
#[derive(Debug, Clone)]
#[derive(Debug, Clone, PartialEq)]
pub struct TxKernel {
/// Options for a kernel's structure or use
pub features: KernelFeatures,
@ -239,7 +239,7 @@ impl Transaction {
/// A transaction input, mostly a reference to an output being spent by the
/// transaction.
#[derive(Debug, Copy, Clone)]
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct Input(pub Commitment);
/// Implementation of Writeable for a transaction Input, defines how to write