some verifications
This commit is contained in:
parent
a77c986e11
commit
1a553e2caf
|
@ -250,6 +250,12 @@ impl Transaction {
|
|||
if self.inputs.len() != 1 { return false; }
|
||||
self.inputs[0].previous_output.hash.is_zero() && self.inputs[0].previous_output.index == 0xffffffff
|
||||
}
|
||||
|
||||
pub fn total_spends(&self) -> u64 {
|
||||
self.outputs
|
||||
.iter()
|
||||
.fold(0u64, |acc, out| acc + out.value)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
|
@ -147,8 +147,9 @@ impl Store for TestStorage {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn transaction_meta(&self, _hash: &H256) -> Option<TransactionMeta> {
|
||||
unimplemented!();
|
||||
// just spawns new meta so far, use real store for proper tests
|
||||
fn transaction_meta(&self, hash: &H256) -> Option<TransactionMeta> {
|
||||
self.transaction(hash).map(|tx| TransactionMeta::new(0, tx.outputs.len()))
|
||||
}
|
||||
|
||||
// supports only main chain in test storage
|
||||
|
|
|
@ -7,7 +7,8 @@ use byteorder::{LittleEndian, ByteOrder};
|
|||
#[derive(Debug)]
|
||||
pub struct TransactionMeta {
|
||||
block_height: u32,
|
||||
spent: BitVec,
|
||||
// first bit is coinbase flag, others - one per output listed
|
||||
bits: BitVec,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -20,25 +21,34 @@ impl TransactionMeta {
|
|||
pub fn new(block_height: u32, outputs: usize) -> Self {
|
||||
TransactionMeta {
|
||||
block_height: block_height,
|
||||
spent: BitVec::from_elem(outputs, false),
|
||||
bits: BitVec::from_elem(outputs + 1, false),
|
||||
}
|
||||
}
|
||||
|
||||
/// note that particular output has been used
|
||||
pub fn note_used(&mut self, index: usize) {
|
||||
self.spent.set(index, true);
|
||||
self.bits.set(index + 1 , true);
|
||||
}
|
||||
|
||||
pub fn coinbase(mut self) -> Self {
|
||||
self.bits.set(0, true);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn is_coinbase(&self) -> bool {
|
||||
self.bits.get(0)
|
||||
.expect("One bit should always exists, since it is created as usize + 1; minimum value of usize is 0; 0 + 1 = 1; qed")
|
||||
}
|
||||
|
||||
/// note that particular output has been used
|
||||
pub fn denote_used(&mut self, index: usize) {
|
||||
self.spent.set(index, false);
|
||||
self.bits.set(index + 1, false);
|
||||
}
|
||||
|
||||
pub fn into_bytes(self) -> Vec<u8> {
|
||||
let mut result = vec![0u8; 4];
|
||||
LittleEndian::write_u32(&mut result[0..4], self.block_height);
|
||||
result.extend(self.spent.to_bytes());
|
||||
result.extend(self.bits.to_bytes());
|
||||
result
|
||||
}
|
||||
|
||||
|
@ -47,12 +57,12 @@ impl TransactionMeta {
|
|||
|
||||
Ok(TransactionMeta {
|
||||
block_height: LittleEndian::read_u32(&bytes[0..4]),
|
||||
spent: BitVec::from_bytes(&bytes[4..]),
|
||||
bits: BitVec::from_bytes(&bytes[4..]),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn height(&self) -> u32 { self.block_height }
|
||||
|
||||
pub fn is_spent(&self, idx: usize) -> bool { self.spent.get(idx).expect("Index should be verified by the caller") }
|
||||
pub fn is_spent(&self, idx: usize) -> bool { self.bits.get(idx + 1).expect("Index should be verified by the caller") }
|
||||
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ use super::{Verify, VerificationResult, Chain, Error, TransactionError, Continue
|
|||
use utils;
|
||||
|
||||
const BLOCK_MAX_FUTURE: i64 = 2 * 60 * 60; // 2 hours
|
||||
const COINBASE_MATURITY: u32 = 100; // 2 hours
|
||||
|
||||
pub struct ChainVerifier {
|
||||
store: Arc<db::Store>,
|
||||
|
@ -19,6 +20,63 @@ impl ChainVerifier {
|
|||
}
|
||||
|
||||
fn ordered_verify(&self, block: &chain::Block, at_height: u32) -> Result<(), Error> {
|
||||
|
||||
let coinbase_spends = block.transactions()[0].total_spends();
|
||||
|
||||
let mut total_unspent = 0u64;
|
||||
for (tx_index, tx) in block.transactions().iter().skip(1).enumerate() {
|
||||
|
||||
let mut total_claimed: u64 = 0;
|
||||
|
||||
for (_, input) in tx.inputs.iter().enumerate() {
|
||||
|
||||
// Coinbase maturity check
|
||||
let previous_meta = try!(
|
||||
self.store
|
||||
.transaction_meta(&input.previous_output.hash)
|
||||
.ok_or(
|
||||
Error::Transaction(tx_index, TransactionError::UnknownReference(input.previous_output.hash.clone()))
|
||||
)
|
||||
);
|
||||
|
||||
if previous_meta.is_coinbase()
|
||||
&& (at_height < COINBASE_MATURITY ||
|
||||
at_height - COINBASE_MATURITY < previous_meta.height())
|
||||
{
|
||||
return Err(Error::Transaction(tx_index, TransactionError::Maturity));
|
||||
}
|
||||
|
||||
let reference_tx = try!(
|
||||
self.store.transaction(&input.previous_output.hash)
|
||||
.ok_or(
|
||||
Error::Transaction(tx_index, TransactionError::UnknownReference(input.previous_output.hash.clone()))
|
||||
)
|
||||
);
|
||||
|
||||
let output = try!(reference_tx.outputs.get(input.previous_output.index as usize)
|
||||
.ok_or(
|
||||
Error::Transaction(tx_index, TransactionError::Input(input.previous_output.index as usize))
|
||||
)
|
||||
);
|
||||
|
||||
total_claimed += output.value;
|
||||
}
|
||||
|
||||
let total_spends = tx.total_spends();
|
||||
|
||||
if total_claimed < total_spends {
|
||||
return Err(Error::Transaction(tx_index, TransactionError::Overspend));
|
||||
}
|
||||
|
||||
// total_claimed is greater than total_spends, checked above and returned otherwise, cannot overflow; qed
|
||||
total_unspent += total_claimed - total_spends;
|
||||
}
|
||||
|
||||
let expected_max = utils::block_reward_satoshi(at_height) + total_unspent;
|
||||
if coinbase_spends > expected_max{
|
||||
return Err(Error::CoinbaseOverspend { expected_max: expected_max, actual: coinbase_spends });
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
|
@ -43,6 +43,8 @@ pub enum Error {
|
|||
Difficulty,
|
||||
/// Invalid merkle root
|
||||
MerkleRoot,
|
||||
/// Coinbase spends too much
|
||||
CoinbaseOverspend { expected_max: u64, actual: u64 },
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
|
@ -56,6 +58,10 @@ pub enum TransactionError {
|
|||
Signature(usize),
|
||||
/// Inconclusive (unknown parent transaction)
|
||||
Inconclusive(H256),
|
||||
/// Unknown previous transaction referenced
|
||||
UnknownReference(H256),
|
||||
/// Spends more than claims
|
||||
Overspend,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Debug)]
|
||||
|
|
Loading…
Reference in New Issue