Merge pull request #1912 from tcharding/06-20-consensus-validation
Separate out bitcoinconsensus validation code
This commit is contained in:
commit
56343bd7b5
|
@ -41,6 +41,7 @@ secp256k1 = { version = "0.27.0", default-features = false, features = ["bitcoin
|
||||||
hex_lit = "0.1.1"
|
hex_lit = "0.1.1"
|
||||||
|
|
||||||
base64 = { version = "0.13.0", optional = true }
|
base64 = { version = "0.13.0", optional = true }
|
||||||
|
# Only use this feature for no-std builds, otherwise use bitcoinconsensus-std.
|
||||||
bitcoinconsensus = { version = "0.20.2-0.5.0", default-features = false, optional = true }
|
bitcoinconsensus = { version = "0.20.2-0.5.0", default-features = false, optional = true }
|
||||||
core2 = { version = "0.3.2", default-features = false, features = ["alloc"], optional = true }
|
core2 = { version = "0.3.2", default-features = false, features = ["alloc"], optional = true }
|
||||||
# Do NOT use this as a feature! Use the `serde` feature instead.
|
# Do NOT use this as a feature! Use the `serde` feature instead.
|
||||||
|
|
|
@ -12,8 +12,6 @@ use secp256k1::{Secp256k1, Verification};
|
||||||
use crate::blockdata::opcodes::all::*;
|
use crate::blockdata::opcodes::all::*;
|
||||||
use crate::blockdata::opcodes::{self};
|
use crate::blockdata::opcodes::{self};
|
||||||
use crate::blockdata::script::witness_version::WitnessVersion;
|
use crate::blockdata::script::witness_version::WitnessVersion;
|
||||||
#[cfg(feature = "bitcoinconsensus")]
|
|
||||||
use crate::blockdata::script::Error;
|
|
||||||
use crate::blockdata::script::{
|
use crate::blockdata::script::{
|
||||||
bytes_to_asm_fmt, Builder, Instruction, InstructionIndices, Instructions, ScriptBuf,
|
bytes_to_asm_fmt, Builder, Instruction, InstructionIndices, Instructions, ScriptBuf,
|
||||||
};
|
};
|
||||||
|
@ -437,46 +435,6 @@ impl Script {
|
||||||
InstructionIndices::from_instructions(self.instructions_minimal())
|
InstructionIndices::from_instructions(self.instructions_minimal())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Shorthand for [`Self::verify_with_flags`] with flag [bitcoinconsensus::VERIFY_ALL].
|
|
||||||
///
|
|
||||||
/// # Parameters
|
|
||||||
/// * `index` - The input index in spending which is spending this transaction.
|
|
||||||
/// * `amount` - The amount this script guards.
|
|
||||||
/// * `spending_tx` - The transaction that attempts to spend the output holding this script.
|
|
||||||
#[cfg(feature = "bitcoinconsensus")]
|
|
||||||
pub fn verify(
|
|
||||||
&self,
|
|
||||||
index: usize,
|
|
||||||
amount: crate::Amount,
|
|
||||||
spending_tx: &[u8],
|
|
||||||
) -> Result<(), Error> {
|
|
||||||
self.verify_with_flags(index, amount, spending_tx, bitcoinconsensus::VERIFY_ALL)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Verifies spend of an input script.
|
|
||||||
///
|
|
||||||
/// # Parameters
|
|
||||||
/// * `index` - The input index in spending which is spending this transaction.
|
|
||||||
/// * `amount` - The amount this script guards.
|
|
||||||
/// * `spending_tx` - The transaction that attempts to spend the output holding this script.
|
|
||||||
/// * `flags` - Verification flags, see [`bitcoinconsensus::VERIFY_ALL`] and similar.
|
|
||||||
#[cfg(feature = "bitcoinconsensus")]
|
|
||||||
pub fn verify_with_flags<F: Into<u32>>(
|
|
||||||
&self,
|
|
||||||
index: usize,
|
|
||||||
amount: crate::Amount,
|
|
||||||
spending_tx: &[u8],
|
|
||||||
flags: F,
|
|
||||||
) -> Result<(), Error> {
|
|
||||||
Ok(bitcoinconsensus::verify_with_flags(
|
|
||||||
&self.0[..],
|
|
||||||
amount.to_sat(),
|
|
||||||
spending_tx,
|
|
||||||
index,
|
|
||||||
flags.into(),
|
|
||||||
)?)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Writes the assembly decoding of the script to the formatter.
|
/// Writes the assembly decoding of the script to the formatter.
|
||||||
pub fn fmt_asm(&self, f: &mut dyn fmt::Write) -> fmt::Result {
|
pub fn fmt_asm(&self, f: &mut dyn fmt::Write) -> fmt::Result {
|
||||||
bytes_to_asm_fmt(self.as_ref(), f)
|
bytes_to_asm_fmt(self.as_ref(), f)
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
// SPDX-License-Identifier: CC0-1.0
|
// SPDX-License-Identifier: CC0-1.0
|
||||||
|
|
||||||
#[cfg(feature = "bitcoinconsensus")]
|
|
||||||
use core::convert::From;
|
|
||||||
use core::default::Default;
|
use core::default::Default;
|
||||||
use core::fmt;
|
use core::fmt;
|
||||||
|
|
||||||
|
|
|
@ -680,66 +680,19 @@ pub enum Error {
|
||||||
EarlyEndOfScript,
|
EarlyEndOfScript,
|
||||||
/// Tried to read an array off the stack as a number when it was more than 4 bytes.
|
/// Tried to read an array off the stack as a number when it was more than 4 bytes.
|
||||||
NumericOverflow,
|
NumericOverflow,
|
||||||
/// Error validating the script with bitcoinconsensus library.
|
|
||||||
#[cfg(feature = "bitcoinconsensus")]
|
|
||||||
BitcoinConsensus(bitcoinconsensus::Error),
|
|
||||||
/// Can not find the spent output.
|
/// Can not find the spent output.
|
||||||
UnknownSpentOutput(OutPoint),
|
UnknownSpentOutput(OutPoint),
|
||||||
/// Can not serialize the spending transaction.
|
/// Can not serialize the spending transaction.
|
||||||
Serialization,
|
Serialization,
|
||||||
}
|
}
|
||||||
|
|
||||||
// If bitcoinonsensus-std is off but bitcoinconsensus is present we patch the error type to
|
|
||||||
// implement `std::error::Error`.
|
|
||||||
#[cfg(all(feature = "std", feature = "bitcoinconsensus", not(feature = "bitcoinconsensus-std")))]
|
|
||||||
mod bitcoinconsensus_hack {
|
|
||||||
use core::fmt;
|
|
||||||
|
|
||||||
#[repr(transparent)]
|
|
||||||
pub(crate) struct Error(bitcoinconsensus::Error);
|
|
||||||
|
|
||||||
impl fmt::Debug for Error {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Debug::fmt(&self.0, f) }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for Error {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Display::fmt(&self.0, f) }
|
|
||||||
}
|
|
||||||
|
|
||||||
// bitcoinconsensus::Error has no sources at this time
|
|
||||||
impl std::error::Error for Error {}
|
|
||||||
|
|
||||||
pub(crate) fn wrap_error(error: &bitcoinconsensus::Error) -> &Error {
|
|
||||||
// Unfortunately, we cannot have the reference inside `Error` struct because of the 'static
|
|
||||||
// bound on `source` return type, so we have to use unsafe to overcome the limitation.
|
|
||||||
// SAFETY: the type is repr(transparent) and the lifetimes match
|
|
||||||
unsafe { &*(error as *const _ as *const Error) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(all(
|
|
||||||
feature = "std",
|
|
||||||
feature = "bitcoinconsensus",
|
|
||||||
not(feature = "bitcoinconsensus-std")
|
|
||||||
)))]
|
|
||||||
mod bitcoinconsensus_hack {
|
|
||||||
#[allow(unused_imports)] // conditionally used
|
|
||||||
pub(crate) use core::convert::identity as wrap_error;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for Error {
|
impl fmt::Display for Error {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
#[cfg(feature = "bitcoinconsensus")]
|
|
||||||
use internals::write_err;
|
|
||||||
|
|
||||||
match *self {
|
match *self {
|
||||||
Error::NonMinimalPush => f.write_str("non-minimal datapush"),
|
Error::NonMinimalPush => f.write_str("non-minimal datapush"),
|
||||||
Error::EarlyEndOfScript => f.write_str("unexpected end of script"),
|
Error::EarlyEndOfScript => f.write_str("unexpected end of script"),
|
||||||
Error::NumericOverflow =>
|
Error::NumericOverflow =>
|
||||||
f.write_str("numeric overflow (number on stack larger than 4 bytes)"),
|
f.write_str("numeric overflow (number on stack larger than 4 bytes)"),
|
||||||
#[cfg(feature = "bitcoinconsensus")]
|
|
||||||
Error::BitcoinConsensus(ref e) =>
|
|
||||||
write_err!(f, "bitcoinconsensus verification failed"; bitcoinconsensus_hack::wrap_error(e)),
|
|
||||||
Error::UnknownSpentOutput(ref point) => write!(f, "unknown spent output: {}", point),
|
Error::UnknownSpentOutput(ref point) => write!(f, "unknown spent output: {}", point),
|
||||||
Error::Serialization =>
|
Error::Serialization =>
|
||||||
f.write_str("can not serialize the spending transaction in Transaction::verify()"),
|
f.write_str("can not serialize the spending transaction in Transaction::verify()"),
|
||||||
|
@ -758,8 +711,6 @@ impl std::error::Error for Error {
|
||||||
| NumericOverflow
|
| NumericOverflow
|
||||||
| UnknownSpentOutput(_)
|
| UnknownSpentOutput(_)
|
||||||
| Serialization => None,
|
| Serialization => None,
|
||||||
#[cfg(feature = "bitcoinconsensus")]
|
|
||||||
BitcoinConsensus(ref e) => Some(bitcoinconsensus_hack::wrap_error(e)),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -779,8 +730,3 @@ impl From<UintError> for Error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "bitcoinconsensus")]
|
|
||||||
impl From<bitcoinconsensus::Error> for Error {
|
|
||||||
fn from(err: bitcoinconsensus::Error) -> Error { Error::BitcoinConsensus(err) }
|
|
||||||
}
|
|
||||||
|
|
|
@ -21,10 +21,10 @@ use internals::write_err;
|
||||||
use super::Weight;
|
use super::Weight;
|
||||||
use crate::blockdata::locktime::absolute::{self, Height, Time};
|
use crate::blockdata::locktime::absolute::{self, Height, Time};
|
||||||
use crate::blockdata::locktime::relative;
|
use crate::blockdata::locktime::relative;
|
||||||
#[cfg(feature = "bitcoinconsensus")]
|
|
||||||
use crate::blockdata::script;
|
|
||||||
use crate::blockdata::script::{Script, ScriptBuf};
|
use crate::blockdata::script::{Script, ScriptBuf};
|
||||||
use crate::blockdata::witness::Witness;
|
use crate::blockdata::witness::Witness;
|
||||||
|
#[cfg(feature = "bitcoinconsensus")]
|
||||||
|
pub use crate::consensus::validation::TxVerifyError;
|
||||||
use crate::consensus::{encode, Decodable, Encodable};
|
use crate::consensus::{encode, Decodable, Encodable};
|
||||||
use crate::crypto::sighash::LegacySighash;
|
use crate::crypto::sighash::LegacySighash;
|
||||||
use crate::hash_types::{Txid, Wtxid};
|
use crate::hash_types::{Txid, Wtxid};
|
||||||
|
@ -929,36 +929,6 @@ impl Transaction {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Shorthand for [`Self::verify_with_flags`] with flag [`bitcoinconsensus::VERIFY_ALL`].
|
|
||||||
#[cfg(feature = "bitcoinconsensus")]
|
|
||||||
pub fn verify<S>(&self, spent: S) -> Result<(), script::Error>
|
|
||||||
where
|
|
||||||
S: FnMut(&OutPoint) -> Option<TxOut>,
|
|
||||||
{
|
|
||||||
self.verify_with_flags(spent, bitcoinconsensus::VERIFY_ALL)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Verify that this transaction is able to spend its inputs.
|
|
||||||
///
|
|
||||||
/// The `spent` closure should not return the same [`TxOut`] twice!
|
|
||||||
#[cfg(feature = "bitcoinconsensus")]
|
|
||||||
pub fn verify_with_flags<S, F>(&self, mut spent: S, flags: F) -> Result<(), script::Error>
|
|
||||||
where
|
|
||||||
S: FnMut(&OutPoint) -> Option<TxOut>,
|
|
||||||
F: Into<u32>,
|
|
||||||
{
|
|
||||||
let tx = encode::serialize(self);
|
|
||||||
let flags: u32 = flags.into();
|
|
||||||
for (idx, input) in self.input.iter().enumerate() {
|
|
||||||
if let Some(output) = spent(&input.previous_output) {
|
|
||||||
output.script_pubkey.verify_with_flags(idx, output.value, tx.as_slice(), flags)?;
|
|
||||||
} else {
|
|
||||||
return Err(script::Error::UnknownSpentOutput(input.previous_output));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Checks if this is a coinbase transaction.
|
/// Checks if this is a coinbase transaction.
|
||||||
///
|
///
|
||||||
/// The first transaction in the block distributes the mining reward and is called the coinbase
|
/// The first transaction in the block distributes the mining reward and is called the coinbase
|
||||||
|
@ -1838,7 +1808,6 @@ mod tests {
|
||||||
fn test_transaction_verify() {
|
fn test_transaction_verify() {
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use crate::blockdata::script;
|
|
||||||
use crate::blockdata::witness::Witness;
|
use crate::blockdata::witness::Witness;
|
||||||
|
|
||||||
// a random recent segwit transaction from blockchain using both old and segwit inputs
|
// a random recent segwit transaction from blockchain using both old and segwit inputs
|
||||||
|
@ -1895,7 +1864,7 @@ mod tests {
|
||||||
.err()
|
.err()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
{
|
{
|
||||||
script::Error::BitcoinConsensus(_) => {}
|
TxVerifyError::ScriptVerification(_) => {}
|
||||||
_ => panic!("Wrong error type"),
|
_ => panic!("Wrong error type"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,11 +8,17 @@
|
||||||
|
|
||||||
pub mod encode;
|
pub mod encode;
|
||||||
pub mod params;
|
pub mod params;
|
||||||
|
#[cfg(feature = "bitcoinconsensus")]
|
||||||
|
pub mod validation;
|
||||||
|
|
||||||
pub use self::encode::{
|
pub use self::encode::{
|
||||||
deserialize, deserialize_partial, serialize, Decodable, Encodable, ReadExt, WriteExt,
|
deserialize, deserialize_partial, serialize, Decodable, Encodable, ReadExt, WriteExt,
|
||||||
};
|
};
|
||||||
pub use self::params::Params;
|
pub use self::params::Params;
|
||||||
|
#[cfg(feature = "bitcoinconsensus")]
|
||||||
|
pub use self::validation::{
|
||||||
|
verify_script, verify_script_with_flags, verify_transaction, verify_transaction_with_flags,
|
||||||
|
};
|
||||||
|
|
||||||
#[cfg(feature = "serde")]
|
#[cfg(feature = "serde")]
|
||||||
pub mod serde;
|
pub mod serde;
|
||||||
|
|
|
@ -0,0 +1,249 @@
|
||||||
|
// SPDX-License-Identifier: CC0-1.0
|
||||||
|
|
||||||
|
//! Transaction and script validation.
|
||||||
|
//!
|
||||||
|
//! Relies on the `bitcoinconsensus` crate that uses Bitcoin Core libconsensus to perform validation.
|
||||||
|
|
||||||
|
use core::fmt;
|
||||||
|
|
||||||
|
use internals::write_err;
|
||||||
|
|
||||||
|
use crate::amount::Amount;
|
||||||
|
use crate::blockdata::script::Script;
|
||||||
|
use crate::blockdata::transaction::{OutPoint, Transaction, TxOut};
|
||||||
|
#[cfg(doc)]
|
||||||
|
use crate::consensus;
|
||||||
|
use crate::consensus::encode;
|
||||||
|
|
||||||
|
/// Verifies spend of an input script.
|
||||||
|
///
|
||||||
|
/// Shorthand for [`consensus::verify_script_with_flags`] with flag
|
||||||
|
/// [`bitcoinconsensus::VERIFY_ALL`].
|
||||||
|
///
|
||||||
|
/// # Parameters
|
||||||
|
/// * `index` - The input index in spending which is spending this transaction.
|
||||||
|
/// * `amount` - The amount this script guards.
|
||||||
|
/// * `spending_tx` - The transaction that attempts to spend the output holding this script.
|
||||||
|
///
|
||||||
|
/// [`bitcoinconsensus::VERIFY_ALL`]: https://docs.rs/bitcoinconsensus/0.20.2-0.5.0/bitcoinconsensus/constant.VERIFY_ALL.html
|
||||||
|
pub fn verify_script(
|
||||||
|
script: &Script,
|
||||||
|
index: usize,
|
||||||
|
amount: Amount,
|
||||||
|
spending_tx: &[u8],
|
||||||
|
) -> Result<(), bitcoinconsensus::Error> {
|
||||||
|
verify_script_with_flags(script, index, amount, spending_tx, bitcoinconsensus::VERIFY_ALL)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Verifies spend of an input script.
|
||||||
|
///
|
||||||
|
/// # Parameters
|
||||||
|
/// * `index` - The input index in spending which is spending this transaction.
|
||||||
|
/// * `amount` - The amount this script guards.
|
||||||
|
/// * `spending_tx` - The transaction that attempts to spend the output holding this script.
|
||||||
|
/// * `flags` - Verification flags, see [`bitcoinconsensus::VERIFY_ALL`] and similar.
|
||||||
|
///
|
||||||
|
/// [`bitcoinconsensus::VERIFY_ALL`]: https://docs.rs/bitcoinconsensus/0.20.2-0.5.0/bitcoinconsensus/constant.VERIFY_ALL.html
|
||||||
|
pub fn verify_script_with_flags<F: Into<u32>>(
|
||||||
|
script: &Script,
|
||||||
|
index: usize,
|
||||||
|
amount: Amount,
|
||||||
|
spending_tx: &[u8],
|
||||||
|
flags: F,
|
||||||
|
) -> Result<(), bitcoinconsensus::Error> {
|
||||||
|
bitcoinconsensus::verify_with_flags(
|
||||||
|
script.as_bytes(),
|
||||||
|
amount.to_sat(),
|
||||||
|
spending_tx,
|
||||||
|
index,
|
||||||
|
flags.into(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Verifies that this transaction is able to spend its inputs.
|
||||||
|
///
|
||||||
|
/// Shorthand for [`consensus::verify_transaction_with_flags`] with flag
|
||||||
|
/// [`bitcoinconsensus::VERIFY_ALL`].
|
||||||
|
///
|
||||||
|
/// The `spent` closure should not return the same [`TxOut`] twice!
|
||||||
|
///
|
||||||
|
/// [`bitcoinconsensus::VERIFY_ALL`]: https://docs.rs/bitcoinconsensus/0.20.2-0.5.0/bitcoinconsensus/constant.VERIFY_ALL.html
|
||||||
|
pub fn verify_transaction<S>(tx: &Transaction, spent: S) -> Result<(), TxVerifyError>
|
||||||
|
where
|
||||||
|
S: FnMut(&OutPoint) -> Option<TxOut>,
|
||||||
|
{
|
||||||
|
verify_transaction_with_flags(tx, spent, bitcoinconsensus::VERIFY_ALL)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Verifies that this transaction is able to spend its inputs.
|
||||||
|
///
|
||||||
|
/// The `spent` closure should not return the same [`TxOut`] twice!
|
||||||
|
pub fn verify_transaction_with_flags<S, F>(
|
||||||
|
tx: &Transaction,
|
||||||
|
mut spent: S,
|
||||||
|
flags: F,
|
||||||
|
) -> Result<(), TxVerifyError>
|
||||||
|
where
|
||||||
|
S: FnMut(&OutPoint) -> Option<TxOut>,
|
||||||
|
F: Into<u32>,
|
||||||
|
{
|
||||||
|
let serialized_tx = encode::serialize(tx);
|
||||||
|
let flags: u32 = flags.into();
|
||||||
|
for (idx, input) in tx.input.iter().enumerate() {
|
||||||
|
if let Some(output) = spent(&input.previous_output) {
|
||||||
|
verify_script_with_flags(
|
||||||
|
&output.script_pubkey,
|
||||||
|
idx,
|
||||||
|
output.value,
|
||||||
|
serialized_tx.as_slice(),
|
||||||
|
flags,
|
||||||
|
)?;
|
||||||
|
} else {
|
||||||
|
return Err(TxVerifyError::UnknownSpentOutput(input.previous_output));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Script {
|
||||||
|
/// Verifies spend of an input script.
|
||||||
|
///
|
||||||
|
/// Shorthand for [`Self::verify_with_flags`] with flag [`bitcoinconsensus::VERIFY_ALL`].
|
||||||
|
///
|
||||||
|
/// # Parameters
|
||||||
|
/// * `index` - The input index in spending which is spending this transaction.
|
||||||
|
/// * `amount` - The amount this script guards.
|
||||||
|
/// * `spending_tx` - The transaction that attempts to spend the output holding this script.
|
||||||
|
///
|
||||||
|
/// [`bitcoinconsensus::VERIFY_ALL`]: https://docs.rs/bitcoinconsensus/0.20.2-0.5.0/bitcoinconsensus/constant.VERIFY_ALL.html
|
||||||
|
pub fn verify(
|
||||||
|
&self,
|
||||||
|
index: usize,
|
||||||
|
amount: crate::Amount,
|
||||||
|
spending_tx: &[u8],
|
||||||
|
) -> Result<(), bitcoinconsensus::Error> {
|
||||||
|
verify_script(self, index, amount, spending_tx)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Verifies spend of an input script.
|
||||||
|
///
|
||||||
|
/// # Parameters
|
||||||
|
/// * `index` - The input index in spending which is spending this transaction.
|
||||||
|
/// * `amount` - The amount this script guards.
|
||||||
|
/// * `spending_tx` - The transaction that attempts to spend the output holding this script.
|
||||||
|
/// * `flags` - Verification flags, see [`bitcoinconsensus::VERIFY_ALL`] and similar.
|
||||||
|
///
|
||||||
|
/// [`bitcoinconsensus::VERIFY_ALL`]: https://docs.rs/bitcoinconsensus/0.20.2-0.5.0/bitcoinconsensus/constant.VERIFY_ALL.html
|
||||||
|
pub fn verify_with_flags<F: Into<u32>>(
|
||||||
|
&self,
|
||||||
|
index: usize,
|
||||||
|
amount: crate::Amount,
|
||||||
|
spending_tx: &[u8],
|
||||||
|
flags: F,
|
||||||
|
) -> Result<(), bitcoinconsensus::Error> {
|
||||||
|
verify_script_with_flags(self, index, amount, spending_tx, flags)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Transaction {
|
||||||
|
/// Verifies that this transaction is able to spend its inputs.
|
||||||
|
///
|
||||||
|
/// Shorthand for [`Self::verify_with_flags`] with flag [`bitcoinconsensus::VERIFY_ALL`].
|
||||||
|
///
|
||||||
|
/// The `spent` closure should not return the same [`TxOut`] twice!
|
||||||
|
///
|
||||||
|
/// [`bitcoinconsensus::VERIFY_ALL`]: https://docs.rs/bitcoinconsensus/0.20.2-0.5.0/bitcoinconsensus/constant.VERIFY_ALL.html
|
||||||
|
pub fn verify<S>(&self, spent: S) -> Result<(), TxVerifyError>
|
||||||
|
where
|
||||||
|
S: FnMut(&OutPoint) -> Option<TxOut>,
|
||||||
|
{
|
||||||
|
verify_transaction(self, spent)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Verifies that this transaction is able to spend its inputs.
|
||||||
|
///
|
||||||
|
/// The `spent` closure should not return the same [`TxOut`] twice!
|
||||||
|
pub fn verify_with_flags<S, F>(&self, spent: S, flags: F) -> Result<(), TxVerifyError>
|
||||||
|
where
|
||||||
|
S: FnMut(&OutPoint) -> Option<TxOut>,
|
||||||
|
F: Into<u32>,
|
||||||
|
{
|
||||||
|
verify_transaction_with_flags(self, spent, flags)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An error during transaction validation.
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
pub enum TxVerifyError {
|
||||||
|
/// Error validating the script with bitcoinconsensus library.
|
||||||
|
ScriptVerification(bitcoinconsensus::Error),
|
||||||
|
/// Can not find the spent output.
|
||||||
|
UnknownSpentOutput(OutPoint),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for TxVerifyError {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
use TxVerifyError::*;
|
||||||
|
|
||||||
|
match *self {
|
||||||
|
ScriptVerification(ref e) => {
|
||||||
|
write_err!(f, "bitcoinconsensus verification failed"; bitcoinconsensus_hack::wrap_error(e))
|
||||||
|
}
|
||||||
|
UnknownSpentOutput(ref p) => write!(f, "unknown spent output: {}", p),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
impl std::error::Error for TxVerifyError {
|
||||||
|
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
|
||||||
|
use TxVerifyError::*;
|
||||||
|
|
||||||
|
match *self {
|
||||||
|
ScriptVerification(ref e) => Some(bitcoinconsensus_hack::wrap_error(e)),
|
||||||
|
UnknownSpentOutput(_) => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<bitcoinconsensus::Error> for TxVerifyError {
|
||||||
|
fn from(e: bitcoinconsensus::Error) -> Self { TxVerifyError::ScriptVerification(e) }
|
||||||
|
}
|
||||||
|
|
||||||
|
// If bitcoinonsensus-std is off but bitcoinconsensus is present we patch the error type to
|
||||||
|
// implement `std::error::Error`.
|
||||||
|
#[cfg(all(feature = "std", feature = "bitcoinconsensus", not(feature = "bitcoinconsensus-std")))]
|
||||||
|
mod bitcoinconsensus_hack {
|
||||||
|
use core::fmt;
|
||||||
|
|
||||||
|
#[repr(transparent)]
|
||||||
|
pub(crate) struct Error(bitcoinconsensus::Error);
|
||||||
|
|
||||||
|
impl fmt::Debug for Error {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Debug::fmt(&self.0, f) }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Error {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Display::fmt(&self.0, f) }
|
||||||
|
}
|
||||||
|
|
||||||
|
// bitcoinconsensus::Error has no sources at this time
|
||||||
|
impl std::error::Error for Error {}
|
||||||
|
|
||||||
|
pub(crate) fn wrap_error(error: &bitcoinconsensus::Error) -> &Error {
|
||||||
|
// Unfortunately, we cannot have the reference inside `Error` struct because of the 'static
|
||||||
|
// bound on `source` return type, so we have to use unsafe to overcome the limitation.
|
||||||
|
// SAFETY: the type is repr(transparent) and the lifetimes match
|
||||||
|
unsafe { &*(error as *const _ as *const Error) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(all(
|
||||||
|
feature = "std",
|
||||||
|
feature = "bitcoinconsensus",
|
||||||
|
not(feature = "bitcoinconsensus-std")
|
||||||
|
)))]
|
||||||
|
mod bitcoinconsensus_hack {
|
||||||
|
#[allow(unused_imports)] // conditionally used
|
||||||
|
pub(crate) use core::convert::identity as wrap_error;
|
||||||
|
}
|
Loading…
Reference in New Issue