psbt: Run the formatter
Run `cargo +nightly fmt`, no other manual changes.
This commit is contained in:
parent
ef306db5e2
commit
a52746d01c
|
@ -1,18 +1,15 @@
|
|||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
use crate::prelude::*;
|
||||
|
||||
use core::fmt;
|
||||
|
||||
use bitcoin_internals::write_err;
|
||||
|
||||
use crate::bip32::ExtendedPubKey;
|
||||
use crate::blockdata::transaction::Transaction;
|
||||
use crate::consensus::encode;
|
||||
use crate::prelude::*;
|
||||
use crate::psbt::raw;
|
||||
|
||||
use crate::hashes;
|
||||
use crate::io;
|
||||
use crate::bip32::ExtendedPubKey;
|
||||
use crate::{hashes, io};
|
||||
|
||||
/// Enum for marking psbt hash error.
|
||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
|
||||
|
@ -113,29 +110,41 @@ impl fmt::Display for Error {
|
|||
Error::InvalidMagic => f.write_str("invalid magic"),
|
||||
Error::MissingUtxo => f.write_str("UTXO information is not present in PSBT"),
|
||||
Error::InvalidSeparator => f.write_str("invalid separator"),
|
||||
Error::PsbtUtxoOutOfbounds => f.write_str("output index is out of bounds of non witness script output array"),
|
||||
Error::PsbtUtxoOutOfbounds =>
|
||||
f.write_str("output index is out of bounds of non witness script output array"),
|
||||
Error::InvalidKey(ref rkey) => write!(f, "invalid key: {}", rkey),
|
||||
Error::InvalidProprietaryKey => write!(f, "non-proprietary key type found when proprietary key was expected"),
|
||||
Error::InvalidProprietaryKey =>
|
||||
write!(f, "non-proprietary key type found when proprietary key was expected"),
|
||||
Error::DuplicateKey(ref rkey) => write!(f, "duplicate key: {}", rkey),
|
||||
Error::UnsignedTxHasScriptSigs => f.write_str("the unsigned transaction has script sigs"),
|
||||
Error::UnsignedTxHasScriptWitnesses => f.write_str("the unsigned transaction has script witnesses"),
|
||||
Error::MustHaveUnsignedTx => {
|
||||
f.write_str("partially signed transactions must have an unsigned transaction")
|
||||
}
|
||||
Error::UnsignedTxHasScriptSigs =>
|
||||
f.write_str("the unsigned transaction has script sigs"),
|
||||
Error::UnsignedTxHasScriptWitnesses =>
|
||||
f.write_str("the unsigned transaction has script witnesses"),
|
||||
Error::MustHaveUnsignedTx =>
|
||||
f.write_str("partially signed transactions must have an unsigned transaction"),
|
||||
Error::NoMorePairs => f.write_str("no more key-value pairs for this psbt map"),
|
||||
Error::UnexpectedUnsignedTx { expected: ref e, actual: ref a } => write!(f, "different unsigned transaction: expected {}, actual {}", e.txid(), a.txid()),
|
||||
Error::NonStandardSighashType(ref sht) => write!(f, "non-standard sighash type: {}", sht),
|
||||
Error::UnexpectedUnsignedTx { expected: ref e, actual: ref a } => write!(
|
||||
f,
|
||||
"different unsigned transaction: expected {}, actual {}",
|
||||
e.txid(),
|
||||
a.txid()
|
||||
),
|
||||
Error::NonStandardSighashType(ref sht) =>
|
||||
write!(f, "non-standard sighash type: {}", sht),
|
||||
Error::HashParse(ref e) => write_err!(f, "hash parse error"; e),
|
||||
Error::InvalidPreimageHashPair{ref preimage, ref hash, ref hash_type} => {
|
||||
Error::InvalidPreimageHashPair { ref preimage, ref hash, ref hash_type } => {
|
||||
// directly using debug forms of psbthash enums
|
||||
write!(f, "Preimage {:?} does not match {:?} hash {:?}", preimage, hash_type, hash )
|
||||
},
|
||||
Error::CombineInconsistentKeySources(ref s) => { write!(f, "combine conflict: {}", s) },
|
||||
write!(f, "Preimage {:?} does not match {:?} hash {:?}", preimage, hash_type, hash)
|
||||
}
|
||||
Error::CombineInconsistentKeySources(ref s) => {
|
||||
write!(f, "combine conflict: {}", s)
|
||||
}
|
||||
Error::ConsensusEncoding(ref e) => write_err!(f, "bitcoin consensus encoding error"; e),
|
||||
Error::NegativeFee => f.write_str("PSBT has a negative fee which is not allowed"),
|
||||
Error::FeeOverflow => f.write_str("integer overflow in fee calculation"),
|
||||
Error::InvalidPublicKey(ref e) => write_err!(f, "invalid public key"; e),
|
||||
Error::InvalidSecp256k1PublicKey(ref e) => write_err!(f, "invalid secp256k1 public key"; e),
|
||||
Error::InvalidSecp256k1PublicKey(ref e) =>
|
||||
write_err!(f, "invalid secp256k1 public key"; e),
|
||||
Error::InvalidXOnlyPublicKey => f.write_str("invalid xonly public key"),
|
||||
Error::InvalidEcdsaSignature(ref e) => write_err!(f, "invalid ECDSA signature"; e),
|
||||
Error::InvalidTaprootSignature(ref e) => write_err!(f, "invalid taproot signature"; e),
|
||||
|
@ -145,7 +154,8 @@ impl fmt::Display for Error {
|
|||
Error::TapTree(ref e) => write_err!(f, "taproot tree error"; e),
|
||||
Error::XPubKey(s) => write!(f, "xpub key error - {}", s),
|
||||
Error::Version(s) => write!(f, "version error {}", s),
|
||||
Error::PartialDataConsumption => f.write_str("data not consumed entirely when explicitly deserializing"),
|
||||
Error::PartialDataConsumption =>
|
||||
f.write_str("data not consumed entirely when explicitly deserializing"),
|
||||
Error::Io(ref e) => write_err!(f, "I/O error"; e),
|
||||
}
|
||||
}
|
||||
|
@ -161,7 +171,7 @@ impl std::error::Error for Error {
|
|||
HashParse(e) => Some(e),
|
||||
ConsensusEncoding(e) => Some(e),
|
||||
Io(e) => Some(e),
|
||||
| InvalidMagic
|
||||
InvalidMagic
|
||||
| MissingUtxo
|
||||
| InvalidSeparator
|
||||
| PsbtUtxoOutOfbounds
|
||||
|
@ -174,7 +184,7 @@ impl std::error::Error for Error {
|
|||
| NoMorePairs
|
||||
| UnexpectedUnsignedTx { .. }
|
||||
| NonStandardSighashType(_)
|
||||
| InvalidPreimageHashPair{ .. }
|
||||
| InvalidPreimageHashPair { .. }
|
||||
| CombineInconsistentKeySources(_)
|
||||
| NegativeFee
|
||||
| FeeOverflow
|
||||
|
@ -189,26 +199,20 @@ impl std::error::Error for Error {
|
|||
| TapTree(_)
|
||||
| XPubKey(_)
|
||||
| Version(_)
|
||||
| PartialDataConsumption=> None,
|
||||
| PartialDataConsumption => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl From<hashes::Error> for Error {
|
||||
fn from(e: hashes::Error) -> Error {
|
||||
Error::HashParse(e)
|
||||
}
|
||||
fn from(e: hashes::Error) -> Error { Error::HashParse(e) }
|
||||
}
|
||||
|
||||
impl From<encode::Error> for Error {
|
||||
fn from(e: encode::Error) -> Self {
|
||||
Error::ConsensusEncoding(e)
|
||||
}
|
||||
fn from(e: encode::Error) -> Self { Error::ConsensusEncoding(e) }
|
||||
}
|
||||
|
||||
impl From<io::Error> for Error {
|
||||
fn from(e: io::Error) -> Self {
|
||||
Error::Io(e)
|
||||
}
|
||||
fn from(e: io::Error) -> Self { Error::Io(e) }
|
||||
}
|
||||
|
|
|
@ -2,7 +2,11 @@
|
|||
|
||||
#[allow(unused_macros)]
|
||||
macro_rules! hex_psbt {
|
||||
($s:expr) => { <$crate::psbt::PartiallySignedTransaction>::deserialize(&<$crate::prelude::Vec<u8> as $crate::hashes::hex::FromHex>::from_hex($s).unwrap()) };
|
||||
($s:expr) => {
|
||||
<$crate::psbt::PartiallySignedTransaction>::deserialize(
|
||||
&<$crate::prelude::Vec<u8> as $crate::hashes::hex::FromHex>::from_hex($s).unwrap(),
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! combine {
|
||||
|
@ -33,9 +37,7 @@ macro_rules! impl_psbt_deserialize {
|
|||
macro_rules! impl_psbt_serialize {
|
||||
($thing:ty) => {
|
||||
impl $crate::psbt::serialize::Serialize for $thing {
|
||||
fn serialize(&self) -> $crate::prelude::Vec<u8> {
|
||||
$crate::consensus::serialize(self)
|
||||
}
|
||||
fn serialize(&self) -> $crate::prelude::Vec<u8> { $crate::consensus::serialize(self) }
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -43,9 +45,7 @@ macro_rules! impl_psbt_serialize {
|
|||
macro_rules! impl_psbtmap_serialize {
|
||||
($thing:ty) => {
|
||||
impl $crate::psbt::serialize::Serialize for $thing {
|
||||
fn serialize(&self) -> Vec<u8> {
|
||||
self.serialize_map()
|
||||
}
|
||||
fn serialize(&self) -> Vec<u8> { self.serialize_map() }
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -157,9 +157,7 @@ macro_rules! impl_psbt_hash_deserialize {
|
|||
($hash_type:ty) => {
|
||||
impl $crate::psbt::serialize::Deserialize for $hash_type {
|
||||
fn deserialize(bytes: &[u8]) -> Result<Self, $crate::psbt::Error> {
|
||||
<$hash_type>::from_slice(&bytes[..]).map_err(|e| {
|
||||
$crate::psbt::Error::from(e)
|
||||
})
|
||||
<$hash_type>::from_slice(&bytes[..]).map_err(|e| $crate::psbt::Error::from(e))
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -168,9 +166,7 @@ macro_rules! impl_psbt_hash_deserialize {
|
|||
macro_rules! impl_psbt_hash_serialize {
|
||||
($hash_type:ty) => {
|
||||
impl $crate::psbt::serialize::Serialize for $hash_type {
|
||||
fn serialize(&self) -> $crate::prelude::Vec<u8> {
|
||||
self.as_byte_array().to_vec()
|
||||
}
|
||||
fn serialize(&self) -> $crate::prelude::Vec<u8> { self.as_byte_array().to_vec() }
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -2,16 +2,14 @@
|
|||
|
||||
use core::convert::TryFrom;
|
||||
|
||||
use crate::prelude::*;
|
||||
|
||||
use crate::io::{self, Cursor, Read};
|
||||
|
||||
use crate::bip32::{ChildNumber, DerivationPath, ExtendedPubKey, Fingerprint};
|
||||
use crate::blockdata::transaction::Transaction;
|
||||
use crate::consensus::encode::MAX_VEC_SIZE;
|
||||
use crate::consensus::{encode, Decodable};
|
||||
use crate::io::{self, Cursor, Read};
|
||||
use crate::prelude::*;
|
||||
use crate::psbt::map::Map;
|
||||
use crate::psbt::{raw, Error, PartiallySignedTransaction};
|
||||
use crate::bip32::{ExtendedPubKey, Fingerprint, DerivationPath, ChildNumber};
|
||||
|
||||
/// Type: Unsigned Transaction PSBT_GLOBAL_UNSIGNED_TX = 0x00
|
||||
const PSBT_GLOBAL_UNSIGNED_TX: u8 = 0x00;
|
||||
|
@ -27,10 +25,7 @@ impl Map for PartiallySignedTransaction {
|
|||
let mut rv: Vec<raw::Pair> = Default::default();
|
||||
|
||||
rv.push(raw::Pair {
|
||||
key: raw::Key {
|
||||
type_value: PSBT_GLOBAL_UNSIGNED_TX,
|
||||
key: vec![],
|
||||
},
|
||||
key: raw::Key { type_value: PSBT_GLOBAL_UNSIGNED_TX, key: vec![] },
|
||||
value: {
|
||||
// Manually serialized to ensure 0-input txs are serialized
|
||||
// without witnesses.
|
||||
|
@ -45,42 +40,30 @@ impl Map for PartiallySignedTransaction {
|
|||
|
||||
for (xpub, (fingerprint, derivation)) in &self.xpub {
|
||||
rv.push(raw::Pair {
|
||||
key: raw::Key {
|
||||
type_value: PSBT_GLOBAL_XPUB,
|
||||
key: xpub.encode().to_vec(),
|
||||
},
|
||||
key: raw::Key { type_value: PSBT_GLOBAL_XPUB, key: xpub.encode().to_vec() },
|
||||
value: {
|
||||
let mut ret = Vec::with_capacity(4 + derivation.len() * 4);
|
||||
ret.extend(fingerprint.as_bytes());
|
||||
derivation.into_iter().for_each(|n| ret.extend(&u32::from(*n).to_le_bytes()));
|
||||
ret
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
// Serializing version only for non-default value; otherwise test vectors fail
|
||||
if self.version > 0 {
|
||||
rv.push(raw::Pair {
|
||||
key: raw::Key {
|
||||
type_value: PSBT_GLOBAL_VERSION,
|
||||
key: vec![],
|
||||
},
|
||||
value: self.version.to_le_bytes().to_vec()
|
||||
key: raw::Key { type_value: PSBT_GLOBAL_VERSION, key: vec![] },
|
||||
value: self.version.to_le_bytes().to_vec(),
|
||||
});
|
||||
}
|
||||
|
||||
for (key, value) in self.proprietary.iter() {
|
||||
rv.push(raw::Pair {
|
||||
key: key.to_key(),
|
||||
value: value.clone(),
|
||||
});
|
||||
rv.push(raw::Pair { key: key.to_key(), value: value.clone() });
|
||||
}
|
||||
|
||||
for (key, value) in self.unknown.iter() {
|
||||
rv.push(raw::Pair {
|
||||
key: key.clone(),
|
||||
value: value.clone(),
|
||||
});
|
||||
rv.push(raw::Pair { key: key.clone(), value: value.clone() });
|
||||
}
|
||||
|
||||
rv
|
||||
|
@ -93,7 +76,8 @@ impl PartiallySignedTransaction {
|
|||
let mut tx: Option<Transaction> = None;
|
||||
let mut version: Option<u32> = None;
|
||||
let mut unknowns: BTreeMap<raw::Key, Vec<u8>> = Default::default();
|
||||
let mut xpub_map: BTreeMap<ExtendedPubKey, (Fingerprint, DerivationPath)> = Default::default();
|
||||
let mut xpub_map: BTreeMap<ExtendedPubKey, (Fingerprint, DerivationPath)> =
|
||||
Default::default();
|
||||
let mut proprietary: BTreeMap<raw::ProprietaryKey, Vec<u8>> = Default::default();
|
||||
|
||||
loop {
|
||||
|
@ -119,13 +103,13 @@ impl PartiallySignedTransaction {
|
|||
});
|
||||
|
||||
if decoder.position() != vlen as u64 {
|
||||
return Err(Error::PartialDataConsumption)
|
||||
return Err(Error::PartialDataConsumption);
|
||||
}
|
||||
} else {
|
||||
return Err(Error::DuplicateKey(pair.key))
|
||||
return Err(Error::DuplicateKey(pair.key));
|
||||
}
|
||||
} else {
|
||||
return Err(Error::InvalidKey(pair.key))
|
||||
return Err(Error::InvalidKey(pair.key));
|
||||
}
|
||||
}
|
||||
PSBT_GLOBAL_XPUB => {
|
||||
|
@ -136,24 +120,33 @@ impl PartiallySignedTransaction {
|
|||
))?;
|
||||
|
||||
if pair.value.is_empty() || pair.value.len() % 4 != 0 {
|
||||
return Err(Error::XPubKey("Incorrect length of global xpub derivation data"))
|
||||
return Err(Error::XPubKey(
|
||||
"Incorrect length of global xpub derivation data",
|
||||
));
|
||||
}
|
||||
|
||||
let child_count = pair.value.len() / 4 - 1;
|
||||
let mut decoder = Cursor::new(pair.value);
|
||||
let mut fingerprint = [0u8; 4];
|
||||
decoder.read_exact(&mut fingerprint[..]).map_err(|_| Error::XPubKey("Can't read global xpub fingerprint"))?;
|
||||
decoder.read_exact(&mut fingerprint[..]).map_err(|_| {
|
||||
Error::XPubKey("Can't read global xpub fingerprint")
|
||||
})?;
|
||||
let mut path = Vec::<ChildNumber>::with_capacity(child_count);
|
||||
while let Ok(index) = u32::consensus_decode(&mut decoder) {
|
||||
path.push(ChildNumber::from(index))
|
||||
}
|
||||
let derivation = DerivationPath::from(path);
|
||||
// Keys, according to BIP-174, must be unique
|
||||
if xpub_map.insert(xpub, (Fingerprint::from(fingerprint), derivation)).is_some() {
|
||||
return Err(Error::XPubKey("Repeated global xpub key"))
|
||||
if xpub_map
|
||||
.insert(xpub, (Fingerprint::from(fingerprint), derivation))
|
||||
.is_some()
|
||||
{
|
||||
return Err(Error::XPubKey("Repeated global xpub key"));
|
||||
}
|
||||
} else {
|
||||
return Err(Error::XPubKey("Xpub global key must contain serialized Xpub data"))
|
||||
return Err(Error::XPubKey(
|
||||
"Xpub global key must contain serialized Xpub data",
|
||||
));
|
||||
}
|
||||
}
|
||||
PSBT_GLOBAL_VERSION => {
|
||||
|
@ -164,33 +157,41 @@ impl PartiallySignedTransaction {
|
|||
let vlen: usize = pair.value.len();
|
||||
let mut decoder = Cursor::new(pair.value);
|
||||
if vlen != 4 {
|
||||
return Err(Error::Version("invalid global version value length (must be 4 bytes)"))
|
||||
return Err(Error::Version(
|
||||
"invalid global version value length (must be 4 bytes)",
|
||||
));
|
||||
}
|
||||
version = Some(Decodable::consensus_decode(&mut decoder)?);
|
||||
// We only understand version 0 PSBTs. According to BIP-174 we
|
||||
// should throw an error if we see anything other than version 0.
|
||||
if version != Some(0) {
|
||||
return Err(Error::Version("PSBT versions greater than 0 are not supported"))
|
||||
return Err(Error::Version(
|
||||
"PSBT versions greater than 0 are not supported",
|
||||
));
|
||||
}
|
||||
} else {
|
||||
return Err(Error::DuplicateKey(pair.key))
|
||||
return Err(Error::DuplicateKey(pair.key));
|
||||
}
|
||||
} else {
|
||||
return Err(Error::InvalidKey(pair.key))
|
||||
return Err(Error::InvalidKey(pair.key));
|
||||
}
|
||||
}
|
||||
PSBT_GLOBAL_PROPRIETARY => match proprietary.entry(raw::ProprietaryKey::try_from(pair.key.clone())?) {
|
||||
PSBT_GLOBAL_PROPRIETARY => match proprietary
|
||||
.entry(raw::ProprietaryKey::try_from(pair.key.clone())?)
|
||||
{
|
||||
btree_map::Entry::Vacant(empty_key) => {
|
||||
empty_key.insert(pair.value);
|
||||
},
|
||||
btree_map::Entry::Occupied(_) => return Err(Error::DuplicateKey(pair.key)),
|
||||
}
|
||||
}
|
||||
btree_map::Entry::Occupied(_) =>
|
||||
return Err(Error::DuplicateKey(pair.key)),
|
||||
},
|
||||
_ => match unknowns.entry(pair.key) {
|
||||
btree_map::Entry::Vacant(empty_key) => {
|
||||
empty_key.insert(pair.value);
|
||||
},
|
||||
btree_map::Entry::Occupied(k) => return Err(Error::DuplicateKey(k.key().clone())),
|
||||
}
|
||||
}
|
||||
btree_map::Entry::Occupied(k) =>
|
||||
return Err(Error::DuplicateKey(k.key().clone())),
|
||||
},
|
||||
}
|
||||
}
|
||||
Err(crate::psbt::Error::NoMorePairs) => break,
|
||||
|
@ -206,7 +207,7 @@ impl PartiallySignedTransaction {
|
|||
proprietary,
|
||||
unknown: unknowns,
|
||||
inputs: vec![],
|
||||
outputs: vec![]
|
||||
outputs: vec![],
|
||||
})
|
||||
} else {
|
||||
Err(Error::MustHaveUnsignedTx)
|
||||
|
|
|
@ -1,24 +1,25 @@
|
|||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
use crate::prelude::*;
|
||||
|
||||
use core::convert::TryFrom;
|
||||
use core::fmt;
|
||||
use core::str::FromStr;
|
||||
use core::convert::TryFrom;
|
||||
|
||||
use secp256k1::XOnlyPublicKey;
|
||||
|
||||
use crate::blockdata::script::ScriptBuf;
|
||||
use crate::blockdata::witness::Witness;
|
||||
use crate::blockdata::transaction::{Transaction, TxOut};
|
||||
use crate::crypto::{ecdsa, taproot};
|
||||
use crate::crypto::key::PublicKey;
|
||||
use crate::hashes::{self, hash160, ripemd160, sha256, sha256d};
|
||||
use crate::bip32::KeySource;
|
||||
use crate::blockdata::script::ScriptBuf;
|
||||
use crate::blockdata::transaction::{Transaction, TxOut};
|
||||
use crate::blockdata::witness::Witness;
|
||||
use crate::crypto::key::PublicKey;
|
||||
use crate::crypto::{ecdsa, taproot};
|
||||
use crate::hashes::{self, hash160, ripemd160, sha256, sha256d};
|
||||
use crate::prelude::*;
|
||||
use crate::psbt::map::Map;
|
||||
use crate::psbt::serialize::Deserialize;
|
||||
use crate::psbt::{self, error, raw, Error};
|
||||
use crate::sighash::{self, NonStandardSighashType, SighashTypeParseError, EcdsaSighashType, TapSighashType};
|
||||
use crate::sighash::{
|
||||
self, EcdsaSighashType, NonStandardSighashType, SighashTypeParseError, TapSighashType,
|
||||
};
|
||||
use crate::taproot::{ControlBlock, LeafVersion, TapLeafHash, TapNodeHash};
|
||||
|
||||
/// Type: Non-Witness UTXO PSBT_IN_NON_WITNESS_UTXO = 0x00
|
||||
|
@ -132,7 +133,6 @@ pub struct Input {
|
|||
pub unknown: BTreeMap<raw::Key, Vec<u8>>,
|
||||
}
|
||||
|
||||
|
||||
/// A Signature hash type for the corresponding input. As of taproot upgrade, the signature hash
|
||||
/// type can be either [`EcdsaSighashType`] or [`TapSighashType`] but it is not possible to know
|
||||
/// directly which signature hash type the user is dealing with. Therefore, the user is responsible
|
||||
|
@ -141,7 +141,7 @@ pub struct Input {
|
|||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
#[cfg_attr(feature = "serde", serde(crate = "actual_serde"))]
|
||||
pub struct PsbtSighashType {
|
||||
pub (in crate::psbt) inner: u32,
|
||||
pub(in crate::psbt) inner: u32,
|
||||
}
|
||||
|
||||
impl fmt::Display for PsbtSighashType {
|
||||
|
@ -172,7 +172,7 @@ impl FromStr for PsbtSighashType {
|
|||
return Ok(PsbtSighashType { inner });
|
||||
}
|
||||
|
||||
Err(SighashTypeParseError{ unrecognized: s.to_owned() })
|
||||
Err(SighashTypeParseError { unrecognized: s.to_owned() })
|
||||
}
|
||||
}
|
||||
impl From<EcdsaSighashType> for PsbtSighashType {
|
||||
|
@ -208,17 +208,12 @@ impl PsbtSighashType {
|
|||
///
|
||||
/// Allows construction of a non-standard or non-valid sighash flag
|
||||
/// ([`EcdsaSighashType`], [`TapSighashType`] respectively).
|
||||
pub fn from_u32(n: u32) -> PsbtSighashType {
|
||||
PsbtSighashType { inner: n }
|
||||
}
|
||||
|
||||
pub fn from_u32(n: u32) -> PsbtSighashType { PsbtSighashType { inner: n } }
|
||||
|
||||
/// Converts [`PsbtSighashType`] to a raw `u32` sighash flag.
|
||||
///
|
||||
/// No guarantees are made as to the standardness or validity of the returned value.
|
||||
pub fn to_u32(self) -> u32 {
|
||||
self.inner
|
||||
}
|
||||
pub fn to_u32(self) -> u32 { self.inner }
|
||||
}
|
||||
|
||||
impl Input {
|
||||
|
@ -247,10 +242,7 @@ impl Input {
|
|||
}
|
||||
|
||||
pub(super) fn insert_pair(&mut self, pair: raw::Pair) -> Result<(), Error> {
|
||||
let raw::Pair {
|
||||
key: raw_key,
|
||||
value: raw_value,
|
||||
} = pair;
|
||||
let raw::Pair { key: raw_key, value: raw_value } = pair;
|
||||
|
||||
match raw_key.type_value {
|
||||
PSBT_IN_NON_WITNESS_UTXO => {
|
||||
|
@ -299,16 +291,36 @@ impl Input {
|
|||
}
|
||||
}
|
||||
PSBT_IN_RIPEMD160 => {
|
||||
psbt_insert_hash_pair(&mut self.ripemd160_preimages, raw_key, raw_value, error::PsbtHash::Ripemd)?;
|
||||
psbt_insert_hash_pair(
|
||||
&mut self.ripemd160_preimages,
|
||||
raw_key,
|
||||
raw_value,
|
||||
error::PsbtHash::Ripemd,
|
||||
)?;
|
||||
}
|
||||
PSBT_IN_SHA256 => {
|
||||
psbt_insert_hash_pair(&mut self.sha256_preimages, raw_key, raw_value, error::PsbtHash::Sha256)?;
|
||||
psbt_insert_hash_pair(
|
||||
&mut self.sha256_preimages,
|
||||
raw_key,
|
||||
raw_value,
|
||||
error::PsbtHash::Sha256,
|
||||
)?;
|
||||
}
|
||||
PSBT_IN_HASH160 => {
|
||||
psbt_insert_hash_pair(&mut self.hash160_preimages, raw_key, raw_value, error::PsbtHash::Hash160)?;
|
||||
psbt_insert_hash_pair(
|
||||
&mut self.hash160_preimages,
|
||||
raw_key,
|
||||
raw_value,
|
||||
error::PsbtHash::Hash160,
|
||||
)?;
|
||||
}
|
||||
PSBT_IN_HASH256 => {
|
||||
psbt_insert_hash_pair(&mut self.hash256_preimages, raw_key, raw_value, error::PsbtHash::Hash256)?;
|
||||
psbt_insert_hash_pair(
|
||||
&mut self.hash256_preimages,
|
||||
raw_key,
|
||||
raw_value,
|
||||
error::PsbtHash::Hash256,
|
||||
)?;
|
||||
}
|
||||
PSBT_IN_TAP_KEY_SIG => {
|
||||
impl_psbt_insert_pair! {
|
||||
|
@ -345,7 +357,7 @@ impl Input {
|
|||
match self.proprietary.entry(key) {
|
||||
btree_map::Entry::Vacant(empty_key) => {
|
||||
empty_key.insert(raw_value);
|
||||
},
|
||||
}
|
||||
btree_map::Entry::Occupied(_) => return Err(Error::DuplicateKey(raw_key)),
|
||||
}
|
||||
}
|
||||
|
@ -471,17 +483,11 @@ impl Map for Input {
|
|||
rv.push(self.tap_merkle_root, PSBT_IN_TAP_MERKLE_ROOT)
|
||||
}
|
||||
for (key, value) in self.proprietary.iter() {
|
||||
rv.push(raw::Pair {
|
||||
key: key.to_key(),
|
||||
value: value.clone(),
|
||||
});
|
||||
rv.push(raw::Pair { key: key.to_key(), value: value.clone() });
|
||||
}
|
||||
|
||||
for (key, value) in self.unknown.iter() {
|
||||
rv.push(raw::Pair {
|
||||
key: key.clone(),
|
||||
value: value.clone(),
|
||||
});
|
||||
rv.push(raw::Pair { key: key.clone(), value: value.clone() });
|
||||
}
|
||||
|
||||
rv
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
use crate::prelude::*;
|
||||
|
||||
use crate::psbt::raw;
|
||||
|
||||
mod global;
|
||||
|
@ -10,7 +9,6 @@ mod output;
|
|||
|
||||
pub use self::input::{Input, PsbtSighashType};
|
||||
pub use self::output::Output;
|
||||
|
||||
use super::serialize::Serialize;
|
||||
|
||||
/// A trait that describes a PSBT key-value map.
|
||||
|
|
|
@ -1,18 +1,16 @@
|
|||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
use crate::prelude::*;
|
||||
use core;
|
||||
use core::convert::TryFrom;
|
||||
|
||||
use crate::blockdata::script::ScriptBuf;
|
||||
use secp256k1::XOnlyPublicKey;
|
||||
use crate::bip32::KeySource;
|
||||
use secp256k1;
|
||||
use crate::psbt::map::Map;
|
||||
use crate::psbt::raw;
|
||||
use crate::psbt::Error;
|
||||
use {core, secp256k1};
|
||||
|
||||
use crate::taproot::{TapTree, TapLeafHash};
|
||||
use crate::bip32::KeySource;
|
||||
use crate::blockdata::script::ScriptBuf;
|
||||
use crate::prelude::*;
|
||||
use crate::psbt::map::Map;
|
||||
use crate::psbt::{raw, Error};
|
||||
use crate::taproot::{TapLeafHash, TapTree};
|
||||
|
||||
/// Type: Redeem ScriptBuf PSBT_OUT_REDEEM_SCRIPT = 0x00
|
||||
const PSBT_OUT_REDEEM_SCRIPT: u8 = 0x00;
|
||||
|
@ -51,25 +49,16 @@ pub struct Output {
|
|||
#[cfg_attr(feature = "serde", serde(with = "crate::serde_utils::btreemap_as_seq"))]
|
||||
pub tap_key_origins: BTreeMap<XOnlyPublicKey, (Vec<TapLeafHash>, KeySource)>,
|
||||
/// Proprietary key-value pairs for this output.
|
||||
#[cfg_attr(
|
||||
feature = "serde",
|
||||
serde(with = "crate::serde_utils::btreemap_as_seq_byte_values")
|
||||
)]
|
||||
#[cfg_attr(feature = "serde", serde(with = "crate::serde_utils::btreemap_as_seq_byte_values"))]
|
||||
pub proprietary: BTreeMap<raw::ProprietaryKey, Vec<u8>>,
|
||||
/// Unknown key-value pairs for this output.
|
||||
#[cfg_attr(
|
||||
feature = "serde",
|
||||
serde(with = "crate::serde_utils::btreemap_as_seq_byte_values")
|
||||
)]
|
||||
#[cfg_attr(feature = "serde", serde(with = "crate::serde_utils::btreemap_as_seq_byte_values"))]
|
||||
pub unknown: BTreeMap<raw::Key, Vec<u8>>,
|
||||
}
|
||||
|
||||
impl Output {
|
||||
pub(super) fn insert_pair(&mut self, pair: raw::Pair) -> Result<(), Error> {
|
||||
let raw::Pair {
|
||||
key: raw_key,
|
||||
value: raw_value,
|
||||
} = pair;
|
||||
let raw::Pair { key: raw_key, value: raw_value } = pair;
|
||||
|
||||
match raw_key.type_value {
|
||||
PSBT_OUT_REDEEM_SCRIPT => {
|
||||
|
@ -92,7 +81,7 @@ impl Output {
|
|||
match self.proprietary.entry(key) {
|
||||
btree_map::Entry::Vacant(empty_key) => {
|
||||
empty_key.insert(raw_value);
|
||||
},
|
||||
}
|
||||
btree_map::Entry::Occupied(_) => return Err(Error::DuplicateKey(raw_key)),
|
||||
}
|
||||
}
|
||||
|
@ -116,7 +105,7 @@ impl Output {
|
|||
empty_key.insert(raw_value);
|
||||
}
|
||||
btree_map::Entry::Occupied(k) => return Err(Error::DuplicateKey(k.key().clone())),
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
@ -165,17 +154,11 @@ impl Map for Output {
|
|||
}
|
||||
|
||||
for (key, value) in self.proprietary.iter() {
|
||||
rv.push(raw::Pair {
|
||||
key: key.to_key(),
|
||||
value: value.clone(),
|
||||
});
|
||||
rv.push(raw::Pair { key: key.to_key(), value: value.clone() });
|
||||
}
|
||||
|
||||
for (key, value) in self.unknown.iter() {
|
||||
rv.push(raw::Pair {
|
||||
key: key.clone(),
|
||||
value: value.clone(),
|
||||
});
|
||||
rv.push(raw::Pair { key: key.clone(), value: value.clone() });
|
||||
}
|
||||
|
||||
rv
|
||||
|
|
|
@ -7,24 +7,22 @@
|
|||
//! except we define PSBTs containing non-standard sighash types as invalid.
|
||||
//!
|
||||
|
||||
use core::{cmp, fmt};
|
||||
#[cfg(feature = "std")]
|
||||
use std::collections::{HashMap, HashSet};
|
||||
|
||||
use core::{fmt, cmp};
|
||||
|
||||
use secp256k1::{Message, Secp256k1, Signing};
|
||||
use bitcoin_internals::write_err;
|
||||
use secp256k1::{Message, Secp256k1, Signing};
|
||||
|
||||
use crate::{prelude::*, Amount};
|
||||
|
||||
use crate::bip32::{self, ExtendedPrivKey, ExtendedPubKey, KeySource};
|
||||
use crate::blockdata::script::ScriptBuf;
|
||||
use crate::blockdata::transaction::{Transaction, TxOut};
|
||||
use crate::bip32::{self, ExtendedPrivKey, ExtendedPubKey, KeySource};
|
||||
use crate::crypto::ecdsa;
|
||||
use crate::crypto::key::{PublicKey, PrivateKey};
|
||||
use crate::sighash::{self, EcdsaSighashType, SighashCache};
|
||||
|
||||
use crate::crypto::key::{PrivateKey, PublicKey};
|
||||
use crate::prelude::*;
|
||||
pub use crate::sighash::Prevouts;
|
||||
use crate::sighash::{self, EcdsaSighashType, SighashCache};
|
||||
use crate::Amount;
|
||||
|
||||
#[macro_use]
|
||||
mod macros;
|
||||
|
@ -86,7 +84,7 @@ impl PartiallySignedTransaction {
|
|||
(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),
|
||||
}
|
||||
})
|
||||
|
@ -161,7 +159,7 @@ impl PartiallySignedTransaction {
|
|||
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
|
||||
|
@ -174,15 +172,17 @@ impl PartiallySignedTransaction {
|
|||
|
||||
let (fingerprint2, derivation2) = entry.get().clone();
|
||||
|
||||
if (derivation1 == derivation2 && fingerprint1 == fingerprint2) ||
|
||||
(derivation1.len() < derivation2.len() && derivation1[..] == derivation2[derivation2.len() - derivation1.len()..])
|
||||
if (derivation1 == derivation2 && fingerprint1 == fingerprint2)
|
||||
|| (derivation1.len() < derivation2.len()
|
||||
&& derivation1[..]
|
||||
== derivation2[derivation2.len() - derivation1.len()..])
|
||||
{
|
||||
continue
|
||||
}
|
||||
else if derivation2[..] == derivation1[derivation1.len() - derivation2.len()..]
|
||||
continue;
|
||||
} else if derivation2[..]
|
||||
== derivation1[derivation1.len() - derivation2.len()..]
|
||||
{
|
||||
entry.insert((fingerprint1, derivation1));
|
||||
continue
|
||||
continue;
|
||||
}
|
||||
return Err(Error::CombineInconsistentKeySources(Box::new(xpub)));
|
||||
}
|
||||
|
@ -237,8 +237,12 @@ impl PartiallySignedTransaction {
|
|||
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); },
|
||||
Ok(v) => {
|
||||
used.insert(i, v);
|
||||
}
|
||||
Err(e) => {
|
||||
errors.insert(i, e);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -272,7 +276,7 @@ impl PartiallySignedTransaction {
|
|||
|
||||
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.
|
||||
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) {
|
||||
|
@ -289,10 +293,8 @@ impl PartiallySignedTransaction {
|
|||
Ok((msg, sighash_ty)) => (msg, sighash_ty),
|
||||
};
|
||||
|
||||
let sig = ecdsa::Signature {
|
||||
sig: secp.sign_ecdsa(&msg, &sk.inner),
|
||||
hash_ty: sighash_ty,
|
||||
};
|
||||
let sig =
|
||||
ecdsa::Signature { sig: secp.sign_ecdsa(&msg, &sk.inner), hash_ty: sighash_ty };
|
||||
|
||||
let pk = sk.public_key(secp);
|
||||
|
||||
|
@ -323,35 +325,42 @@ impl PartiallySignedTransaction {
|
|||
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 hash_ty = input.ecdsa_hash_ty().map_err(|_| SignError::InvalidSighashType)?; // Only support standard sighash types.
|
||||
|
||||
match self.output_type(input_index)? {
|
||||
Bare => {
|
||||
let sighash = cache.legacy_signature_hash(input_index, spk, hash_ty.to_u32())?;
|
||||
Ok((Message::from(sighash), hash_ty))
|
||||
},
|
||||
}
|
||||
Sh => {
|
||||
let script_code = input.redeem_script.as_ref().ok_or(SignError::MissingRedeemScript)?;
|
||||
let sighash = cache.legacy_signature_hash(input_index, script_code, hash_ty.to_u32())?;
|
||||
let script_code =
|
||||
input.redeem_script.as_ref().ok_or(SignError::MissingRedeemScript)?;
|
||||
let sighash =
|
||||
cache.legacy_signature_hash(input_index, script_code, hash_ty.to_u32())?;
|
||||
Ok((Message::from(sighash), hash_ty))
|
||||
},
|
||||
}
|
||||
Wpkh => {
|
||||
let script_code = ScriptBuf::p2wpkh_script_code(spk).ok_or(SignError::NotWpkh)?;
|
||||
let sighash = cache.segwit_signature_hash(input_index, &script_code, utxo.value, hash_ty)?;
|
||||
let sighash =
|
||||
cache.segwit_signature_hash(input_index, &script_code, utxo.value, hash_ty)?;
|
||||
Ok((Message::from(sighash), hash_ty))
|
||||
},
|
||||
}
|
||||
ShWpkh => {
|
||||
let script_code = ScriptBuf::p2wpkh_script_code(input.redeem_script.as_ref().expect("checked above"))
|
||||
.ok_or(SignError::NotWpkh)?;
|
||||
let sighash = cache.segwit_signature_hash(input_index, &script_code, utxo.value, hash_ty)?;
|
||||
let script_code = ScriptBuf::p2wpkh_script_code(
|
||||
input.redeem_script.as_ref().expect("checked above"),
|
||||
)
|
||||
.ok_or(SignError::NotWpkh)?;
|
||||
let sighash =
|
||||
cache.segwit_signature_hash(input_index, &script_code, utxo.value, hash_ty)?;
|
||||
Ok((Message::from(sighash), hash_ty))
|
||||
},
|
||||
}
|
||||
Wsh | ShWsh => {
|
||||
let script_code = input.witness_script.as_ref().ok_or(SignError::MissingWitnessScript)?;
|
||||
let sighash = cache.segwit_signature_hash(input_index, script_code, utxo.value, hash_ty)?;
|
||||
let script_code =
|
||||
input.witness_script.as_ref().ok_or(SignError::MissingWitnessScript)?;
|
||||
let sighash =
|
||||
cache.segwit_signature_hash(input_index, script_code, utxo.value, hash_ty)?;
|
||||
Ok((Message::from(sighash), hash_ty))
|
||||
},
|
||||
}
|
||||
Tr => {
|
||||
// This PSBT signing API is WIP, taproot to come shortly.
|
||||
Err(SignError::Unsupported)
|
||||
|
@ -368,7 +377,7 @@ impl PartiallySignedTransaction {
|
|||
let vout = self.unsigned_tx.input[input_index].previous_output.vout;
|
||||
&non_witness_utxo.output[vout as usize]
|
||||
} else {
|
||||
return Err(SignError::MissingSpendUtxo);
|
||||
return Err(SignError::MissingSpendUtxo);
|
||||
};
|
||||
Ok(utxo)
|
||||
}
|
||||
|
@ -481,13 +490,21 @@ pub trait GetKey {
|
|||
/// - `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>;
|
||||
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> {
|
||||
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)) => {
|
||||
|
@ -577,7 +594,8 @@ impl fmt::Display for 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"),
|
||||
NotSupported =>
|
||||
f.write_str("the GetKey operation is not supported for this key request"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -596,9 +614,7 @@ impl std::error::Error for GetKeyError {
|
|||
}
|
||||
|
||||
impl From<bip32::Error> for GetKeyError {
|
||||
fn from(e: bip32::Error) -> Self {
|
||||
GetKeyError::Bip32(e)
|
||||
}
|
||||
fn from(e: bip32::Error) -> Self { GetKeyError::Bip32(e) }
|
||||
}
|
||||
|
||||
/// The various output types supported by the Bitcoin network.
|
||||
|
@ -676,7 +692,7 @@ pub enum SignError {
|
|||
/// Attempt to sign an input with the wrong signing algorithm.
|
||||
WrongSigningAlgorithm,
|
||||
/// Signing request currently unsupported.
|
||||
Unsupported
|
||||
Unsupported,
|
||||
}
|
||||
|
||||
impl fmt::Display for SignError {
|
||||
|
@ -698,7 +714,8 @@ impl fmt::Display for SignError {
|
|||
SighashComputation(e) => write!(f, "sighash: {}", 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"),
|
||||
WrongSigningAlgorithm =>
|
||||
write!(f, "attempt to sign an input with the wrong signing algorithm"),
|
||||
Unsupported => write!(f, "signing request currently unsupported"),
|
||||
}
|
||||
}
|
||||
|
@ -712,37 +729,37 @@ impl std::error::Error for SignError {
|
|||
|
||||
match *self {
|
||||
IndexOutOfBounds(_, _)
|
||||
| InvalidSighashType
|
||||
| MissingInputUtxo
|
||||
| MissingRedeemScript
|
||||
| MissingSpendUtxo
|
||||
| MissingWitnessScript
|
||||
| MismatchedAlgoKey
|
||||
| NotEcdsa
|
||||
| NotWpkh
|
||||
| UnknownOutputType
|
||||
| KeyNotFound
|
||||
| WrongSigningAlgorithm
|
||||
| Unsupported => None,
|
||||
| InvalidSighashType
|
||||
| MissingInputUtxo
|
||||
| MissingRedeemScript
|
||||
| MissingSpendUtxo
|
||||
| MissingWitnessScript
|
||||
| MismatchedAlgoKey
|
||||
| NotEcdsa
|
||||
| NotWpkh
|
||||
| UnknownOutputType
|
||||
| KeyNotFound
|
||||
| WrongSigningAlgorithm
|
||||
| Unsupported => None,
|
||||
SighashComputation(ref e) => Some(e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<sighash::Error> for SignError {
|
||||
fn from(e: sighash::Error) -> Self {
|
||||
SignError::SighashComputation(e)
|
||||
}
|
||||
fn from(e: sighash::Error) -> Self { SignError::SighashComputation(e) }
|
||||
}
|
||||
|
||||
#[cfg(feature = "base64")]
|
||||
mod display_from_str {
|
||||
use super::{PartiallySignedTransaction, Error};
|
||||
use core::fmt::{Display, Formatter, self};
|
||||
use core::fmt::{self, Display, Formatter};
|
||||
use core::str::FromStr;
|
||||
|
||||
use base64::display::Base64Display;
|
||||
use bitcoin_internals::write_err;
|
||||
|
||||
use super::{Error, PartiallySignedTransaction};
|
||||
|
||||
/// Error encountered during PSBT decoding from Base64 string.
|
||||
#[derive(Debug)]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "base64")))]
|
||||
|
@ -751,7 +768,7 @@ mod display_from_str {
|
|||
/// Error in internal PSBT data structure.
|
||||
PsbtEncoding(Error),
|
||||
/// Error in PSBT Base64 encoding.
|
||||
Base64Encoding(::base64::DecodeError)
|
||||
Base64Encoding(::base64::DecodeError),
|
||||
}
|
||||
|
||||
impl Display for PsbtParseError {
|
||||
|
@ -801,26 +818,24 @@ pub use self::display_from_str::PsbtParseError;
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use crate::blockdata::locktime::absolute;
|
||||
use crate::hashes::{sha256, hash160, Hash, ripemd160};
|
||||
use crate::psbt::serialize::{Serialize, Deserialize};
|
||||
|
||||
use secp256k1::{Secp256k1, self};
|
||||
use secp256k1::{self, Secp256k1};
|
||||
#[cfg(feature = "rand-std")]
|
||||
use secp256k1::{All, SecretKey};
|
||||
|
||||
use crate::blockdata::script::ScriptBuf;
|
||||
use crate::blockdata::transaction::{Transaction, TxIn, TxOut, OutPoint, Sequence};
|
||||
use crate::network::constants::Network::Bitcoin;
|
||||
use super::*;
|
||||
use crate::bip32::{ChildNumber, ExtendedPrivKey, ExtendedPubKey, KeySource};
|
||||
use crate::psbt::map::{Output, Input};
|
||||
use crate::psbt::raw;
|
||||
use crate::internal_macros::hex;
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
use crate::blockdata::locktime::absolute;
|
||||
use crate::blockdata::script::ScriptBuf;
|
||||
use crate::blockdata::transaction::{OutPoint, Sequence, Transaction, TxIn, TxOut};
|
||||
use crate::blockdata::witness::Witness;
|
||||
use crate::hashes::{hash160, ripemd160, sha256, Hash};
|
||||
use crate::internal_macros::hex;
|
||||
use crate::network::constants::Network::Bitcoin;
|
||||
use crate::psbt::map::{Input, Output};
|
||||
use crate::psbt::raw;
|
||||
use crate::psbt::serialize::{Deserialize, Serialize};
|
||||
|
||||
#[test]
|
||||
fn trivial_psbt() {
|
||||
|
@ -843,14 +858,13 @@ mod tests {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn psbt_uncompressed_key() {
|
||||
fn psbt_uncompressed_key() {
|
||||
let psbt: PartiallySignedTransaction = hex_psbt!("70736274ff01003302000000010000000000000000000000000000000000000000000000000000000000000000ffffffff00ffffffff000000000000420204bb0d5d0cca36e7b9c80f63bc04c1240babb83bcd2803ef7ac8b6e2af594291daec281e856c98d210c5ab14dfd5828761f8ee7d5f45ca21ad3e4c4b41b747a3a047304402204f67e2afb76142d44fae58a2495d33a3419daa26cd0db8d04f3452b63289ac0f022010762a9fb67e94cc5cad9026f6dc99ff7f070f4278d30fbc7d0c869dd38c7fe70100").unwrap();
|
||||
|
||||
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);
|
||||
}
|
||||
assert!(psbt.inputs[0].partial_sigs.len() == 1);
|
||||
let pk = psbt.inputs[0].partial_sigs.iter().next().unwrap().0;
|
||||
assert!(!pk.compressed);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serialize_then_deserialize_output() {
|
||||
|
@ -881,8 +895,12 @@ mod tests {
|
|||
hd_keypaths.insert(pk.public_key, (fprint, dpath.into()));
|
||||
|
||||
let expected: Output = Output {
|
||||
redeem_script: Some(ScriptBuf::from_hex("76a914d0c59903c5bac2868760e90fd521a4665aa7652088ac").unwrap()),
|
||||
witness_script: Some(ScriptBuf::from_hex("a9143545e6e33b832c47050f24d3eeb93c9c03948bc787").unwrap()),
|
||||
redeem_script: Some(
|
||||
ScriptBuf::from_hex("76a914d0c59903c5bac2868760e90fd521a4665aa7652088ac").unwrap(),
|
||||
),
|
||||
witness_script: Some(
|
||||
ScriptBuf::from_hex("a9143545e6e33b832c47050f24d3eeb93c9c03948bc787").unwrap(),
|
||||
),
|
||||
bip32_derivation: hd_keypaths,
|
||||
..Default::default()
|
||||
};
|
||||
|
@ -898,25 +916,31 @@ mod tests {
|
|||
unsigned_tx: Transaction {
|
||||
version: 2,
|
||||
lock_time: absolute::LockTime::from_consensus(1257139),
|
||||
input: vec![
|
||||
TxIn {
|
||||
previous_output: OutPoint {
|
||||
txid: "f61b1742ca13176464adb3cb66050c00787bb3a4eead37e985f2df1e37718126".parse().unwrap(),
|
||||
vout: 0,
|
||||
},
|
||||
script_sig: ScriptBuf::new(),
|
||||
sequence: Sequence::ENABLE_LOCKTIME_NO_RBF,
|
||||
witness: Witness::default(),
|
||||
}
|
||||
],
|
||||
input: vec![TxIn {
|
||||
previous_output: OutPoint {
|
||||
txid: "f61b1742ca13176464adb3cb66050c00787bb3a4eead37e985f2df1e37718126"
|
||||
.parse()
|
||||
.unwrap(),
|
||||
vout: 0,
|
||||
},
|
||||
script_sig: ScriptBuf::new(),
|
||||
sequence: Sequence::ENABLE_LOCKTIME_NO_RBF,
|
||||
witness: Witness::default(),
|
||||
}],
|
||||
output: vec![
|
||||
TxOut {
|
||||
value: 99999699,
|
||||
script_pubkey: ScriptBuf::from_hex("76a914d0c59903c5bac2868760e90fd521a4665aa7652088ac").unwrap(),
|
||||
script_pubkey: ScriptBuf::from_hex(
|
||||
"76a914d0c59903c5bac2868760e90fd521a4665aa7652088ac",
|
||||
)
|
||||
.unwrap(),
|
||||
},
|
||||
TxOut {
|
||||
value: 100000000,
|
||||
script_pubkey: ScriptBuf::from_hex("a9143545e6e33b832c47050f24d3eeb93c9c03948bc787").unwrap(),
|
||||
script_pubkey: ScriptBuf::from_hex(
|
||||
"a9143545e6e33b832c47050f24d3eeb93c9c03948bc787",
|
||||
)
|
||||
.unwrap(),
|
||||
},
|
||||
],
|
||||
},
|
||||
|
@ -935,10 +959,7 @@ mod tests {
|
|||
#[test]
|
||||
fn serialize_then_deserialize_psbtkvpair() {
|
||||
let expected = raw::Pair {
|
||||
key: raw::Key {
|
||||
type_value: 0u8,
|
||||
key: vec![42u8, 69u8],
|
||||
},
|
||||
key: raw::Key { type_value: 0u8, key: vec![42u8, 69u8] },
|
||||
value: vec![69u8, 42u8, 4u8],
|
||||
};
|
||||
|
||||
|
@ -965,33 +986,39 @@ mod tests {
|
|||
let tx = Transaction {
|
||||
version: 1,
|
||||
lock_time: absolute::LockTime::ZERO,
|
||||
input: vec![
|
||||
TxIn {
|
||||
previous_output: OutPoint {
|
||||
txid: "e567952fb6cc33857f392efa3a46c995a28f69cca4bb1b37e0204dab1ec7a389".parse().unwrap(),
|
||||
vout: 1,
|
||||
},
|
||||
script_sig: ScriptBuf::from_hex("160014be18d152a9b012039daf3da7de4f53349eecb985").unwrap(),
|
||||
sequence: Sequence::MAX,
|
||||
witness: Witness::from_slice(&[hex!("03d2e15674941bad4a996372cb87e1856d3652606d98562fe39c5e9e7e413f2105")]),
|
||||
}
|
||||
],
|
||||
output: vec![
|
||||
TxOut {
|
||||
value: 190303501938,
|
||||
script_pubkey: ScriptBuf::from_hex("a914339725ba21efd62ac753a9bcd067d6c7a6a39d0587").unwrap(),
|
||||
input: vec![TxIn {
|
||||
previous_output: OutPoint {
|
||||
txid: "e567952fb6cc33857f392efa3a46c995a28f69cca4bb1b37e0204dab1ec7a389"
|
||||
.parse()
|
||||
.unwrap(),
|
||||
vout: 1,
|
||||
},
|
||||
],
|
||||
script_sig: ScriptBuf::from_hex("160014be18d152a9b012039daf3da7de4f53349eecb985")
|
||||
.unwrap(),
|
||||
sequence: Sequence::MAX,
|
||||
witness: Witness::from_slice(&[hex!(
|
||||
"03d2e15674941bad4a996372cb87e1856d3652606d98562fe39c5e9e7e413f2105"
|
||||
)]),
|
||||
}],
|
||||
output: vec![TxOut {
|
||||
value: 190303501938,
|
||||
script_pubkey: ScriptBuf::from_hex(
|
||||
"a914339725ba21efd62ac753a9bcd067d6c7a6a39d0587",
|
||||
)
|
||||
.unwrap(),
|
||||
}],
|
||||
};
|
||||
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 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());
|
||||
let keypaths: BTreeMap<secp256k1::PublicKey, KeySource> = vec![(
|
||||
"0339880dc92394b7355e3d0439fa283c31de7590812ea011c4245c0674a685e883".parse().unwrap(),
|
||||
key_source.clone(),
|
||||
)].into_iter().collect();
|
||||
)]
|
||||
.into_iter()
|
||||
.collect();
|
||||
|
||||
let proprietary: BTreeMap<raw::ProprietaryKey, Vec<u8>> = vec![(
|
||||
raw::ProprietaryKey {
|
||||
|
@ -1000,7 +1027,9 @@ mod tests {
|
|||
key: "test_key".as_bytes().to_vec(),
|
||||
},
|
||||
vec![5, 6, 7],
|
||||
)].into_iter().collect();
|
||||
)]
|
||||
.into_iter()
|
||||
.collect();
|
||||
|
||||
let psbt = PartiallySignedTransaction {
|
||||
version: 0,
|
||||
|
@ -1059,19 +1088,18 @@ mod tests {
|
|||
}
|
||||
|
||||
mod bip_vectors {
|
||||
use super::*;
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
#[cfg(feature = "base64")]
|
||||
use std::str::FromStr;
|
||||
|
||||
use crate::blockdata::script::ScriptBuf;
|
||||
use crate::blockdata::transaction::{Transaction, TxIn, TxOut, OutPoint, Sequence};
|
||||
use super::*;
|
||||
use crate::blockdata::locktime::absolute;
|
||||
use crate::psbt::map::{Map, Input, Output};
|
||||
use crate::psbt::{raw, PartiallySignedTransaction, Error};
|
||||
use crate::sighash::EcdsaSighashType;
|
||||
use std::collections::BTreeMap;
|
||||
use crate::blockdata::script::ScriptBuf;
|
||||
use crate::blockdata::transaction::{OutPoint, Sequence, Transaction, TxIn, TxOut};
|
||||
use crate::blockdata::witness::Witness;
|
||||
use crate::psbt::map::{Input, Map, Output};
|
||||
use crate::psbt::{raw, Error, PartiallySignedTransaction};
|
||||
use crate::sighash::EcdsaSighashType;
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "InvalidMagic")]
|
||||
|
@ -1242,11 +1270,15 @@ mod tests {
|
|||
assert_eq!(unserialized.serialize_hex(), base16str);
|
||||
assert_eq!(unserialized, hex_psbt!(base16str).unwrap());
|
||||
|
||||
#[cfg(feature = "base64")] {
|
||||
#[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());
|
||||
assert_eq!(
|
||||
PartiallySignedTransaction::from_str(base64str).unwrap(),
|
||||
hex_psbt!(base16str).unwrap()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1260,7 +1292,8 @@ mod tests {
|
|||
assert!(&psbt.inputs[0].final_script_sig.is_some());
|
||||
|
||||
let redeem_script = psbt.inputs[1].redeem_script.as_ref().unwrap();
|
||||
let expected_out = ScriptBuf::from_hex("a9143545e6e33b832c47050f24d3eeb93c9c03948bc787").unwrap();
|
||||
let expected_out =
|
||||
ScriptBuf::from_hex("a9143545e6e33b832c47050f24d3eeb93c9c03948bc787").unwrap();
|
||||
|
||||
assert!(redeem_script.is_v0_p2wpkh());
|
||||
assert_eq!(
|
||||
|
@ -1286,9 +1319,8 @@ mod tests {
|
|||
|
||||
assert_eq!(tx_input.previous_output.txid, psbt_non_witness_utxo.txid());
|
||||
assert!(psbt_non_witness_utxo.output[tx_input.previous_output.vout as usize]
|
||||
.script_pubkey
|
||||
.is_p2pkh()
|
||||
);
|
||||
.script_pubkey
|
||||
.is_p2pkh());
|
||||
assert_eq!(
|
||||
psbt.inputs[0].sighash_type.as_ref().unwrap().ecdsa_hash_ty().unwrap(),
|
||||
EcdsaSighashType::All
|
||||
|
@ -1306,7 +1338,8 @@ mod tests {
|
|||
assert!(&psbt.inputs[1].final_script_sig.is_none());
|
||||
|
||||
let redeem_script = psbt.inputs[1].redeem_script.as_ref().unwrap();
|
||||
let expected_out = ScriptBuf::from_hex("a9143545e6e33b832c47050f24d3eeb93c9c03948bc787").unwrap();
|
||||
let expected_out =
|
||||
ScriptBuf::from_hex("a9143545e6e33b832c47050f24d3eeb93c9c03948bc787").unwrap();
|
||||
|
||||
assert!(redeem_script.is_v0_p2wpkh());
|
||||
assert_eq!(
|
||||
|
@ -1330,7 +1363,8 @@ mod tests {
|
|||
assert!(&psbt.inputs[0].final_script_sig.is_none());
|
||||
|
||||
let redeem_script = psbt.inputs[0].redeem_script.as_ref().unwrap();
|
||||
let expected_out = ScriptBuf::from_hex("a9146345200f68d189e1adc0df1c4d16ea8f14c0dbeb87").unwrap();
|
||||
let expected_out =
|
||||
ScriptBuf::from_hex("a9146345200f68d189e1adc0df1c4d16ea8f14c0dbeb87").unwrap();
|
||||
|
||||
assert!(redeem_script.is_v0_p2wsh());
|
||||
assert_eq!(
|
||||
|
@ -1355,10 +1389,7 @@ mod tests {
|
|||
);
|
||||
|
||||
let mut unknown: BTreeMap<raw::Key, Vec<u8>> = BTreeMap::new();
|
||||
let key: raw::Key = raw::Key {
|
||||
type_value: 0x0fu8,
|
||||
key: hex!("010203040506070809"),
|
||||
};
|
||||
let key: raw::Key = raw::Key { type_value: 0x0fu8, key: hex!("010203040506070809") };
|
||||
let value: Vec<u8> = hex!("0102030405060708090a0b0c0d0e0f");
|
||||
|
||||
unknown.insert(key, value);
|
||||
|
@ -1378,7 +1409,10 @@ mod tests {
|
|||
#[cfg(feature = "std")]
|
||||
assert_eq!(err.to_string(), "invalid taproot signature");
|
||||
#[cfg(not(feature = "std"))]
|
||||
assert_eq!(err.to_string(), "invalid taproot signature: invalid taproot signature size: 66");
|
||||
assert_eq!(
|
||||
err.to_string(),
|
||||
"invalid taproot signature: invalid taproot signature size: 66"
|
||||
);
|
||||
let err = hex_psbt!("70736274ff010071020000000127744ababf3027fe0d6cf23a96eee2efb188ef52301954585883e69b6624b2420000000000ffffffff02787c01000000000016001483a7e34bd99ff03a4962ef8a1a101bb295461ece606b042a010000001600147ac369df1b20e033d6116623957b0ac49f3c52e8000000000001012b00f2052a010000002251205a2c2cf5b52cf31f83ad2e8da63ff03183ecd8f609c7510ae8a48e03910a0757221602fe349064c98d6e2a853fa3c9b12bd8b304a19c195c60efa7ee2393046d3fa2321900772b2da75600008001000080000000800100000000000000000000").unwrap_err();
|
||||
assert_eq!(err.to_string(), "invalid xonly public key");
|
||||
let err = hex_psbt!("70736274ff01007d020000000127744ababf3027fe0d6cf23a96eee2efb188ef52301954585883e69b6624b2420000000000ffffffff02887b0100000000001600142382871c7e8421a00093f754d91281e675874b9f606b042a010000002251205a2c2cf5b52cf31f83ad2e8da63ff03183ecd8f609c7510ae8a48e03910a0757000000000001012b00f2052a010000002251205a2c2cf5b52cf31f83ad2e8da63ff03183ecd8f609c7510ae8a48e03910a0757000001052102fe349064c98d6e2a853fa3c9b12bd8b304a19c195c60efa7ee2393046d3fa23200").unwrap_err();
|
||||
|
@ -1394,12 +1428,18 @@ mod tests {
|
|||
#[cfg(feature = "std")]
|
||||
assert_eq!(err.to_string(), "invalid taproot signature");
|
||||
#[cfg(not(feature = "std"))]
|
||||
assert_eq!(err.to_string(), "invalid taproot signature: invalid taproot signature size: 66");
|
||||
assert_eq!(
|
||||
err.to_string(),
|
||||
"invalid taproot signature: invalid taproot signature size: 66"
|
||||
);
|
||||
let err = hex_psbt!("70736274ff01005e02000000019bd48765230bf9a72e662001f972556e54f0c6f97feb56bcb5600d817f6995260100000000ffffffff0148e6052a01000000225120030da4fce4f7db28c2cb2951631e003713856597fe963882cb500e68112cca63000000000001012b00f2052a01000000225120c2247efbfd92ac47f6f40b8d42d169175a19fa9fa10e4a25d7f35eb4dd85b69241142cb13ac68248de806aa6a3659cf3c03eb6821d09c8114a4e868febde865bb6d2cd970e15f53fc0c82f950fd560ffa919b76172be017368a89913af074f400b093989756aa3739ccc689ec0fcf3a360be32cc0b59b16e93a1e8bb4605726b2ca7a3ff706c4176649632b2cc68e1f912b8a578e3719ce7710885c7a966f49bcd43cb0000").unwrap_err();
|
||||
#[cfg(feature = "std")]
|
||||
assert_eq!(err.to_string(), "invalid taproot signature");
|
||||
#[cfg(not(feature = "std"))]
|
||||
assert_eq!(err.to_string(), "invalid taproot signature: invalid taproot signature size: 57");
|
||||
assert_eq!(
|
||||
err.to_string(),
|
||||
"invalid taproot signature: invalid taproot signature size: 57"
|
||||
);
|
||||
let err = hex_psbt!("70736274ff01005e02000000019bd48765230bf9a72e662001f972556e54f0c6f97feb56bcb5600d817f6995260100000000ffffffff0148e6052a01000000225120030da4fce4f7db28c2cb2951631e003713856597fe963882cb500e68112cca63000000000001012b00f2052a01000000225120c2247efbfd92ac47f6f40b8d42d169175a19fa9fa10e4a25d7f35eb4dd85b6926315c150929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac06f7d62059e9497a1a4a267569d9876da60101aff38e3529b9b939ce7f91ae970115f2e490af7cc45c4f78511f36057ce5c5a5c56325a29fb44dfc203f356e1f80023202cb13ac68248de806aa6a3659cf3c03eb6821d09c8114a4e868febde865bb6d2acc00000").unwrap_err();
|
||||
assert_eq!(err.to_string(), "invalid control block");
|
||||
let err = hex_psbt!("70736274ff01005e02000000019bd48765230bf9a72e662001f972556e54f0c6f97feb56bcb5600d817f6995260100000000ffffffff0148e6052a01000000225120030da4fce4f7db28c2cb2951631e003713856597fe963882cb500e68112cca63000000000001012b00f2052a01000000225120c2247efbfd92ac47f6f40b8d42d169175a19fa9fa10e4a25d7f35eb4dd85b6926115c150929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac06f7d62059e9497a1a4a267569d9876da60101aff38e3529b9b939ce7f91ae970115f2e490af7cc45c4f78511f36057ce5c5a5c56325a29fb44dfc203f356e123202cb13ac68248de806aa6a3659cf3c03eb6821d09c8114a4e868febde865bb6d2acc00000").unwrap_err();
|
||||
|
@ -1575,11 +1615,10 @@ mod tests {
|
|||
#[test]
|
||||
fn serialize_and_deserialize_proprietary() {
|
||||
let mut psbt: PartiallySignedTransaction = hex_psbt!("70736274ff0100a00200000002ab0949a08c5af7c49b8212f417e2f15ab3f5c33dcf153821a8139f877a5b7be40000000000feffffffab0949a08c5af7c49b8212f417e2f15ab3f5c33dcf153821a8139f877a5b7be40100000000feffffff02603bea0b000000001976a914768a40bbd740cbe81d988e71de2a4d5c71396b1d88ac8e240000000000001976a9146f4620b553fa095e721b9ee0efe9fa039cca459788ac000000000001076a47304402204759661797c01b036b25928948686218347d89864b719e1f7fcf57d1e511658702205309eabf56aa4d8891ffd111fdf1336f3a29da866d7f8486d75546ceedaf93190121035cdc61fc7ba971c0b501a646a2a83b102cb43881217ca682dc86e2d73fa882920001012000e1f5050000000017a9143545e6e33b832c47050f24d3eeb93c9c03948bc787010416001485d13537f2e265405a34dbafa9e3dda01fb82308000000").unwrap();
|
||||
psbt.proprietary.insert(raw::ProprietaryKey {
|
||||
prefix: b"test".to_vec(),
|
||||
subtype: 0u8,
|
||||
key: b"test".to_vec(),
|
||||
}, b"test".to_vec());
|
||||
psbt.proprietary.insert(
|
||||
raw::ProprietaryKey { prefix: b"test".to_vec(), subtype: 0u8, key: b"test".to_vec() },
|
||||
b"test".to_vec(),
|
||||
);
|
||||
assert!(!psbt.proprietary.is_empty());
|
||||
let rtt: PartiallySignedTransaction = hex_psbt!(&psbt.serialize_hex()).unwrap();
|
||||
assert!(!rtt.proprietary.is_empty());
|
||||
|
@ -1725,31 +1764,31 @@ mod tests {
|
|||
let mut t2 = t.clone();
|
||||
t2.inputs[0].non_witness_utxo = None;
|
||||
match t2.fee().unwrap_err() {
|
||||
Error::MissingUtxo => {},
|
||||
e => panic!("unexpected error: {:?}", e)
|
||||
Error::MissingUtxo => {}
|
||||
e => panic!("unexpected error: {:?}", e),
|
||||
}
|
||||
// negative fee
|
||||
let mut t3 = t.clone();
|
||||
t3.unsigned_tx.output[0].value = prev_output_val;
|
||||
match t3.fee().unwrap_err() {
|
||||
Error::NegativeFee => {},
|
||||
e => panic!("unexpected error: {:?}", e)
|
||||
Error::NegativeFee => {}
|
||||
e => panic!("unexpected error: {:?}", e),
|
||||
}
|
||||
// overflow
|
||||
t.unsigned_tx.output[0].value = u64::max_value();
|
||||
t.unsigned_tx.output[1].value = u64::max_value();
|
||||
match t.fee().unwrap_err() {
|
||||
Error::FeeOverflow => {},
|
||||
e => panic!("unexpected error: {:?}", e)
|
||||
Error::FeeOverflow => {}
|
||||
e => panic!("unexpected error: {:?}", e),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "rand-std")]
|
||||
fn sign_psbt() {
|
||||
use crate::WPubkeyHash;
|
||||
use crate::address::WitnessProgram;
|
||||
use crate::bip32::{Fingerprint, DerivationPath};
|
||||
use crate::bip32::{DerivationPath, Fingerprint};
|
||||
use crate::WPubkeyHash;
|
||||
|
||||
let unsigned_tx = Transaction {
|
||||
version: 2,
|
||||
|
@ -1767,7 +1806,7 @@ mod tests {
|
|||
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{
|
||||
let txout_wpkh = TxOut {
|
||||
value: 10,
|
||||
script_pubkey: ScriptBuf::new_v0_p2wpkh(&WPubkeyHash::hash(&pk.to_bytes())),
|
||||
};
|
||||
|
@ -1778,13 +1817,10 @@ mod tests {
|
|||
psbt.inputs[0].bip32_derivation = map;
|
||||
|
||||
// Second input is unspendable by us e.g., from another wallet that supports future upgrades.
|
||||
let unknown_prog = WitnessProgram::new(
|
||||
crate::address::WitnessVersion::V4, vec![0xaa; 34]
|
||||
).unwrap();
|
||||
let txout_unknown_future = TxOut{
|
||||
value: 10,
|
||||
script_pubkey: ScriptBuf::new_witness_program(&unknown_prog),
|
||||
};
|
||||
let unknown_prog =
|
||||
WitnessProgram::new(crate::address::WitnessVersion::V4, vec![0xaa; 34]).unwrap();
|
||||
let txout_unknown_future =
|
||||
TxOut { value: 10, script_pubkey: ScriptBuf::new_witness_program(&unknown_prog) };
|
||||
psbt.inputs[1].witness_utxo = Some(txout_unknown_future);
|
||||
|
||||
let sigs = psbt.sign(&key_map, &secp).unwrap();
|
||||
|
|
|
@ -6,16 +6,17 @@
|
|||
//! <https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki>.
|
||||
//!
|
||||
|
||||
use crate::prelude::*;
|
||||
use core::fmt;
|
||||
use core::convert::TryFrom;
|
||||
use core::fmt;
|
||||
|
||||
use super::serialize::{Deserialize, Serialize};
|
||||
use crate::consensus::encode::{
|
||||
self, deserialize, serialize, Decodable, Encodable, ReadExt, VarInt, WriteExt, MAX_VEC_SIZE,
|
||||
};
|
||||
use crate::io;
|
||||
use crate::consensus::encode::{self, ReadExt, WriteExt, Decodable, Encodable, VarInt, serialize, deserialize, MAX_VEC_SIZE};
|
||||
use crate::prelude::*;
|
||||
use crate::psbt::Error;
|
||||
|
||||
use super::serialize::{Serialize, Deserialize};
|
||||
|
||||
/// A PSBT key in its raw byte form.
|
||||
#[derive(Debug, PartialEq, Hash, Eq, Clone, Ord, PartialOrd)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
|
@ -51,7 +52,10 @@ pub type ProprietaryType = u8;
|
|||
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
#[cfg_attr(feature = "serde", serde(crate = "actual_serde"))]
|
||||
pub struct ProprietaryKey<Subtype=ProprietaryType> where Subtype: Copy + From<u8> + Into<u8> {
|
||||
pub struct ProprietaryKey<Subtype = ProprietaryType>
|
||||
where
|
||||
Subtype: Copy + From<u8> + Into<u8>,
|
||||
{
|
||||
/// Proprietary type prefix used for grouping together keys under some
|
||||
/// application and avoid namespace collision
|
||||
#[cfg_attr(feature = "serde", serde(with = "crate::serde_utils::hex_bytes"))]
|
||||
|
@ -83,7 +87,7 @@ impl Key {
|
|||
return Err(encode::Error::OversizedVectorAllocation {
|
||||
requested: key_byte_size as usize,
|
||||
max: MAX_VEC_SIZE,
|
||||
})?
|
||||
})?;
|
||||
}
|
||||
|
||||
let type_value: u8 = Decodable::consensus_decode(r)?;
|
||||
|
@ -100,7 +104,9 @@ impl Key {
|
|||
impl Serialize for Key {
|
||||
fn serialize(&self) -> Vec<u8> {
|
||||
let mut buf = Vec::new();
|
||||
VarInt((self.key.len() + 1) as u64).consensus_encode(&mut buf).expect("in-memory writers don't error");
|
||||
VarInt((self.key.len() + 1) as u64)
|
||||
.consensus_encode(&mut buf)
|
||||
.expect("in-memory writers don't error");
|
||||
|
||||
self.type_value.consensus_encode(&mut buf).expect("in-memory writers don't error");
|
||||
|
||||
|
@ -131,14 +137,14 @@ impl Deserialize for Pair {
|
|||
|
||||
impl Pair {
|
||||
pub(crate) fn decode<R: io::Read + ?Sized>(r: &mut R) -> Result<Self, Error> {
|
||||
Ok(Pair {
|
||||
key: Key::decode(r)?,
|
||||
value: Decodable::consensus_decode(r)?,
|
||||
})
|
||||
Ok(Pair { key: Key::decode(r)?, value: Decodable::consensus_decode(r)? })
|
||||
}
|
||||
}
|
||||
|
||||
impl<Subtype> Encodable for ProprietaryKey<Subtype> where Subtype: Copy + From<u8> + Into<u8> {
|
||||
impl<Subtype> Encodable for ProprietaryKey<Subtype>
|
||||
where
|
||||
Subtype: Copy + From<u8> + Into<u8>,
|
||||
{
|
||||
fn consensus_encode<W: io::Write + ?Sized>(&self, w: &mut W) -> Result<usize, io::Error> {
|
||||
let mut len = self.prefix.consensus_encode(w)? + 1;
|
||||
w.emit_u8(self.subtype.into())?;
|
||||
|
@ -148,7 +154,10 @@ impl<Subtype> Encodable for ProprietaryKey<Subtype> where Subtype: Copy + From<u
|
|||
}
|
||||
}
|
||||
|
||||
impl<Subtype> Decodable for ProprietaryKey<Subtype> where Subtype: Copy + From<u8> + Into<u8> {
|
||||
impl<Subtype> Decodable for ProprietaryKey<Subtype>
|
||||
where
|
||||
Subtype: Copy + From<u8> + Into<u8>,
|
||||
{
|
||||
fn consensus_decode<R: io::Read + ?Sized>(r: &mut R) -> Result<Self, encode::Error> {
|
||||
let prefix = Vec::<u8>::consensus_decode(r)?;
|
||||
let subtype = Subtype::from(r.read_u8()?);
|
||||
|
@ -158,19 +167,18 @@ impl<Subtype> Decodable for ProprietaryKey<Subtype> where Subtype: Copy + From<u
|
|||
}
|
||||
}
|
||||
|
||||
impl<Subtype> ProprietaryKey<Subtype> where Subtype: Copy + From<u8> + Into<u8> {
|
||||
impl<Subtype> ProprietaryKey<Subtype>
|
||||
where
|
||||
Subtype: Copy + From<u8> + Into<u8>,
|
||||
{
|
||||
/// Constructs full [Key] corresponding to this proprietary key type
|
||||
pub fn to_key(&self) -> Key {
|
||||
Key {
|
||||
type_value: 0xFC,
|
||||
key: serialize(self)
|
||||
}
|
||||
}
|
||||
pub fn to_key(&self) -> Key { Key { type_value: 0xFC, key: serialize(self) } }
|
||||
}
|
||||
|
||||
impl<Subtype> TryFrom<Key> for ProprietaryKey<Subtype>
|
||||
where
|
||||
Subtype:Copy + From<u8> + Into<u8> {
|
||||
Subtype: Copy + From<u8> + Into<u8>,
|
||||
{
|
||||
type Error = Error;
|
||||
|
||||
/// Constructs a [`ProprietaryKey`] from a [`Key`].
|
||||
|
@ -179,7 +187,7 @@ where
|
|||
/// Returns [`Error::InvalidProprietaryKey`] if `key` does not start with `0xFC` byte.
|
||||
fn try_from(key: Key) -> Result<Self, Self::Error> {
|
||||
if key.type_value != 0xFC {
|
||||
return Err(Error::InvalidProprietaryKey)
|
||||
return Err(Error::InvalidProprietaryKey);
|
||||
}
|
||||
|
||||
Ok(deserialize(&key.key)?)
|
||||
|
@ -194,7 +202,7 @@ pub(crate) fn read_to_end<D: io::Read>(mut d: D) -> Result<Vec<u8>, io::Error> {
|
|||
match d.read(&mut buf) {
|
||||
Ok(0) => break,
|
||||
Ok(n) => result.extend_from_slice(&buf[0..n]),
|
||||
Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {},
|
||||
Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {}
|
||||
Err(e) => return Err(e),
|
||||
};
|
||||
}
|
||||
|
|
|
@ -6,31 +6,26 @@
|
|||
//! according to the BIP-174 specification.
|
||||
//!
|
||||
|
||||
use core::convert::TryFrom;
|
||||
use core::convert::TryInto;
|
||||
use core::convert::{TryFrom, TryInto};
|
||||
|
||||
use crate::VarInt;
|
||||
use crate::prelude::*;
|
||||
|
||||
use crate::io;
|
||||
|
||||
use crate::blockdata::script::ScriptBuf;
|
||||
use crate::blockdata::witness::Witness;
|
||||
use crate::blockdata::transaction::{Transaction, TxOut};
|
||||
use crate::consensus::encode::{self, serialize, Decodable, Encodable, deserialize_partial};
|
||||
use crate::taproot::TapTree;
|
||||
use secp256k1::{self, XOnlyPublicKey};
|
||||
use crate::bip32::{ChildNumber, Fingerprint, KeySource};
|
||||
use crate::hashes::{hash160, ripemd160, sha256, sha256d, Hash};
|
||||
use crate::crypto::{ecdsa, taproot};
|
||||
use crate::psbt::{Error, PartiallySignedTransaction};
|
||||
use crate::taproot::{TapNodeHash, TapLeafHash, ControlBlock, LeafVersion};
|
||||
use crate::crypto::key::PublicKey;
|
||||
|
||||
use super::map::{Map, Input, Output, PsbtSighashType};
|
||||
use super::map::{Input, Map, Output, PsbtSighashType};
|
||||
use super::Psbt;
|
||||
|
||||
use crate::taproot::TaprootBuilder;
|
||||
use crate::bip32::{ChildNumber, Fingerprint, KeySource};
|
||||
use crate::blockdata::script::ScriptBuf;
|
||||
use crate::blockdata::transaction::{Transaction, TxOut};
|
||||
use crate::blockdata::witness::Witness;
|
||||
use crate::consensus::encode::{self, deserialize_partial, serialize, Decodable, Encodable};
|
||||
use crate::crypto::key::PublicKey;
|
||||
use crate::crypto::{ecdsa, taproot};
|
||||
use crate::hashes::{hash160, ripemd160, sha256, sha256d, Hash};
|
||||
use crate::prelude::*;
|
||||
use crate::psbt::{Error, PartiallySignedTransaction};
|
||||
use crate::taproot::{
|
||||
ControlBlock, LeafVersion, TapLeafHash, TapNodeHash, TapTree, TaprootBuilder,
|
||||
};
|
||||
use crate::{io, VarInt};
|
||||
/// A trait for serializing a value as raw data for insertion into PSBT
|
||||
/// key-value maps.
|
||||
pub(crate) trait Serialize {
|
||||
|
@ -46,9 +41,7 @@ pub(crate) trait Deserialize: Sized {
|
|||
|
||||
impl PartiallySignedTransaction {
|
||||
/// Serialize a value as bytes in hex.
|
||||
pub fn serialize_hex(&self) -> String {
|
||||
self.serialize().to_lower_hex_string()
|
||||
}
|
||||
pub fn serialize_hex(&self) -> String { self.serialize().to_lower_hex_string() }
|
||||
|
||||
/// Serialize as raw binary data
|
||||
pub fn serialize(&self) -> Vec<u8> {
|
||||
|
@ -72,7 +65,6 @@ impl PartiallySignedTransaction {
|
|||
buf
|
||||
}
|
||||
|
||||
|
||||
/// Deserialize a value from raw binary data.
|
||||
pub fn deserialize(bytes: &[u8]) -> Result<Self, Error> {
|
||||
const MAGIC_BYTES: &[u8] = b"psbt";
|
||||
|
@ -133,15 +125,11 @@ impl_psbt_hash_de_serialize!(sha256d::Hash);
|
|||
impl_psbt_de_serialize!(Vec<TapLeafHash>);
|
||||
|
||||
impl Serialize for ScriptBuf {
|
||||
fn serialize(&self) -> Vec<u8> {
|
||||
self.to_bytes()
|
||||
}
|
||||
fn serialize(&self) -> Vec<u8> { self.to_bytes() }
|
||||
}
|
||||
|
||||
impl Deserialize for ScriptBuf {
|
||||
fn deserialize(bytes: &[u8]) -> Result<Self, Error> {
|
||||
Ok(Self::from(bytes.to_vec()))
|
||||
}
|
||||
fn deserialize(bytes: &[u8]) -> Result<Self, Error> { Ok(Self::from(bytes.to_vec())) }
|
||||
}
|
||||
|
||||
impl Serialize for PublicKey {
|
||||
|
@ -154,28 +142,22 @@ impl Serialize for PublicKey {
|
|||
|
||||
impl Deserialize for PublicKey {
|
||||
fn deserialize(bytes: &[u8]) -> Result<Self, Error> {
|
||||
PublicKey::from_slice(bytes)
|
||||
.map_err(Error::InvalidPublicKey)
|
||||
PublicKey::from_slice(bytes).map_err(Error::InvalidPublicKey)
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for secp256k1::PublicKey {
|
||||
fn serialize(&self) -> Vec<u8> {
|
||||
self.serialize().to_vec()
|
||||
}
|
||||
fn serialize(&self) -> Vec<u8> { self.serialize().to_vec() }
|
||||
}
|
||||
|
||||
impl Deserialize for secp256k1::PublicKey {
|
||||
fn deserialize(bytes: &[u8]) -> Result<Self, Error> {
|
||||
secp256k1::PublicKey::from_slice(bytes)
|
||||
.map_err(Error::InvalidSecp256k1PublicKey)
|
||||
secp256k1::PublicKey::from_slice(bytes).map_err(Error::InvalidSecp256k1PublicKey)
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for ecdsa::Signature {
|
||||
fn serialize(&self) -> Vec<u8> {
|
||||
self.to_vec()
|
||||
}
|
||||
fn serialize(&self) -> Vec<u8> { self.to_vec() }
|
||||
}
|
||||
|
||||
impl Deserialize for ecdsa::Signature {
|
||||
|
@ -193,21 +175,14 @@ impl Deserialize for ecdsa::Signature {
|
|||
// also has a field sighash_u32 (See BIP141). For example, when signing with non-standard
|
||||
// 0x05, the sighash message would have the last field as 0x05u32 while, the verification
|
||||
// would use check the signature assuming sighash_u32 as `0x01`.
|
||||
ecdsa::Signature::from_slice(bytes)
|
||||
.map_err(|e| match e {
|
||||
ecdsa::Error::EmptySignature => {
|
||||
Error::InvalidEcdsaSignature(e)
|
||||
}
|
||||
ecdsa::Error::NonStandardSighashType(flag) => {
|
||||
Error::NonStandardSighashType(flag)
|
||||
}
|
||||
ecdsa::Error::Secp256k1(..) => {
|
||||
Error::InvalidEcdsaSignature(e)
|
||||
}
|
||||
ecdsa::Error::HexEncoding(..) => {
|
||||
unreachable!("Decoding from slice, not hex")
|
||||
}
|
||||
})
|
||||
ecdsa::Signature::from_slice(bytes).map_err(|e| match e {
|
||||
ecdsa::Error::EmptySignature => Error::InvalidEcdsaSignature(e),
|
||||
ecdsa::Error::NonStandardSighashType(flag) => Error::NonStandardSighashType(flag),
|
||||
ecdsa::Error::Secp256k1(..) => Error::InvalidEcdsaSignature(e),
|
||||
ecdsa::Error::HexEncoding(..) => {
|
||||
unreachable!("Decoding from slice, not hex")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -228,7 +203,7 @@ impl Serialize for KeySource {
|
|||
impl Deserialize for KeySource {
|
||||
fn deserialize(bytes: &[u8]) -> Result<Self, Error> {
|
||||
if bytes.len() < 4 {
|
||||
return Err(io::Error::from(io::ErrorKind::UnexpectedEof).into())
|
||||
return Err(io::Error::from(io::ErrorKind::UnexpectedEof).into());
|
||||
}
|
||||
|
||||
let fprint: Fingerprint = bytes[0..4].try_into().expect("4 is the fingerprint length");
|
||||
|
@ -248,21 +223,15 @@ impl Deserialize for KeySource {
|
|||
|
||||
// partial sigs
|
||||
impl Serialize for Vec<u8> {
|
||||
fn serialize(&self) -> Vec<u8> {
|
||||
self.clone()
|
||||
}
|
||||
fn serialize(&self) -> Vec<u8> { self.clone() }
|
||||
}
|
||||
|
||||
impl Deserialize for Vec<u8> {
|
||||
fn deserialize(bytes: &[u8]) -> Result<Self, Error> {
|
||||
Ok(bytes.to_vec())
|
||||
}
|
||||
fn deserialize(bytes: &[u8]) -> Result<Self, Error> { Ok(bytes.to_vec()) }
|
||||
}
|
||||
|
||||
impl Serialize for PsbtSighashType {
|
||||
fn serialize(&self) -> Vec<u8> {
|
||||
serialize(&self.to_u32())
|
||||
}
|
||||
fn serialize(&self) -> Vec<u8> { serialize(&self.to_u32()) }
|
||||
}
|
||||
|
||||
impl Deserialize for PsbtSighashType {
|
||||
|
@ -274,38 +243,26 @@ impl Deserialize for PsbtSighashType {
|
|||
|
||||
// Taproot related ser/deser
|
||||
impl Serialize for XOnlyPublicKey {
|
||||
fn serialize(&self) -> Vec<u8> {
|
||||
XOnlyPublicKey::serialize(self).to_vec()
|
||||
}
|
||||
fn serialize(&self) -> Vec<u8> { XOnlyPublicKey::serialize(self).to_vec() }
|
||||
}
|
||||
|
||||
impl Deserialize for XOnlyPublicKey {
|
||||
fn deserialize(bytes: &[u8]) -> Result<Self, Error> {
|
||||
XOnlyPublicKey::from_slice(bytes)
|
||||
.map_err(|_| Error::InvalidXOnlyPublicKey)
|
||||
XOnlyPublicKey::from_slice(bytes).map_err(|_| Error::InvalidXOnlyPublicKey)
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for taproot::Signature {
|
||||
fn serialize(&self) -> Vec<u8> {
|
||||
self.to_vec()
|
||||
}
|
||||
impl Serialize for taproot::Signature {
|
||||
fn serialize(&self) -> Vec<u8> { self.to_vec() }
|
||||
}
|
||||
|
||||
impl Deserialize for taproot::Signature {
|
||||
fn deserialize(bytes: &[u8]) -> Result<Self, Error> {
|
||||
taproot::Signature::from_slice(bytes)
|
||||
.map_err(|e| match e {
|
||||
taproot::Error::InvalidSighashType(flag) => {
|
||||
Error::NonStandardSighashType(flag as u32)
|
||||
}
|
||||
taproot::Error::InvalidSignatureSize(_) => {
|
||||
Error::InvalidTaprootSignature(e)
|
||||
}
|
||||
taproot::Error::Secp256k1(..) => {
|
||||
Error::InvalidTaprootSignature(e)
|
||||
}
|
||||
})
|
||||
taproot::Signature::from_slice(bytes).map_err(|e| match e {
|
||||
taproot::Error::InvalidSighashType(flag) => Error::NonStandardSighashType(flag as u32),
|
||||
taproot::Error::InvalidSignatureSize(_) => Error::InvalidTaprootSignature(e),
|
||||
taproot::Error::Secp256k1(..) => Error::InvalidTaprootSignature(e),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -322,7 +279,7 @@ impl Serialize for (XOnlyPublicKey, TapLeafHash) {
|
|||
impl Deserialize for (XOnlyPublicKey, TapLeafHash) {
|
||||
fn deserialize(bytes: &[u8]) -> Result<Self, Error> {
|
||||
if bytes.len() < 32 {
|
||||
return Err(io::Error::from(io::ErrorKind::UnexpectedEof).into())
|
||||
return Err(io::Error::from(io::ErrorKind::UnexpectedEof).into());
|
||||
}
|
||||
let a: XOnlyPublicKey = Deserialize::deserialize(&bytes[..32])?;
|
||||
let b: TapLeafHash = Deserialize::deserialize(&bytes[32..])?;
|
||||
|
@ -331,15 +288,12 @@ impl Deserialize for (XOnlyPublicKey, TapLeafHash) {
|
|||
}
|
||||
|
||||
impl Serialize for ControlBlock {
|
||||
fn serialize(&self) -> Vec<u8> {
|
||||
ControlBlock::serialize(self)
|
||||
}
|
||||
fn serialize(&self) -> Vec<u8> { ControlBlock::serialize(self) }
|
||||
}
|
||||
|
||||
impl Deserialize for ControlBlock {
|
||||
fn deserialize(bytes: &[u8]) -> Result<Self, Error> {
|
||||
Self::decode(bytes)
|
||||
.map_err(|_| Error::InvalidControlBlock)
|
||||
Self::decode(bytes).map_err(|_| Error::InvalidControlBlock)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -356,7 +310,7 @@ impl Serialize for (ScriptBuf, LeafVersion) {
|
|||
impl Deserialize for (ScriptBuf, LeafVersion) {
|
||||
fn deserialize(bytes: &[u8]) -> Result<Self, Error> {
|
||||
if bytes.is_empty() {
|
||||
return Err(io::Error::from(io::ErrorKind::UnexpectedEof).into())
|
||||
return Err(io::Error::from(io::ErrorKind::UnexpectedEof).into());
|
||||
}
|
||||
// The last byte is LeafVersion.
|
||||
let script = ScriptBuf::deserialize(&bytes[..bytes.len() - 1])?;
|
||||
|
@ -366,10 +320,9 @@ impl Deserialize for (ScriptBuf, LeafVersion) {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
impl Serialize for (Vec<TapLeafHash>, KeySource) {
|
||||
fn serialize(&self) -> Vec<u8> {
|
||||
let mut buf = Vec::with_capacity( 32 * self.0.len() + key_source_len(&self.1));
|
||||
let mut buf = Vec::with_capacity(32 * self.0.len() + key_source_len(&self.1));
|
||||
self.0.consensus_encode(&mut buf).expect("Vecs don't error allocation");
|
||||
// TODO: Add support for writing into a writer for key-source
|
||||
buf.extend(self.1.serialize());
|
||||
|
@ -387,11 +340,14 @@ impl Deserialize for (Vec<TapLeafHash>, KeySource) {
|
|||
|
||||
impl Serialize for TapTree {
|
||||
fn serialize(&self) -> Vec<u8> {
|
||||
let capacity = self.script_leaves().map(|l| {
|
||||
l.script().len() + VarInt(l.script().len() as u64).len() // script version
|
||||
let capacity = self
|
||||
.script_leaves()
|
||||
.map(|l| {
|
||||
l.script().len() + VarInt(l.script().len() as u64).len() // script version
|
||||
+ 1 // merkle branch
|
||||
+ 1 // leaf version
|
||||
}).sum::<usize>();
|
||||
})
|
||||
.sum::<usize>();
|
||||
let mut buf = Vec::with_capacity(capacity);
|
||||
for leaf_info in self.script_leaves() {
|
||||
// # Cast Safety:
|
||||
|
@ -416,9 +372,10 @@ impl Deserialize for TapTree {
|
|||
if consumed > 0 {
|
||||
bytes_iter.nth(consumed - 1);
|
||||
}
|
||||
let leaf_version = LeafVersion::from_consensus(*version)
|
||||
.map_err(|_| Error::InvalidLeafVersion)?;
|
||||
builder = builder.add_leaf_with_ver(*depth, script, leaf_version)
|
||||
let leaf_version =
|
||||
LeafVersion::from_consensus(*version).map_err(|_| Error::InvalidLeafVersion)?;
|
||||
builder = builder
|
||||
.add_leaf_with_ver(*depth, script, leaf_version)
|
||||
.map_err(|_| Error::Taproot("Tree not in DFS order"))?;
|
||||
}
|
||||
TapTree::try_from(builder).map_err(Error::TapTree)
|
||||
|
@ -426,9 +383,7 @@ impl Deserialize for TapTree {
|
|||
}
|
||||
|
||||
// Helper function to compute key source len
|
||||
fn key_source_len(key_source: &KeySource) -> usize {
|
||||
4 + 4 * (key_source.1).as_ref().len()
|
||||
}
|
||||
fn key_source_len(key_source: &KeySource) -> usize { 4 + 4 * (key_source.1).as_ref().len() }
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
@ -439,7 +394,10 @@ mod tests {
|
|||
// Composes tree matching a given depth map, filled with dumb script leafs,
|
||||
// each of which consists of a single push-int op code, with int value
|
||||
// increased for each consecutive leaf.
|
||||
pub fn compose_taproot_builder<'map>(opcode: u8, depth_map: impl IntoIterator<Item = &'map u8>) -> TaprootBuilder {
|
||||
pub fn compose_taproot_builder<'map>(
|
||||
opcode: u8,
|
||||
depth_map: impl IntoIterator<Item = &'map u8>,
|
||||
) -> TaprootBuilder {
|
||||
let mut val = opcode;
|
||||
let mut builder = TaprootBuilder::new();
|
||||
for depth in depth_map {
|
||||
|
@ -454,7 +412,13 @@ mod tests {
|
|||
#[test]
|
||||
fn taptree_hidden() {
|
||||
let mut builder = compose_taproot_builder(0x51, &[2, 2, 2]);
|
||||
builder = builder.add_leaf_with_ver(3, ScriptBuf::from_hex("b9").unwrap(), LeafVersion::from_consensus(0xC2).unwrap()).unwrap();
|
||||
builder = builder
|
||||
.add_leaf_with_ver(
|
||||
3,
|
||||
ScriptBuf::from_hex("b9").unwrap(),
|
||||
LeafVersion::from_consensus(0xC2).unwrap(),
|
||||
)
|
||||
.unwrap();
|
||||
builder = builder.add_hidden_node(3, TapNodeHash::all_zeros()).unwrap();
|
||||
assert!(TapTree::try_from(builder).is_err());
|
||||
}
|
||||
|
@ -462,7 +426,13 @@ mod tests {
|
|||
#[test]
|
||||
fn taptree_roundtrip() {
|
||||
let mut builder = compose_taproot_builder(0x51, &[2, 2, 2, 3]);
|
||||
builder = builder.add_leaf_with_ver(3, ScriptBuf::from_hex("b9").unwrap(), LeafVersion::from_consensus(0xC2).unwrap()).unwrap();
|
||||
builder = builder
|
||||
.add_leaf_with_ver(
|
||||
3,
|
||||
ScriptBuf::from_hex("b9").unwrap(),
|
||||
LeafVersion::from_consensus(0xC2).unwrap(),
|
||||
)
|
||||
.unwrap();
|
||||
let tree = TapTree::try_from(builder).unwrap();
|
||||
let tree_prime = TapTree::deserialize(&tree.serialize()).unwrap();
|
||||
assert_eq!(tree, tree_prime);
|
||||
|
|
Loading…
Reference in New Issue