use lambda instead of a hash map to find spent outputs (#319)
* use lambda instead of a hash map to find spent outputs * check for double use of an input
This commit is contained in:
parent
8ff904c747
commit
f01568c85a
|
@ -34,7 +34,7 @@ use consensus::{encode, Decodable, Encodable};
|
|||
use hashes::{hash160, sha256, Hash};
|
||||
#[cfg(feature="bitcoinconsensus")] use bitcoinconsensus;
|
||||
#[cfg(feature="bitcoinconsensus")] use std::convert;
|
||||
#[cfg(feature="bitcoinconsensus")] use hashes::sha256d;
|
||||
#[cfg(feature="bitcoinconsensus")] use OutPoint;
|
||||
|
||||
use util::key::PublicKey;
|
||||
|
||||
|
@ -95,11 +95,8 @@ pub enum Error {
|
|||
/// Error validating the script with bitcoinconsensus library
|
||||
BitcoinConsensus(bitcoinconsensus::Error),
|
||||
#[cfg(feature="bitcoinconsensus")]
|
||||
/// Can not find the spent transaction
|
||||
UnknownSpentTransaction(sha256d::Hash),
|
||||
#[cfg(feature="bitcoinconsensus")]
|
||||
/// The spent transaction does not have the referred output
|
||||
WrongSpentOutputIndex(usize),
|
||||
/// Can not find the spent output
|
||||
UnknownSpentOutput(OutPoint),
|
||||
#[cfg(feature="bitcoinconsensus")]
|
||||
/// Can not serialize the spending transaction
|
||||
SerializationError
|
||||
|
@ -122,9 +119,7 @@ impl error::Error for Error {
|
|||
#[cfg(feature="bitcoinconsensus")]
|
||||
Error::BitcoinConsensus(ref _n) => "bitcoinconsensus verification failed",
|
||||
#[cfg(feature="bitcoinconsensus")]
|
||||
Error::UnknownSpentTransaction (ref _hash) => "unknown transaction referred in Transaction::verify()",
|
||||
#[cfg(feature="bitcoinconsensus")]
|
||||
Error::WrongSpentOutputIndex(ref _ix) => "unknown output index {} referred in Transaction::verify()",
|
||||
Error::UnknownSpentOutput(ref _point) => "unknown spent output Transaction::verify()",
|
||||
#[cfg(feature="bitcoinconsensus")]
|
||||
Error::SerializationError => "can not serialize the spending transaction in Transaction::verify()",
|
||||
}
|
||||
|
|
|
@ -26,7 +26,6 @@
|
|||
use byteorder::{LittleEndian, WriteBytesExt};
|
||||
use std::default::Default;
|
||||
use std::{fmt, io};
|
||||
#[cfg(feature="bitcoinconsensus")] use std::collections::HashMap;
|
||||
|
||||
use hashes::{self, sha256d, Hash};
|
||||
use hashes::hex::FromHex;
|
||||
|
@ -406,18 +405,16 @@ impl Transaction {
|
|||
}
|
||||
|
||||
#[cfg(feature="bitcoinconsensus")]
|
||||
/// Verify that this transaction is able to spend some outputs of spent transactions
|
||||
pub fn verify(&self, spent: &HashMap<sha256d::Hash, Transaction>) -> Result<(), script::Error> {
|
||||
/// Verify that this transaction is able to spend its inputs
|
||||
/// The lambda spent should not return the same TxOut twice!
|
||||
pub fn verify<S>(&self, mut spent: S) -> Result<(), script::Error>
|
||||
where S: FnMut(&OutPoint) -> Option<TxOut> {
|
||||
let tx = serialize(&*self);
|
||||
for (idx, input) in self.input.iter().enumerate() {
|
||||
if let Some(ref s) = spent.get(&input.previous_output.txid) {
|
||||
if let Some(ref output) = s.output.get(input.previous_output.vout as usize) {
|
||||
output.script_pubkey.verify(idx, output.value, tx.as_slice())?;
|
||||
} else {
|
||||
return Err(script::Error::WrongSpentOutputIndex(input.previous_output.vout as usize));
|
||||
}
|
||||
if let Some(output) = spent(&input.previous_output) {
|
||||
output.script_pubkey.verify(idx, output.value, tx.as_slice())?;
|
||||
} else {
|
||||
return Err(script::Error::UnknownSpentTransaction(input.previous_output.txid));
|
||||
return Err(script::Error::UnknownSpentOutput(input.previous_output.clone()));
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
|
@ -1130,12 +1127,36 @@ mod tests {
|
|||
spent.insert(spent1.txid(), spent1);
|
||||
spent.insert(spent2.txid(), spent2);
|
||||
spent.insert(spent3.txid(), spent3);
|
||||
let mut spent2 = spent.clone();
|
||||
let mut spent3 = spent.clone();
|
||||
|
||||
spending.verify(&spent).unwrap();
|
||||
spending.verify(|point: &OutPoint| {
|
||||
if let Some(tx) = spent.remove(&point.txid) {
|
||||
return tx.output.get(point.vout as usize).cloned();
|
||||
}
|
||||
None
|
||||
}).unwrap();
|
||||
|
||||
// test that we fail with repeated use of same input
|
||||
let mut double_spending = spending.clone();
|
||||
let re_use = double_spending.input[0].clone();
|
||||
double_spending.input.push (re_use);
|
||||
|
||||
assert!(double_spending.verify(|point: &OutPoint| {
|
||||
if let Some(tx) = spent2.remove(&point.txid) {
|
||||
return tx.output.get(point.vout as usize).cloned();
|
||||
}
|
||||
None
|
||||
}).is_err());
|
||||
|
||||
// test that we get a failure if we corrupt a signature
|
||||
spending.input[1].witness[0][10] = 42;
|
||||
match spending.verify(&spent).err().unwrap() {
|
||||
match spending.verify(|point: &OutPoint| {
|
||||
if let Some(tx) = spent3.remove(&point.txid) {
|
||||
return tx.output.get(point.vout as usize).cloned();
|
||||
}
|
||||
None
|
||||
}).err().unwrap() {
|
||||
script::Error::BitcoinConsensus(_) => {},
|
||||
_ => panic!("Wrong error type"),
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue