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