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};
|
use hashes::{hash160, sha256, Hash};
|
||||||
#[cfg(feature="bitcoinconsensus")] use bitcoinconsensus;
|
#[cfg(feature="bitcoinconsensus")] use bitcoinconsensus;
|
||||||
#[cfg(feature="bitcoinconsensus")] use std::convert;
|
#[cfg(feature="bitcoinconsensus")] use std::convert;
|
||||||
#[cfg(feature="bitcoinconsensus")] use hashes::sha256d;
|
#[cfg(feature="bitcoinconsensus")] use OutPoint;
|
||||||
|
|
||||||
use util::key::PublicKey;
|
use util::key::PublicKey;
|
||||||
|
|
||||||
|
@ -95,11 +95,8 @@ pub enum Error {
|
||||||
/// Error validating the script with bitcoinconsensus library
|
/// Error validating the script with bitcoinconsensus library
|
||||||
BitcoinConsensus(bitcoinconsensus::Error),
|
BitcoinConsensus(bitcoinconsensus::Error),
|
||||||
#[cfg(feature="bitcoinconsensus")]
|
#[cfg(feature="bitcoinconsensus")]
|
||||||
/// Can not find the spent transaction
|
/// Can not find the spent output
|
||||||
UnknownSpentTransaction(sha256d::Hash),
|
UnknownSpentOutput(OutPoint),
|
||||||
#[cfg(feature="bitcoinconsensus")]
|
|
||||||
/// The spent transaction does not have the referred output
|
|
||||||
WrongSpentOutputIndex(usize),
|
|
||||||
#[cfg(feature="bitcoinconsensus")]
|
#[cfg(feature="bitcoinconsensus")]
|
||||||
/// Can not serialize the spending transaction
|
/// Can not serialize the spending transaction
|
||||||
SerializationError
|
SerializationError
|
||||||
|
@ -122,9 +119,7 @@ impl error::Error for Error {
|
||||||
#[cfg(feature="bitcoinconsensus")]
|
#[cfg(feature="bitcoinconsensus")]
|
||||||
Error::BitcoinConsensus(ref _n) => "bitcoinconsensus verification failed",
|
Error::BitcoinConsensus(ref _n) => "bitcoinconsensus verification failed",
|
||||||
#[cfg(feature="bitcoinconsensus")]
|
#[cfg(feature="bitcoinconsensus")]
|
||||||
Error::UnknownSpentTransaction (ref _hash) => "unknown transaction referred in Transaction::verify()",
|
Error::UnknownSpentOutput(ref _point) => "unknown spent output Transaction::verify()",
|
||||||
#[cfg(feature="bitcoinconsensus")]
|
|
||||||
Error::WrongSpentOutputIndex(ref _ix) => "unknown output index {} referred in Transaction::verify()",
|
|
||||||
#[cfg(feature="bitcoinconsensus")]
|
#[cfg(feature="bitcoinconsensus")]
|
||||||
Error::SerializationError => "can not serialize the spending transaction in Transaction::verify()",
|
Error::SerializationError => "can not serialize the spending transaction in Transaction::verify()",
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,7 +26,6 @@
|
||||||
use byteorder::{LittleEndian, WriteBytesExt};
|
use byteorder::{LittleEndian, WriteBytesExt};
|
||||||
use std::default::Default;
|
use std::default::Default;
|
||||||
use std::{fmt, io};
|
use std::{fmt, io};
|
||||||
#[cfg(feature="bitcoinconsensus")] use std::collections::HashMap;
|
|
||||||
|
|
||||||
use hashes::{self, sha256d, Hash};
|
use hashes::{self, sha256d, Hash};
|
||||||
use hashes::hex::FromHex;
|
use hashes::hex::FromHex;
|
||||||
|
@ -406,18 +405,16 @@ impl Transaction {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature="bitcoinconsensus")]
|
#[cfg(feature="bitcoinconsensus")]
|
||||||
/// Verify that this transaction is able to spend some outputs of spent transactions
|
/// Verify that this transaction is able to spend its inputs
|
||||||
pub fn verify(&self, spent: &HashMap<sha256d::Hash, Transaction>) -> Result<(), script::Error> {
|
/// 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);
|
let tx = serialize(&*self);
|
||||||
for (idx, input) in self.input.iter().enumerate() {
|
for (idx, input) in self.input.iter().enumerate() {
|
||||||
if let Some(ref s) = spent.get(&input.previous_output.txid) {
|
if let Some(output) = spent(&input.previous_output) {
|
||||||
if let Some(ref output) = s.output.get(input.previous_output.vout as usize) {
|
|
||||||
output.script_pubkey.verify(idx, output.value, tx.as_slice())?;
|
output.script_pubkey.verify(idx, output.value, tx.as_slice())?;
|
||||||
} else {
|
} else {
|
||||||
return Err(script::Error::WrongSpentOutputIndex(input.previous_output.vout as usize));
|
return Err(script::Error::UnknownSpentOutput(input.previous_output.clone()));
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return Err(script::Error::UnknownSpentTransaction(input.previous_output.txid));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -1130,12 +1127,36 @@ mod tests {
|
||||||
spent.insert(spent1.txid(), spent1);
|
spent.insert(spent1.txid(), spent1);
|
||||||
spent.insert(spent2.txid(), spent2);
|
spent.insert(spent2.txid(), spent2);
|
||||||
spent.insert(spent3.txid(), spent3);
|
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
|
// test that we get a failure if we corrupt a signature
|
||||||
spending.input[1].witness[0][10] = 42;
|
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(_) => {},
|
script::Error::BitcoinConsensus(_) => {},
|
||||||
_ => panic!("Wrong error type"),
|
_ => panic!("Wrong error type"),
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue