2022-06-29 04:05:31 +00:00
|
|
|
// SPDX-License-Identifier: CC0-1.0
|
2018-09-09 10:34:29 +00:00
|
|
|
|
2021-11-05 21:58:18 +00:00
|
|
|
//! Partially Signed Bitcoin Transactions.
|
2018-08-08 06:36:51 +00:00
|
|
|
//!
|
|
|
|
//! Implementation of BIP174 Partially Signed Bitcoin Transaction Format as
|
2021-05-03 11:46:10 +00:00
|
|
|
//! defined at <https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki>
|
2022-03-28 23:45:46 +00:00
|
|
|
//! except we define PSBTs containing non-standard sighash types as invalid.
|
2021-11-05 21:58:18 +00:00
|
|
|
//!
|
2018-08-08 06:36:51 +00:00
|
|
|
|
2022-04-19 05:46:59 +00:00
|
|
|
#[cfg(feature = "std")]
|
|
|
|
use std::collections::{HashMap, HashSet};
|
|
|
|
|
|
|
|
use core::{fmt, cmp};
|
|
|
|
use core::ops::Deref;
|
|
|
|
|
|
|
|
use secp256k1::{Message, Secp256k1, Signing};
|
|
|
|
use bitcoin_internals::write_err;
|
2022-02-22 13:41:20 +00:00
|
|
|
|
2022-10-22 18:27:49 +00:00
|
|
|
use crate::{prelude::*, Amount};
|
2022-06-08 04:07:06 +00:00
|
|
|
use crate::io;
|
|
|
|
|
2022-05-02 22:13:57 +00:00
|
|
|
use crate::blockdata::script::Script;
|
2022-04-19 05:46:59 +00:00
|
|
|
use crate::blockdata::transaction::{Transaction, TxOut};
|
Take Writer/Reader by `&mut` in consensus en/decoding
Fix #1020 (see more relevant discussion there)
This definitely makes the amount of generics compiler
has to generate by avoding generating the same functions
for `R`, &mut R`, `&mut &mut R` and so on.
old:
```
> ls -al target/release/deps/bitcoin-07a9dabf1f3e0266
-rwxrwxr-x 1 dpc dpc 9947832 Jun 2 22:42 target/release/deps/bitcoin-07a9dabf1f3e0266
> strip target/release/deps/bitcoin-07a9dabf1f3e0266
> ls -al target/release/deps/bitcoin-07a9dabf1f3e0266
-rwxrwxr-x 1 dpc dpc 4463024 Jun 2 22:46 target/release/deps/bitcoin-07a9dabf1f3e0266
```
new:
```
> ls -al target/release/deps/bitcoin-07a9dabf1f3e0266
-rwxrwxr-x 1 dpc dpc 9866800 Jun 2 22:44 target/release/deps/bitcoin-07a9dabf1f3e0266
> strip target/release/deps/bitcoin-07a9dabf1f3e0266
> ls -al target/release/deps/bitcoin-07a9dabf1f3e0266
-rwxrwxr-x 1 dpc dpc 4393392 Jun 2 22:45 target/release/deps/bitcoin-07a9dabf1f3e0266
```
In the unit-test binary itself, it saves ~100KB of data.
I did not expect much performance gains, but turn out I was wrong(*):
old:
```
test blockdata::block::benches::bench_block_deserialize ... bench: 1,072,710 ns/iter (+/- 21,871)
test blockdata::block::benches::bench_block_serialize ... bench: 191,223 ns/iter (+/- 5,833)
test blockdata::block::benches::bench_block_serialize_logic ... bench: 37,543 ns/iter (+/- 732)
test blockdata::block::benches::bench_stream_reader ... bench: 1,872,455 ns/iter (+/- 149,519)
test blockdata::transaction::benches::bench_transaction_deserialize ... bench: 136 ns/iter (+/- 3)
test blockdata::transaction::benches::bench_transaction_serialize ... bench: 51 ns/iter (+/- 8)
test blockdata::transaction::benches::bench_transaction_serialize_logic ... bench: 5 ns/iter (+/- 0)
test blockdata::transaction::benches::bench_transaction_size ... bench: 3 ns/iter (+/- 0)
```
new:
```
test blockdata::block::benches::bench_block_deserialize ... bench: 1,028,574 ns/iter (+/- 10,910)
test blockdata::block::benches::bench_block_serialize ... bench: 162,143 ns/iter (+/- 3,363)
test blockdata::block::benches::bench_block_serialize_logic ... bench: 30,725 ns/iter (+/- 695)
test blockdata::block::benches::bench_stream_reader ... bench: 1,437,071 ns/iter (+/- 53,694)
test blockdata::transaction::benches::bench_transaction_deserialize ... bench: 92 ns/iter (+/- 2)
test blockdata::transaction::benches::bench_transaction_serialize ... bench: 17 ns/iter (+/- 0)
test blockdata::transaction::benches::bench_transaction_serialize_logic ... bench: 5 ns/iter (+/- 0)
test blockdata::transaction::benches::bench_transaction_size ... bench: 4 ns/iter (+/- 0)
```
(*) - I'm benchmarking on a noisy laptop. Take this with a grain of salt. But I think
at least it doesn't make anything slower.
While doing all this manual labor that will probably generate conflicts,
I took a liberty of changing generic type names and variable names to
`r` and `R` (reader) and `w` and `W` for writer.
2022-06-03 04:50:42 +00:00
|
|
|
use crate::consensus::{encode, Encodable, Decodable};
|
2022-04-19 05:46:59 +00:00
|
|
|
use crate::util::bip32::{self, ExtendedPrivKey, ExtendedPubKey, KeySource};
|
|
|
|
use crate::util::ecdsa::{EcdsaSig, EcdsaSigError};
|
|
|
|
use crate::util::key::{PublicKey, PrivateKey};
|
2022-10-24 22:14:01 +00:00
|
|
|
use crate::sighash::{self, EcdsaSighashType, SighashCache};
|
2018-08-10 15:28:48 +00:00
|
|
|
|
2022-10-24 22:14:01 +00:00
|
|
|
pub use crate::sighash::Prevouts;
|
2018-08-10 15:28:48 +00:00
|
|
|
|
2018-09-09 04:07:07 +00:00
|
|
|
#[macro_use]
|
|
|
|
mod macros;
|
2022-06-08 04:07:06 +00:00
|
|
|
pub mod raw;
|
2018-09-09 04:07:07 +00:00
|
|
|
pub mod serialize;
|
|
|
|
|
2022-06-08 04:07:06 +00:00
|
|
|
mod error;
|
|
|
|
pub use self::error::Error;
|
|
|
|
|
2018-09-09 03:50:45 +00:00
|
|
|
mod map;
|
2022-04-05 20:24:08 +00:00
|
|
|
pub use self::map::{Input, Output, TapTree, PsbtSighashType, IncompleteTapTree};
|
2022-01-04 02:23:40 +00:00
|
|
|
use self::map::Map;
|
2021-09-15 05:35:07 +00:00
|
|
|
|
2022-04-17 21:21:19 +00:00
|
|
|
/// Partially signed transaction, commonly referred to as a PSBT.
|
|
|
|
pub type Psbt = PartiallySignedTransaction;
|
|
|
|
|
2018-08-10 20:31:58 +00:00
|
|
|
/// A Partially Signed Transaction.
|
2022-05-06 03:37:24 +00:00
|
|
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
2020-10-25 18:03:20 +00:00
|
|
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
2022-05-25 06:41:59 +00:00
|
|
|
#[cfg_attr(feature = "serde", serde(crate = "actual_serde"))]
|
2018-08-10 20:31:58 +00:00
|
|
|
pub struct PartiallySignedTransaction {
|
2022-04-19 05:26:26 +00:00
|
|
|
/// The unsigned transaction, scriptSigs and witnesses for each input must be empty.
|
2021-09-15 05:35:07 +00:00
|
|
|
pub unsigned_tx: Transaction,
|
|
|
|
/// The version number of this PSBT. If omitted, the version number is 0.
|
|
|
|
pub version: u32,
|
|
|
|
/// A global map from extended public keys to the used key fingerprint and
|
2022-04-19 05:26:26 +00:00
|
|
|
/// derivation path as defined by BIP 32.
|
2021-09-15 05:35:07 +00:00
|
|
|
pub xpub: BTreeMap<ExtendedPubKey, KeySource>,
|
|
|
|
/// Global proprietary key-value pairs.
|
2022-05-02 23:02:58 +00:00
|
|
|
#[cfg_attr(feature = "serde", serde(with = "crate::serde_utils::btreemap_as_seq_byte_values"))]
|
2021-09-15 05:35:07 +00:00
|
|
|
pub proprietary: BTreeMap<raw::ProprietaryKey, Vec<u8>>,
|
|
|
|
/// Unknown global key-value pairs.
|
2022-05-02 23:02:58 +00:00
|
|
|
#[cfg_attr(feature = "serde", serde(with = "crate::serde_utils::btreemap_as_seq_byte_values"))]
|
2021-09-15 05:35:07 +00:00
|
|
|
pub unknown: BTreeMap<raw::Key, Vec<u8>>,
|
|
|
|
|
2022-04-19 05:26:26 +00:00
|
|
|
/// The corresponding key-value map for each input in the unsigned transaction.
|
2018-08-10 20:31:58 +00:00
|
|
|
pub inputs: Vec<Input>,
|
2022-04-19 05:26:26 +00:00
|
|
|
/// The corresponding key-value map for each output in the unsigned transaction.
|
2018-08-10 20:31:58 +00:00
|
|
|
pub outputs: Vec<Output>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl PartiallySignedTransaction {
|
2022-02-28 14:01:02 +00:00
|
|
|
/// Returns an iterator for the funding UTXOs of the psbt
|
|
|
|
///
|
|
|
|
/// For each PSBT input that contains UTXO information `Ok` is returned containing that information.
|
|
|
|
/// The order of returned items is same as the order of inputs.
|
|
|
|
///
|
|
|
|
/// ## Errors
|
|
|
|
///
|
|
|
|
/// The function returns error when UTXO information is not present or is invalid.
|
|
|
|
///
|
|
|
|
/// ## Panics
|
|
|
|
///
|
|
|
|
/// The function panics if the length of transaction inputs is not equal to the length of PSBT inputs.
|
|
|
|
pub fn iter_funding_utxos(&self) -> impl Iterator<Item = Result<&TxOut, Error>> {
|
|
|
|
assert_eq!(self.inputs.len(), self.unsigned_tx.input.len());
|
|
|
|
self.unsigned_tx.input.iter().zip(&self.inputs).map(|(tx_input, psbt_input)| {
|
|
|
|
match (&psbt_input.witness_utxo, &psbt_input.non_witness_utxo) {
|
|
|
|
(Some(witness_utxo), _) => Ok(witness_utxo),
|
|
|
|
(None, Some(non_witness_utxo)) => {
|
|
|
|
let vout = tx_input.previous_output.vout as usize;
|
|
|
|
non_witness_utxo.output.get(vout).ok_or(Error::PsbtUtxoOutOfbounds)
|
|
|
|
},
|
|
|
|
(None, None) => Err(Error::MissingUtxo),
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2022-04-19 05:26:26 +00:00
|
|
|
/// Checks that unsigned transaction does not have scriptSig's or witness data.
|
2021-09-15 05:35:07 +00:00
|
|
|
fn unsigned_tx_checks(&self) -> Result<(), Error> {
|
|
|
|
for txin in &self.unsigned_tx.input {
|
|
|
|
if !txin.script_sig.is_empty() {
|
|
|
|
return Err(Error::UnsignedTxHasScriptSigs);
|
|
|
|
}
|
|
|
|
|
|
|
|
if !txin.witness.is_empty() {
|
|
|
|
return Err(Error::UnsignedTxHasScriptWitnesses);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2022-04-19 05:26:26 +00:00
|
|
|
/// Creates a PSBT from an unsigned transaction.
|
|
|
|
///
|
|
|
|
/// # Errors
|
|
|
|
///
|
|
|
|
/// If transactions is not unsigned.
|
2021-09-15 05:35:07 +00:00
|
|
|
pub fn from_unsigned_tx(tx: Transaction) -> Result<Self, Error> {
|
|
|
|
let psbt = PartiallySignedTransaction {
|
2018-08-10 20:31:58 +00:00
|
|
|
inputs: vec![Default::default(); tx.input.len()],
|
|
|
|
outputs: vec![Default::default(); tx.output.len()],
|
2021-09-15 05:35:07 +00:00
|
|
|
|
|
|
|
unsigned_tx: tx,
|
|
|
|
xpub: Default::default(),
|
|
|
|
version: 0,
|
|
|
|
proprietary: Default::default(),
|
|
|
|
unknown: Default::default(),
|
|
|
|
};
|
|
|
|
psbt.unsigned_tx_checks()?;
|
|
|
|
Ok(psbt)
|
2018-08-10 20:31:58 +00:00
|
|
|
}
|
|
|
|
|
2022-04-19 05:26:26 +00:00
|
|
|
/// Extracts the `Transaction` from a PSBT by filling in the available signature information.
|
2018-08-10 20:31:58 +00:00
|
|
|
pub fn extract_tx(self) -> Transaction {
|
2021-09-15 05:35:07 +00:00
|
|
|
let mut tx: Transaction = self.unsigned_tx;
|
2018-08-10 20:31:58 +00:00
|
|
|
|
|
|
|
for (vin, psbtin) in tx.input.iter_mut().zip(self.inputs.into_iter()) {
|
2019-08-05 18:52:34 +00:00
|
|
|
vin.script_sig = psbtin.final_script_sig.unwrap_or_else(Script::new);
|
2022-01-11 09:10:28 +00:00
|
|
|
vin.witness = psbtin.final_script_witness.unwrap_or_default();
|
2018-08-10 20:31:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
tx
|
|
|
|
}
|
2022-02-22 13:41:20 +00:00
|
|
|
|
|
|
|
/// Combines this [`PartiallySignedTransaction`] with `other` PSBT as described by BIP 174.
|
|
|
|
///
|
|
|
|
/// In accordance with BIP 174 this function is commutative i.e., `A.combine(B) == B.combine(A)`
|
|
|
|
pub fn combine(&mut self, other: Self) -> Result<(), Error> {
|
|
|
|
if self.unsigned_tx != other.unsigned_tx {
|
|
|
|
return Err(Error::UnexpectedUnsignedTx {
|
|
|
|
expected: Box::new(self.unsigned_tx.clone()),
|
|
|
|
actual: Box::new(other.unsigned_tx),
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
// BIP 174: The Combiner must remove any duplicate key-value pairs, in accordance with
|
|
|
|
// the specification. It can pick arbitrarily when conflicts occur.
|
|
|
|
|
|
|
|
// Keeping the highest version
|
|
|
|
self.version = cmp::max(self.version, other.version);
|
|
|
|
|
|
|
|
// Merging xpubs
|
|
|
|
for (xpub, (fingerprint1, derivation1)) in other.xpub {
|
|
|
|
match self.xpub.entry(xpub) {
|
|
|
|
btree_map::Entry::Vacant(entry) => {
|
|
|
|
entry.insert((fingerprint1, derivation1));
|
|
|
|
},
|
|
|
|
btree_map::Entry::Occupied(mut entry) => {
|
|
|
|
// Here in case of the conflict we select the version with algorithm:
|
|
|
|
// 1) if everything is equal we do nothing
|
|
|
|
// 2) report an error if
|
|
|
|
// - derivation paths are equal and fingerprints are not
|
|
|
|
// - derivation paths are of the same length, but not equal
|
|
|
|
// - derivation paths has different length, but the shorter one
|
|
|
|
// is not the strict suffix of the longer one
|
|
|
|
// 3) choose longest derivation otherwise
|
|
|
|
|
|
|
|
let (fingerprint2, derivation2) = entry.get().clone();
|
|
|
|
|
|
|
|
if (derivation1 == derivation2 && fingerprint1 == fingerprint2) ||
|
|
|
|
(derivation1.len() < derivation2.len() && derivation1[..] == derivation2[derivation2.len() - derivation1.len()..])
|
|
|
|
{
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
else if derivation2[..] == derivation1[derivation1.len() - derivation2.len()..]
|
|
|
|
{
|
|
|
|
entry.insert((fingerprint1, derivation1));
|
|
|
|
continue
|
|
|
|
}
|
2022-05-19 15:05:42 +00:00
|
|
|
return Err(Error::CombineInconsistentKeySources(Box::new(xpub)));
|
2022-02-22 13:41:20 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
self.proprietary.extend(other.proprietary);
|
|
|
|
self.unknown.extend(other.unknown);
|
|
|
|
|
|
|
|
for (self_input, other_input) in self.inputs.iter_mut().zip(other.inputs.into_iter()) {
|
|
|
|
self_input.combine(other_input);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (self_output, other_output) in self.outputs.iter_mut().zip(other.outputs.into_iter()) {
|
|
|
|
self_output.combine(other_output);
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
2022-04-19 05:46:59 +00:00
|
|
|
|
|
|
|
/// Attempts to create _all_ the required signatures for this PSBT using `k`.
|
|
|
|
///
|
|
|
|
/// **NOTE**: Taproot inputs are, as yet, not supported by this function. We currently only
|
|
|
|
/// attempt to sign ECDSA inputs.
|
|
|
|
///
|
|
|
|
/// If you just want to sign an input with one specific key consider using `sighash_ecdsa`. This
|
|
|
|
/// function does not support scripts that contain `OP_CODESEPARATOR`.
|
|
|
|
///
|
|
|
|
/// # Returns
|
|
|
|
///
|
|
|
|
/// Either Ok(SigningKeys) or Err((SigningKeys, SigningErrors)), where
|
|
|
|
/// - SigningKeys: A map of input index -> pubkey associated with secret key used to sign.
|
|
|
|
/// - SigningKeys: A map of input index -> the error encountered while attempting to sign.
|
|
|
|
///
|
|
|
|
/// If an error is returned some signatures may already have been added to the PSBT. Since
|
|
|
|
/// `partial_sigs` is a [`BTreeMap`] it is safe to retry, previous sigs will be overwritten.
|
|
|
|
pub fn sign<C, K>(
|
|
|
|
&mut self,
|
|
|
|
k: &K,
|
|
|
|
secp: &Secp256k1<C>,
|
|
|
|
) -> Result<SigningKeys, (SigningKeys, SigningErrors)>
|
|
|
|
where
|
|
|
|
C: Signing,
|
|
|
|
K: GetKey,
|
|
|
|
{
|
|
|
|
let tx = self.unsigned_tx.clone(); // clone because we need to mutably borrow when signing.
|
|
|
|
let mut cache = SighashCache::new(&tx);
|
|
|
|
|
|
|
|
let mut used = BTreeMap::new();
|
|
|
|
let mut errors = BTreeMap::new();
|
|
|
|
|
|
|
|
for i in 0..self.inputs.len() {
|
|
|
|
if let Ok(SigningAlgorithm::Ecdsa) = self.signing_algorithm(i) {
|
|
|
|
match self.bip32_sign_ecdsa(k, i, &mut cache, secp) {
|
|
|
|
Ok(v) => { used.insert(i, v); },
|
|
|
|
Err(e) => { errors.insert(i, e); },
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
if errors.is_empty() {
|
|
|
|
Ok(used)
|
|
|
|
} else {
|
|
|
|
Err((used, errors))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Attempts to create all signatures required by this PSBT's `bip32_derivation` field, adding
|
|
|
|
/// them to `partial_sigs`.
|
|
|
|
///
|
|
|
|
/// # Returns
|
|
|
|
///
|
|
|
|
/// - Ok: A list of the public keys used in signing.
|
|
|
|
/// - Err: Error encountered trying to calculate the sighash AND we had the signing key.
|
|
|
|
fn bip32_sign_ecdsa<C, K, T>(
|
|
|
|
&mut self,
|
|
|
|
k: &K,
|
|
|
|
input_index: usize,
|
|
|
|
cache: &mut SighashCache<T>,
|
|
|
|
secp: &Secp256k1<C>,
|
|
|
|
) -> Result<Vec<PublicKey>, SignError>
|
|
|
|
where
|
|
|
|
C: Signing,
|
|
|
|
T: Deref<Target=Transaction>,
|
|
|
|
K: GetKey,
|
|
|
|
{
|
|
|
|
let msg_sighash_ty_res = self.sighash_ecdsa(input_index, cache);
|
|
|
|
|
|
|
|
let input = &mut self.inputs[input_index]; // Index checked in call to `sighash_ecdsa`.
|
|
|
|
|
|
|
|
let mut used = vec![]; // List of pubkeys used to sign the input.
|
|
|
|
|
|
|
|
for (pk, key_source) in input.bip32_derivation.iter() {
|
|
|
|
let sk = if let Ok(Some(sk)) = k.get_key(KeyRequest::Bip32(key_source.clone()), secp) {
|
|
|
|
sk
|
|
|
|
} else if let Ok(Some(sk)) = k.get_key(KeyRequest::Pubkey(PublicKey::new(*pk)), secp) {
|
|
|
|
sk
|
|
|
|
} else {
|
|
|
|
continue;
|
|
|
|
};
|
|
|
|
|
|
|
|
// Only return the error if we have a secret key to sign this input.
|
|
|
|
let (msg, sighash_ty) = match msg_sighash_ty_res {
|
|
|
|
Err(e) => return Err(e),
|
|
|
|
Ok((msg, sighash_ty)) => (msg, sighash_ty),
|
|
|
|
};
|
|
|
|
|
|
|
|
let sig = EcdsaSig {
|
|
|
|
sig: secp.sign_ecdsa(&msg, &sk.inner),
|
|
|
|
hash_ty: sighash_ty,
|
|
|
|
};
|
|
|
|
|
|
|
|
let pk = sk.public_key(secp);
|
|
|
|
|
|
|
|
input.partial_sigs.insert(pk, sig);
|
|
|
|
used.push(pk);
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(used)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns the sighash message to sign an ECDSA input along with the sighash type.
|
|
|
|
///
|
|
|
|
/// Uses the [`EcdsaSighashType`] from this input if one is specified. If no sighash type is
|
|
|
|
/// specified uses [`EcdsaSighashType::All`]. This function does not support scripts that
|
|
|
|
/// contain `OP_CODESEPARATOR`.
|
|
|
|
pub fn sighash_ecdsa<T: Deref<Target=Transaction>>(
|
|
|
|
&self,
|
|
|
|
input_index: usize,
|
|
|
|
cache: &mut SighashCache<T>,
|
|
|
|
) -> Result<(Message, EcdsaSighashType), SignError> {
|
|
|
|
use OutputType::*;
|
|
|
|
|
|
|
|
if self.signing_algorithm(input_index)? != SigningAlgorithm::Ecdsa {
|
|
|
|
return Err(SignError::WrongSigningAlgorithm);
|
|
|
|
}
|
|
|
|
|
|
|
|
let input = self.checked_input(input_index)?;
|
|
|
|
let utxo = self.spend_utxo(input_index)?;
|
|
|
|
let spk = &utxo.script_pubkey; // scriptPubkey for input spend utxo.
|
|
|
|
|
|
|
|
let hash_ty = input.ecdsa_hash_ty()
|
|
|
|
.map_err(|_| SignError::InvalidSighashType)?; // Only support standard sighash types.
|
|
|
|
|
|
|
|
let sighash = match self.output_type(input_index)? {
|
|
|
|
Bare => {
|
|
|
|
cache.legacy_signature_hash(input_index, spk, hash_ty.to_u32())?
|
|
|
|
},
|
|
|
|
Sh => {
|
|
|
|
let script_code = input.redeem_script.as_ref().ok_or(SignError::MissingRedeemScript)?;
|
|
|
|
cache.legacy_signature_hash(input_index, script_code, hash_ty.to_u32())?
|
|
|
|
},
|
|
|
|
Wpkh => {
|
|
|
|
let script_code = Script::p2wpkh_script_code(spk).ok_or(SignError::NotWpkh)?;
|
|
|
|
cache.segwit_signature_hash(input_index, &script_code, utxo.value, hash_ty)?
|
|
|
|
}
|
|
|
|
ShWpkh => {
|
|
|
|
let script_code = Script::p2wpkh_script_code(input.redeem_script.as_ref().expect("checked above"))
|
|
|
|
.ok_or(SignError::NotWpkh)?;
|
|
|
|
cache.segwit_signature_hash(input_index, &script_code, utxo.value, hash_ty)?
|
|
|
|
},
|
|
|
|
Wsh | ShWsh => {
|
|
|
|
let script_code = input.witness_script.as_ref().ok_or(SignError::MissingWitnessScript)?;
|
|
|
|
cache.segwit_signature_hash(input_index, script_code, utxo.value, hash_ty)?
|
|
|
|
},
|
|
|
|
Tr => {
|
|
|
|
// This PSBT signing API is WIP, taproot to come shortly.
|
|
|
|
return Err(SignError::Unsupported);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
Ok((Message::from_slice(&sighash).expect("sighashes are 32 bytes"), hash_ty))
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns the spending utxo for this PSBT's input at `input_index`.
|
|
|
|
pub fn spend_utxo(&self, input_index: usize) -> Result<&TxOut, SignError> {
|
|
|
|
let input = self.checked_input(input_index)?;
|
|
|
|
let utxo = if let Some(witness_utxo) = &input.witness_utxo {
|
|
|
|
witness_utxo
|
|
|
|
} else if let Some(non_witness_utxo) = &input.non_witness_utxo {
|
|
|
|
let vout = self.unsigned_tx.input[input_index].previous_output.vout;
|
|
|
|
&non_witness_utxo.output[vout as usize]
|
|
|
|
} else {
|
|
|
|
return Err(SignError::MissingSpendUtxo);
|
|
|
|
};
|
|
|
|
Ok(utxo)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Gets the input at `input_index` after checking that it is a valid index.
|
|
|
|
fn checked_input(&self, input_index: usize) -> Result<&Input, SignError> {
|
|
|
|
self.check_index_is_within_bounds(input_index)?;
|
|
|
|
Ok(&self.inputs[input_index])
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Checks `input_index` is within bounds for the PSBT `inputs` array and
|
|
|
|
/// for the PSBT `unsigned_tx` `input` array.
|
|
|
|
fn check_index_is_within_bounds(&self, input_index: usize) -> Result<(), SignError> {
|
|
|
|
if input_index >= self.inputs.len() {
|
|
|
|
return Err(SignError::IndexOutOfBounds(input_index, self.inputs.len()));
|
|
|
|
}
|
|
|
|
|
|
|
|
if input_index >= self.unsigned_tx.input.len() {
|
|
|
|
return Err(SignError::IndexOutOfBounds(input_index, self.unsigned_tx.input.len()));
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns the algorithm used to sign this PSBT's input at `input_index`.
|
|
|
|
fn signing_algorithm(&self, input_index: usize) -> Result<SigningAlgorithm, SignError> {
|
|
|
|
let output_type = self.output_type(input_index)?;
|
|
|
|
Ok(output_type.signing_algorithm())
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns the [`OutputType`] of the spend utxo for this PBST's input at `input_index`.
|
|
|
|
fn output_type(&self, input_index: usize) -> Result<OutputType, SignError> {
|
|
|
|
let input = self.checked_input(input_index)?;
|
|
|
|
let utxo = self.spend_utxo(input_index)?;
|
|
|
|
let spk = utxo.script_pubkey.clone();
|
|
|
|
|
|
|
|
// Anything that is not segwit and is not p2sh is `Bare`.
|
|
|
|
if !(spk.is_witness_program() || spk.is_p2sh()) {
|
|
|
|
return Ok(OutputType::Bare);
|
|
|
|
}
|
|
|
|
|
|
|
|
if spk.is_v0_p2wpkh() {
|
|
|
|
return Ok(OutputType::Wpkh);
|
|
|
|
}
|
|
|
|
|
|
|
|
if spk.is_v0_p2wsh() {
|
|
|
|
return Ok(OutputType::Wsh);
|
|
|
|
}
|
|
|
|
|
|
|
|
if spk.is_p2sh() {
|
|
|
|
if input.redeem_script.as_ref().map(|s| s.is_v0_p2wpkh()).unwrap_or(false) {
|
|
|
|
return Ok(OutputType::ShWpkh);
|
|
|
|
}
|
|
|
|
if input.redeem_script.as_ref().map(|x| x.is_v0_p2wsh()).unwrap_or(false) {
|
|
|
|
return Ok(OutputType::ShWsh);
|
|
|
|
}
|
|
|
|
return Ok(OutputType::Sh);
|
|
|
|
}
|
|
|
|
|
|
|
|
if spk.is_v1_p2tr() {
|
|
|
|
return Ok(OutputType::Tr);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Something is wrong with the input scriptPubkey or we do not know how to sign
|
|
|
|
// because there has been a new softfork that we do not yet support.
|
|
|
|
Err(SignError::UnknownOutputType)
|
|
|
|
}
|
2022-10-22 18:27:49 +00:00
|
|
|
|
|
|
|
/// Calculates transaction fee.
|
|
|
|
///
|
|
|
|
/// 'Fee' being the amount that will be paid for mining a transaction with the current inputs
|
|
|
|
/// and outputs i.e., the difference in value of the total inputs and the total outputs.
|
|
|
|
///
|
|
|
|
/// ## Errors
|
|
|
|
///
|
|
|
|
/// - [`Error::MissingUtxo`] when UTXO information for any input is not present or is invalid.
|
|
|
|
/// - [`Error::NegativeFee`] if calculated value is negative.
|
|
|
|
/// - [`Error::FeeOverflow`] if an integer overflow occurs.
|
|
|
|
pub fn fee(&self) -> Result<Amount, Error> {
|
|
|
|
let mut inputs: u64 = 0;
|
|
|
|
for utxo in self.iter_funding_utxos() {
|
|
|
|
inputs = inputs.checked_add(utxo?.value).ok_or(Error::FeeOverflow)?;
|
|
|
|
}
|
|
|
|
let mut outputs: u64 = 0;
|
|
|
|
for out in &self.unsigned_tx.output {
|
|
|
|
outputs = outputs.checked_add(out.value).ok_or(Error::FeeOverflow)?;
|
|
|
|
}
|
|
|
|
inputs.checked_sub(outputs).map(Amount::from_sat).ok_or(Error::NegativeFee)
|
|
|
|
}
|
2022-04-19 05:46:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Data required to call [`GetKey`] to get the private key to sign an input.
|
|
|
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
|
|
#[non_exhaustive]
|
|
|
|
pub enum KeyRequest {
|
|
|
|
/// Request a private key using the associated public key.
|
|
|
|
Pubkey(PublicKey),
|
|
|
|
/// Request a private key using BIP-32 fingerprint and derivation path.
|
|
|
|
Bip32(KeySource),
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Trait to get a private key from a key request, key is then used to sign an input.
|
|
|
|
pub trait GetKey {
|
|
|
|
/// An error occurred while getting the key.
|
|
|
|
type Error: core::fmt::Debug;
|
|
|
|
|
|
|
|
/// Attempts to get the private key for `key_request`.
|
|
|
|
///
|
|
|
|
/// # Returns
|
|
|
|
/// - `Some(key)` if the key is found.
|
|
|
|
/// - `None` if the key was not found but no error was encountered.
|
|
|
|
/// - `Err` if an error was encountered while looking for the key.
|
|
|
|
fn get_key<C: Signing>(&self, key_request: KeyRequest, secp: &Secp256k1<C>) -> Result<Option<PrivateKey>, Self::Error>;
|
|
|
|
}
|
|
|
|
|
|
|
|
impl GetKey for ExtendedPrivKey {
|
|
|
|
type Error = GetKeyError;
|
|
|
|
|
|
|
|
fn get_key<C: Signing>(&self, key_request: KeyRequest, secp: &Secp256k1<C>) -> Result<Option<PrivateKey>, Self::Error> {
|
|
|
|
match key_request {
|
|
|
|
KeyRequest::Pubkey(_) => Err(GetKeyError::NotSupported),
|
|
|
|
KeyRequest::Bip32((fingerprint, path)) => {
|
|
|
|
let key = if self.fingerprint(secp) == fingerprint {
|
|
|
|
let k = self.derive_priv(secp, &path)?;
|
|
|
|
Some(k.to_priv())
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
};
|
|
|
|
Ok(key)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Map of input index -> pubkey associated with secret key used to create signature for that input.
|
|
|
|
pub type SigningKeys = BTreeMap<usize, Vec<PublicKey>>;
|
|
|
|
|
|
|
|
/// Map of input index -> the error encountered while attempting to sign that input.
|
|
|
|
pub type SigningErrors = BTreeMap<usize, SignError>;
|
|
|
|
|
|
|
|
#[rustfmt::skip]
|
|
|
|
macro_rules! impl_get_key_for_set {
|
|
|
|
($set:ident) => {
|
|
|
|
|
|
|
|
impl GetKey for $set<ExtendedPrivKey> {
|
|
|
|
type Error = GetKeyError;
|
|
|
|
|
|
|
|
fn get_key<C: Signing>(
|
|
|
|
&self,
|
|
|
|
key_request: KeyRequest,
|
|
|
|
secp: &Secp256k1<C>
|
|
|
|
) -> Result<Option<PrivateKey>, Self::Error> {
|
|
|
|
match key_request {
|
|
|
|
KeyRequest::Pubkey(_) => Err(GetKeyError::NotSupported),
|
|
|
|
KeyRequest::Bip32((fingerprint, path)) => {
|
|
|
|
for xpriv in self.iter() {
|
|
|
|
if xpriv.parent_fingerprint == fingerprint {
|
|
|
|
let k = xpriv.derive_priv(secp, &path)?;
|
|
|
|
return Ok(Some(k.to_priv()));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Ok(None)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}}}
|
|
|
|
impl_get_key_for_set!(BTreeSet);
|
|
|
|
#[cfg(feature = "std")]
|
|
|
|
impl_get_key_for_set!(HashSet);
|
|
|
|
|
|
|
|
#[rustfmt::skip]
|
|
|
|
macro_rules! impl_get_key_for_map {
|
|
|
|
($map:ident) => {
|
|
|
|
|
|
|
|
impl GetKey for $map<PublicKey, PrivateKey> {
|
|
|
|
type Error = GetKeyError;
|
|
|
|
|
|
|
|
fn get_key<C: Signing>(
|
|
|
|
&self,
|
|
|
|
key_request: KeyRequest,
|
|
|
|
_: &Secp256k1<C>,
|
|
|
|
) -> Result<Option<PrivateKey>, Self::Error> {
|
|
|
|
match key_request {
|
|
|
|
KeyRequest::Pubkey(pk) => Ok(self.get(&pk).cloned()),
|
|
|
|
KeyRequest::Bip32(_) => Err(GetKeyError::NotSupported),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}}}
|
|
|
|
impl_get_key_for_map!(BTreeMap);
|
|
|
|
#[cfg(feature = "std")]
|
|
|
|
impl_get_key_for_map!(HashMap);
|
|
|
|
|
|
|
|
/// Errors when getting a key.
|
|
|
|
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
|
|
|
|
#[non_exhaustive]
|
|
|
|
pub enum GetKeyError {
|
|
|
|
/// A bip32 error.
|
|
|
|
Bip32(bip32::Error),
|
|
|
|
/// The GetKey operation is not supported for this key request.
|
|
|
|
NotSupported,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl fmt::Display for GetKeyError {
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
|
|
use GetKeyError::*;
|
|
|
|
|
|
|
|
match *self {
|
|
|
|
Bip32(ref e) => write_err!(f, "a bip23 error"; e),
|
|
|
|
NotSupported => f.write_str("the GetKey operation is not supported for this key request"),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(feature = "std")]
|
|
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
|
|
|
|
impl std::error::Error for GetKeyError {
|
|
|
|
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
|
|
|
|
use GetKeyError::*;
|
|
|
|
|
|
|
|
match *self {
|
|
|
|
NotSupported => None,
|
|
|
|
Bip32(ref e) => Some(e),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<bip32::Error> for GetKeyError {
|
|
|
|
fn from(e: bip32::Error) -> Self {
|
|
|
|
GetKeyError::Bip32(e)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// The various output types supported by the Bitcoin network.
|
|
|
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
|
|
|
#[non_exhaustive]
|
|
|
|
pub enum OutputType {
|
|
|
|
/// An output of type: pay-to-pubkey or pay-to-pubkey-hash.
|
|
|
|
Bare,
|
|
|
|
/// A pay-to-witness-pubkey-hash output (P2WPKH).
|
|
|
|
Wpkh,
|
|
|
|
/// A pay-to-witness-script-hash output (P2WSH).
|
|
|
|
Wsh,
|
|
|
|
/// A nested segwit output, pay-to-witness-pubkey-hash nested in a pay-to-script-hash.
|
|
|
|
ShWpkh,
|
|
|
|
/// A nested segwit output, pay-to-witness-script-hash nested in a pay-to-script-hash.
|
|
|
|
ShWsh,
|
|
|
|
/// A pay-to-script-hash output excluding wrapped segwit (P2SH).
|
|
|
|
Sh,
|
|
|
|
/// A taproot output (P2TR).
|
|
|
|
Tr,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl OutputType {
|
|
|
|
/// The signing algorithm used to sign this output type.
|
|
|
|
pub fn signing_algorithm(&self) -> SigningAlgorithm {
|
|
|
|
use OutputType::*;
|
|
|
|
|
|
|
|
match self {
|
|
|
|
Bare | Wpkh | Wsh | ShWpkh | ShWsh | Sh => SigningAlgorithm::Ecdsa,
|
|
|
|
Tr => SigningAlgorithm::Schnorr,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Signing algorithms supported by the Bitcoin network.
|
|
|
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
|
|
|
pub enum SigningAlgorithm {
|
|
|
|
/// The Elliptic Curve Digital Signature Algorithm (see [wikipedia]).
|
|
|
|
///
|
|
|
|
/// [wikipedia]: https://en.wikipedia.org/wiki/Elliptic_Curve_Digital_Signature_Algorithm
|
|
|
|
Ecdsa,
|
|
|
|
/// The Schnorr signature algorithm (see [wikipedia]).
|
|
|
|
///
|
|
|
|
/// [wikipedia]: https://en.wikipedia.org/wiki/Schnorr_signature
|
|
|
|
Schnorr,
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Errors encountered while calculating the sighash message.
|
|
|
|
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone)]
|
|
|
|
pub enum SignError {
|
|
|
|
/// An ECDSA key-related error occurred.
|
|
|
|
EcdsaSig(EcdsaSigError),
|
|
|
|
/// Input index out of bounds (actual index, maximum index allowed).
|
|
|
|
IndexOutOfBounds(usize, usize),
|
|
|
|
/// Invalid Sighash type.
|
|
|
|
InvalidSighashType,
|
|
|
|
/// Missing input utxo.
|
|
|
|
MissingInputUtxo,
|
|
|
|
/// Missing Redeem script.
|
|
|
|
MissingRedeemScript,
|
|
|
|
/// Missing spending utxo.
|
|
|
|
MissingSpendUtxo,
|
|
|
|
/// Missing witness script.
|
|
|
|
MissingWitnessScript,
|
|
|
|
/// Signing algorithm and key type does not match.
|
|
|
|
MismatchedAlgoKey,
|
|
|
|
/// Attempted to ECDSA sign an non-ECDSA input.
|
|
|
|
NotEcdsa,
|
|
|
|
/// The `scriptPubkey` is not a P2WPKH script.
|
|
|
|
NotWpkh,
|
|
|
|
/// Sighash computation error.
|
|
|
|
SighashComputation(sighash::Error),
|
|
|
|
/// Unable to determine the output type.
|
|
|
|
UnknownOutputType,
|
|
|
|
/// Unable to find key.
|
|
|
|
KeyNotFound,
|
|
|
|
/// Attempt to sign an input with the wrong signing algorithm.
|
|
|
|
WrongSigningAlgorithm,
|
|
|
|
/// Signing request currently unsupported.
|
|
|
|
Unsupported
|
|
|
|
}
|
|
|
|
|
|
|
|
impl fmt::Display for SignError {
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
|
|
use self::SignError::*;
|
|
|
|
|
|
|
|
match *self {
|
|
|
|
IndexOutOfBounds(ind, len) => {
|
|
|
|
write!(f, "index {}, psbt input len: {}", ind, len)
|
|
|
|
}
|
|
|
|
InvalidSighashType => write!(f, "invalid sighash type"),
|
|
|
|
MissingInputUtxo => write!(f, "missing input utxo in PBST"),
|
|
|
|
MissingRedeemScript => write!(f, "missing redeem script"),
|
|
|
|
MissingSpendUtxo => write!(f, "missing spend utxo in PSBT"),
|
|
|
|
MissingWitnessScript => write!(f, "missing witness script"),
|
|
|
|
MismatchedAlgoKey => write!(f, "signing algorithm and key type does not match"),
|
|
|
|
NotEcdsa => write!(f, "attempted to ECDSA sign an non-ECDSA input"),
|
|
|
|
NotWpkh => write!(f, "the scriptPubkey is not a P2WPKH script"),
|
|
|
|
SighashComputation(e) => write!(f, "sighash: {}", e),
|
|
|
|
EcdsaSig(ref e) => write_err!(f, "ecdsa signature"; e),
|
|
|
|
UnknownOutputType => write!(f, "unable to determine the output type"),
|
|
|
|
KeyNotFound => write!(f, "unable to find key"),
|
|
|
|
WrongSigningAlgorithm => write!(f, "attempt to sign an input with the wrong signing algorithm"),
|
|
|
|
Unsupported => write!(f, "signing request currently unsupported"),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(feature = "std")]
|
|
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
|
|
|
|
impl std::error::Error for SignError {
|
|
|
|
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
|
|
|
|
use self::SignError::*;
|
|
|
|
|
|
|
|
match *self {
|
|
|
|
IndexOutOfBounds(_, _)
|
|
|
|
| InvalidSighashType
|
|
|
|
| MissingInputUtxo
|
|
|
|
| MissingRedeemScript
|
|
|
|
| MissingSpendUtxo
|
|
|
|
| MissingWitnessScript
|
|
|
|
| MismatchedAlgoKey
|
|
|
|
| NotEcdsa
|
|
|
|
| NotWpkh
|
|
|
|
| UnknownOutputType
|
|
|
|
| KeyNotFound
|
|
|
|
| WrongSigningAlgorithm
|
|
|
|
| Unsupported => None,
|
|
|
|
EcdsaSig(ref e) => Some(e),
|
|
|
|
SighashComputation(ref e) => Some(e),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<sighash::Error> for SignError {
|
|
|
|
fn from(e: sighash::Error) -> Self {
|
|
|
|
SignError::SighashComputation(e)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<EcdsaSigError> for SignError {
|
|
|
|
fn from(e: EcdsaSigError) -> Self {
|
|
|
|
SignError::EcdsaSig(e)
|
|
|
|
}
|
2018-08-10 20:31:58 +00:00
|
|
|
}
|
|
|
|
|
2021-01-27 20:19:04 +00:00
|
|
|
#[cfg(feature = "base64")]
|
2021-02-21 19:18:48 +00:00
|
|
|
mod display_from_str {
|
|
|
|
use super::PartiallySignedTransaction;
|
2021-06-09 10:34:44 +00:00
|
|
|
use core::fmt::{Display, Formatter, self};
|
|
|
|
use core::str::FromStr;
|
2022-05-02 23:02:58 +00:00
|
|
|
use crate::consensus::encode::{Error, self};
|
|
|
|
use base64::display::Base64Display;
|
2022-09-05 02:19:28 +00:00
|
|
|
use bitcoin_internals::write_err;
|
2021-01-27 20:19:04 +00:00
|
|
|
|
2022-04-19 05:26:26 +00:00
|
|
|
/// Error encountered during PSBT decoding from Base64 string.
|
2021-02-21 19:18:48 +00:00
|
|
|
#[derive(Debug)]
|
2021-07-28 19:38:58 +00:00
|
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "base64")))]
|
2022-05-31 04:29:50 +00:00
|
|
|
#[non_exhaustive]
|
2021-02-21 19:18:48 +00:00
|
|
|
pub enum PsbtParseError {
|
2022-04-19 05:26:26 +00:00
|
|
|
/// Error in internal PSBT data structure.
|
2021-02-21 19:18:48 +00:00
|
|
|
PsbtEncoding(Error),
|
2022-04-19 05:26:26 +00:00
|
|
|
/// Error in PSBT Base64 encoding.
|
2021-02-21 19:18:48 +00:00
|
|
|
Base64Encoding(::base64::DecodeError)
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Display for PsbtParseError {
|
|
|
|
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
2022-05-25 02:56:51 +00:00
|
|
|
use self::PsbtParseError::*;
|
|
|
|
|
|
|
|
match *self {
|
|
|
|
PsbtEncoding(ref e) => write_err!(f, "error in internal PSBT data structure"; e),
|
|
|
|
Base64Encoding(ref e) => write_err!(f, "error in PSBT base64 encoding"; e),
|
2021-02-21 19:18:48 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-09 10:34:44 +00:00
|
|
|
#[cfg(feature = "std")]
|
2021-07-28 19:38:58 +00:00
|
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
|
2022-05-04 05:56:24 +00:00
|
|
|
impl std::error::Error for PsbtParseError {
|
|
|
|
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
|
|
|
|
use self::PsbtParseError::*;
|
|
|
|
|
|
|
|
match self {
|
|
|
|
PsbtEncoding(e) => Some(e),
|
|
|
|
Base64Encoding(e) => Some(e),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-02-21 19:18:48 +00:00
|
|
|
|
2021-07-28 19:38:58 +00:00
|
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "base64")))]
|
2021-01-27 20:19:04 +00:00
|
|
|
impl Display for PartiallySignedTransaction {
|
|
|
|
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
2022-06-01 22:08:56 +00:00
|
|
|
write!(f, "{}", Base64Display::with_config(&encode::serialize(self), base64::STANDARD))
|
2021-01-27 20:19:04 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-07-28 19:38:58 +00:00
|
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "base64")))]
|
2021-01-27 20:19:04 +00:00
|
|
|
impl FromStr for PartiallySignedTransaction {
|
2021-02-21 19:18:48 +00:00
|
|
|
type Err = PsbtParseError;
|
2021-01-27 20:19:04 +00:00
|
|
|
|
|
|
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
2022-06-01 22:08:56 +00:00
|
|
|
let data = base64::decode(s).map_err(PsbtParseError::Base64Encoding)?;
|
2022-06-23 03:58:29 +00:00
|
|
|
encode::deserialize(&data).map_err(PsbtParseError::PsbtEncoding)
|
2021-01-27 20:19:04 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-02-21 19:18:48 +00:00
|
|
|
#[cfg(feature = "base64")]
|
2021-07-28 19:38:58 +00:00
|
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "base64")))]
|
2021-02-21 19:18:48 +00:00
|
|
|
pub use self::display_from_str::PsbtParseError;
|
2021-01-27 20:19:04 +00:00
|
|
|
|
2019-07-11 14:56:37 +00:00
|
|
|
impl Encodable for PartiallySignedTransaction {
|
2022-06-29 01:22:12 +00:00
|
|
|
fn consensus_encode<W: io::Write + ?Sized>(&self, w: &mut W) -> Result<usize, io::Error> {
|
2019-05-23 20:28:10 +00:00
|
|
|
let mut len = 0;
|
Take Writer/Reader by `&mut` in consensus en/decoding
Fix #1020 (see more relevant discussion there)
This definitely makes the amount of generics compiler
has to generate by avoding generating the same functions
for `R`, &mut R`, `&mut &mut R` and so on.
old:
```
> ls -al target/release/deps/bitcoin-07a9dabf1f3e0266
-rwxrwxr-x 1 dpc dpc 9947832 Jun 2 22:42 target/release/deps/bitcoin-07a9dabf1f3e0266
> strip target/release/deps/bitcoin-07a9dabf1f3e0266
> ls -al target/release/deps/bitcoin-07a9dabf1f3e0266
-rwxrwxr-x 1 dpc dpc 4463024 Jun 2 22:46 target/release/deps/bitcoin-07a9dabf1f3e0266
```
new:
```
> ls -al target/release/deps/bitcoin-07a9dabf1f3e0266
-rwxrwxr-x 1 dpc dpc 9866800 Jun 2 22:44 target/release/deps/bitcoin-07a9dabf1f3e0266
> strip target/release/deps/bitcoin-07a9dabf1f3e0266
> ls -al target/release/deps/bitcoin-07a9dabf1f3e0266
-rwxrwxr-x 1 dpc dpc 4393392 Jun 2 22:45 target/release/deps/bitcoin-07a9dabf1f3e0266
```
In the unit-test binary itself, it saves ~100KB of data.
I did not expect much performance gains, but turn out I was wrong(*):
old:
```
test blockdata::block::benches::bench_block_deserialize ... bench: 1,072,710 ns/iter (+/- 21,871)
test blockdata::block::benches::bench_block_serialize ... bench: 191,223 ns/iter (+/- 5,833)
test blockdata::block::benches::bench_block_serialize_logic ... bench: 37,543 ns/iter (+/- 732)
test blockdata::block::benches::bench_stream_reader ... bench: 1,872,455 ns/iter (+/- 149,519)
test blockdata::transaction::benches::bench_transaction_deserialize ... bench: 136 ns/iter (+/- 3)
test blockdata::transaction::benches::bench_transaction_serialize ... bench: 51 ns/iter (+/- 8)
test blockdata::transaction::benches::bench_transaction_serialize_logic ... bench: 5 ns/iter (+/- 0)
test blockdata::transaction::benches::bench_transaction_size ... bench: 3 ns/iter (+/- 0)
```
new:
```
test blockdata::block::benches::bench_block_deserialize ... bench: 1,028,574 ns/iter (+/- 10,910)
test blockdata::block::benches::bench_block_serialize ... bench: 162,143 ns/iter (+/- 3,363)
test blockdata::block::benches::bench_block_serialize_logic ... bench: 30,725 ns/iter (+/- 695)
test blockdata::block::benches::bench_stream_reader ... bench: 1,437,071 ns/iter (+/- 53,694)
test blockdata::transaction::benches::bench_transaction_deserialize ... bench: 92 ns/iter (+/- 2)
test blockdata::transaction::benches::bench_transaction_serialize ... bench: 17 ns/iter (+/- 0)
test blockdata::transaction::benches::bench_transaction_serialize_logic ... bench: 5 ns/iter (+/- 0)
test blockdata::transaction::benches::bench_transaction_size ... bench: 4 ns/iter (+/- 0)
```
(*) - I'm benchmarking on a noisy laptop. Take this with a grain of salt. But I think
at least it doesn't make anything slower.
While doing all this manual labor that will probably generate conflicts,
I took a liberty of changing generic type names and variable names to
`r` and `R` (reader) and `w` and `W` for writer.
2022-06-03 04:50:42 +00:00
|
|
|
len += b"psbt".consensus_encode(w)?;
|
2018-08-10 20:31:58 +00:00
|
|
|
|
Take Writer/Reader by `&mut` in consensus en/decoding
Fix #1020 (see more relevant discussion there)
This definitely makes the amount of generics compiler
has to generate by avoding generating the same functions
for `R`, &mut R`, `&mut &mut R` and so on.
old:
```
> ls -al target/release/deps/bitcoin-07a9dabf1f3e0266
-rwxrwxr-x 1 dpc dpc 9947832 Jun 2 22:42 target/release/deps/bitcoin-07a9dabf1f3e0266
> strip target/release/deps/bitcoin-07a9dabf1f3e0266
> ls -al target/release/deps/bitcoin-07a9dabf1f3e0266
-rwxrwxr-x 1 dpc dpc 4463024 Jun 2 22:46 target/release/deps/bitcoin-07a9dabf1f3e0266
```
new:
```
> ls -al target/release/deps/bitcoin-07a9dabf1f3e0266
-rwxrwxr-x 1 dpc dpc 9866800 Jun 2 22:44 target/release/deps/bitcoin-07a9dabf1f3e0266
> strip target/release/deps/bitcoin-07a9dabf1f3e0266
> ls -al target/release/deps/bitcoin-07a9dabf1f3e0266
-rwxrwxr-x 1 dpc dpc 4393392 Jun 2 22:45 target/release/deps/bitcoin-07a9dabf1f3e0266
```
In the unit-test binary itself, it saves ~100KB of data.
I did not expect much performance gains, but turn out I was wrong(*):
old:
```
test blockdata::block::benches::bench_block_deserialize ... bench: 1,072,710 ns/iter (+/- 21,871)
test blockdata::block::benches::bench_block_serialize ... bench: 191,223 ns/iter (+/- 5,833)
test blockdata::block::benches::bench_block_serialize_logic ... bench: 37,543 ns/iter (+/- 732)
test blockdata::block::benches::bench_stream_reader ... bench: 1,872,455 ns/iter (+/- 149,519)
test blockdata::transaction::benches::bench_transaction_deserialize ... bench: 136 ns/iter (+/- 3)
test blockdata::transaction::benches::bench_transaction_serialize ... bench: 51 ns/iter (+/- 8)
test blockdata::transaction::benches::bench_transaction_serialize_logic ... bench: 5 ns/iter (+/- 0)
test blockdata::transaction::benches::bench_transaction_size ... bench: 3 ns/iter (+/- 0)
```
new:
```
test blockdata::block::benches::bench_block_deserialize ... bench: 1,028,574 ns/iter (+/- 10,910)
test blockdata::block::benches::bench_block_serialize ... bench: 162,143 ns/iter (+/- 3,363)
test blockdata::block::benches::bench_block_serialize_logic ... bench: 30,725 ns/iter (+/- 695)
test blockdata::block::benches::bench_stream_reader ... bench: 1,437,071 ns/iter (+/- 53,694)
test blockdata::transaction::benches::bench_transaction_deserialize ... bench: 92 ns/iter (+/- 2)
test blockdata::transaction::benches::bench_transaction_serialize ... bench: 17 ns/iter (+/- 0)
test blockdata::transaction::benches::bench_transaction_serialize_logic ... bench: 5 ns/iter (+/- 0)
test blockdata::transaction::benches::bench_transaction_size ... bench: 4 ns/iter (+/- 0)
```
(*) - I'm benchmarking on a noisy laptop. Take this with a grain of salt. But I think
at least it doesn't make anything slower.
While doing all this manual labor that will probably generate conflicts,
I took a liberty of changing generic type names and variable names to
`r` and `R` (reader) and `w` and `W` for writer.
2022-06-03 04:50:42 +00:00
|
|
|
len += 0xff_u8.consensus_encode(w)?;
|
2018-08-10 20:31:58 +00:00
|
|
|
|
Take Writer/Reader by `&mut` in consensus en/decoding
Fix #1020 (see more relevant discussion there)
This definitely makes the amount of generics compiler
has to generate by avoding generating the same functions
for `R`, &mut R`, `&mut &mut R` and so on.
old:
```
> ls -al target/release/deps/bitcoin-07a9dabf1f3e0266
-rwxrwxr-x 1 dpc dpc 9947832 Jun 2 22:42 target/release/deps/bitcoin-07a9dabf1f3e0266
> strip target/release/deps/bitcoin-07a9dabf1f3e0266
> ls -al target/release/deps/bitcoin-07a9dabf1f3e0266
-rwxrwxr-x 1 dpc dpc 4463024 Jun 2 22:46 target/release/deps/bitcoin-07a9dabf1f3e0266
```
new:
```
> ls -al target/release/deps/bitcoin-07a9dabf1f3e0266
-rwxrwxr-x 1 dpc dpc 9866800 Jun 2 22:44 target/release/deps/bitcoin-07a9dabf1f3e0266
> strip target/release/deps/bitcoin-07a9dabf1f3e0266
> ls -al target/release/deps/bitcoin-07a9dabf1f3e0266
-rwxrwxr-x 1 dpc dpc 4393392 Jun 2 22:45 target/release/deps/bitcoin-07a9dabf1f3e0266
```
In the unit-test binary itself, it saves ~100KB of data.
I did not expect much performance gains, but turn out I was wrong(*):
old:
```
test blockdata::block::benches::bench_block_deserialize ... bench: 1,072,710 ns/iter (+/- 21,871)
test blockdata::block::benches::bench_block_serialize ... bench: 191,223 ns/iter (+/- 5,833)
test blockdata::block::benches::bench_block_serialize_logic ... bench: 37,543 ns/iter (+/- 732)
test blockdata::block::benches::bench_stream_reader ... bench: 1,872,455 ns/iter (+/- 149,519)
test blockdata::transaction::benches::bench_transaction_deserialize ... bench: 136 ns/iter (+/- 3)
test blockdata::transaction::benches::bench_transaction_serialize ... bench: 51 ns/iter (+/- 8)
test blockdata::transaction::benches::bench_transaction_serialize_logic ... bench: 5 ns/iter (+/- 0)
test blockdata::transaction::benches::bench_transaction_size ... bench: 3 ns/iter (+/- 0)
```
new:
```
test blockdata::block::benches::bench_block_deserialize ... bench: 1,028,574 ns/iter (+/- 10,910)
test blockdata::block::benches::bench_block_serialize ... bench: 162,143 ns/iter (+/- 3,363)
test blockdata::block::benches::bench_block_serialize_logic ... bench: 30,725 ns/iter (+/- 695)
test blockdata::block::benches::bench_stream_reader ... bench: 1,437,071 ns/iter (+/- 53,694)
test blockdata::transaction::benches::bench_transaction_deserialize ... bench: 92 ns/iter (+/- 2)
test blockdata::transaction::benches::bench_transaction_serialize ... bench: 17 ns/iter (+/- 0)
test blockdata::transaction::benches::bench_transaction_serialize_logic ... bench: 5 ns/iter (+/- 0)
test blockdata::transaction::benches::bench_transaction_size ... bench: 4 ns/iter (+/- 0)
```
(*) - I'm benchmarking on a noisy laptop. Take this with a grain of salt. But I think
at least it doesn't make anything slower.
While doing all this manual labor that will probably generate conflicts,
I took a liberty of changing generic type names and variable names to
`r` and `R` (reader) and `w` and `W` for writer.
2022-06-03 04:50:42 +00:00
|
|
|
len += self.consensus_encode_map(w)?;
|
2018-08-10 20:31:58 +00:00
|
|
|
|
|
|
|
for i in &self.inputs {
|
Take Writer/Reader by `&mut` in consensus en/decoding
Fix #1020 (see more relevant discussion there)
This definitely makes the amount of generics compiler
has to generate by avoding generating the same functions
for `R`, &mut R`, `&mut &mut R` and so on.
old:
```
> ls -al target/release/deps/bitcoin-07a9dabf1f3e0266
-rwxrwxr-x 1 dpc dpc 9947832 Jun 2 22:42 target/release/deps/bitcoin-07a9dabf1f3e0266
> strip target/release/deps/bitcoin-07a9dabf1f3e0266
> ls -al target/release/deps/bitcoin-07a9dabf1f3e0266
-rwxrwxr-x 1 dpc dpc 4463024 Jun 2 22:46 target/release/deps/bitcoin-07a9dabf1f3e0266
```
new:
```
> ls -al target/release/deps/bitcoin-07a9dabf1f3e0266
-rwxrwxr-x 1 dpc dpc 9866800 Jun 2 22:44 target/release/deps/bitcoin-07a9dabf1f3e0266
> strip target/release/deps/bitcoin-07a9dabf1f3e0266
> ls -al target/release/deps/bitcoin-07a9dabf1f3e0266
-rwxrwxr-x 1 dpc dpc 4393392 Jun 2 22:45 target/release/deps/bitcoin-07a9dabf1f3e0266
```
In the unit-test binary itself, it saves ~100KB of data.
I did not expect much performance gains, but turn out I was wrong(*):
old:
```
test blockdata::block::benches::bench_block_deserialize ... bench: 1,072,710 ns/iter (+/- 21,871)
test blockdata::block::benches::bench_block_serialize ... bench: 191,223 ns/iter (+/- 5,833)
test blockdata::block::benches::bench_block_serialize_logic ... bench: 37,543 ns/iter (+/- 732)
test blockdata::block::benches::bench_stream_reader ... bench: 1,872,455 ns/iter (+/- 149,519)
test blockdata::transaction::benches::bench_transaction_deserialize ... bench: 136 ns/iter (+/- 3)
test blockdata::transaction::benches::bench_transaction_serialize ... bench: 51 ns/iter (+/- 8)
test blockdata::transaction::benches::bench_transaction_serialize_logic ... bench: 5 ns/iter (+/- 0)
test blockdata::transaction::benches::bench_transaction_size ... bench: 3 ns/iter (+/- 0)
```
new:
```
test blockdata::block::benches::bench_block_deserialize ... bench: 1,028,574 ns/iter (+/- 10,910)
test blockdata::block::benches::bench_block_serialize ... bench: 162,143 ns/iter (+/- 3,363)
test blockdata::block::benches::bench_block_serialize_logic ... bench: 30,725 ns/iter (+/- 695)
test blockdata::block::benches::bench_stream_reader ... bench: 1,437,071 ns/iter (+/- 53,694)
test blockdata::transaction::benches::bench_transaction_deserialize ... bench: 92 ns/iter (+/- 2)
test blockdata::transaction::benches::bench_transaction_serialize ... bench: 17 ns/iter (+/- 0)
test blockdata::transaction::benches::bench_transaction_serialize_logic ... bench: 5 ns/iter (+/- 0)
test blockdata::transaction::benches::bench_transaction_size ... bench: 4 ns/iter (+/- 0)
```
(*) - I'm benchmarking on a noisy laptop. Take this with a grain of salt. But I think
at least it doesn't make anything slower.
While doing all this manual labor that will probably generate conflicts,
I took a liberty of changing generic type names and variable names to
`r` and `R` (reader) and `w` and `W` for writer.
2022-06-03 04:50:42 +00:00
|
|
|
len += i.consensus_encode(w)?;
|
2018-08-10 20:31:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
for i in &self.outputs {
|
Take Writer/Reader by `&mut` in consensus en/decoding
Fix #1020 (see more relevant discussion there)
This definitely makes the amount of generics compiler
has to generate by avoding generating the same functions
for `R`, &mut R`, `&mut &mut R` and so on.
old:
```
> ls -al target/release/deps/bitcoin-07a9dabf1f3e0266
-rwxrwxr-x 1 dpc dpc 9947832 Jun 2 22:42 target/release/deps/bitcoin-07a9dabf1f3e0266
> strip target/release/deps/bitcoin-07a9dabf1f3e0266
> ls -al target/release/deps/bitcoin-07a9dabf1f3e0266
-rwxrwxr-x 1 dpc dpc 4463024 Jun 2 22:46 target/release/deps/bitcoin-07a9dabf1f3e0266
```
new:
```
> ls -al target/release/deps/bitcoin-07a9dabf1f3e0266
-rwxrwxr-x 1 dpc dpc 9866800 Jun 2 22:44 target/release/deps/bitcoin-07a9dabf1f3e0266
> strip target/release/deps/bitcoin-07a9dabf1f3e0266
> ls -al target/release/deps/bitcoin-07a9dabf1f3e0266
-rwxrwxr-x 1 dpc dpc 4393392 Jun 2 22:45 target/release/deps/bitcoin-07a9dabf1f3e0266
```
In the unit-test binary itself, it saves ~100KB of data.
I did not expect much performance gains, but turn out I was wrong(*):
old:
```
test blockdata::block::benches::bench_block_deserialize ... bench: 1,072,710 ns/iter (+/- 21,871)
test blockdata::block::benches::bench_block_serialize ... bench: 191,223 ns/iter (+/- 5,833)
test blockdata::block::benches::bench_block_serialize_logic ... bench: 37,543 ns/iter (+/- 732)
test blockdata::block::benches::bench_stream_reader ... bench: 1,872,455 ns/iter (+/- 149,519)
test blockdata::transaction::benches::bench_transaction_deserialize ... bench: 136 ns/iter (+/- 3)
test blockdata::transaction::benches::bench_transaction_serialize ... bench: 51 ns/iter (+/- 8)
test blockdata::transaction::benches::bench_transaction_serialize_logic ... bench: 5 ns/iter (+/- 0)
test blockdata::transaction::benches::bench_transaction_size ... bench: 3 ns/iter (+/- 0)
```
new:
```
test blockdata::block::benches::bench_block_deserialize ... bench: 1,028,574 ns/iter (+/- 10,910)
test blockdata::block::benches::bench_block_serialize ... bench: 162,143 ns/iter (+/- 3,363)
test blockdata::block::benches::bench_block_serialize_logic ... bench: 30,725 ns/iter (+/- 695)
test blockdata::block::benches::bench_stream_reader ... bench: 1,437,071 ns/iter (+/- 53,694)
test blockdata::transaction::benches::bench_transaction_deserialize ... bench: 92 ns/iter (+/- 2)
test blockdata::transaction::benches::bench_transaction_serialize ... bench: 17 ns/iter (+/- 0)
test blockdata::transaction::benches::bench_transaction_serialize_logic ... bench: 5 ns/iter (+/- 0)
test blockdata::transaction::benches::bench_transaction_size ... bench: 4 ns/iter (+/- 0)
```
(*) - I'm benchmarking on a noisy laptop. Take this with a grain of salt. But I think
at least it doesn't make anything slower.
While doing all this manual labor that will probably generate conflicts,
I took a liberty of changing generic type names and variable names to
`r` and `R` (reader) and `w` and `W` for writer.
2022-06-03 04:50:42 +00:00
|
|
|
len += i.consensus_encode(w)?;
|
2018-08-10 20:31:58 +00:00
|
|
|
}
|
|
|
|
|
2019-05-23 20:28:10 +00:00
|
|
|
Ok(len)
|
2018-08-10 20:31:58 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-07-11 17:06:42 +00:00
|
|
|
impl Decodable for PartiallySignedTransaction {
|
2022-06-29 01:22:12 +00:00
|
|
|
fn consensus_decode_from_finite_reader<R: io::Read + ?Sized>(r: &mut R) -> Result<Self, encode::Error> {
|
Take Writer/Reader by `&mut` in consensus en/decoding
Fix #1020 (see more relevant discussion there)
This definitely makes the amount of generics compiler
has to generate by avoding generating the same functions
for `R`, &mut R`, `&mut &mut R` and so on.
old:
```
> ls -al target/release/deps/bitcoin-07a9dabf1f3e0266
-rwxrwxr-x 1 dpc dpc 9947832 Jun 2 22:42 target/release/deps/bitcoin-07a9dabf1f3e0266
> strip target/release/deps/bitcoin-07a9dabf1f3e0266
> ls -al target/release/deps/bitcoin-07a9dabf1f3e0266
-rwxrwxr-x 1 dpc dpc 4463024 Jun 2 22:46 target/release/deps/bitcoin-07a9dabf1f3e0266
```
new:
```
> ls -al target/release/deps/bitcoin-07a9dabf1f3e0266
-rwxrwxr-x 1 dpc dpc 9866800 Jun 2 22:44 target/release/deps/bitcoin-07a9dabf1f3e0266
> strip target/release/deps/bitcoin-07a9dabf1f3e0266
> ls -al target/release/deps/bitcoin-07a9dabf1f3e0266
-rwxrwxr-x 1 dpc dpc 4393392 Jun 2 22:45 target/release/deps/bitcoin-07a9dabf1f3e0266
```
In the unit-test binary itself, it saves ~100KB of data.
I did not expect much performance gains, but turn out I was wrong(*):
old:
```
test blockdata::block::benches::bench_block_deserialize ... bench: 1,072,710 ns/iter (+/- 21,871)
test blockdata::block::benches::bench_block_serialize ... bench: 191,223 ns/iter (+/- 5,833)
test blockdata::block::benches::bench_block_serialize_logic ... bench: 37,543 ns/iter (+/- 732)
test blockdata::block::benches::bench_stream_reader ... bench: 1,872,455 ns/iter (+/- 149,519)
test blockdata::transaction::benches::bench_transaction_deserialize ... bench: 136 ns/iter (+/- 3)
test blockdata::transaction::benches::bench_transaction_serialize ... bench: 51 ns/iter (+/- 8)
test blockdata::transaction::benches::bench_transaction_serialize_logic ... bench: 5 ns/iter (+/- 0)
test blockdata::transaction::benches::bench_transaction_size ... bench: 3 ns/iter (+/- 0)
```
new:
```
test blockdata::block::benches::bench_block_deserialize ... bench: 1,028,574 ns/iter (+/- 10,910)
test blockdata::block::benches::bench_block_serialize ... bench: 162,143 ns/iter (+/- 3,363)
test blockdata::block::benches::bench_block_serialize_logic ... bench: 30,725 ns/iter (+/- 695)
test blockdata::block::benches::bench_stream_reader ... bench: 1,437,071 ns/iter (+/- 53,694)
test blockdata::transaction::benches::bench_transaction_deserialize ... bench: 92 ns/iter (+/- 2)
test blockdata::transaction::benches::bench_transaction_serialize ... bench: 17 ns/iter (+/- 0)
test blockdata::transaction::benches::bench_transaction_serialize_logic ... bench: 5 ns/iter (+/- 0)
test blockdata::transaction::benches::bench_transaction_size ... bench: 4 ns/iter (+/- 0)
```
(*) - I'm benchmarking on a noisy laptop. Take this with a grain of salt. But I think
at least it doesn't make anything slower.
While doing all this manual labor that will probably generate conflicts,
I took a liberty of changing generic type names and variable names to
`r` and `R` (reader) and `w` and `W` for writer.
2022-06-03 04:50:42 +00:00
|
|
|
let magic: [u8; 4] = Decodable::consensus_decode(r)?;
|
2018-08-10 20:31:58 +00:00
|
|
|
|
|
|
|
if *b"psbt" != magic {
|
|
|
|
return Err(Error::InvalidMagic.into());
|
|
|
|
}
|
|
|
|
|
Take Writer/Reader by `&mut` in consensus en/decoding
Fix #1020 (see more relevant discussion there)
This definitely makes the amount of generics compiler
has to generate by avoding generating the same functions
for `R`, &mut R`, `&mut &mut R` and so on.
old:
```
> ls -al target/release/deps/bitcoin-07a9dabf1f3e0266
-rwxrwxr-x 1 dpc dpc 9947832 Jun 2 22:42 target/release/deps/bitcoin-07a9dabf1f3e0266
> strip target/release/deps/bitcoin-07a9dabf1f3e0266
> ls -al target/release/deps/bitcoin-07a9dabf1f3e0266
-rwxrwxr-x 1 dpc dpc 4463024 Jun 2 22:46 target/release/deps/bitcoin-07a9dabf1f3e0266
```
new:
```
> ls -al target/release/deps/bitcoin-07a9dabf1f3e0266
-rwxrwxr-x 1 dpc dpc 9866800 Jun 2 22:44 target/release/deps/bitcoin-07a9dabf1f3e0266
> strip target/release/deps/bitcoin-07a9dabf1f3e0266
> ls -al target/release/deps/bitcoin-07a9dabf1f3e0266
-rwxrwxr-x 1 dpc dpc 4393392 Jun 2 22:45 target/release/deps/bitcoin-07a9dabf1f3e0266
```
In the unit-test binary itself, it saves ~100KB of data.
I did not expect much performance gains, but turn out I was wrong(*):
old:
```
test blockdata::block::benches::bench_block_deserialize ... bench: 1,072,710 ns/iter (+/- 21,871)
test blockdata::block::benches::bench_block_serialize ... bench: 191,223 ns/iter (+/- 5,833)
test blockdata::block::benches::bench_block_serialize_logic ... bench: 37,543 ns/iter (+/- 732)
test blockdata::block::benches::bench_stream_reader ... bench: 1,872,455 ns/iter (+/- 149,519)
test blockdata::transaction::benches::bench_transaction_deserialize ... bench: 136 ns/iter (+/- 3)
test blockdata::transaction::benches::bench_transaction_serialize ... bench: 51 ns/iter (+/- 8)
test blockdata::transaction::benches::bench_transaction_serialize_logic ... bench: 5 ns/iter (+/- 0)
test blockdata::transaction::benches::bench_transaction_size ... bench: 3 ns/iter (+/- 0)
```
new:
```
test blockdata::block::benches::bench_block_deserialize ... bench: 1,028,574 ns/iter (+/- 10,910)
test blockdata::block::benches::bench_block_serialize ... bench: 162,143 ns/iter (+/- 3,363)
test blockdata::block::benches::bench_block_serialize_logic ... bench: 30,725 ns/iter (+/- 695)
test blockdata::block::benches::bench_stream_reader ... bench: 1,437,071 ns/iter (+/- 53,694)
test blockdata::transaction::benches::bench_transaction_deserialize ... bench: 92 ns/iter (+/- 2)
test blockdata::transaction::benches::bench_transaction_serialize ... bench: 17 ns/iter (+/- 0)
test blockdata::transaction::benches::bench_transaction_serialize_logic ... bench: 5 ns/iter (+/- 0)
test blockdata::transaction::benches::bench_transaction_size ... bench: 4 ns/iter (+/- 0)
```
(*) - I'm benchmarking on a noisy laptop. Take this with a grain of salt. But I think
at least it doesn't make anything slower.
While doing all this manual labor that will probably generate conflicts,
I took a liberty of changing generic type names and variable names to
`r` and `R` (reader) and `w` and `W` for writer.
2022-06-03 04:50:42 +00:00
|
|
|
if 0xff_u8 != u8::consensus_decode(r)? {
|
2018-08-10 20:31:58 +00:00
|
|
|
return Err(Error::InvalidSeparator.into());
|
|
|
|
}
|
|
|
|
|
Take Writer/Reader by `&mut` in consensus en/decoding
Fix #1020 (see more relevant discussion there)
This definitely makes the amount of generics compiler
has to generate by avoding generating the same functions
for `R`, &mut R`, `&mut &mut R` and so on.
old:
```
> ls -al target/release/deps/bitcoin-07a9dabf1f3e0266
-rwxrwxr-x 1 dpc dpc 9947832 Jun 2 22:42 target/release/deps/bitcoin-07a9dabf1f3e0266
> strip target/release/deps/bitcoin-07a9dabf1f3e0266
> ls -al target/release/deps/bitcoin-07a9dabf1f3e0266
-rwxrwxr-x 1 dpc dpc 4463024 Jun 2 22:46 target/release/deps/bitcoin-07a9dabf1f3e0266
```
new:
```
> ls -al target/release/deps/bitcoin-07a9dabf1f3e0266
-rwxrwxr-x 1 dpc dpc 9866800 Jun 2 22:44 target/release/deps/bitcoin-07a9dabf1f3e0266
> strip target/release/deps/bitcoin-07a9dabf1f3e0266
> ls -al target/release/deps/bitcoin-07a9dabf1f3e0266
-rwxrwxr-x 1 dpc dpc 4393392 Jun 2 22:45 target/release/deps/bitcoin-07a9dabf1f3e0266
```
In the unit-test binary itself, it saves ~100KB of data.
I did not expect much performance gains, but turn out I was wrong(*):
old:
```
test blockdata::block::benches::bench_block_deserialize ... bench: 1,072,710 ns/iter (+/- 21,871)
test blockdata::block::benches::bench_block_serialize ... bench: 191,223 ns/iter (+/- 5,833)
test blockdata::block::benches::bench_block_serialize_logic ... bench: 37,543 ns/iter (+/- 732)
test blockdata::block::benches::bench_stream_reader ... bench: 1,872,455 ns/iter (+/- 149,519)
test blockdata::transaction::benches::bench_transaction_deserialize ... bench: 136 ns/iter (+/- 3)
test blockdata::transaction::benches::bench_transaction_serialize ... bench: 51 ns/iter (+/- 8)
test blockdata::transaction::benches::bench_transaction_serialize_logic ... bench: 5 ns/iter (+/- 0)
test blockdata::transaction::benches::bench_transaction_size ... bench: 3 ns/iter (+/- 0)
```
new:
```
test blockdata::block::benches::bench_block_deserialize ... bench: 1,028,574 ns/iter (+/- 10,910)
test blockdata::block::benches::bench_block_serialize ... bench: 162,143 ns/iter (+/- 3,363)
test blockdata::block::benches::bench_block_serialize_logic ... bench: 30,725 ns/iter (+/- 695)
test blockdata::block::benches::bench_stream_reader ... bench: 1,437,071 ns/iter (+/- 53,694)
test blockdata::transaction::benches::bench_transaction_deserialize ... bench: 92 ns/iter (+/- 2)
test blockdata::transaction::benches::bench_transaction_serialize ... bench: 17 ns/iter (+/- 0)
test blockdata::transaction::benches::bench_transaction_serialize_logic ... bench: 5 ns/iter (+/- 0)
test blockdata::transaction::benches::bench_transaction_size ... bench: 4 ns/iter (+/- 0)
```
(*) - I'm benchmarking on a noisy laptop. Take this with a grain of salt. But I think
at least it doesn't make anything slower.
While doing all this manual labor that will probably generate conflicts,
I took a liberty of changing generic type names and variable names to
`r` and `R` (reader) and `w` and `W` for writer.
2022-06-03 04:50:42 +00:00
|
|
|
let mut global = PartiallySignedTransaction::consensus_decode_global(r)?;
|
2021-09-15 05:35:07 +00:00
|
|
|
global.unsigned_tx_checks()?;
|
2018-08-10 20:31:58 +00:00
|
|
|
|
|
|
|
let inputs: Vec<Input> = {
|
2022-09-22 22:00:05 +00:00
|
|
|
let inputs_len: usize = global.unsigned_tx.input.len();
|
2018-08-10 20:31:58 +00:00
|
|
|
|
|
|
|
let mut inputs: Vec<Input> = Vec::with_capacity(inputs_len);
|
|
|
|
|
|
|
|
for _ in 0..inputs_len {
|
Take Writer/Reader by `&mut` in consensus en/decoding
Fix #1020 (see more relevant discussion there)
This definitely makes the amount of generics compiler
has to generate by avoding generating the same functions
for `R`, &mut R`, `&mut &mut R` and so on.
old:
```
> ls -al target/release/deps/bitcoin-07a9dabf1f3e0266
-rwxrwxr-x 1 dpc dpc 9947832 Jun 2 22:42 target/release/deps/bitcoin-07a9dabf1f3e0266
> strip target/release/deps/bitcoin-07a9dabf1f3e0266
> ls -al target/release/deps/bitcoin-07a9dabf1f3e0266
-rwxrwxr-x 1 dpc dpc 4463024 Jun 2 22:46 target/release/deps/bitcoin-07a9dabf1f3e0266
```
new:
```
> ls -al target/release/deps/bitcoin-07a9dabf1f3e0266
-rwxrwxr-x 1 dpc dpc 9866800 Jun 2 22:44 target/release/deps/bitcoin-07a9dabf1f3e0266
> strip target/release/deps/bitcoin-07a9dabf1f3e0266
> ls -al target/release/deps/bitcoin-07a9dabf1f3e0266
-rwxrwxr-x 1 dpc dpc 4393392 Jun 2 22:45 target/release/deps/bitcoin-07a9dabf1f3e0266
```
In the unit-test binary itself, it saves ~100KB of data.
I did not expect much performance gains, but turn out I was wrong(*):
old:
```
test blockdata::block::benches::bench_block_deserialize ... bench: 1,072,710 ns/iter (+/- 21,871)
test blockdata::block::benches::bench_block_serialize ... bench: 191,223 ns/iter (+/- 5,833)
test blockdata::block::benches::bench_block_serialize_logic ... bench: 37,543 ns/iter (+/- 732)
test blockdata::block::benches::bench_stream_reader ... bench: 1,872,455 ns/iter (+/- 149,519)
test blockdata::transaction::benches::bench_transaction_deserialize ... bench: 136 ns/iter (+/- 3)
test blockdata::transaction::benches::bench_transaction_serialize ... bench: 51 ns/iter (+/- 8)
test blockdata::transaction::benches::bench_transaction_serialize_logic ... bench: 5 ns/iter (+/- 0)
test blockdata::transaction::benches::bench_transaction_size ... bench: 3 ns/iter (+/- 0)
```
new:
```
test blockdata::block::benches::bench_block_deserialize ... bench: 1,028,574 ns/iter (+/- 10,910)
test blockdata::block::benches::bench_block_serialize ... bench: 162,143 ns/iter (+/- 3,363)
test blockdata::block::benches::bench_block_serialize_logic ... bench: 30,725 ns/iter (+/- 695)
test blockdata::block::benches::bench_stream_reader ... bench: 1,437,071 ns/iter (+/- 53,694)
test blockdata::transaction::benches::bench_transaction_deserialize ... bench: 92 ns/iter (+/- 2)
test blockdata::transaction::benches::bench_transaction_serialize ... bench: 17 ns/iter (+/- 0)
test blockdata::transaction::benches::bench_transaction_serialize_logic ... bench: 5 ns/iter (+/- 0)
test blockdata::transaction::benches::bench_transaction_size ... bench: 4 ns/iter (+/- 0)
```
(*) - I'm benchmarking on a noisy laptop. Take this with a grain of salt. But I think
at least it doesn't make anything slower.
While doing all this manual labor that will probably generate conflicts,
I took a liberty of changing generic type names and variable names to
`r` and `R` (reader) and `w` and `W` for writer.
2022-06-03 04:50:42 +00:00
|
|
|
inputs.push(Decodable::consensus_decode(r)?);
|
2018-08-10 20:31:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
inputs
|
|
|
|
};
|
|
|
|
|
|
|
|
let outputs: Vec<Output> = {
|
2022-09-22 22:00:05 +00:00
|
|
|
let outputs_len: usize = global.unsigned_tx.output.len();
|
2018-08-10 20:31:58 +00:00
|
|
|
|
|
|
|
let mut outputs: Vec<Output> = Vec::with_capacity(outputs_len);
|
|
|
|
|
|
|
|
for _ in 0..outputs_len {
|
Take Writer/Reader by `&mut` in consensus en/decoding
Fix #1020 (see more relevant discussion there)
This definitely makes the amount of generics compiler
has to generate by avoding generating the same functions
for `R`, &mut R`, `&mut &mut R` and so on.
old:
```
> ls -al target/release/deps/bitcoin-07a9dabf1f3e0266
-rwxrwxr-x 1 dpc dpc 9947832 Jun 2 22:42 target/release/deps/bitcoin-07a9dabf1f3e0266
> strip target/release/deps/bitcoin-07a9dabf1f3e0266
> ls -al target/release/deps/bitcoin-07a9dabf1f3e0266
-rwxrwxr-x 1 dpc dpc 4463024 Jun 2 22:46 target/release/deps/bitcoin-07a9dabf1f3e0266
```
new:
```
> ls -al target/release/deps/bitcoin-07a9dabf1f3e0266
-rwxrwxr-x 1 dpc dpc 9866800 Jun 2 22:44 target/release/deps/bitcoin-07a9dabf1f3e0266
> strip target/release/deps/bitcoin-07a9dabf1f3e0266
> ls -al target/release/deps/bitcoin-07a9dabf1f3e0266
-rwxrwxr-x 1 dpc dpc 4393392 Jun 2 22:45 target/release/deps/bitcoin-07a9dabf1f3e0266
```
In the unit-test binary itself, it saves ~100KB of data.
I did not expect much performance gains, but turn out I was wrong(*):
old:
```
test blockdata::block::benches::bench_block_deserialize ... bench: 1,072,710 ns/iter (+/- 21,871)
test blockdata::block::benches::bench_block_serialize ... bench: 191,223 ns/iter (+/- 5,833)
test blockdata::block::benches::bench_block_serialize_logic ... bench: 37,543 ns/iter (+/- 732)
test blockdata::block::benches::bench_stream_reader ... bench: 1,872,455 ns/iter (+/- 149,519)
test blockdata::transaction::benches::bench_transaction_deserialize ... bench: 136 ns/iter (+/- 3)
test blockdata::transaction::benches::bench_transaction_serialize ... bench: 51 ns/iter (+/- 8)
test blockdata::transaction::benches::bench_transaction_serialize_logic ... bench: 5 ns/iter (+/- 0)
test blockdata::transaction::benches::bench_transaction_size ... bench: 3 ns/iter (+/- 0)
```
new:
```
test blockdata::block::benches::bench_block_deserialize ... bench: 1,028,574 ns/iter (+/- 10,910)
test blockdata::block::benches::bench_block_serialize ... bench: 162,143 ns/iter (+/- 3,363)
test blockdata::block::benches::bench_block_serialize_logic ... bench: 30,725 ns/iter (+/- 695)
test blockdata::block::benches::bench_stream_reader ... bench: 1,437,071 ns/iter (+/- 53,694)
test blockdata::transaction::benches::bench_transaction_deserialize ... bench: 92 ns/iter (+/- 2)
test blockdata::transaction::benches::bench_transaction_serialize ... bench: 17 ns/iter (+/- 0)
test blockdata::transaction::benches::bench_transaction_serialize_logic ... bench: 5 ns/iter (+/- 0)
test blockdata::transaction::benches::bench_transaction_size ... bench: 4 ns/iter (+/- 0)
```
(*) - I'm benchmarking on a noisy laptop. Take this with a grain of salt. But I think
at least it doesn't make anything slower.
While doing all this manual labor that will probably generate conflicts,
I took a liberty of changing generic type names and variable names to
`r` and `R` (reader) and `w` and `W` for writer.
2022-06-03 04:50:42 +00:00
|
|
|
outputs.push(Decodable::consensus_decode(r)?);
|
2018-08-10 20:31:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
outputs
|
|
|
|
};
|
|
|
|
|
2021-09-15 05:35:07 +00:00
|
|
|
global.inputs = inputs;
|
|
|
|
global.outputs = outputs;
|
|
|
|
Ok(global)
|
2018-08-10 20:31:58 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-10 15:28:48 +00:00
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
2022-02-22 13:41:20 +00:00
|
|
|
use super::*;
|
2021-04-12 11:47:03 +00:00
|
|
|
|
2022-08-16 01:24:56 +00:00
|
|
|
use crate::blockdata::locktime::absolute;
|
2022-05-02 22:13:57 +00:00
|
|
|
use crate::hashes::hex::FromHex;
|
|
|
|
use crate::hashes::{sha256, hash160, Hash, ripemd160};
|
|
|
|
use crate::hash_types::Txid;
|
2018-09-09 04:07:07 +00:00
|
|
|
|
2021-04-12 11:47:03 +00:00
|
|
|
use secp256k1::{Secp256k1, self};
|
2022-04-19 05:46:59 +00:00
|
|
|
#[cfg(feature = "rand")]
|
|
|
|
use secp256k1::{All, SecretKey};
|
2018-09-09 04:20:29 +00:00
|
|
|
|
2022-05-02 22:13:57 +00:00
|
|
|
use crate::blockdata::script::Script;
|
2022-07-16 00:49:03 +00:00
|
|
|
use crate::blockdata::transaction::{Transaction, TxIn, TxOut, OutPoint, Sequence};
|
2022-05-02 22:13:57 +00:00
|
|
|
use crate::network::constants::Network::Bitcoin;
|
|
|
|
use crate::consensus::encode::{deserialize, serialize, serialize_hex};
|
2022-06-07 05:07:59 +00:00
|
|
|
use crate::util::bip32::{ChildNumber, ExtendedPrivKey, ExtendedPubKey, KeySource};
|
2022-05-02 22:13:57 +00:00
|
|
|
use crate::util::psbt::map::{Output, Input};
|
|
|
|
use crate::util::psbt::raw;
|
2022-06-07 03:25:27 +00:00
|
|
|
use crate::internal_macros::hex_script;
|
2018-08-10 15:28:48 +00:00
|
|
|
|
2021-06-09 10:34:44 +00:00
|
|
|
use std::collections::BTreeMap;
|
2022-05-02 22:13:57 +00:00
|
|
|
use crate::blockdata::witness::Witness;
|
2018-08-10 20:31:58 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn trivial_psbt() {
|
|
|
|
let psbt = PartiallySignedTransaction {
|
2021-09-15 05:35:07 +00:00
|
|
|
unsigned_tx: Transaction {
|
|
|
|
version: 2,
|
2022-08-16 01:24:56 +00:00
|
|
|
lock_time: absolute::PackedLockTime::ZERO,
|
2021-09-15 05:35:07 +00:00
|
|
|
input: vec![],
|
|
|
|
output: vec![],
|
2018-08-10 20:31:58 +00:00
|
|
|
},
|
2021-09-15 05:35:07 +00:00
|
|
|
xpub: Default::default(),
|
|
|
|
version: 0,
|
|
|
|
proprietary: BTreeMap::new(),
|
|
|
|
unknown: BTreeMap::new(),
|
|
|
|
|
2018-08-10 20:31:58 +00:00
|
|
|
inputs: vec![],
|
|
|
|
outputs: vec![],
|
|
|
|
};
|
2022-01-24 00:31:39 +00:00
|
|
|
assert_eq!(serialize_hex(&psbt), "70736274ff01000a0200000000000000000000");
|
2018-08-10 20:31:58 +00:00
|
|
|
}
|
|
|
|
|
2022-02-17 10:48:29 +00:00
|
|
|
#[test]
|
|
|
|
fn psbt_uncompressed_key() {
|
|
|
|
|
|
|
|
let psbt: PartiallySignedTransaction = hex_psbt!("70736274ff01003302000000010000000000000000000000000000000000000000000000000000000000000000ffffffff00ffffffff000000000000420204bb0d5d0cca36e7b9c80f63bc04c1240babb83bcd2803ef7ac8b6e2af594291daec281e856c98d210c5ab14dfd5828761f8ee7d5f45ca21ad3e4c4b41b747a3a047304402204f67e2afb76142d44fae58a2495d33a3419daa26cd0db8d04f3452b63289ac0f022010762a9fb67e94cc5cad9026f6dc99ff7f070f4278d30fbc7d0c869dd38c7fe70100").unwrap();
|
|
|
|
|
|
|
|
assert!(psbt.inputs[0].partial_sigs.len() == 1);
|
|
|
|
let pk = psbt.inputs[0].partial_sigs.iter().next().unwrap().0;
|
|
|
|
assert!(!pk.compressed);
|
|
|
|
}
|
|
|
|
|
2018-09-09 04:20:29 +00:00
|
|
|
#[test]
|
|
|
|
fn serialize_then_deserialize_output() {
|
|
|
|
let secp = &Secp256k1::new();
|
2020-01-08 17:02:30 +00:00
|
|
|
let seed = Vec::from_hex("000102030405060708090a0b0c0d0e0f").unwrap();
|
2018-09-09 04:20:29 +00:00
|
|
|
|
2021-04-12 11:47:03 +00:00
|
|
|
let mut hd_keypaths: BTreeMap<secp256k1::PublicKey, KeySource> = Default::default();
|
2018-09-09 04:20:29 +00:00
|
|
|
|
|
|
|
let mut sk: ExtendedPrivKey = ExtendedPrivKey::new_master(Bitcoin, &seed).unwrap();
|
|
|
|
|
2022-06-07 05:07:59 +00:00
|
|
|
let fprint = sk.fingerprint(secp);
|
2018-09-09 04:20:29 +00:00
|
|
|
|
|
|
|
let dpath: Vec<ChildNumber> = vec![
|
|
|
|
ChildNumber::from_normal_idx(0).unwrap(),
|
|
|
|
ChildNumber::from_normal_idx(1).unwrap(),
|
|
|
|
ChildNumber::from_normal_idx(2).unwrap(),
|
|
|
|
ChildNumber::from_normal_idx(4).unwrap(),
|
|
|
|
ChildNumber::from_normal_idx(42).unwrap(),
|
|
|
|
ChildNumber::from_hardened_idx(69).unwrap(),
|
|
|
|
ChildNumber::from_normal_idx(420).unwrap(),
|
|
|
|
ChildNumber::from_normal_idx(31337).unwrap(),
|
|
|
|
];
|
|
|
|
|
|
|
|
sk = sk.derive_priv(secp, &dpath).unwrap();
|
|
|
|
|
2022-06-07 05:07:59 +00:00
|
|
|
let pk = ExtendedPubKey::from_priv(secp, &sk);
|
2018-09-09 04:20:29 +00:00
|
|
|
|
2020-09-13 22:37:29 +00:00
|
|
|
hd_keypaths.insert(pk.public_key, (fprint, dpath.into()));
|
2018-09-09 04:20:29 +00:00
|
|
|
|
|
|
|
let expected: Output = Output {
|
2022-01-24 00:31:39 +00:00
|
|
|
redeem_script: Some(hex_script!("76a914d0c59903c5bac2868760e90fd521a4665aa7652088ac")),
|
|
|
|
witness_script: Some(hex_script!("a9143545e6e33b832c47050f24d3eeb93c9c03948bc787")),
|
2021-04-12 12:09:02 +00:00
|
|
|
bip32_derivation: hd_keypaths,
|
2018-09-09 04:20:29 +00:00
|
|
|
..Default::default()
|
|
|
|
};
|
|
|
|
|
|
|
|
let actual: Output = deserialize(&serialize(&expected)).unwrap();
|
|
|
|
|
|
|
|
assert_eq!(expected, actual);
|
|
|
|
}
|
|
|
|
|
2018-09-09 04:07:07 +00:00
|
|
|
#[test]
|
|
|
|
fn serialize_then_deserialize_global() {
|
2021-09-15 05:35:07 +00:00
|
|
|
let expected = PartiallySignedTransaction {
|
2018-09-09 04:07:07 +00:00
|
|
|
unsigned_tx: Transaction {
|
|
|
|
version: 2,
|
2022-08-16 01:24:56 +00:00
|
|
|
lock_time: absolute::PackedLockTime(1257139),
|
2018-09-09 04:07:07 +00:00
|
|
|
input: vec![TxIn {
|
|
|
|
previous_output: OutPoint {
|
2019-12-09 22:37:52 +00:00
|
|
|
txid: Txid::from_hex(
|
2018-09-09 04:07:07 +00:00
|
|
|
"f61b1742ca13176464adb3cb66050c00787bb3a4eead37e985f2df1e37718126",
|
|
|
|
).unwrap(),
|
|
|
|
vout: 0,
|
|
|
|
},
|
|
|
|
script_sig: Script::new(),
|
2022-07-16 00:49:03 +00:00
|
|
|
sequence: Sequence::ENABLE_LOCKTIME_NO_RBF,
|
2021-10-05 13:07:55 +00:00
|
|
|
witness: Witness::default(),
|
2018-09-09 04:07:07 +00:00
|
|
|
}],
|
|
|
|
output: vec![
|
|
|
|
TxOut {
|
|
|
|
value: 99999699,
|
|
|
|
script_pubkey: hex_script!(
|
|
|
|
"76a914d0c59903c5bac2868760e90fd521a4665aa7652088ac"
|
|
|
|
),
|
|
|
|
},
|
|
|
|
TxOut {
|
|
|
|
value: 100000000,
|
|
|
|
script_pubkey: hex_script!(
|
|
|
|
"a9143545e6e33b832c47050f24d3eeb93c9c03948bc787"
|
|
|
|
),
|
|
|
|
},
|
|
|
|
],
|
|
|
|
},
|
2020-11-10 08:34:29 +00:00
|
|
|
xpub: Default::default(),
|
|
|
|
version: 0,
|
2020-12-05 14:09:36 +00:00
|
|
|
proprietary: Default::default(),
|
2018-09-09 04:07:07 +00:00
|
|
|
unknown: Default::default(),
|
2022-01-24 00:31:39 +00:00
|
|
|
inputs: vec![Input::default()],
|
|
|
|
outputs: vec![Output::default(), Output::default()],
|
2018-09-09 04:07:07 +00:00
|
|
|
};
|
|
|
|
|
2021-09-15 05:35:07 +00:00
|
|
|
let actual: PartiallySignedTransaction = deserialize(&serialize(&expected)).unwrap();
|
2018-09-09 04:07:07 +00:00
|
|
|
|
|
|
|
assert_eq!(expected, actual);
|
|
|
|
}
|
|
|
|
|
2018-08-10 15:28:48 +00:00
|
|
|
#[test]
|
|
|
|
fn serialize_then_deserialize_psbtkvpair() {
|
|
|
|
let expected = raw::Pair {
|
|
|
|
key: raw::Key {
|
|
|
|
type_value: 0u8,
|
|
|
|
key: vec![42u8, 69u8],
|
|
|
|
},
|
|
|
|
value: vec![69u8, 42u8, 4u8],
|
|
|
|
};
|
|
|
|
|
|
|
|
let actual: raw::Pair = deserialize(&serialize(&expected)).unwrap();
|
|
|
|
|
|
|
|
assert_eq!(expected, actual);
|
|
|
|
}
|
2018-08-10 20:34:35 +00:00
|
|
|
|
2019-08-09 15:03:12 +00:00
|
|
|
#[test]
|
|
|
|
fn deserialize_and_serialize_psbt_with_two_partial_sigs() {
|
|
|
|
let hex = "70736274ff0100890200000001207ae985d787dfe6143d5c58fad79cc7105e0e799fcf033b7f2ba17e62d7b3200000000000ffffffff02563d03000000000022002019899534b9a011043c0dd57c3ff9a381c3522c5f27c6a42319085b56ca543a1d6adc020000000000220020618b47a07ebecca4e156edb1b9ea7c24bdee0139fc049237965ffdaf56d5ee73000000000001012b801a0600000000002200201148e93e9315e37dbed2121be5239257af35adc03ffdfc5d914b083afa44dab82202025fe7371376d53cf8a2783917c28bf30bd690b0a4d4a207690093ca2b920ee076473044022007e06b362e89912abd4661f47945430739b006a85d1b2a16c01dc1a4bd07acab022061576d7aa834988b7ab94ef21d8eebd996ea59ea20529a19b15f0c9cebe3d8ac01220202b3fe93530020a8294f0e527e33fbdff184f047eb6b5a1558a352f62c29972f8a473044022002787f926d6817504431ee281183b8119b6845bfaa6befae45e13b6d430c9d2f02202859f149a6cd26ae2f03a107e7f33c7d91730dade305fe077bae677b5d44952a01010547522102b3fe93530020a8294f0e527e33fbdff184f047eb6b5a1558a352f62c29972f8a21025fe7371376d53cf8a2783917c28bf30bd690b0a4d4a207690093ca2b920ee07652ae0001014752210283ef76537f2d58ae3aa3a4bd8ae41c3f230ccadffb1a0bd3ca504d871cff05e7210353d79cc0cb1396f4ce278d005f16d948e02a6aec9ed1109f13747ecb1507b37b52ae00010147522102b3937241777b6665e0d694e52f9c1b188433641df852da6fc42187b5d8a368a321034cdd474f01cc5aa7ff834ad8bcc882a87e854affc775486bc2a9f62e8f49bd7852ae00";
|
|
|
|
let psbt: PartiallySignedTransaction = hex_psbt!(hex).unwrap();
|
|
|
|
assert_eq!(hex, serialize_hex(&psbt));
|
|
|
|
}
|
|
|
|
|
2020-10-25 18:27:45 +00:00
|
|
|
#[cfg(feature = "serde")]
|
|
|
|
#[test]
|
|
|
|
fn test_serde_psbt() {
|
|
|
|
//! Create a full PSBT value with various fields filled and make sure it can be JSONized.
|
2022-06-07 02:57:09 +00:00
|
|
|
use crate::hashes::sha256d;
|
2022-05-02 23:02:58 +00:00
|
|
|
use crate::util::psbt::map::Input;
|
2022-09-15 02:59:36 +00:00
|
|
|
use crate::sighash::EcdsaSighashType;
|
2020-10-25 18:27:45 +00:00
|
|
|
|
|
|
|
// create some values to use in the PSBT
|
|
|
|
let tx = Transaction {
|
|
|
|
version: 1,
|
2022-08-16 01:24:56 +00:00
|
|
|
lock_time: absolute::PackedLockTime::ZERO,
|
2020-10-25 18:27:45 +00:00
|
|
|
input: vec![TxIn {
|
|
|
|
previous_output: OutPoint {
|
|
|
|
txid: Txid::from_hex("e567952fb6cc33857f392efa3a46c995a28f69cca4bb1b37e0204dab1ec7a389").unwrap(),
|
|
|
|
vout: 1,
|
|
|
|
},
|
|
|
|
script_sig: hex_script!("160014be18d152a9b012039daf3da7de4f53349eecb985"),
|
2022-07-16 00:49:03 +00:00
|
|
|
sequence: Sequence::MAX,
|
2021-10-05 13:07:55 +00:00
|
|
|
witness: Witness::from_vec(vec![Vec::from_hex("03d2e15674941bad4a996372cb87e1856d3652606d98562fe39c5e9e7e413f2105").unwrap()]),
|
2020-10-25 18:27:45 +00:00
|
|
|
}],
|
|
|
|
output: vec![
|
|
|
|
TxOut {
|
|
|
|
value: 190303501938,
|
|
|
|
script_pubkey: hex_script!("a914339725ba21efd62ac753a9bcd067d6c7a6a39d0587"),
|
|
|
|
},
|
|
|
|
],
|
|
|
|
};
|
|
|
|
let unknown: BTreeMap<raw::Key, Vec<u8>> = vec![(
|
|
|
|
raw::Key { type_value: 1, key: vec![0, 1] },
|
|
|
|
vec![3, 4 ,5],
|
|
|
|
)].into_iter().collect();
|
|
|
|
let key_source = ("deadbeef".parse().unwrap(), "m/0'/1".parse().unwrap());
|
2021-04-12 11:47:03 +00:00
|
|
|
let keypaths: BTreeMap<secp256k1::PublicKey, KeySource> = vec![(
|
2020-10-25 18:27:45 +00:00
|
|
|
"0339880dc92394b7355e3d0439fa283c31de7590812ea011c4245c0674a685e883".parse().unwrap(),
|
|
|
|
key_source.clone(),
|
|
|
|
)].into_iter().collect();
|
|
|
|
|
|
|
|
let proprietary: BTreeMap<raw::ProprietaryKey, Vec<u8>> = vec![(
|
|
|
|
raw::ProprietaryKey {
|
|
|
|
prefix: "prefx".as_bytes().to_vec(),
|
|
|
|
subtype: 42,
|
|
|
|
key: "test_key".as_bytes().to_vec(),
|
|
|
|
},
|
|
|
|
vec![5, 6, 7],
|
|
|
|
)].into_iter().collect();
|
|
|
|
|
|
|
|
let psbt = PartiallySignedTransaction {
|
2021-09-15 05:35:07 +00:00
|
|
|
version: 0,
|
|
|
|
xpub: {
|
|
|
|
let xpub: ExtendedPubKey =
|
|
|
|
"xpub661MyMwAqRbcGoRVtwfvzZsq2VBJR1LAHfQstHUoxqDorV89vRoMxUZ27kLrraAj6MPi\
|
|
|
|
QfrDb27gigC1VS1dBXi5jGpxmMeBXEkKkcXUTg4".parse().unwrap();
|
2022-07-20 03:00:10 +00:00
|
|
|
vec![(xpub, key_source)].into_iter().collect()
|
2021-09-15 05:35:07 +00:00
|
|
|
},
|
|
|
|
unsigned_tx: {
|
|
|
|
let mut unsigned = tx.clone();
|
|
|
|
unsigned.input[0].script_sig = Script::new();
|
2021-10-05 13:07:55 +00:00
|
|
|
unsigned.input[0].witness = Witness::default();
|
2021-09-15 05:35:07 +00:00
|
|
|
unsigned
|
2020-10-25 18:27:45 +00:00
|
|
|
},
|
2021-09-15 05:35:07 +00:00
|
|
|
proprietary: proprietary.clone(),
|
|
|
|
unknown: unknown.clone(),
|
|
|
|
|
2020-10-25 18:27:45 +00:00
|
|
|
inputs: vec![Input {
|
|
|
|
non_witness_utxo: Some(tx),
|
|
|
|
witness_utxo: Some(TxOut {
|
|
|
|
value: 190303501938,
|
|
|
|
script_pubkey: hex_script!("a914339725ba21efd62ac753a9bcd067d6c7a6a39d0587"),
|
|
|
|
}),
|
2022-03-28 21:48:53 +00:00
|
|
|
sighash_type: Some("SIGHASH_SINGLE|SIGHASH_ANYONECANPAY".parse::<EcdsaSighashType>().unwrap().into()),
|
2020-10-25 18:27:45 +00:00
|
|
|
redeem_script: Some(vec![0x51].into()),
|
|
|
|
witness_script: None,
|
|
|
|
partial_sigs: vec![(
|
|
|
|
"0339880dc92394b7355e3d0439fa283c31de7590812ea011c4245c0674a685e883".parse().unwrap(),
|
2022-01-06 11:06:49 +00:00
|
|
|
"304402204f67e2afb76142d44fae58a2495d33a3419daa26cd0db8d04f3452b63289ac0f022010762a9fb67e94cc5cad9026f6dc99ff7f070f4278d30fbc7d0c869dd38c7fe701".parse().unwrap(),
|
2020-10-25 18:27:45 +00:00
|
|
|
)].into_iter().collect(),
|
2021-04-12 12:09:02 +00:00
|
|
|
bip32_derivation: keypaths.clone(),
|
2022-01-11 09:10:28 +00:00
|
|
|
final_script_witness: Some(Witness::from_vec(vec![vec![1, 3], vec![5]])),
|
2020-12-27 21:05:26 +00:00
|
|
|
ripemd160_preimages: vec![(ripemd160::Hash::hash(&[]), vec![1, 2])].into_iter().collect(),
|
|
|
|
sha256_preimages: vec![(sha256::Hash::hash(&[]), vec![1, 2])].into_iter().collect(),
|
|
|
|
hash160_preimages: vec![(hash160::Hash::hash(&[]), vec![1, 2])].into_iter().collect(),
|
|
|
|
hash256_preimages: vec![(sha256d::Hash::hash(&[]), vec![1, 2])].into_iter().collect(),
|
2020-10-25 18:27:45 +00:00
|
|
|
proprietary: proprietary.clone(),
|
|
|
|
unknown: unknown.clone(),
|
|
|
|
..Default::default()
|
|
|
|
}],
|
|
|
|
outputs: vec![Output {
|
2021-04-12 12:09:02 +00:00
|
|
|
bip32_derivation: keypaths,
|
2022-07-20 03:01:34 +00:00
|
|
|
proprietary,
|
|
|
|
unknown,
|
2020-10-25 18:27:45 +00:00
|
|
|
..Default::default()
|
|
|
|
}],
|
|
|
|
};
|
2022-06-01 22:08:56 +00:00
|
|
|
let encoded = serde_json::to_string(&psbt).unwrap();
|
|
|
|
let decoded: PartiallySignedTransaction = serde_json::from_str(&encoded).unwrap();
|
2020-10-25 18:27:45 +00:00
|
|
|
assert_eq!(psbt, decoded);
|
|
|
|
}
|
|
|
|
|
2018-08-10 20:34:35 +00:00
|
|
|
mod bip_vectors {
|
2021-01-27 20:19:15 +00:00
|
|
|
#[cfg(feature = "base64")]
|
|
|
|
use std::str::FromStr;
|
2018-08-10 20:34:35 +00:00
|
|
|
|
2022-05-02 22:13:57 +00:00
|
|
|
use crate::hashes::hex::FromHex;
|
|
|
|
use crate::hash_types::Txid;
|
2018-08-10 20:34:35 +00:00
|
|
|
|
2022-05-02 22:13:57 +00:00
|
|
|
use crate::blockdata::script::Script;
|
2022-07-19 01:41:35 +00:00
|
|
|
use crate::blockdata::transaction::{Transaction, TxIn, TxOut, OutPoint, Sequence};
|
2022-05-02 22:13:57 +00:00
|
|
|
use crate::consensus::encode::serialize_hex;
|
2022-08-16 01:24:56 +00:00
|
|
|
use crate::blockdata::locktime::absolute;
|
2022-05-02 22:13:57 +00:00
|
|
|
use crate::util::psbt::map::{Map, Input, Output};
|
|
|
|
use crate::util::psbt::raw;
|
|
|
|
use crate::util::psbt::{PartiallySignedTransaction, Error};
|
2022-10-24 22:14:01 +00:00
|
|
|
use crate::sighash::EcdsaSighashType;
|
2021-06-09 10:34:44 +00:00
|
|
|
use std::collections::BTreeMap;
|
2022-05-02 22:13:57 +00:00
|
|
|
use crate::blockdata::witness::Witness;
|
2022-06-07 03:25:27 +00:00
|
|
|
use crate::internal_macros::hex_script;
|
2018-08-10 20:34:35 +00:00
|
|
|
|
|
|
|
#[test]
|
2021-02-19 12:28:13 +00:00
|
|
|
#[should_panic(expected = "InvalidMagic")]
|
2018-08-10 20:34:35 +00:00
|
|
|
fn invalid_vector_1() {
|
2021-02-19 12:28:13 +00:00
|
|
|
hex_psbt!("0200000001268171371edff285e937adeea4b37b78000c0566cbb3ad64641713ca42171bf6000000006a473044022070b2245123e6bf474d60c5b50c043d4c691a5d2435f09a34a7662a9dc251790a022001329ca9dacf280bdf30740ec0390422422c81cb45839457aeb76fc12edd95b3012102657d118d3357b8e0f4c2cd46db7b39f6d9c38d9a70abcb9b2de5dc8dbfe4ce31feffffff02d3dff505000000001976a914d0c59903c5bac2868760e90fd521a4665aa7652088ac00e1f5050000000017a9143545e6e33b832c47050f24d3eeb93c9c03948bc787b32e1300").unwrap();
|
|
|
|
}
|
2021-01-27 20:19:15 +00:00
|
|
|
|
2021-02-19 12:28:13 +00:00
|
|
|
#[cfg(feature = "base64")]
|
|
|
|
#[test]
|
|
|
|
#[should_panic(expected = "InvalidMagic")]
|
|
|
|
fn invalid_vector_1_base64() {
|
|
|
|
PartiallySignedTransaction::from_str("AgAAAAEmgXE3Ht/yhek3re6ks3t4AAwFZsuzrWRkFxPKQhcb9gAAAABqRzBEAiBwsiRRI+a/R01gxbUMBD1MaRpdJDXwmjSnZiqdwlF5CgIgATKcqdrPKAvfMHQOwDkEIkIsgctFg5RXrrdvwS7dlbMBIQJlfRGNM1e44PTCzUbbezn22cONmnCry5st5dyNv+TOMf7///8C09/1BQAAAAAZdqkU0MWZA8W6woaHYOkP1SGkZlqnZSCIrADh9QUAAAAAF6kUNUXm4zuDLEcFDyTT7rk8nAOUi8eHsy4TAA==").unwrap();
|
2018-08-10 20:34:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2021-02-19 12:28:13 +00:00
|
|
|
#[should_panic(expected = "ConsensusEncoding")]
|
2018-08-10 20:34:35 +00:00
|
|
|
fn invalid_vector_2() {
|
2021-02-19 12:28:13 +00:00
|
|
|
hex_psbt!("70736274ff0100750200000001268171371edff285e937adeea4b37b78000c0566cbb3ad64641713ca42171bf60000000000feffffff02d3dff505000000001976a914d0c59903c5bac2868760e90fd521a4665aa7652088ac00e1f5050000000017a9143545e6e33b832c47050f24d3eeb93c9c03948bc787b32e1300000100fda5010100000000010289a3c71eab4d20e0371bbba4cc698fa295c9463afa2e397f8533ccb62f9567e50100000017160014be18d152a9b012039daf3da7de4f53349eecb985ffffffff86f8aa43a71dff1448893a530a7237ef6b4608bbb2dd2d0171e63aec6a4890b40100000017160014fe3e9ef1a745e974d902c4355943abcb34bd5353ffffffff0200c2eb0b000000001976a91485cff1097fd9e008bb34af709c62197b38978a4888ac72fef84e2c00000017a914339725ba21efd62ac753a9bcd067d6c7a6a39d05870247304402202712be22e0270f394f568311dc7ca9a68970b8025fdd3b240229f07f8a5f3a240220018b38d7dcd314e734c9276bd6fb40f673325bc4baa144c800d2f2f02db2765c012103d2e15674941bad4a996372cb87e1856d3652606d98562fe39c5e9e7e413f210502483045022100d12b852d85dcd961d2f5f4ab660654df6eedcc794c0c33ce5cc309ffb5fce58d022067338a8e0e1725c197fb1a88af59f51e44e4255b20167c8684031c05d1f2592a01210223b72beef0965d10be0778efecd61fcac6f79a4ea169393380734464f84f2ab30000000000")
|
2021-02-21 19:18:48 +00:00
|
|
|
// This weird thing is necessary since rustc 0.29 prints out I/O error in a different format than later versions
|
2021-02-19 12:28:13 +00:00
|
|
|
.map_err(Error::from)
|
|
|
|
.unwrap();
|
|
|
|
}
|
2021-01-27 20:19:15 +00:00
|
|
|
|
2021-02-19 12:28:13 +00:00
|
|
|
#[cfg(feature = "base64")]
|
|
|
|
#[test]
|
|
|
|
#[should_panic(expected = "ConsensusEncoding")]
|
|
|
|
fn invalid_vector_2_base64() {
|
2022-05-02 23:02:58 +00:00
|
|
|
use crate::util::psbt::PsbtParseError;
|
2021-02-21 19:18:48 +00:00
|
|
|
PartiallySignedTransaction::from_str("cHNidP8BAHUCAAAAASaBcTce3/KF6Tet7qSze3gADAVmy7OtZGQXE8pCFxv2AAAAAAD+////AtPf9QUAAAAAGXapFNDFmQPFusKGh2DpD9UhpGZap2UgiKwA4fUFAAAAABepFDVF5uM7gyxHBQ8k0+65PJwDlIvHh7MuEwAAAQD9pQEBAAAAAAECiaPHHqtNIOA3G7ukzGmPopXJRjr6Ljl/hTPMti+VZ+UBAAAAFxYAFL4Y0VKpsBIDna89p95PUzSe7LmF/////4b4qkOnHf8USIk6UwpyN+9rRgi7st0tAXHmOuxqSJC0AQAAABcWABT+Pp7xp0XpdNkCxDVZQ6vLNL1TU/////8CAMLrCwAAAAAZdqkUhc/xCX/Z4Ai7NK9wnGIZeziXikiIrHL++E4sAAAAF6kUM5cluiHv1irHU6m80GfWx6ajnQWHAkcwRAIgJxK+IuAnDzlPVoMR3HyppolwuAJf3TskAinwf4pfOiQCIAGLONfc0xTnNMkna9b7QPZzMlvEuqFEyADS8vAtsnZcASED0uFWdJQbrUqZY3LLh+GFbTZSYG2YVi/jnF6efkE/IQUCSDBFAiEA0SuFLYXc2WHS9fSrZgZU327tzHlMDDPOXMMJ/7X85Y0CIGczio4OFyXBl/saiK9Z9R5E5CVbIBZ8hoQDHAXR8lkqASECI7cr7vCWXRC+B3jv7NYfysb3mk6haTkzgHNEZPhPKrMAAAAAAA==")
|
|
|
|
// This weird thing is necessary since rustc 0.29 prints out I/O error in a different format than later versions
|
|
|
|
.map_err(|err| match err {
|
|
|
|
PsbtParseError::PsbtEncoding(err) => err,
|
|
|
|
PsbtParseError::Base64Encoding(_) => panic!("PSBT Base64 decoding failed")
|
|
|
|
})
|
|
|
|
.map_err(Error::from)
|
|
|
|
.unwrap();
|
2018-08-10 20:34:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2021-02-19 12:28:13 +00:00
|
|
|
#[should_panic(expected = "UnsignedTxHasScriptSigs")]
|
2018-08-10 20:34:35 +00:00
|
|
|
fn invalid_vector_3() {
|
2021-02-19 12:28:13 +00:00
|
|
|
hex_psbt!("70736274ff0100fd0a010200000002ab0949a08c5af7c49b8212f417e2f15ab3f5c33dcf153821a8139f877a5b7be4000000006a47304402204759661797c01b036b25928948686218347d89864b719e1f7fcf57d1e511658702205309eabf56aa4d8891ffd111fdf1336f3a29da866d7f8486d75546ceedaf93190121035cdc61fc7ba971c0b501a646a2a83b102cb43881217ca682dc86e2d73fa88292feffffffab0949a08c5af7c49b8212f417e2f15ab3f5c33dcf153821a8139f877a5b7be40100000000feffffff02603bea0b000000001976a914768a40bbd740cbe81d988e71de2a4d5c71396b1d88ac8e240000000000001976a9146f4620b553fa095e721b9ee0efe9fa039cca459788ac00000000000001012000e1f5050000000017a9143545e6e33b832c47050f24d3eeb93c9c03948bc787010416001485d13537f2e265405a34dbafa9e3dda01fb82308000000").unwrap();
|
|
|
|
}
|
2021-01-27 20:19:15 +00:00
|
|
|
|
2021-02-19 12:28:13 +00:00
|
|
|
#[cfg(feature = "base64")]
|
|
|
|
#[test]
|
|
|
|
#[should_panic(expected = "UnsignedTxHasScriptSigs")]
|
|
|
|
fn invalid_vector_3_base64() {
|
|
|
|
PartiallySignedTransaction::from_str("cHNidP8BAP0KAQIAAAACqwlJoIxa98SbghL0F+LxWrP1wz3PFTghqBOfh3pbe+QAAAAAakcwRAIgR1lmF5fAGwNrJZKJSGhiGDR9iYZLcZ4ff89X0eURZYcCIFMJ6r9Wqk2Ikf/REf3xM286KdqGbX+EhtdVRs7tr5MZASEDXNxh/HupccC1AaZGoqg7ECy0OIEhfKaC3Ibi1z+ogpL+////qwlJoIxa98SbghL0F+LxWrP1wz3PFTghqBOfh3pbe+QBAAAAAP7///8CYDvqCwAAAAAZdqkUdopAu9dAy+gdmI5x3ipNXHE5ax2IrI4kAAAAAAAAGXapFG9GILVT+glechue4O/p+gOcykWXiKwAAAAAAAABASAA4fUFAAAAABepFDVF5uM7gyxHBQ8k0+65PJwDlIvHhwEEFgAUhdE1N/LiZUBaNNuvqePdoB+4IwgAAAA=").unwrap();
|
2018-08-10 20:34:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2021-02-19 12:28:13 +00:00
|
|
|
#[should_panic(expected = "MustHaveUnsignedTx")]
|
2018-08-10 20:34:35 +00:00
|
|
|
fn invalid_vector_4() {
|
2021-02-19 12:28:13 +00:00
|
|
|
hex_psbt!("70736274ff000100fda5010100000000010289a3c71eab4d20e0371bbba4cc698fa295c9463afa2e397f8533ccb62f9567e50100000017160014be18d152a9b012039daf3da7de4f53349eecb985ffffffff86f8aa43a71dff1448893a530a7237ef6b4608bbb2dd2d0171e63aec6a4890b40100000017160014fe3e9ef1a745e974d902c4355943abcb34bd5353ffffffff0200c2eb0b000000001976a91485cff1097fd9e008bb34af709c62197b38978a4888ac72fef84e2c00000017a914339725ba21efd62ac753a9bcd067d6c7a6a39d05870247304402202712be22e0270f394f568311dc7ca9a68970b8025fdd3b240229f07f8a5f3a240220018b38d7dcd314e734c9276bd6fb40f673325bc4baa144c800d2f2f02db2765c012103d2e15674941bad4a996372cb87e1856d3652606d98562fe39c5e9e7e413f210502483045022100d12b852d85dcd961d2f5f4ab660654df6eedcc794c0c33ce5cc309ffb5fce58d022067338a8e0e1725c197fb1a88af59f51e44e4255b20167c8684031c05d1f2592a01210223b72beef0965d10be0778efecd61fcac6f79a4ea169393380734464f84f2ab30000000000").unwrap();
|
|
|
|
}
|
2021-01-27 20:19:15 +00:00
|
|
|
|
2021-02-19 12:28:13 +00:00
|
|
|
#[cfg(feature = "base64")]
|
|
|
|
#[test]
|
|
|
|
#[should_panic(expected = "MustHaveUnsignedTx")]
|
|
|
|
fn invalid_vector_4_base64() {
|
|
|
|
PartiallySignedTransaction::from_str("cHNidP8AAQD9pQEBAAAAAAECiaPHHqtNIOA3G7ukzGmPopXJRjr6Ljl/hTPMti+VZ+UBAAAAFxYAFL4Y0VKpsBIDna89p95PUzSe7LmF/////4b4qkOnHf8USIk6UwpyN+9rRgi7st0tAXHmOuxqSJC0AQAAABcWABT+Pp7xp0XpdNkCxDVZQ6vLNL1TU/////8CAMLrCwAAAAAZdqkUhc/xCX/Z4Ai7NK9wnGIZeziXikiIrHL++E4sAAAAF6kUM5cluiHv1irHU6m80GfWx6ajnQWHAkcwRAIgJxK+IuAnDzlPVoMR3HyppolwuAJf3TskAinwf4pfOiQCIAGLONfc0xTnNMkna9b7QPZzMlvEuqFEyADS8vAtsnZcASED0uFWdJQbrUqZY3LLh+GFbTZSYG2YVi/jnF6efkE/IQUCSDBFAiEA0SuFLYXc2WHS9fSrZgZU327tzHlMDDPOXMMJ/7X85Y0CIGczio4OFyXBl/saiK9Z9R5E5CVbIBZ8hoQDHAXR8lkqASECI7cr7vCWXRC+B3jv7NYfysb3mk6haTkzgHNEZPhPKrMAAAAAAA==").unwrap();
|
2018-08-10 20:34:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2021-02-19 12:28:13 +00:00
|
|
|
#[should_panic(expected = "DuplicateKey(Key { type_value: 0, key: [] })")]
|
2018-08-10 20:34:35 +00:00
|
|
|
fn invalid_vector_5() {
|
2021-02-19 12:28:13 +00:00
|
|
|
hex_psbt!("70736274ff0100750200000001268171371edff285e937adeea4b37b78000c0566cbb3ad64641713ca42171bf60000000000feffffff02d3dff505000000001976a914d0c59903c5bac2868760e90fd521a4665aa7652088ac00e1f5050000000017a9143545e6e33b832c47050f24d3eeb93c9c03948bc787b32e1300000100fda5010100000000010289a3c71eab4d20e0371bbba4cc698fa295c9463afa2e397f8533ccb62f9567e50100000017160014be18d152a9b012039daf3da7de4f53349eecb985ffffffff86f8aa43a71dff1448893a530a7237ef6b4608bbb2dd2d0171e63aec6a4890b40100000017160014fe3e9ef1a745e974d902c4355943abcb34bd5353ffffffff0200c2eb0b000000001976a91485cff1097fd9e008bb34af709c62197b38978a4888ac72fef84e2c00000017a914339725ba21efd62ac753a9bcd067d6c7a6a39d05870247304402202712be22e0270f394f568311dc7ca9a68970b8025fdd3b240229f07f8a5f3a240220018b38d7dcd314e734c9276bd6fb40f673325bc4baa144c800d2f2f02db2765c012103d2e15674941bad4a996372cb87e1856d3652606d98562fe39c5e9e7e413f210502483045022100d12b852d85dcd961d2f5f4ab660654df6eedcc794c0c33ce5cc309ffb5fce58d022067338a8e0e1725c197fb1a88af59f51e44e4255b20167c8684031c05d1f2592a01210223b72beef0965d10be0778efecd61fcac6f79a4ea169393380734464f84f2ab30000000001003f0200000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000ffffffff010000000000000000036a010000000000000000").unwrap();
|
|
|
|
}
|
2021-01-27 20:19:15 +00:00
|
|
|
|
2021-02-19 12:28:13 +00:00
|
|
|
#[cfg(feature = "base64")]
|
|
|
|
#[test]
|
|
|
|
#[should_panic(expected = "DuplicateKey(Key { type_value: 0, key: [] })")]
|
|
|
|
fn invalid_vector_5_base64() {
|
|
|
|
PartiallySignedTransaction::from_str("cHNidP8BAHUCAAAAASaBcTce3/KF6Tet7qSze3gADAVmy7OtZGQXE8pCFxv2AAAAAAD+////AtPf9QUAAAAAGXapFNDFmQPFusKGh2DpD9UhpGZap2UgiKwA4fUFAAAAABepFDVF5uM7gyxHBQ8k0+65PJwDlIvHh7MuEwAAAQD9pQEBAAAAAAECiaPHHqtNIOA3G7ukzGmPopXJRjr6Ljl/hTPMti+VZ+UBAAAAFxYAFL4Y0VKpsBIDna89p95PUzSe7LmF/////4b4qkOnHf8USIk6UwpyN+9rRgi7st0tAXHmOuxqSJC0AQAAABcWABT+Pp7xp0XpdNkCxDVZQ6vLNL1TU/////8CAMLrCwAAAAAZdqkUhc/xCX/Z4Ai7NK9wnGIZeziXikiIrHL++E4sAAAAF6kUM5cluiHv1irHU6m80GfWx6ajnQWHAkcwRAIgJxK+IuAnDzlPVoMR3HyppolwuAJf3TskAinwf4pfOiQCIAGLONfc0xTnNMkna9b7QPZzMlvEuqFEyADS8vAtsnZcASED0uFWdJQbrUqZY3LLh+GFbTZSYG2YVi/jnF6efkE/IQUCSDBFAiEA0SuFLYXc2WHS9fSrZgZU327tzHlMDDPOXMMJ/7X85Y0CIGczio4OFyXBl/saiK9Z9R5E5CVbIBZ8hoQDHAXR8lkqASECI7cr7vCWXRC+B3jv7NYfysb3mk6haTkzgHNEZPhPKrMAAAAAAQA/AgAAAAH//////////////////////////////////////////wAAAAAA/////wEAAAAAAAAAAANqAQAAAAAAAAAA").unwrap();
|
2018-08-10 20:34:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn valid_vector_1() {
|
|
|
|
let unserialized = PartiallySignedTransaction {
|
2021-09-15 05:35:07 +00:00
|
|
|
unsigned_tx: Transaction {
|
|
|
|
version: 2,
|
2022-08-16 01:24:56 +00:00
|
|
|
lock_time: absolute::PackedLockTime(1257139),
|
2021-09-15 05:35:07 +00:00
|
|
|
input: vec![TxIn {
|
|
|
|
previous_output: OutPoint {
|
|
|
|
txid: Txid::from_hex(
|
|
|
|
"f61b1742ca13176464adb3cb66050c00787bb3a4eead37e985f2df1e37718126",
|
|
|
|
).unwrap(),
|
|
|
|
vout: 0,
|
|
|
|
},
|
|
|
|
script_sig: Script::new(),
|
2022-07-16 00:49:03 +00:00
|
|
|
sequence: Sequence::ENABLE_LOCKTIME_NO_RBF,
|
2021-10-05 13:07:55 +00:00
|
|
|
witness: Witness::default(),
|
2021-09-15 05:35:07 +00:00
|
|
|
}],
|
|
|
|
output: vec![
|
|
|
|
TxOut {
|
|
|
|
value: 99999699,
|
|
|
|
script_pubkey: hex_script!("76a914d0c59903c5bac2868760e90fd521a4665aa7652088ac"),
|
|
|
|
},
|
|
|
|
TxOut {
|
|
|
|
value: 100000000,
|
|
|
|
script_pubkey: hex_script!("a9143545e6e33b832c47050f24d3eeb93c9c03948bc787"),
|
|
|
|
},
|
|
|
|
],
|
2018-08-10 20:34:35 +00:00
|
|
|
},
|
2021-09-15 05:35:07 +00:00
|
|
|
xpub: Default::default(),
|
|
|
|
version: 0,
|
|
|
|
proprietary: BTreeMap::new(),
|
|
|
|
unknown: BTreeMap::new(),
|
|
|
|
|
2018-08-10 20:34:35 +00:00
|
|
|
inputs: vec![Input {
|
|
|
|
non_witness_utxo: Some(Transaction {
|
|
|
|
version: 1,
|
2022-08-16 01:24:56 +00:00
|
|
|
lock_time: absolute::PackedLockTime::ZERO,
|
2018-08-10 20:34:35 +00:00
|
|
|
input: vec![TxIn {
|
|
|
|
previous_output: OutPoint {
|
2019-12-09 22:37:52 +00:00
|
|
|
txid: Txid::from_hex(
|
2018-08-10 20:34:35 +00:00
|
|
|
"e567952fb6cc33857f392efa3a46c995a28f69cca4bb1b37e0204dab1ec7a389",
|
|
|
|
).unwrap(),
|
|
|
|
vout: 1,
|
|
|
|
},
|
|
|
|
script_sig: hex_script!("160014be18d152a9b012039daf3da7de4f53349eecb985"),
|
2022-07-16 00:49:03 +00:00
|
|
|
sequence: Sequence::MAX,
|
2021-10-05 13:07:55 +00:00
|
|
|
witness: Witness::from_vec(vec![
|
2020-01-08 17:02:30 +00:00
|
|
|
Vec::from_hex("304402202712be22e0270f394f568311dc7ca9a68970b8025fdd3b240229f07f8a5f3a240220018b38d7dcd314e734c9276bd6fb40f673325bc4baa144c800d2f2f02db2765c01").unwrap(),
|
|
|
|
Vec::from_hex("03d2e15674941bad4a996372cb87e1856d3652606d98562fe39c5e9e7e413f2105").unwrap(),
|
2021-10-05 13:07:55 +00:00
|
|
|
]),
|
2018-08-10 20:34:35 +00:00
|
|
|
},
|
|
|
|
TxIn {
|
|
|
|
previous_output: OutPoint {
|
2019-12-09 22:37:52 +00:00
|
|
|
txid: Txid::from_hex(
|
2018-08-10 20:34:35 +00:00
|
|
|
"b490486aec3ae671012dddb2bb08466bef37720a533a894814ff1da743aaf886",
|
|
|
|
).unwrap(),
|
|
|
|
vout: 1,
|
|
|
|
},
|
|
|
|
script_sig: hex_script!("160014fe3e9ef1a745e974d902c4355943abcb34bd5353"),
|
2022-07-16 00:49:03 +00:00
|
|
|
sequence: Sequence::MAX,
|
2021-10-05 13:07:55 +00:00
|
|
|
witness: Witness::from_vec(vec![
|
2020-01-08 17:02:30 +00:00
|
|
|
Vec::from_hex("3045022100d12b852d85dcd961d2f5f4ab660654df6eedcc794c0c33ce5cc309ffb5fce58d022067338a8e0e1725c197fb1a88af59f51e44e4255b20167c8684031c05d1f2592a01").unwrap(),
|
|
|
|
Vec::from_hex("0223b72beef0965d10be0778efecd61fcac6f79a4ea169393380734464f84f2ab3").unwrap(),
|
2021-10-05 13:07:55 +00:00
|
|
|
]),
|
2018-08-10 20:34:35 +00:00
|
|
|
}],
|
|
|
|
output: vec![
|
|
|
|
TxOut {
|
|
|
|
value: 200000000,
|
|
|
|
script_pubkey: hex_script!("76a91485cff1097fd9e008bb34af709c62197b38978a4888ac"),
|
|
|
|
},
|
|
|
|
TxOut {
|
|
|
|
value: 190303501938,
|
|
|
|
script_pubkey: hex_script!("a914339725ba21efd62ac753a9bcd067d6c7a6a39d0587"),
|
|
|
|
},
|
|
|
|
],
|
|
|
|
}),
|
|
|
|
..Default::default()
|
|
|
|
},],
|
|
|
|
outputs: vec![
|
|
|
|
Output {
|
|
|
|
..Default::default()
|
|
|
|
},
|
|
|
|
Output {
|
|
|
|
..Default::default()
|
|
|
|
},
|
|
|
|
],
|
|
|
|
};
|
|
|
|
|
2021-01-27 20:19:15 +00:00
|
|
|
let base16str = "70736274ff0100750200000001268171371edff285e937adeea4b37b78000c0566cbb3ad64641713ca42171bf60000000000feffffff02d3dff505000000001976a914d0c59903c5bac2868760e90fd521a4665aa7652088ac00e1f5050000000017a9143545e6e33b832c47050f24d3eeb93c9c03948bc787b32e1300000100fda5010100000000010289a3c71eab4d20e0371bbba4cc698fa295c9463afa2e397f8533ccb62f9567e50100000017160014be18d152a9b012039daf3da7de4f53349eecb985ffffffff86f8aa43a71dff1448893a530a7237ef6b4608bbb2dd2d0171e63aec6a4890b40100000017160014fe3e9ef1a745e974d902c4355943abcb34bd5353ffffffff0200c2eb0b000000001976a91485cff1097fd9e008bb34af709c62197b38978a4888ac72fef84e2c00000017a914339725ba21efd62ac753a9bcd067d6c7a6a39d05870247304402202712be22e0270f394f568311dc7ca9a68970b8025fdd3b240229f07f8a5f3a240220018b38d7dcd314e734c9276bd6fb40f673325bc4baa144c800d2f2f02db2765c012103d2e15674941bad4a996372cb87e1856d3652606d98562fe39c5e9e7e413f210502483045022100d12b852d85dcd961d2f5f4ab660654df6eedcc794c0c33ce5cc309ffb5fce58d022067338a8e0e1725c197fb1a88af59f51e44e4255b20167c8684031c05d1f2592a01210223b72beef0965d10be0778efecd61fcac6f79a4ea169393380734464f84f2ab300000000000000";
|
|
|
|
|
|
|
|
assert_eq!(serialize_hex(&unserialized), base16str);
|
|
|
|
assert_eq!(unserialized, hex_psbt!(base16str).unwrap());
|
2018-08-10 20:34:35 +00:00
|
|
|
|
2021-01-27 20:19:15 +00:00
|
|
|
#[cfg(feature = "base64")] {
|
|
|
|
let base64str = "cHNidP8BAHUCAAAAASaBcTce3/KF6Tet7qSze3gADAVmy7OtZGQXE8pCFxv2AAAAAAD+////AtPf9QUAAAAAGXapFNDFmQPFusKGh2DpD9UhpGZap2UgiKwA4fUFAAAAABepFDVF5uM7gyxHBQ8k0+65PJwDlIvHh7MuEwAAAQD9pQEBAAAAAAECiaPHHqtNIOA3G7ukzGmPopXJRjr6Ljl/hTPMti+VZ+UBAAAAFxYAFL4Y0VKpsBIDna89p95PUzSe7LmF/////4b4qkOnHf8USIk6UwpyN+9rRgi7st0tAXHmOuxqSJC0AQAAABcWABT+Pp7xp0XpdNkCxDVZQ6vLNL1TU/////8CAMLrCwAAAAAZdqkUhc/xCX/Z4Ai7NK9wnGIZeziXikiIrHL++E4sAAAAF6kUM5cluiHv1irHU6m80GfWx6ajnQWHAkcwRAIgJxK+IuAnDzlPVoMR3HyppolwuAJf3TskAinwf4pfOiQCIAGLONfc0xTnNMkna9b7QPZzMlvEuqFEyADS8vAtsnZcASED0uFWdJQbrUqZY3LLh+GFbTZSYG2YVi/jnF6efkE/IQUCSDBFAiEA0SuFLYXc2WHS9fSrZgZU327tzHlMDDPOXMMJ/7X85Y0CIGczio4OFyXBl/saiK9Z9R5E5CVbIBZ8hoQDHAXR8lkqASECI7cr7vCWXRC+B3jv7NYfysb3mk6haTkzgHNEZPhPKrMAAAAAAAAA";
|
|
|
|
assert_eq!(PartiallySignedTransaction::from_str(base64str).unwrap(), unserialized);
|
|
|
|
assert_eq!(base64str, unserialized.to_string());
|
|
|
|
assert_eq!(PartiallySignedTransaction::from_str(base64str).unwrap(), hex_psbt!(base16str).unwrap());
|
|
|
|
}
|
2018-08-10 20:34:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn valid_vector_2() {
|
|
|
|
let psbt: PartiallySignedTransaction = hex_psbt!("70736274ff0100a00200000002ab0949a08c5af7c49b8212f417e2f15ab3f5c33dcf153821a8139f877a5b7be40000000000feffffffab0949a08c5af7c49b8212f417e2f15ab3f5c33dcf153821a8139f877a5b7be40100000000feffffff02603bea0b000000001976a914768a40bbd740cbe81d988e71de2a4d5c71396b1d88ac8e240000000000001976a9146f4620b553fa095e721b9ee0efe9fa039cca459788ac000000000001076a47304402204759661797c01b036b25928948686218347d89864b719e1f7fcf57d1e511658702205309eabf56aa4d8891ffd111fdf1336f3a29da866d7f8486d75546ceedaf93190121035cdc61fc7ba971c0b501a646a2a83b102cb43881217ca682dc86e2d73fa882920001012000e1f5050000000017a9143545e6e33b832c47050f24d3eeb93c9c03948bc787010416001485d13537f2e265405a34dbafa9e3dda01fb82308000000").unwrap();
|
|
|
|
|
|
|
|
assert_eq!(psbt.inputs.len(), 2);
|
|
|
|
assert_eq!(psbt.outputs.len(), 2);
|
|
|
|
|
|
|
|
assert!(&psbt.inputs[0].final_script_sig.is_some());
|
|
|
|
|
2022-06-07 05:07:59 +00:00
|
|
|
let redeem_script = psbt.inputs[1].redeem_script.as_ref().unwrap();
|
2018-08-10 20:34:35 +00:00
|
|
|
let expected_out = hex_script!("a9143545e6e33b832c47050f24d3eeb93c9c03948bc787");
|
|
|
|
|
|
|
|
assert!(redeem_script.is_v0_p2wpkh());
|
|
|
|
assert_eq!(
|
|
|
|
redeem_script.to_p2sh(),
|
|
|
|
psbt.inputs[1].witness_utxo.as_ref().unwrap().script_pubkey
|
|
|
|
);
|
|
|
|
assert_eq!(redeem_script.to_p2sh(), expected_out);
|
|
|
|
|
|
|
|
for output in psbt.outputs {
|
|
|
|
assert_eq!(output.get_pairs().unwrap().len(), 0)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn valid_vector_3() {
|
|
|
|
let psbt: PartiallySignedTransaction = hex_psbt!("70736274ff0100750200000001268171371edff285e937adeea4b37b78000c0566cbb3ad64641713ca42171bf60000000000feffffff02d3dff505000000001976a914d0c59903c5bac2868760e90fd521a4665aa7652088ac00e1f5050000000017a9143545e6e33b832c47050f24d3eeb93c9c03948bc787b32e1300000100fda5010100000000010289a3c71eab4d20e0371bbba4cc698fa295c9463afa2e397f8533ccb62f9567e50100000017160014be18d152a9b012039daf3da7de4f53349eecb985ffffffff86f8aa43a71dff1448893a530a7237ef6b4608bbb2dd2d0171e63aec6a4890b40100000017160014fe3e9ef1a745e974d902c4355943abcb34bd5353ffffffff0200c2eb0b000000001976a91485cff1097fd9e008bb34af709c62197b38978a4888ac72fef84e2c00000017a914339725ba21efd62ac753a9bcd067d6c7a6a39d05870247304402202712be22e0270f394f568311dc7ca9a68970b8025fdd3b240229f07f8a5f3a240220018b38d7dcd314e734c9276bd6fb40f673325bc4baa144c800d2f2f02db2765c012103d2e15674941bad4a996372cb87e1856d3652606d98562fe39c5e9e7e413f210502483045022100d12b852d85dcd961d2f5f4ab660654df6eedcc794c0c33ce5cc309ffb5fce58d022067338a8e0e1725c197fb1a88af59f51e44e4255b20167c8684031c05d1f2592a01210223b72beef0965d10be0778efecd61fcac6f79a4ea169393380734464f84f2ab30000000001030401000000000000").unwrap();
|
|
|
|
|
|
|
|
assert_eq!(psbt.inputs.len(), 1);
|
|
|
|
assert_eq!(psbt.outputs.len(), 2);
|
|
|
|
|
2021-09-15 05:35:07 +00:00
|
|
|
let tx_input = &psbt.unsigned_tx.input[0];
|
2022-09-22 22:00:05 +00:00
|
|
|
let psbt_non_witness_utxo = psbt.inputs[0].non_witness_utxo.as_ref().unwrap();
|
2018-08-10 20:34:35 +00:00
|
|
|
|
|
|
|
assert_eq!(tx_input.previous_output.txid, psbt_non_witness_utxo.txid());
|
2022-01-24 00:31:39 +00:00
|
|
|
assert!(psbt_non_witness_utxo.output[tx_input.previous_output.vout as usize]
|
2018-08-10 20:34:35 +00:00
|
|
|
.script_pubkey
|
|
|
|
.is_p2pkh()
|
|
|
|
);
|
|
|
|
assert_eq!(
|
2022-09-22 22:00:05 +00:00
|
|
|
psbt.inputs[0].sighash_type.as_ref().unwrap().ecdsa_hash_ty().unwrap(),
|
2022-03-28 21:48:53 +00:00
|
|
|
EcdsaSighashType::All
|
2018-08-10 20:34:35 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn valid_vector_4() {
|
|
|
|
let psbt: PartiallySignedTransaction = hex_psbt!("70736274ff0100a00200000002ab0949a08c5af7c49b8212f417e2f15ab3f5c33dcf153821a8139f877a5b7be40000000000feffffffab0949a08c5af7c49b8212f417e2f15ab3f5c33dcf153821a8139f877a5b7be40100000000feffffff02603bea0b000000001976a914768a40bbd740cbe81d988e71de2a4d5c71396b1d88ac8e240000000000001976a9146f4620b553fa095e721b9ee0efe9fa039cca459788ac00000000000100df0200000001268171371edff285e937adeea4b37b78000c0566cbb3ad64641713ca42171bf6000000006a473044022070b2245123e6bf474d60c5b50c043d4c691a5d2435f09a34a7662a9dc251790a022001329ca9dacf280bdf30740ec0390422422c81cb45839457aeb76fc12edd95b3012102657d118d3357b8e0f4c2cd46db7b39f6d9c38d9a70abcb9b2de5dc8dbfe4ce31feffffff02d3dff505000000001976a914d0c59903c5bac2868760e90fd521a4665aa7652088ac00e1f5050000000017a9143545e6e33b832c47050f24d3eeb93c9c03948bc787b32e13000001012000e1f5050000000017a9143545e6e33b832c47050f24d3eeb93c9c03948bc787010416001485d13537f2e265405a34dbafa9e3dda01fb8230800220202ead596687ca806043edc3de116cdf29d5e9257c196cd055cf698c8d02bf24e9910b4a6ba670000008000000080020000800022020394f62be9df19952c5587768aeb7698061ad2c4a25c894f47d8c162b4d7213d0510b4a6ba6700000080010000800200008000").unwrap();
|
|
|
|
|
|
|
|
assert_eq!(psbt.inputs.len(), 2);
|
|
|
|
assert_eq!(psbt.outputs.len(), 2);
|
|
|
|
|
|
|
|
assert!(&psbt.inputs[0].final_script_sig.is_none());
|
|
|
|
assert!(&psbt.inputs[1].final_script_sig.is_none());
|
|
|
|
|
2022-06-07 05:07:59 +00:00
|
|
|
let redeem_script = psbt.inputs[1].redeem_script.as_ref().unwrap();
|
2018-08-10 20:34:35 +00:00
|
|
|
let expected_out = hex_script!("a9143545e6e33b832c47050f24d3eeb93c9c03948bc787");
|
|
|
|
|
|
|
|
assert!(redeem_script.is_v0_p2wpkh());
|
|
|
|
assert_eq!(
|
|
|
|
redeem_script.to_p2sh(),
|
|
|
|
psbt.inputs[1].witness_utxo.as_ref().unwrap().script_pubkey
|
|
|
|
);
|
|
|
|
assert_eq!(redeem_script.to_p2sh(), expected_out);
|
|
|
|
|
|
|
|
for output in psbt.outputs {
|
2022-06-07 05:17:20 +00:00
|
|
|
assert!(!output.get_pairs().unwrap().is_empty())
|
2018-08-10 20:34:35 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn valid_vector_5() {
|
|
|
|
let psbt: PartiallySignedTransaction = hex_psbt!("70736274ff0100550200000001279a2323a5dfb51fc45f220fa58b0fc13e1e3342792a85d7e36cd6333b5cbc390000000000ffffffff01a05aea0b000000001976a914ffe9c0061097cc3b636f2cb0460fa4fc427d2b4588ac0000000000010120955eea0b0000000017a9146345200f68d189e1adc0df1c4d16ea8f14c0dbeb87220203b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4646304302200424b58effaaa694e1559ea5c93bbfd4a89064224055cdf070b6771469442d07021f5c8eb0fea6516d60b8acb33ad64ede60e8785bfb3aa94b99bdf86151db9a9a010104220020771fd18ad459666dd49f3d564e3dbc42f4c84774e360ada16816a8ed488d5681010547522103b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd462103de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd52ae220603b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4610b4a6ba67000000800000008004000080220603de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd10b4a6ba670000008000000080050000800000").unwrap();
|
|
|
|
|
|
|
|
assert_eq!(psbt.inputs.len(), 1);
|
|
|
|
assert_eq!(psbt.outputs.len(), 1);
|
|
|
|
|
|
|
|
assert!(&psbt.inputs[0].final_script_sig.is_none());
|
|
|
|
|
2022-06-07 05:07:59 +00:00
|
|
|
let redeem_script = psbt.inputs[0].redeem_script.as_ref().unwrap();
|
2018-08-10 20:34:35 +00:00
|
|
|
let expected_out = hex_script!("a9146345200f68d189e1adc0df1c4d16ea8f14c0dbeb87");
|
|
|
|
|
|
|
|
assert!(redeem_script.is_v0_p2wsh());
|
|
|
|
assert_eq!(
|
|
|
|
redeem_script.to_p2sh(),
|
|
|
|
psbt.inputs[0].witness_utxo.as_ref().unwrap().script_pubkey
|
|
|
|
);
|
|
|
|
|
|
|
|
assert_eq!(redeem_script.to_p2sh(), expected_out);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn valid_vector_6() {
|
|
|
|
let psbt: PartiallySignedTransaction = hex_psbt!("70736274ff01003f0200000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000ffffffff010000000000000000036a010000000000000a0f0102030405060708090f0102030405060708090a0b0c0d0e0f0000").unwrap();
|
|
|
|
|
|
|
|
assert_eq!(psbt.inputs.len(), 1);
|
|
|
|
assert_eq!(psbt.outputs.len(), 1);
|
|
|
|
|
2021-09-15 05:35:07 +00:00
|
|
|
let tx = &psbt.unsigned_tx;
|
2018-08-10 20:34:35 +00:00
|
|
|
assert_eq!(
|
|
|
|
tx.txid(),
|
2022-01-24 00:31:39 +00:00
|
|
|
Txid::from_hex("75c5c9665a570569ad77dd1279e6fd4628a093c4dcbf8d41532614044c14c115").unwrap(),
|
2018-08-10 20:34:35 +00:00
|
|
|
);
|
|
|
|
|
2019-08-09 15:03:12 +00:00
|
|
|
let mut unknown: BTreeMap<raw::Key, Vec<u8>> = BTreeMap::new();
|
2018-08-10 20:34:35 +00:00
|
|
|
let key: raw::Key = raw::Key {
|
|
|
|
type_value: 0x0fu8,
|
2020-01-08 17:02:30 +00:00
|
|
|
key: Vec::from_hex("010203040506070809").unwrap(),
|
2018-08-10 20:34:35 +00:00
|
|
|
};
|
2020-01-08 17:02:30 +00:00
|
|
|
let value: Vec<u8> = Vec::from_hex("0102030405060708090a0b0c0d0e0f").unwrap();
|
2018-08-10 20:34:35 +00:00
|
|
|
|
|
|
|
unknown.insert(key, value);
|
|
|
|
|
|
|
|
assert_eq!(psbt.inputs[0].unknown, unknown)
|
|
|
|
}
|
|
|
|
}
|
2020-09-01 21:45:34 +00:00
|
|
|
|
2021-10-27 13:06:20 +00:00
|
|
|
mod bip_371_vectors {
|
|
|
|
use super::*;
|
|
|
|
use super::serialize;
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn invalid_vectors() {
|
|
|
|
let err = hex_psbt!("70736274ff010071020000000127744ababf3027fe0d6cf23a96eee2efb188ef52301954585883e69b6624b2420000000000ffffffff02787c01000000000016001483a7e34bd99ff03a4962ef8a1a101bb295461ece606b042a010000001600147ac369df1b20e033d6116623957b0ac49f3c52e8000000000001012b00f2052a010000002251205a2c2cf5b52cf31f83ad2e8da63ff03183ecd8f609c7510ae8a48e03910a075701172102fe349064c98d6e2a853fa3c9b12bd8b304a19c195c60efa7ee2393046d3fa232000000").unwrap_err();
|
|
|
|
assert_eq!(err.to_string(), "parse failed: Invalid xonly public key");
|
|
|
|
let err = hex_psbt!("70736274ff010071020000000127744ababf3027fe0d6cf23a96eee2efb188ef52301954585883e69b6624b2420000000000ffffffff02787c01000000000016001483a7e34bd99ff03a4962ef8a1a101bb295461ece606b042a010000001600147ac369df1b20e033d6116623957b0ac49f3c52e8000000000001012b00f2052a010000002251205a2c2cf5b52cf31f83ad2e8da63ff03183ecd8f609c7510ae8a48e03910a0757011342173bb3d36c074afb716fec6307a069a2e450b995f3c82785945ab8df0e24260dcd703b0cbf34de399184a9481ac2b3586db6601f026a77f7e4938481bc34751701aa000000").unwrap_err();
|
2022-01-13 04:27:20 +00:00
|
|
|
assert_eq!(err.to_string(), "parse failed: Invalid Schnorr signature length");
|
2021-10-27 13:06:20 +00:00
|
|
|
let err = hex_psbt!("70736274ff010071020000000127744ababf3027fe0d6cf23a96eee2efb188ef52301954585883e69b6624b2420000000000ffffffff02787c01000000000016001483a7e34bd99ff03a4962ef8a1a101bb295461ece606b042a010000001600147ac369df1b20e033d6116623957b0ac49f3c52e8000000000001012b00f2052a010000002251205a2c2cf5b52cf31f83ad2e8da63ff03183ecd8f609c7510ae8a48e03910a0757221602fe349064c98d6e2a853fa3c9b12bd8b304a19c195c60efa7ee2393046d3fa2321900772b2da75600008001000080000000800100000000000000000000").unwrap_err();
|
|
|
|
assert_eq!(err.to_string(), "parse failed: Invalid xonly public key");
|
|
|
|
let err = hex_psbt!("70736274ff01007d020000000127744ababf3027fe0d6cf23a96eee2efb188ef52301954585883e69b6624b2420000000000ffffffff02887b0100000000001600142382871c7e8421a00093f754d91281e675874b9f606b042a010000002251205a2c2cf5b52cf31f83ad2e8da63ff03183ecd8f609c7510ae8a48e03910a0757000000000001012b00f2052a010000002251205a2c2cf5b52cf31f83ad2e8da63ff03183ecd8f609c7510ae8a48e03910a0757000001052102fe349064c98d6e2a853fa3c9b12bd8b304a19c195c60efa7ee2393046d3fa23200").unwrap_err();
|
|
|
|
assert_eq!(err.to_string(), "parse failed: Invalid xonly public key");
|
|
|
|
let err = hex_psbt!("70736274ff01007d020000000127744ababf3027fe0d6cf23a96eee2efb188ef52301954585883e69b6624b2420000000000ffffffff02887b0100000000001600142382871c7e8421a00093f754d91281e675874b9f606b042a010000002251205a2c2cf5b52cf31f83ad2e8da63ff03183ecd8f609c7510ae8a48e03910a0757000000000001012b00f2052a010000002251205a2c2cf5b52cf31f83ad2e8da63ff03183ecd8f609c7510ae8a48e03910a07570000220702fe349064c98d6e2a853fa3c9b12bd8b304a19c195c60efa7ee2393046d3fa2321900772b2da7560000800100008000000080010000000000000000").unwrap_err();
|
|
|
|
assert_eq!(err.to_string(), "parse failed: Invalid xonly public key");
|
|
|
|
let err = hex_psbt!("70736274ff01005e02000000019bd48765230bf9a72e662001f972556e54f0c6f97feb56bcb5600d817f6995260100000000ffffffff0148e6052a01000000225120030da4fce4f7db28c2cb2951631e003713856597fe963882cb500e68112cca63000000000001012b00f2052a01000000225120c2247efbfd92ac47f6f40b8d42d169175a19fa9fa10e4a25d7f35eb4dd85b6924214022cb13ac68248de806aa6a3659cf3c03eb6821d09c8114a4e868febde865bb6d2cd970e15f53fc0c82f950fd560ffa919b76172be017368a89913af074f400b094089756aa3739ccc689ec0fcf3a360be32cc0b59b16e93a1e8bb4605726b2ca7a3ff706c4176649632b2cc68e1f912b8a578e3719ce7710885c7a966f49bcd43cb0000").unwrap_err();
|
2022-05-25 02:56:51 +00:00
|
|
|
#[cfg(feature = "std")]
|
|
|
|
assert_eq!(err.to_string(), "PSBT error");
|
|
|
|
#[cfg(not(feature = "std"))]
|
|
|
|
assert_eq!(err.to_string(), "PSBT error: hash parse error: bad slice length 33 (expected 32)");
|
2021-10-27 13:06:20 +00:00
|
|
|
let err = hex_psbt!("70736274ff01005e02000000019bd48765230bf9a72e662001f972556e54f0c6f97feb56bcb5600d817f6995260100000000ffffffff0148e6052a01000000225120030da4fce4f7db28c2cb2951631e003713856597fe963882cb500e68112cca63000000000001012b00f2052a01000000225120c2247efbfd92ac47f6f40b8d42d169175a19fa9fa10e4a25d7f35eb4dd85b69241142cb13ac68248de806aa6a3659cf3c03eb6821d09c8114a4e868febde865bb6d2cd970e15f53fc0c82f950fd560ffa919b76172be017368a89913af074f400b094289756aa3739ccc689ec0fcf3a360be32cc0b59b16e93a1e8bb4605726b2ca7a3ff706c4176649632b2cc68e1f912b8a578e3719ce7710885c7a966f49bcd43cb01010000").unwrap_err();
|
2022-01-13 04:27:20 +00:00
|
|
|
assert_eq!(err.to_string(), "parse failed: Invalid Schnorr signature length");
|
2021-10-27 13:06:20 +00:00
|
|
|
let err = hex_psbt!("70736274ff01005e02000000019bd48765230bf9a72e662001f972556e54f0c6f97feb56bcb5600d817f6995260100000000ffffffff0148e6052a01000000225120030da4fce4f7db28c2cb2951631e003713856597fe963882cb500e68112cca63000000000001012b00f2052a01000000225120c2247efbfd92ac47f6f40b8d42d169175a19fa9fa10e4a25d7f35eb4dd85b69241142cb13ac68248de806aa6a3659cf3c03eb6821d09c8114a4e868febde865bb6d2cd970e15f53fc0c82f950fd560ffa919b76172be017368a89913af074f400b093989756aa3739ccc689ec0fcf3a360be32cc0b59b16e93a1e8bb4605726b2ca7a3ff706c4176649632b2cc68e1f912b8a578e3719ce7710885c7a966f49bcd43cb0000").unwrap_err();
|
2022-01-13 04:27:20 +00:00
|
|
|
assert_eq!(err.to_string(), "parse failed: Invalid Schnorr signature length");
|
2021-10-27 13:06:20 +00:00
|
|
|
let err = hex_psbt!("70736274ff01005e02000000019bd48765230bf9a72e662001f972556e54f0c6f97feb56bcb5600d817f6995260100000000ffffffff0148e6052a01000000225120030da4fce4f7db28c2cb2951631e003713856597fe963882cb500e68112cca63000000000001012b00f2052a01000000225120c2247efbfd92ac47f6f40b8d42d169175a19fa9fa10e4a25d7f35eb4dd85b6926315c150929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac06f7d62059e9497a1a4a267569d9876da60101aff38e3529b9b939ce7f91ae970115f2e490af7cc45c4f78511f36057ce5c5a5c56325a29fb44dfc203f356e1f80023202cb13ac68248de806aa6a3659cf3c03eb6821d09c8114a4e868febde865bb6d2acc00000").unwrap_err();
|
|
|
|
assert_eq!(err.to_string(), "parse failed: Invalid control block");
|
|
|
|
let err = hex_psbt!("70736274ff01005e02000000019bd48765230bf9a72e662001f972556e54f0c6f97feb56bcb5600d817f6995260100000000ffffffff0148e6052a01000000225120030da4fce4f7db28c2cb2951631e003713856597fe963882cb500e68112cca63000000000001012b00f2052a01000000225120c2247efbfd92ac47f6f40b8d42d169175a19fa9fa10e4a25d7f35eb4dd85b6926115c150929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac06f7d62059e9497a1a4a267569d9876da60101aff38e3529b9b939ce7f91ae970115f2e490af7cc45c4f78511f36057ce5c5a5c56325a29fb44dfc203f356e123202cb13ac68248de806aa6a3659cf3c03eb6821d09c8114a4e868febde865bb6d2acc00000").unwrap_err();
|
|
|
|
assert_eq!(err.to_string(), "parse failed: Invalid control block");
|
|
|
|
}
|
|
|
|
|
|
|
|
fn rtt_psbt(psbt: PartiallySignedTransaction) {
|
|
|
|
let enc = serialize(&psbt);
|
|
|
|
let psbt2 = deserialize::<PartiallySignedTransaction>(&enc).unwrap();
|
|
|
|
assert_eq!(psbt, psbt2);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn valid_psbt_vectors() {
|
|
|
|
let psbt = hex_psbt!("70736274ff010052020000000127744ababf3027fe0d6cf23a96eee2efb188ef52301954585883e69b6624b2420000000000ffffffff0148e6052a01000000160014768e1eeb4cf420866033f80aceff0f9720744969000000000001012b00f2052a010000002251205a2c2cf5b52cf31f83ad2e8da63ff03183ecd8f609c7510ae8a48e03910a07572116fe349064c98d6e2a853fa3c9b12bd8b304a19c195c60efa7ee2393046d3fa2321900772b2da75600008001000080000000800100000000000000011720fe349064c98d6e2a853fa3c9b12bd8b304a19c195c60efa7ee2393046d3fa232002202036b772a6db74d8753c98a827958de6c78ab3312109f37d3e0304484242ece73d818772b2da7540000800100008000000080000000000000000000").unwrap();
|
|
|
|
let internal_key = psbt.inputs[0].tap_internal_key.unwrap();
|
|
|
|
assert!(psbt.inputs[0].tap_key_origins.contains_key(&internal_key));
|
|
|
|
rtt_psbt(psbt);
|
|
|
|
|
|
|
|
// vector 2
|
|
|
|
let psbt = hex_psbt!("70736274ff010052020000000127744ababf3027fe0d6cf23a96eee2efb188ef52301954585883e69b6624b2420000000000ffffffff0148e6052a01000000160014768e1eeb4cf420866033f80aceff0f9720744969000000000001012b00f2052a010000002251205a2c2cf5b52cf31f83ad2e8da63ff03183ecd8f609c7510ae8a48e03910a0757011340bb53ec917bad9d906af1ba87181c48b86ace5aae2b53605a725ca74625631476fc6f5baedaf4f2ee0f477f36f58f3970d5b8273b7e497b97af2e3f125c97af342116fe349064c98d6e2a853fa3c9b12bd8b304a19c195c60efa7ee2393046d3fa2321900772b2da75600008001000080000000800100000000000000011720fe349064c98d6e2a853fa3c9b12bd8b304a19c195c60efa7ee2393046d3fa232002202036b772a6db74d8753c98a827958de6c78ab3312109f37d3e0304484242ece73d818772b2da7540000800100008000000080000000000000000000").unwrap();
|
|
|
|
let internal_key = psbt.inputs[0].tap_internal_key.unwrap();
|
|
|
|
assert!(psbt.inputs[0].tap_key_origins.contains_key(&internal_key));
|
|
|
|
assert!(psbt.inputs[0].tap_key_sig.is_some());
|
|
|
|
rtt_psbt(psbt);
|
|
|
|
|
|
|
|
// vector 3
|
|
|
|
let psbt = hex_psbt!("70736274ff01005e020000000127744ababf3027fe0d6cf23a96eee2efb188ef52301954585883e69b6624b2420000000000ffffffff0148e6052a0100000022512083698e458c6664e1595d75da2597de1e22ee97d798e706c4c0a4b5a9823cd743000000000001012b00f2052a010000002251205a2c2cf5b52cf31f83ad2e8da63ff03183ecd8f609c7510ae8a48e03910a07572116fe349064c98d6e2a853fa3c9b12bd8b304a19c195c60efa7ee2393046d3fa2321900772b2da75600008001000080000000800100000000000000011720fe349064c98d6e2a853fa3c9b12bd8b304a19c195c60efa7ee2393046d3fa232000105201124da7aec92ccd06c954562647f437b138b95721a84be2bf2276bbddab3e67121071124da7aec92ccd06c954562647f437b138b95721a84be2bf2276bbddab3e6711900772b2da7560000800100008000000080000000000500000000").unwrap();
|
|
|
|
let internal_key = psbt.outputs[0].tap_internal_key.unwrap();
|
|
|
|
assert!(psbt.outputs[0].tap_key_origins.contains_key(&internal_key));
|
|
|
|
rtt_psbt(psbt);
|
|
|
|
|
|
|
|
// vector 4
|
|
|
|
let psbt = hex_psbt!("70736274ff01005e02000000019bd48765230bf9a72e662001f972556e54f0c6f97feb56bcb5600d817f6995260100000000ffffffff0148e6052a0100000022512083698e458c6664e1595d75da2597de1e22ee97d798e706c4c0a4b5a9823cd743000000000001012b00f2052a01000000225120c2247efbfd92ac47f6f40b8d42d169175a19fa9fa10e4a25d7f35eb4dd85b6926215c150929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac06f7d62059e9497a1a4a267569d9876da60101aff38e3529b9b939ce7f91ae970115f2e490af7cc45c4f78511f36057ce5c5a5c56325a29fb44dfc203f356e1f823202cb13ac68248de806aa6a3659cf3c03eb6821d09c8114a4e868febde865bb6d2acc04215c150929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac097c6e6fea5ff714ff5724499990810e406e98aa10f5bf7e5f6784bc1d0a9a6ce23204320b0bf16f011b53ea7be615924aa7f27e5d29ad20ea1155d848676c3bad1b2acc06215c150929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0cd970e15f53fc0c82f950fd560ffa919b76172be017368a89913af074f400b09115f2e490af7cc45c4f78511f36057ce5c5a5c56325a29fb44dfc203f356e1f82320fa0f7a3cef3b1d0c0a6ce7d26e17ada0b2e5c92d19efad48b41859cb8a451ca9acc021162cb13ac68248de806aa6a3659cf3c03eb6821d09c8114a4e868febde865bb6d23901cd970e15f53fc0c82f950fd560ffa919b76172be017368a89913af074f400b09772b2da7560000800100008002000080000000000000000021164320b0bf16f011b53ea7be615924aa7f27e5d29ad20ea1155d848676c3bad1b23901115f2e490af7cc45c4f78511f36057ce5c5a5c56325a29fb44dfc203f356e1f8772b2da75600008001000080010000800000000000000000211650929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac005007c461e5d2116fa0f7a3cef3b1d0c0a6ce7d26e17ada0b2e5c92d19efad48b41859cb8a451ca939016f7d62059e9497a1a4a267569d9876da60101aff38e3529b9b939ce7f91ae970772b2da7560000800100008003000080000000000000000001172050929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0011820f0362e2f75a6f420a5bde3eb221d96ae6720cf25f81890c95b1d775acb515e65000105201124da7aec92ccd06c954562647f437b138b95721a84be2bf2276bbddab3e67121071124da7aec92ccd06c954562647f437b138b95721a84be2bf2276bbddab3e6711900772b2da7560000800100008000000080000000000500000000").unwrap();
|
|
|
|
assert!(psbt.inputs[0].tap_internal_key.is_some());
|
|
|
|
assert!(psbt.inputs[0].tap_merkle_root.is_some());
|
|
|
|
assert!(!psbt.inputs[0].tap_key_origins.is_empty());
|
|
|
|
assert!(!psbt.inputs[0].tap_scripts.is_empty());
|
|
|
|
rtt_psbt(psbt);
|
|
|
|
|
|
|
|
// vector 5
|
|
|
|
let psbt = hex_psbt!("70736274ff01005e020000000127744ababf3027fe0d6cf23a96eee2efb188ef52301954585883e69b6624b2420000000000ffffffff0148e6052a010000002251200a8cbdc86de1ce1c0f9caeb22d6df7ced3683fe423e05d1e402a879341d6f6f5000000000001012b00f2052a010000002251205a2c2cf5b52cf31f83ad2e8da63ff03183ecd8f609c7510ae8a48e03910a07572116fe349064c98d6e2a853fa3c9b12bd8b304a19c195c60efa7ee2393046d3fa2321900772b2da75600008001000080000000800100000000000000011720fe349064c98d6e2a853fa3c9b12bd8b304a19c195c60efa7ee2393046d3fa2320001052050929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac001066f02c02220736e572900fe1252589a2143c8f3c79f71a0412d2353af755e9701c782694a02ac02c02220631c5f3b5832b8fbdebfb19704ceeb323c21f40f7a24f43d68ef0cc26b125969ac01c0222044faa49a0338de488c8dfffecdfb6f329f380bd566ef20c8df6d813eab1c4273ac210744faa49a0338de488c8dfffecdfb6f329f380bd566ef20c8df6d813eab1c42733901f06b798b92a10ed9a9d0bbfd3af173a53b1617da3a4159ca008216cd856b2e0e772b2da75600008001000080010000800000000003000000210750929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac005007c461e5d2107631c5f3b5832b8fbdebfb19704ceeb323c21f40f7a24f43d68ef0cc26b125969390118ace409889785e0ea70ceebb8e1ca892a7a78eaede0f2e296cf435961a8f4ca772b2da756000080010000800200008000000000030000002107736e572900fe1252589a2143c8f3c79f71a0412d2353af755e9701c782694a02390129a5b4915090162d759afd3fe0f93fa3326056d0b4088cb933cae7826cb8d82c772b2da7560000800100008003000080000000000300000000").unwrap();
|
|
|
|
assert!(psbt.outputs[0].tap_internal_key.is_some());
|
|
|
|
assert!(!psbt.outputs[0].tap_key_origins.is_empty());
|
|
|
|
assert!(psbt.outputs[0].tap_tree.is_some());
|
|
|
|
rtt_psbt(psbt);
|
|
|
|
|
|
|
|
// vector 6
|
|
|
|
let psbt = hex_psbt!("70736274ff01005e02000000019bd48765230bf9a72e662001f972556e54f0c6f97feb56bcb5600d817f6995260100000000ffffffff0148e6052a0100000022512083698e458c6664e1595d75da2597de1e22ee97d798e706c4c0a4b5a9823cd743000000000001012b00f2052a01000000225120c2247efbfd92ac47f6f40b8d42d169175a19fa9fa10e4a25d7f35eb4dd85b69241142cb13ac68248de806aa6a3659cf3c03eb6821d09c8114a4e868febde865bb6d2cd970e15f53fc0c82f950fd560ffa919b76172be017368a89913af074f400b0940bf818d9757d6ffeb538ba057fb4c1fc4e0f5ef186e765beb564791e02af5fd3d5e2551d4e34e33d86f276b82c99c79aed3f0395a081efcd2cc2c65dd7e693d7941144320b0bf16f011b53ea7be615924aa7f27e5d29ad20ea1155d848676c3bad1b2115f2e490af7cc45c4f78511f36057ce5c5a5c56325a29fb44dfc203f356e1f840e1f1ab6fabfa26b236f21833719dc1d428ab768d80f91f9988d8abef47bfb863bb1f2a529f768c15f00ce34ec283cdc07e88f8428be28f6ef64043c32911811a4114fa0f7a3cef3b1d0c0a6ce7d26e17ada0b2e5c92d19efad48b41859cb8a451ca96f7d62059e9497a1a4a267569d9876da60101aff38e3529b9b939ce7f91ae97040ec1f0379206461c83342285423326708ab031f0da4a253ee45aafa5b8c92034d8b605490f8cd13e00f989989b97e215faa36f12dee3693d2daccf3781c1757f66215c150929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac06f7d62059e9497a1a4a267569d9876da60101aff38e3529b9b939ce7f91ae970115f2e490af7cc45c4f78511f36057ce5c5a5c56325a29fb44dfc203f356e1f823202cb13ac68248de806aa6a3659cf3c03eb6821d09c8114a4e868febde865bb6d2acc04215c150929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac097c6e6fea5ff714ff5724499990810e406e98aa10f5bf7e5f6784bc1d0a9a6ce23204320b0bf16f011b53ea7be615924aa7f27e5d29ad20ea1155d848676c3bad1b2acc06215c150929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0cd970e15f53fc0c82f950fd560ffa919b76172be017368a89913af074f400b09115f2e490af7cc45c4f78511f36057ce5c5a5c56325a29fb44dfc203f356e1f82320fa0f7a3cef3b1d0c0a6ce7d26e17ada0b2e5c92d19efad48b41859cb8a451ca9acc021162cb13ac68248de806aa6a3659cf3c03eb6821d09c8114a4e868febde865bb6d23901cd970e15f53fc0c82f950fd560ffa919b76172be017368a89913af074f400b09772b2da7560000800100008002000080000000000000000021164320b0bf16f011b53ea7be615924aa7f27e5d29ad20ea1155d848676c3bad1b23901115f2e490af7cc45c4f78511f36057ce5c5a5c56325a29fb44dfc203f356e1f8772b2da75600008001000080010000800000000000000000211650929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac005007c461e5d2116fa0f7a3cef3b1d0c0a6ce7d26e17ada0b2e5c92d19efad48b41859cb8a451ca939016f7d62059e9497a1a4a267569d9876da60101aff38e3529b9b939ce7f91ae970772b2da7560000800100008003000080000000000000000001172050929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0011820f0362e2f75a6f420a5bde3eb221d96ae6720cf25f81890c95b1d775acb515e65000105201124da7aec92ccd06c954562647f437b138b95721a84be2bf2276bbddab3e67121071124da7aec92ccd06c954562647f437b138b95721a84be2bf2276bbddab3e6711900772b2da7560000800100008000000080000000000500000000").unwrap();
|
|
|
|
assert!(psbt.inputs[0].tap_internal_key.is_some());
|
|
|
|
assert!(psbt.inputs[0].tap_merkle_root.is_some());
|
|
|
|
assert!(!psbt.inputs[0].tap_scripts.is_empty());
|
|
|
|
assert!(!psbt.inputs[0].tap_script_sigs.is_empty());
|
|
|
|
assert!(!psbt.inputs[0].tap_key_origins.is_empty());
|
|
|
|
rtt_psbt(psbt);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-01 21:45:34 +00:00
|
|
|
#[test]
|
2022-01-24 00:26:29 +00:00
|
|
|
fn serialize_and_deserialize_preimage_psbt() {
|
2020-09-01 21:45:34 +00:00
|
|
|
// create a sha preimage map
|
|
|
|
let mut sha256_preimages = BTreeMap::new();
|
|
|
|
sha256_preimages.insert(sha256::Hash::hash(&[1u8, 2u8]), vec![1u8, 2u8]);
|
|
|
|
sha256_preimages.insert(sha256::Hash::hash(&[1u8]), vec![1u8]);
|
|
|
|
|
|
|
|
// same for hash160
|
|
|
|
let mut hash160_preimages = BTreeMap::new();
|
|
|
|
hash160_preimages.insert(hash160::Hash::hash(&[1u8, 2u8]), vec![1u8, 2u8]);
|
|
|
|
hash160_preimages.insert(hash160::Hash::hash(&[1u8]), vec![1u8]);
|
|
|
|
|
|
|
|
// same vector as valid_vector_1 from BIPs with added
|
|
|
|
let mut unserialized = PartiallySignedTransaction {
|
2021-09-15 05:35:07 +00:00
|
|
|
unsigned_tx: Transaction {
|
|
|
|
version: 2,
|
2022-08-16 01:24:56 +00:00
|
|
|
lock_time: absolute::PackedLockTime(1257139),
|
2021-09-15 05:35:07 +00:00
|
|
|
input: vec![TxIn {
|
|
|
|
previous_output: OutPoint {
|
|
|
|
txid: Txid::from_hex(
|
|
|
|
"f61b1742ca13176464adb3cb66050c00787bb3a4eead37e985f2df1e37718126",
|
|
|
|
).unwrap(),
|
|
|
|
vout: 0,
|
|
|
|
},
|
|
|
|
script_sig: Script::new(),
|
2022-07-16 00:49:03 +00:00
|
|
|
sequence: Sequence::ENABLE_LOCKTIME_NO_RBF,
|
2021-10-05 13:07:55 +00:00
|
|
|
witness: Witness::default(),
|
2021-09-15 05:35:07 +00:00
|
|
|
}],
|
|
|
|
output: vec![
|
|
|
|
TxOut {
|
|
|
|
value: 99999699,
|
|
|
|
script_pubkey: hex_script!("76a914d0c59903c5bac2868760e90fd521a4665aa7652088ac"),
|
|
|
|
},
|
|
|
|
TxOut {
|
|
|
|
value: 100000000,
|
|
|
|
script_pubkey: hex_script!("a9143545e6e33b832c47050f24d3eeb93c9c03948bc787"),
|
|
|
|
},
|
|
|
|
],
|
2020-09-01 21:45:34 +00:00
|
|
|
},
|
2021-09-15 05:35:07 +00:00
|
|
|
version: 0,
|
|
|
|
xpub: Default::default(),
|
|
|
|
proprietary: Default::default(),
|
|
|
|
unknown: BTreeMap::new(),
|
|
|
|
|
2020-09-01 21:45:34 +00:00
|
|
|
inputs: vec![Input {
|
|
|
|
non_witness_utxo: Some(Transaction {
|
|
|
|
version: 1,
|
2022-08-16 01:24:56 +00:00
|
|
|
lock_time: absolute::PackedLockTime::ZERO,
|
2020-09-01 21:45:34 +00:00
|
|
|
input: vec![TxIn {
|
|
|
|
previous_output: OutPoint {
|
|
|
|
txid: Txid::from_hex(
|
|
|
|
"e567952fb6cc33857f392efa3a46c995a28f69cca4bb1b37e0204dab1ec7a389",
|
|
|
|
).unwrap(),
|
|
|
|
vout: 1,
|
|
|
|
},
|
|
|
|
script_sig: hex_script!("160014be18d152a9b012039daf3da7de4f53349eecb985"),
|
2022-07-16 00:49:03 +00:00
|
|
|
sequence: Sequence::MAX,
|
2021-10-05 13:07:55 +00:00
|
|
|
witness: Witness::from_vec(vec![
|
2020-09-01 21:45:34 +00:00
|
|
|
Vec::from_hex("304402202712be22e0270f394f568311dc7ca9a68970b8025fdd3b240229f07f8a5f3a240220018b38d7dcd314e734c9276bd6fb40f673325bc4baa144c800d2f2f02db2765c01").unwrap(),
|
|
|
|
Vec::from_hex("03d2e15674941bad4a996372cb87e1856d3652606d98562fe39c5e9e7e413f2105").unwrap(),
|
2021-10-05 13:07:55 +00:00
|
|
|
]),
|
2020-09-01 21:45:34 +00:00
|
|
|
},
|
|
|
|
TxIn {
|
|
|
|
previous_output: OutPoint {
|
|
|
|
txid: Txid::from_hex(
|
|
|
|
"b490486aec3ae671012dddb2bb08466bef37720a533a894814ff1da743aaf886",
|
|
|
|
).unwrap(),
|
|
|
|
vout: 1,
|
|
|
|
},
|
|
|
|
script_sig: hex_script!("160014fe3e9ef1a745e974d902c4355943abcb34bd5353"),
|
2022-07-16 00:49:03 +00:00
|
|
|
sequence: Sequence::MAX,
|
2021-10-05 13:07:55 +00:00
|
|
|
witness: Witness::from_vec(vec![
|
2020-09-01 21:45:34 +00:00
|
|
|
Vec::from_hex("3045022100d12b852d85dcd961d2f5f4ab660654df6eedcc794c0c33ce5cc309ffb5fce58d022067338a8e0e1725c197fb1a88af59f51e44e4255b20167c8684031c05d1f2592a01").unwrap(),
|
|
|
|
Vec::from_hex("0223b72beef0965d10be0778efecd61fcac6f79a4ea169393380734464f84f2ab3").unwrap(),
|
2021-10-05 13:07:55 +00:00
|
|
|
]),
|
2020-09-01 21:45:34 +00:00
|
|
|
}],
|
|
|
|
output: vec![
|
|
|
|
TxOut {
|
|
|
|
value: 200000000,
|
|
|
|
script_pubkey: hex_script!("76a91485cff1097fd9e008bb34af709c62197b38978a4888ac"),
|
|
|
|
},
|
|
|
|
TxOut {
|
|
|
|
value: 190303501938,
|
|
|
|
script_pubkey: hex_script!("a914339725ba21efd62ac753a9bcd067d6c7a6a39d0587"),
|
|
|
|
},
|
|
|
|
],
|
|
|
|
}),
|
|
|
|
..Default::default()
|
|
|
|
},],
|
|
|
|
outputs: vec![
|
|
|
|
Output {
|
|
|
|
..Default::default()
|
|
|
|
},
|
|
|
|
Output {
|
|
|
|
..Default::default()
|
|
|
|
},
|
|
|
|
],
|
|
|
|
};
|
|
|
|
unserialized.inputs[0].hash160_preimages = hash160_preimages;
|
|
|
|
unserialized.inputs[0].sha256_preimages = sha256_preimages;
|
|
|
|
|
2022-01-24 00:26:29 +00:00
|
|
|
let rtt: PartiallySignedTransaction = hex_psbt!(&serialize_hex(&unserialized)).unwrap();
|
2020-09-01 21:45:34 +00:00
|
|
|
assert_eq!(rtt, unserialized);
|
|
|
|
|
|
|
|
// Now add an ripemd160 with incorrect preimage
|
|
|
|
let mut ripemd160_preimages = BTreeMap::new();
|
|
|
|
ripemd160_preimages.insert(ripemd160::Hash::hash(&[17u8]), vec![18u8]);
|
2020-12-05 14:46:30 +00:00
|
|
|
unserialized.inputs[0].ripemd160_preimages = ripemd160_preimages;
|
2020-09-01 21:45:34 +00:00
|
|
|
|
|
|
|
// Now the roundtrip should fail as the preimage is incorrect.
|
2022-01-24 00:26:29 +00:00
|
|
|
let rtt: Result<PartiallySignedTransaction, _> = hex_psbt!(&serialize_hex(&unserialized));
|
2020-09-01 21:45:34 +00:00
|
|
|
assert!(rtt.is_err());
|
|
|
|
}
|
2021-03-13 14:19:21 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn serialize_and_deserialize_proprietary() {
|
|
|
|
let mut psbt: PartiallySignedTransaction = hex_psbt!("70736274ff0100a00200000002ab0949a08c5af7c49b8212f417e2f15ab3f5c33dcf153821a8139f877a5b7be40000000000feffffffab0949a08c5af7c49b8212f417e2f15ab3f5c33dcf153821a8139f877a5b7be40100000000feffffff02603bea0b000000001976a914768a40bbd740cbe81d988e71de2a4d5c71396b1d88ac8e240000000000001976a9146f4620b553fa095e721b9ee0efe9fa039cca459788ac000000000001076a47304402204759661797c01b036b25928948686218347d89864b719e1f7fcf57d1e511658702205309eabf56aa4d8891ffd111fdf1336f3a29da866d7f8486d75546ceedaf93190121035cdc61fc7ba971c0b501a646a2a83b102cb43881217ca682dc86e2d73fa882920001012000e1f5050000000017a9143545e6e33b832c47050f24d3eeb93c9c03948bc787010416001485d13537f2e265405a34dbafa9e3dda01fb82308000000").unwrap();
|
2021-04-12 11:47:03 +00:00
|
|
|
psbt.proprietary.insert(raw::ProprietaryKey {
|
2021-03-13 14:19:21 +00:00
|
|
|
prefix: b"test".to_vec(),
|
|
|
|
subtype: 0u8,
|
|
|
|
key: b"test".to_vec(),
|
|
|
|
}, b"test".to_vec());
|
2021-09-15 05:35:07 +00:00
|
|
|
assert!(!psbt.proprietary.is_empty());
|
2022-01-24 00:26:29 +00:00
|
|
|
let rtt: PartiallySignedTransaction = hex_psbt!(&serialize_hex(&psbt)).unwrap();
|
2021-09-15 05:35:07 +00:00
|
|
|
assert!(!rtt.proprietary.is_empty());
|
2021-03-13 14:19:21 +00:00
|
|
|
}
|
|
|
|
|
2022-02-22 13:41:20 +00:00
|
|
|
// PSBTs taken from BIP 174 test vectors.
|
|
|
|
#[test]
|
|
|
|
fn combine_psbts() {
|
2022-01-12 03:22:45 +00:00
|
|
|
let mut psbt1 = hex_psbt!(include_str!("../../../tests/data/psbt1.hex")).unwrap();
|
|
|
|
let psbt2 = hex_psbt!(include_str!("../../../tests/data/psbt2.hex")).unwrap();
|
|
|
|
let psbt_combined = hex_psbt!(include_str!("../../../tests/data/psbt2.hex")).unwrap();
|
2022-02-22 13:41:20 +00:00
|
|
|
|
|
|
|
psbt1.combine(psbt2).expect("psbt combine to succeed");
|
|
|
|
assert_eq!(psbt1, psbt_combined);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn combine_psbts_commutative() {
|
2022-01-12 03:22:45 +00:00
|
|
|
let mut psbt1 = hex_psbt!(include_str!("../../../tests/data/psbt1.hex")).unwrap();
|
|
|
|
let mut psbt2 = hex_psbt!(include_str!("../../../tests/data/psbt2.hex")).unwrap();
|
2022-02-22 13:41:20 +00:00
|
|
|
|
|
|
|
let psbt1_clone = psbt1.clone();
|
|
|
|
let psbt2_clone = psbt2.clone();
|
|
|
|
|
|
|
|
psbt1.combine(psbt2_clone).expect("psbt1 combine to succeed");
|
|
|
|
psbt2.combine(psbt1_clone).expect("psbt2 combine to succeed");
|
|
|
|
|
|
|
|
assert_eq!(psbt1, psbt2);
|
|
|
|
}
|
2022-04-19 05:46:59 +00:00
|
|
|
|
|
|
|
#[cfg(feature = "rand")]
|
|
|
|
fn gen_keys() -> (PrivateKey, PublicKey, Secp256k1<All>) {
|
|
|
|
use secp256k1::rand::thread_rng;
|
|
|
|
|
|
|
|
let secp = Secp256k1::new();
|
|
|
|
|
|
|
|
let sk = SecretKey::new(&mut thread_rng());
|
|
|
|
let priv_key = PrivateKey::new(sk, crate::Network::Regtest);
|
|
|
|
let pk = PublicKey::from_private_key(&secp, &priv_key);
|
|
|
|
|
|
|
|
(priv_key, pk, secp)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
#[cfg(feature = "rand")]
|
|
|
|
fn get_key_btree_map() {
|
|
|
|
let (priv_key, pk, secp) = gen_keys();
|
|
|
|
|
|
|
|
let mut key_map = BTreeMap::new();
|
|
|
|
key_map.insert(pk, priv_key);
|
|
|
|
|
|
|
|
let got = key_map.get_key(KeyRequest::Pubkey(pk), &secp).expect("failed to get key");
|
|
|
|
assert_eq!(got.unwrap(), priv_key)
|
|
|
|
}
|
|
|
|
|
2022-10-22 18:27:49 +00:00
|
|
|
#[test]
|
|
|
|
fn test_fee() {
|
|
|
|
let output_0_val = 99999699;
|
|
|
|
let output_1_val = 100000000;
|
|
|
|
let prev_output_val = 200000000;
|
|
|
|
|
|
|
|
let mut t = PartiallySignedTransaction {
|
|
|
|
unsigned_tx: Transaction {
|
|
|
|
version: 2,
|
|
|
|
lock_time: absolute::PackedLockTime(1257139),
|
|
|
|
input: vec![TxIn {
|
|
|
|
previous_output: OutPoint {
|
|
|
|
txid: Txid::from_hex(
|
|
|
|
"f61b1742ca13176464adb3cb66050c00787bb3a4eead37e985f2df1e37718126",
|
|
|
|
).unwrap(),
|
|
|
|
vout: 0,
|
|
|
|
},
|
|
|
|
sequence: Sequence::ENABLE_LOCKTIME_NO_RBF,
|
|
|
|
..Default::default()
|
|
|
|
}],
|
|
|
|
output: vec![
|
|
|
|
TxOut {
|
|
|
|
value: output_0_val,
|
|
|
|
..Default::default()
|
|
|
|
},
|
|
|
|
TxOut {
|
|
|
|
value: output_1_val,
|
|
|
|
..Default::default()
|
|
|
|
},
|
|
|
|
],
|
|
|
|
},
|
|
|
|
xpub: Default::default(),
|
|
|
|
version: 0,
|
|
|
|
proprietary: BTreeMap::new(),
|
|
|
|
unknown: BTreeMap::new(),
|
|
|
|
|
|
|
|
inputs: vec![Input {
|
|
|
|
non_witness_utxo: Some(Transaction {
|
|
|
|
version: 1,
|
|
|
|
lock_time: absolute::PackedLockTime::ZERO,
|
|
|
|
input: vec![TxIn {
|
|
|
|
previous_output: OutPoint {
|
|
|
|
txid: Txid::from_hex(
|
|
|
|
"e567952fb6cc33857f392efa3a46c995a28f69cca4bb1b37e0204dab1ec7a389",
|
|
|
|
).unwrap(),
|
|
|
|
vout: 1,
|
|
|
|
},
|
|
|
|
sequence: Sequence::MAX,
|
|
|
|
..Default::default()
|
|
|
|
},
|
|
|
|
TxIn {
|
|
|
|
previous_output: OutPoint {
|
|
|
|
txid: Txid::from_hex(
|
|
|
|
"b490486aec3ae671012dddb2bb08466bef37720a533a894814ff1da743aaf886",
|
|
|
|
).unwrap(),
|
|
|
|
vout: 1,
|
|
|
|
},
|
|
|
|
sequence: Sequence::MAX,
|
|
|
|
..Default::default()
|
|
|
|
}],
|
|
|
|
output: vec![
|
|
|
|
TxOut {
|
|
|
|
value: prev_output_val,
|
|
|
|
..Default::default()
|
|
|
|
},
|
|
|
|
TxOut {
|
|
|
|
value: 190303501938,
|
|
|
|
..Default::default()
|
|
|
|
},
|
|
|
|
],
|
|
|
|
}),
|
|
|
|
..Default::default()
|
|
|
|
},],
|
|
|
|
outputs: vec![
|
|
|
|
Output {
|
|
|
|
..Default::default()
|
|
|
|
},
|
|
|
|
Output {
|
|
|
|
..Default::default()
|
|
|
|
},
|
|
|
|
],
|
|
|
|
};
|
|
|
|
assert_eq!(
|
|
|
|
t.fee().expect("fee calculation"),
|
|
|
|
Amount::from_sat(prev_output_val - (output_0_val + output_1_val))
|
|
|
|
);
|
|
|
|
// no previous output
|
|
|
|
let mut t2 = t.clone();
|
|
|
|
t2.inputs[0].non_witness_utxo = None;
|
|
|
|
assert_eq!(t2.fee(), Err(Error::MissingUtxo));
|
|
|
|
// negative fee
|
|
|
|
let mut t3 = t.clone();
|
|
|
|
t3.unsigned_tx.output[0].value = prev_output_val;
|
|
|
|
assert_eq!(t3.fee(), Err(Error::NegativeFee));
|
|
|
|
// overflow
|
|
|
|
t.unsigned_tx.output[0].value = u64::max_value();
|
|
|
|
t.unsigned_tx.output[1].value = u64::max_value();
|
|
|
|
assert_eq!(t.fee(), Err(Error::FeeOverflow));
|
|
|
|
}
|
|
|
|
|
2022-04-19 05:46:59 +00:00
|
|
|
#[test]
|
|
|
|
#[cfg(feature = "rand")]
|
|
|
|
fn sign_psbt() {
|
|
|
|
use crate::WPubkeyHash;
|
|
|
|
use crate::util::bip32::{Fingerprint, DerivationPath};
|
|
|
|
|
|
|
|
let unsigned_tx = Transaction {
|
|
|
|
version: 2,
|
|
|
|
lock_time: absolute::PackedLockTime::ZERO,
|
|
|
|
input: vec![TxIn::default(), TxIn::default()],
|
|
|
|
output: vec![TxOut::default()],
|
|
|
|
};
|
|
|
|
let mut psbt = PartiallySignedTransaction::from_unsigned_tx(unsigned_tx).unwrap();
|
|
|
|
|
|
|
|
let (priv_key, pk, secp) = gen_keys();
|
|
|
|
|
|
|
|
// key_map implements `GetKey` using KeyRequest::Pubkey. A pubkey key request does not use
|
|
|
|
// keysource so we use default `KeySource` (fingreprint and derivation path) below.
|
|
|
|
let mut key_map = BTreeMap::new();
|
|
|
|
key_map.insert(pk, priv_key);
|
|
|
|
|
|
|
|
// First input we can spend. See comment above on key_map for why we use defaults here.
|
|
|
|
let txout_wpkh = TxOut{
|
|
|
|
value: 10,
|
|
|
|
script_pubkey: Script::new_v0_p2wpkh(&WPubkeyHash::hash(&pk.to_bytes())),
|
|
|
|
};
|
|
|
|
psbt.inputs[0].witness_utxo = Some(txout_wpkh);
|
|
|
|
|
|
|
|
let mut map = BTreeMap::new();
|
|
|
|
map.insert(pk.inner, (Fingerprint::default(), DerivationPath::default()));
|
|
|
|
psbt.inputs[0].bip32_derivation = map;
|
|
|
|
|
|
|
|
// Second input is unspendable by us e.g., from another wallet that supports future upgrades.
|
|
|
|
let txout_unknown_future = TxOut{
|
|
|
|
value: 10,
|
|
|
|
script_pubkey: Script::new_witness_program(crate::address::WitnessVersion::V4, &[0xaa; 34]),
|
|
|
|
};
|
|
|
|
psbt.inputs[1].witness_utxo = Some(txout_unknown_future);
|
|
|
|
|
|
|
|
let sigs = psbt.sign(&key_map, &secp).unwrap();
|
|
|
|
|
|
|
|
assert!(sigs.len() == 1);
|
|
|
|
assert!(sigs[&0] == vec![pk]);
|
|
|
|
}
|
2018-08-10 15:28:48 +00:00
|
|
|
}
|