few functions are more idiomatic, initial support for bip30

This commit is contained in:
debris 2016-11-26 15:05:54 +01:00
parent 7f07b60a85
commit 9d0d251a37
4 changed files with 84 additions and 50 deletions

View File

@ -175,7 +175,7 @@ impl Storage {
if let Some(accepted_tx) = accepted_txs.iter().next() {
context.meta.insert(
accepted_tx.hash(),
TransactionMeta::new(number, accepted_tx.outputs.len()).coinbase()
TransactionMeta::new_coinbase(number, accepted_tx.outputs.len())
);
}
@ -186,29 +186,27 @@ impl Storage {
);
for input in &accepted_tx.inputs {
if !match context.meta.get_mut(&input.previous_output.hash) {
Some(ref mut meta) => {
use std::collections::hash_map::Entry;
match context.meta.entry(input.previous_output.hash.clone()) {
Entry::Occupied(mut entry) => {
let meta = entry.get_mut();
if meta.is_spent(input.previous_output.index as usize) {
return Err(Error::double_spend(&input.previous_output.hash));
}
meta.denote_used(input.previous_output.index as usize);
},
Entry::Vacant(entry) => {
let mut meta = self.transaction_meta(&input.previous_output.hash)
.ok_or(Error::unknown_spending(&input.previous_output.hash))?;
if meta.is_spent(input.previous_output.index as usize) {
return Err(Error::double_spend(&input.previous_output.hash));
}
meta.denote_used(input.previous_output.index as usize);
true
entry.insert(meta);
},
None => false,
} {
let mut meta = self.transaction_meta(&input.previous_output.hash)
.ok_or(Error::unknown_spending(&input.previous_output.hash))?;
if meta.is_spent(input.previous_output.index as usize) {
return Err(Error::double_spend(&input.previous_output.hash));
}
meta.denote_used(input.previous_output.index as usize);
context.meta.insert(
input.previous_output.hash.clone(),
meta);
}
}
}
@ -238,30 +236,24 @@ impl Storage {
// remove meta
context.db_transaction.delete(Some(COL_TRANSACTIONS_META), &**tx_hash);
// denote outputs used
if tx_hash_num == 0 { continue; } // coinbase transaction does not have inputs
// coinbase transaction does not have inputs
if tx_hash_num == 0 {
continue;
}
// denote outputs as unused
for input in &tx.inputs {
if !match context.meta.get_mut(&input.previous_output.hash) {
Some(ref mut meta) => {
meta.denote_unused(input.previous_output.index as usize);
true
use std::collections::hash_map::Entry;
match context.meta.entry(input.previous_output.hash.clone()) {
Entry::Occupied(mut entry) => {
entry.get_mut().denote_unused(input.previous_output.index as usize);
},
Entry::Vacant(entry) => {
let mut meta = self.transaction_meta(&input.previous_output.hash)
.expect("No transaction metadata! Possible db corruption");
meta.denote_unused(input.previous_output.index as usize);
entry.insert(meta);
},
None => false,
} {
let mut meta =
self.transaction_meta(&input.previous_output.hash)
.unwrap_or_else(|| panic!(
// decanonization should always have meta
// because block could not have made canonical without writing meta
"No transaction metadata for {}! Corrupted DB? Reindex?",
&input.previous_output.hash
));
meta.denote_unused(input.previous_output.index as usize);
context.meta.insert(
input.previous_output.hash.clone(),
meta);
}
}
}

View File

@ -7,7 +7,8 @@ use byteorder::{LittleEndian, ByteOrder};
#[derive(Debug, Clone)]
pub struct TransactionMeta {
block_height: u32,
// first bit is coinbase flag, others - one per output listed
/// first bit indicate if transaction is a coinbase transaction
/// next bits indicate if transaction has spend outputs
bits: BitVec,
}
@ -17,7 +18,7 @@ pub enum Error {
}
impl TransactionMeta {
/// new transaction description for indexing
/// New transaction description for indexing
pub fn new(block_height: u32, outputs: usize) -> Self {
TransactionMeta {
block_height: block_height,
@ -25,22 +26,25 @@ impl TransactionMeta {
}
}
pub fn coinbase(mut self) -> Self {
self.bits.set(0, true);
self
/// New coinbase transaction
pub fn new_coinbase(block_height: u32, outputs: usize) -> Self {
let mut result = Self::new(block_height, outputs);
result.bits.set(0, true);
result
}
/// Returns true if it is a coinbase transaction
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")
.expect("One bit should always exists, since it is created as usize + 1; minimum value of usize is 0; 0 + 1 = 1; qed")
}
/// denote particular output as used
/// Denote particular output as used
pub fn denote_used(&mut self, index: usize) {
self.bits.set(index + 1 , true);
}
/// denote particular output as not used
/// Denote particular output as not used
pub fn denote_unused(&mut self, index: usize) {
self.bits.set(index + 1, false);
}
@ -61,8 +65,34 @@ impl TransactionMeta {
})
}
pub fn height(&self) -> u32 { self.block_height }
pub fn height(&self) -> u32 {
self.block_height
}
pub fn is_spent(&self, idx: usize) -> bool { self.bits.get(idx + 1).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")
}
pub fn is_fully_spent(&self) -> bool {
// skip coinbase bit, the rest needs to true
self.bits.iter().skip(1).all(|x| x)
}
}
#[cfg(test)]
mod tests {
use super::TransactionMeta;
#[test]
fn test_is_fully_spent() {
let t = TransactionMeta::new(0, 0);
assert!(t.is_fully_spent());
let mut t = TransactionMeta::new(0, 1);
assert!(!t.is_fully_spent());
t.denote_used(0);
assert!(t.is_fully_spent());
t.denote_unused(0);
assert!(!t.is_fully_spent());
}
}

View File

@ -68,6 +68,16 @@ impl ChainVerifier {
let coinbase_spends = block.transactions()[0].total_spends();
// bip30
// TODO: add exceptions
for (tx_index, tx) in block.transactions.iter().enumerate() {
if let Some(meta) = self.store.transaction_meta(&tx.hash()) {
if !meta.is_fully_spent() {
return Err(Error::Transaction(tx_index, TransactionError::BIP30));
}
}
}
let mut total_unspent = 0u64;
for (tx_index, tx) in block.transactions().iter().enumerate().skip(1) {

View File

@ -84,6 +84,8 @@ pub enum TransactionError {
SigopsP2SH(usize),
/// Coinbase transaction is found at position that is not 0
MisplacedCoinbase(usize),
/// Not fully spent transaction with the same hash already exists.
BIP30,
}
#[derive(PartialEq, Debug)]