Moving globals into PSBT struct
This commit is contained in:
parent
57d7baf05b
commit
55c627715f
|
@ -57,17 +57,9 @@ macro_rules! impl_psbtmap_consensus_encoding {
|
||||||
impl $crate::consensus::Encodable for $thing {
|
impl $crate::consensus::Encodable for $thing {
|
||||||
fn consensus_encode<S: $crate::io::Write>(
|
fn consensus_encode<S: $crate::io::Write>(
|
||||||
&self,
|
&self,
|
||||||
mut s: S,
|
s: S,
|
||||||
) -> Result<usize, $crate::io::Error> {
|
) -> Result<usize, $crate::io::Error> {
|
||||||
let mut len = 0;
|
self.consensus_encode_map(s)
|
||||||
for pair in $crate::util::psbt::Map::get_pairs(self)? {
|
|
||||||
len += $crate::consensus::Encodable::consensus_encode(
|
|
||||||
&pair,
|
|
||||||
&mut s,
|
|
||||||
)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(len + $crate::consensus::Encodable::consensus_encode(&0x00_u8, s)?)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -21,11 +21,11 @@ use blockdata::transaction::Transaction;
|
||||||
use consensus::{encode, Encodable, Decodable};
|
use consensus::{encode, Encodable, Decodable};
|
||||||
use consensus::encode::MAX_VEC_SIZE;
|
use consensus::encode::MAX_VEC_SIZE;
|
||||||
use util::psbt::map::Map;
|
use util::psbt::map::Map;
|
||||||
use util::psbt::raw;
|
use util::psbt::{raw, PartiallySignedTransaction};
|
||||||
use util::psbt;
|
use util::psbt;
|
||||||
use util::psbt::Error;
|
use util::psbt::Error;
|
||||||
use util::endian::u32_to_array_le;
|
use util::endian::u32_to_array_le;
|
||||||
use util::bip32::{ExtendedPubKey, KeySource, Fingerprint, DerivationPath, ChildNumber};
|
use util::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;
|
||||||
|
@ -36,50 +36,7 @@ const PSBT_GLOBAL_VERSION: u8 = 0xFB;
|
||||||
/// Type: Proprietary Use Type PSBT_GLOBAL_PROPRIETARY = 0xFC
|
/// Type: Proprietary Use Type PSBT_GLOBAL_PROPRIETARY = 0xFC
|
||||||
const PSBT_GLOBAL_PROPRIETARY: u8 = 0xFC;
|
const PSBT_GLOBAL_PROPRIETARY: u8 = 0xFC;
|
||||||
|
|
||||||
/// A key-value map for global data.
|
impl Map for PartiallySignedTransaction {
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
|
||||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
|
||||||
pub struct Global {
|
|
||||||
/// The unsigned transaction, scriptSigs and witnesses for each input must be
|
|
||||||
/// empty.
|
|
||||||
pub unsigned_tx: Transaction,
|
|
||||||
/// The version number of this PSBT. If omitted, the version number is 0.
|
|
||||||
pub version: u32,
|
|
||||||
/// A global map from extended public keys to the used key fingerprint and
|
|
||||||
/// derivation path as defined by BIP 32
|
|
||||||
pub xpub: BTreeMap<ExtendedPubKey, KeySource>,
|
|
||||||
/// Global proprietary key-value pairs.
|
|
||||||
#[cfg_attr(feature = "serde", serde(with = "::serde_utils::btreemap_as_seq_byte_values"))]
|
|
||||||
pub proprietary: BTreeMap<raw::ProprietaryKey, Vec<u8>>,
|
|
||||||
/// Unknown global key-value pairs.
|
|
||||||
#[cfg_attr(feature = "serde", serde(with = "::serde_utils::btreemap_as_seq_byte_values"))]
|
|
||||||
pub unknown: BTreeMap<raw::Key, Vec<u8>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Global {
|
|
||||||
/// Create a Global from an unsigned transaction, error if not unsigned
|
|
||||||
pub fn from_unsigned_tx(tx: Transaction) -> Result<Self, psbt::Error> {
|
|
||||||
for txin in &tx.input {
|
|
||||||
if !txin.script_sig.is_empty() {
|
|
||||||
return Err(Error::UnsignedTxHasScriptSigs);
|
|
||||||
}
|
|
||||||
|
|
||||||
if !txin.witness.is_empty() {
|
|
||||||
return Err(Error::UnsignedTxHasScriptWitnesses);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(Global {
|
|
||||||
unsigned_tx: tx,
|
|
||||||
xpub: Default::default(),
|
|
||||||
version: 0,
|
|
||||||
proprietary: Default::default(),
|
|
||||||
unknown: Default::default(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Map for Global {
|
|
||||||
fn insert_pair(&mut self, pair: raw::Pair) -> Result<(), encode::Error> {
|
fn insert_pair(&mut self, pair: raw::Pair) -> Result<(), encode::Error> {
|
||||||
let raw::Pair {
|
let raw::Pair {
|
||||||
key: raw_key,
|
key: raw_key,
|
||||||
|
@ -220,14 +177,21 @@ impl Map for Global {
|
||||||
|
|
||||||
self.proprietary.extend(other.proprietary);
|
self.proprietary.extend(other.proprietary);
|
||||||
self.unknown.extend(other.unknown);
|
self.unknown.extend(other.unknown);
|
||||||
|
|
||||||
|
for (self_input, other_input) in self.inputs.iter_mut().zip(other.inputs.into_iter()) {
|
||||||
|
self_input.merge(other_input)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (self_output, other_output) in self.outputs.iter_mut().zip(other.outputs.into_iter()) {
|
||||||
|
self_output.merge(other_output)?;
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl_psbtmap_consensus_encoding!(Global);
|
impl PartiallySignedTransaction {
|
||||||
|
pub(crate) fn consensus_decode_global<D: io::Read>(d: D) -> Result<Self, encode::Error> {
|
||||||
impl Decodable for Global {
|
|
||||||
fn consensus_decode<D: io::Read>(d: D) -> Result<Self, encode::Error> {
|
|
||||||
let mut d = d.take(MAX_VEC_SIZE as u64);
|
let mut d = d.take(MAX_VEC_SIZE as u64);
|
||||||
let mut tx: Option<Transaction> = None;
|
let mut tx: Option<Transaction> = None;
|
||||||
let mut version: Option<u32> = None;
|
let mut version: Option<u32> = None;
|
||||||
|
@ -334,12 +298,15 @@ impl Decodable for Global {
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(tx) = tx {
|
if let Some(tx) = tx {
|
||||||
let mut rv: Global = Global::from_unsigned_tx(tx)?;
|
Ok(PartiallySignedTransaction {
|
||||||
rv.version = version.unwrap_or(0);
|
unsigned_tx: tx,
|
||||||
rv.xpub = xpub_map;
|
version: version.unwrap_or(0),
|
||||||
rv.unknown = unknowns;
|
xpub: xpub_map,
|
||||||
rv.proprietary = proprietary;
|
proprietary,
|
||||||
Ok(rv)
|
unknown: unknowns,
|
||||||
|
inputs: vec![],
|
||||||
|
outputs: vec![]
|
||||||
|
})
|
||||||
} else {
|
} else {
|
||||||
Err(Error::MustHaveUnsignedTx.into())
|
Err(Error::MustHaveUnsignedTx.into())
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,6 +30,22 @@ pub trait Map {
|
||||||
|
|
||||||
/// Attempt to merge with another key-value map of the same type.
|
/// Attempt to merge with another key-value map of the same type.
|
||||||
fn merge(&mut self, other: Self) -> Result<(), psbt::Error>;
|
fn merge(&mut self, other: Self) -> Result<(), psbt::Error>;
|
||||||
|
|
||||||
|
/// Encodes map data with bitcoin consensus encoding
|
||||||
|
fn consensus_encode_map<S: io::Write>(
|
||||||
|
&self,
|
||||||
|
mut s: S,
|
||||||
|
) -> Result<usize, io::Error> {
|
||||||
|
let mut len = 0;
|
||||||
|
for pair in Map::get_pairs(self)? {
|
||||||
|
len += encode::Encodable::consensus_encode(
|
||||||
|
&pair,
|
||||||
|
&mut s,
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(len + encode::Encodable::consensus_encode(&0x00_u8, s)?)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// place at end to pick up macros
|
// place at end to pick up macros
|
||||||
|
@ -37,6 +53,5 @@ mod global;
|
||||||
mod input;
|
mod input;
|
||||||
mod output;
|
mod output;
|
||||||
|
|
||||||
pub use self::global::Global;
|
|
||||||
pub use self::input::Input;
|
pub use self::input::Input;
|
||||||
pub use self::output::Output;
|
pub use self::output::Output;
|
||||||
|
|
|
@ -38,14 +38,29 @@ mod macros;
|
||||||
pub mod serialize;
|
pub mod serialize;
|
||||||
|
|
||||||
mod map;
|
mod map;
|
||||||
pub use self::map::{Map, Global, Input, Output};
|
pub use self::map::{Map, Input, Output};
|
||||||
|
|
||||||
|
use util::bip32::{ExtendedPubKey, KeySource};
|
||||||
|
|
||||||
/// A Partially Signed Transaction.
|
/// A Partially Signed Transaction.
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
pub struct PartiallySignedTransaction {
|
pub struct PartiallySignedTransaction {
|
||||||
/// The key-value pairs for all global data.
|
/// The unsigned transaction, scriptSigs and witnesses for each input must be
|
||||||
pub global: Global,
|
/// empty.
|
||||||
|
pub unsigned_tx: Transaction,
|
||||||
|
/// The version number of this PSBT. If omitted, the version number is 0.
|
||||||
|
pub version: u32,
|
||||||
|
/// A global map from extended public keys to the used key fingerprint and
|
||||||
|
/// derivation path as defined by BIP 32
|
||||||
|
pub xpub: BTreeMap<ExtendedPubKey, KeySource>,
|
||||||
|
/// Global proprietary key-value pairs.
|
||||||
|
#[cfg_attr(feature = "serde", serde(with = "::serde_utils::btreemap_as_seq_byte_values"))]
|
||||||
|
pub proprietary: BTreeMap<raw::ProprietaryKey, Vec<u8>>,
|
||||||
|
/// Unknown global key-value pairs.
|
||||||
|
#[cfg_attr(feature = "serde", serde(with = "::serde_utils::btreemap_as_seq_byte_values"))]
|
||||||
|
pub unknown: BTreeMap<raw::Key, Vec<u8>>,
|
||||||
|
|
||||||
/// The corresponding key-value map for each input in the unsigned
|
/// The corresponding key-value map for each input in the unsigned
|
||||||
/// transaction.
|
/// transaction.
|
||||||
pub inputs: Vec<Input>,
|
pub inputs: Vec<Input>,
|
||||||
|
@ -55,20 +70,43 @@ pub struct PartiallySignedTransaction {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PartiallySignedTransaction {
|
impl PartiallySignedTransaction {
|
||||||
|
/// Checks that unsigned transaction does not have scriptSig's or witness
|
||||||
|
/// data
|
||||||
|
fn unsigned_tx_checks(&self) -> Result<(), Error> {
|
||||||
|
for txin in &self.unsigned_tx.input {
|
||||||
|
if !txin.script_sig.is_empty() {
|
||||||
|
return Err(Error::UnsignedTxHasScriptSigs);
|
||||||
|
}
|
||||||
|
|
||||||
|
if !txin.witness.is_empty() {
|
||||||
|
return Err(Error::UnsignedTxHasScriptWitnesses);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
/// Create a PartiallySignedTransaction from an unsigned transaction, error
|
/// Create a PartiallySignedTransaction from an unsigned transaction, error
|
||||||
/// if not unsigned
|
/// if not unsigned
|
||||||
pub fn from_unsigned_tx(tx: Transaction) -> Result<Self, self::Error> {
|
pub fn from_unsigned_tx(tx: Transaction) -> Result<Self, Error> {
|
||||||
Ok(PartiallySignedTransaction {
|
let psbt = PartiallySignedTransaction {
|
||||||
inputs: vec![Default::default(); tx.input.len()],
|
inputs: vec![Default::default(); tx.input.len()],
|
||||||
outputs: vec![Default::default(); tx.output.len()],
|
outputs: vec![Default::default(); tx.output.len()],
|
||||||
global: Global::from_unsigned_tx(tx)?,
|
|
||||||
})
|
unsigned_tx: tx,
|
||||||
|
xpub: Default::default(),
|
||||||
|
version: 0,
|
||||||
|
proprietary: Default::default(),
|
||||||
|
unknown: Default::default(),
|
||||||
|
};
|
||||||
|
psbt.unsigned_tx_checks()?;
|
||||||
|
Ok(psbt)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Extract the Transaction from a PartiallySignedTransaction by filling in
|
/// Extract the Transaction from a PartiallySignedTransaction by filling in
|
||||||
/// the available signature information in place.
|
/// the available signature information in place.
|
||||||
pub fn extract_tx(self) -> Transaction {
|
pub fn extract_tx(self) -> Transaction {
|
||||||
let mut tx: Transaction = self.global.unsigned_tx;
|
let mut tx: Transaction = self.unsigned_tx;
|
||||||
|
|
||||||
for (vin, psbtin) in tx.input.iter_mut().zip(self.inputs.into_iter()) {
|
for (vin, psbtin) in tx.input.iter_mut().zip(self.inputs.into_iter()) {
|
||||||
vin.script_sig = psbtin.final_script_sig.unwrap_or_else(Script::new);
|
vin.script_sig = psbtin.final_script_sig.unwrap_or_else(Script::new);
|
||||||
|
@ -77,21 +115,6 @@ impl PartiallySignedTransaction {
|
||||||
|
|
||||||
tx
|
tx
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Attempt to merge with another `PartiallySignedTransaction`.
|
|
||||||
pub fn merge(&mut self, other: Self) -> Result<(), self::Error> {
|
|
||||||
self.global.merge(other.global)?;
|
|
||||||
|
|
||||||
for (self_input, other_input) in self.inputs.iter_mut().zip(other.inputs.into_iter()) {
|
|
||||||
self_input.merge(other_input)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (self_output, other_output) in self.outputs.iter_mut().zip(other.outputs.into_iter()) {
|
|
||||||
self_output.merge(other_output)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "base64")]
|
#[cfg(feature = "base64")]
|
||||||
|
@ -151,7 +174,7 @@ impl Encodable for PartiallySignedTransaction {
|
||||||
|
|
||||||
len += 0xff_u8.consensus_encode(&mut s)?;
|
len += 0xff_u8.consensus_encode(&mut s)?;
|
||||||
|
|
||||||
len += self.global.consensus_encode(&mut s)?;
|
len += self.consensus_encode_map(&mut s)?;
|
||||||
|
|
||||||
for i in &self.inputs {
|
for i in &self.inputs {
|
||||||
len += i.consensus_encode(&mut s)?;
|
len += i.consensus_encode(&mut s)?;
|
||||||
|
@ -178,7 +201,8 @@ impl Decodable for PartiallySignedTransaction {
|
||||||
return Err(Error::InvalidSeparator.into());
|
return Err(Error::InvalidSeparator.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
let global: Global = Decodable::consensus_decode(&mut d)?;
|
let mut global = PartiallySignedTransaction::consensus_decode_global(&mut d)?;
|
||||||
|
global.unsigned_tx_checks()?;
|
||||||
|
|
||||||
let inputs: Vec<Input> = {
|
let inputs: Vec<Input> = {
|
||||||
let inputs_len: usize = (&global.unsigned_tx.input).len();
|
let inputs_len: usize = (&global.unsigned_tx.input).len();
|
||||||
|
@ -204,11 +228,9 @@ impl Decodable for PartiallySignedTransaction {
|
||||||
outputs
|
outputs
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(PartiallySignedTransaction {
|
global.inputs = inputs;
|
||||||
global: global,
|
global.outputs = outputs;
|
||||||
inputs: inputs,
|
Ok(global)
|
||||||
outputs: outputs,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -227,7 +249,7 @@ mod tests {
|
||||||
use consensus::encode::{deserialize, serialize, serialize_hex};
|
use consensus::encode::{deserialize, serialize, serialize_hex};
|
||||||
use util::bip32::{ChildNumber, ExtendedPrivKey, ExtendedPubKey, Fingerprint, KeySource};
|
use util::bip32::{ChildNumber, ExtendedPrivKey, ExtendedPubKey, Fingerprint, KeySource};
|
||||||
use util::ecdsa::PublicKey;
|
use util::ecdsa::PublicKey;
|
||||||
use util::psbt::map::{Global, Output, Input};
|
use util::psbt::map::{Output, Input};
|
||||||
use util::psbt::raw;
|
use util::psbt::raw;
|
||||||
|
|
||||||
use super::PartiallySignedTransaction;
|
use super::PartiallySignedTransaction;
|
||||||
|
@ -237,18 +259,17 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn trivial_psbt() {
|
fn trivial_psbt() {
|
||||||
let psbt = PartiallySignedTransaction {
|
let psbt = PartiallySignedTransaction {
|
||||||
global: Global {
|
unsigned_tx: Transaction {
|
||||||
unsigned_tx: Transaction {
|
version: 2,
|
||||||
version: 2,
|
lock_time: 0,
|
||||||
lock_time: 0,
|
input: vec![],
|
||||||
input: vec![],
|
output: vec![],
|
||||||
output: vec![],
|
|
||||||
},
|
|
||||||
xpub: Default::default(),
|
|
||||||
version: 0,
|
|
||||||
proprietary: BTreeMap::new(),
|
|
||||||
unknown: BTreeMap::new(),
|
|
||||||
},
|
},
|
||||||
|
xpub: Default::default(),
|
||||||
|
version: 0,
|
||||||
|
proprietary: BTreeMap::new(),
|
||||||
|
unknown: BTreeMap::new(),
|
||||||
|
|
||||||
inputs: vec![],
|
inputs: vec![],
|
||||||
outputs: vec![],
|
outputs: vec![],
|
||||||
};
|
};
|
||||||
|
@ -304,7 +325,7 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn serialize_then_deserialize_global() {
|
fn serialize_then_deserialize_global() {
|
||||||
let expected = Global {
|
let expected = PartiallySignedTransaction {
|
||||||
unsigned_tx: Transaction {
|
unsigned_tx: Transaction {
|
||||||
version: 2,
|
version: 2,
|
||||||
lock_time: 1257139,
|
lock_time: 1257139,
|
||||||
|
@ -338,9 +359,16 @@ mod tests {
|
||||||
version: 0,
|
version: 0,
|
||||||
proprietary: Default::default(),
|
proprietary: Default::default(),
|
||||||
unknown: Default::default(),
|
unknown: Default::default(),
|
||||||
|
inputs: vec![
|
||||||
|
Input::default(),
|
||||||
|
],
|
||||||
|
outputs: vec![
|
||||||
|
Output::default(),
|
||||||
|
Output::default()
|
||||||
|
]
|
||||||
};
|
};
|
||||||
|
|
||||||
let actual: Global = deserialize(&serialize(&expected)).unwrap();
|
let actual: PartiallySignedTransaction = deserialize(&serialize(&expected)).unwrap();
|
||||||
|
|
||||||
assert_eq!(expected, actual);
|
assert_eq!(expected, actual);
|
||||||
}
|
}
|
||||||
|
@ -414,23 +442,22 @@ mod tests {
|
||||||
)].into_iter().collect();
|
)].into_iter().collect();
|
||||||
|
|
||||||
let psbt = PartiallySignedTransaction {
|
let psbt = PartiallySignedTransaction {
|
||||||
global: Global {
|
version: 0,
|
||||||
version: 0,
|
xpub: {
|
||||||
xpub: {
|
let xpub: ExtendedPubKey =
|
||||||
let xpub: ExtendedPubKey =
|
"xpub661MyMwAqRbcGoRVtwfvzZsq2VBJR1LAHfQstHUoxqDorV89vRoMxUZ27kLrraAj6MPi\
|
||||||
"xpub661MyMwAqRbcGoRVtwfvzZsq2VBJR1LAHfQstHUoxqDorV89vRoMxUZ27kLrraAj6MPi\
|
QfrDb27gigC1VS1dBXi5jGpxmMeBXEkKkcXUTg4".parse().unwrap();
|
||||||
QfrDb27gigC1VS1dBXi5jGpxmMeBXEkKkcXUTg4".parse().unwrap();
|
vec![(xpub, key_source.clone())].into_iter().collect()
|
||||||
vec![(xpub, key_source.clone())].into_iter().collect()
|
|
||||||
},
|
|
||||||
unsigned_tx: {
|
|
||||||
let mut unsigned = tx.clone();
|
|
||||||
unsigned.input[0].script_sig = Script::new();
|
|
||||||
unsigned.input[0].witness = Vec::new();
|
|
||||||
unsigned
|
|
||||||
},
|
|
||||||
proprietary: proprietary.clone(),
|
|
||||||
unknown: unknown.clone(),
|
|
||||||
},
|
},
|
||||||
|
unsigned_tx: {
|
||||||
|
let mut unsigned = tx.clone();
|
||||||
|
unsigned.input[0].script_sig = Script::new();
|
||||||
|
unsigned.input[0].witness = Vec::new();
|
||||||
|
unsigned
|
||||||
|
},
|
||||||
|
proprietary: proprietary.clone(),
|
||||||
|
unknown: unknown.clone(),
|
||||||
|
|
||||||
inputs: vec![Input {
|
inputs: vec![Input {
|
||||||
non_witness_utxo: Some(tx),
|
non_witness_utxo: Some(tx),
|
||||||
witness_utxo: Some(TxOut {
|
witness_utxo: Some(TxOut {
|
||||||
|
@ -476,7 +503,7 @@ mod tests {
|
||||||
use blockdata::script::Script;
|
use blockdata::script::Script;
|
||||||
use blockdata::transaction::{SigHashType, Transaction, TxIn, TxOut, OutPoint};
|
use blockdata::transaction::{SigHashType, Transaction, TxIn, TxOut, OutPoint};
|
||||||
use consensus::encode::serialize_hex;
|
use consensus::encode::serialize_hex;
|
||||||
use util::psbt::map::{Map, Global, Input, Output};
|
use util::psbt::map::{Map, Input, Output};
|
||||||
use util::psbt::raw;
|
use util::psbt::raw;
|
||||||
use util::psbt::{PartiallySignedTransaction, Error};
|
use util::psbt::{PartiallySignedTransaction, Error};
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
|
@ -560,37 +587,36 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn valid_vector_1() {
|
fn valid_vector_1() {
|
||||||
let unserialized = PartiallySignedTransaction {
|
let unserialized = PartiallySignedTransaction {
|
||||||
global: Global {
|
unsigned_tx: Transaction {
|
||||||
unsigned_tx: Transaction {
|
version: 2,
|
||||||
version: 2,
|
lock_time: 1257139,
|
||||||
lock_time: 1257139,
|
input: vec![TxIn {
|
||||||
input: vec![TxIn {
|
previous_output: OutPoint {
|
||||||
previous_output: OutPoint {
|
txid: Txid::from_hex(
|
||||||
txid: Txid::from_hex(
|
"f61b1742ca13176464adb3cb66050c00787bb3a4eead37e985f2df1e37718126",
|
||||||
"f61b1742ca13176464adb3cb66050c00787bb3a4eead37e985f2df1e37718126",
|
).unwrap(),
|
||||||
).unwrap(),
|
vout: 0,
|
||||||
vout: 0,
|
},
|
||||||
},
|
script_sig: Script::new(),
|
||||||
script_sig: Script::new(),
|
sequence: 4294967294,
|
||||||
sequence: 4294967294,
|
witness: vec![],
|
||||||
witness: vec![],
|
}],
|
||||||
}],
|
output: vec![
|
||||||
output: vec![
|
TxOut {
|
||||||
TxOut {
|
value: 99999699,
|
||||||
value: 99999699,
|
script_pubkey: hex_script!("76a914d0c59903c5bac2868760e90fd521a4665aa7652088ac"),
|
||||||
script_pubkey: hex_script!("76a914d0c59903c5bac2868760e90fd521a4665aa7652088ac"),
|
},
|
||||||
},
|
TxOut {
|
||||||
TxOut {
|
value: 100000000,
|
||||||
value: 100000000,
|
script_pubkey: hex_script!("a9143545e6e33b832c47050f24d3eeb93c9c03948bc787"),
|
||||||
script_pubkey: hex_script!("a9143545e6e33b832c47050f24d3eeb93c9c03948bc787"),
|
},
|
||||||
},
|
],
|
||||||
],
|
|
||||||
},
|
|
||||||
xpub: Default::default(),
|
|
||||||
version: 0,
|
|
||||||
proprietary: BTreeMap::new(),
|
|
||||||
unknown: BTreeMap::new(),
|
|
||||||
},
|
},
|
||||||
|
xpub: Default::default(),
|
||||||
|
version: 0,
|
||||||
|
proprietary: BTreeMap::new(),
|
||||||
|
unknown: BTreeMap::new(),
|
||||||
|
|
||||||
inputs: vec![Input {
|
inputs: vec![Input {
|
||||||
non_witness_utxo: Some(Transaction {
|
non_witness_utxo: Some(Transaction {
|
||||||
version: 1,
|
version: 1,
|
||||||
|
@ -690,7 +716,7 @@ mod tests {
|
||||||
assert_eq!(psbt.inputs.len(), 1);
|
assert_eq!(psbt.inputs.len(), 1);
|
||||||
assert_eq!(psbt.outputs.len(), 2);
|
assert_eq!(psbt.outputs.len(), 2);
|
||||||
|
|
||||||
let tx_input = &psbt.global.unsigned_tx.input[0];
|
let tx_input = &psbt.unsigned_tx.input[0];
|
||||||
let psbt_non_witness_utxo = (&psbt.inputs[0].non_witness_utxo).as_ref().unwrap();
|
let psbt_non_witness_utxo = (&psbt.inputs[0].non_witness_utxo).as_ref().unwrap();
|
||||||
|
|
||||||
assert_eq!(tx_input.previous_output.txid, psbt_non_witness_utxo.txid());
|
assert_eq!(tx_input.previous_output.txid, psbt_non_witness_utxo.txid());
|
||||||
|
@ -758,7 +784,7 @@ mod tests {
|
||||||
assert_eq!(psbt.inputs.len(), 1);
|
assert_eq!(psbt.inputs.len(), 1);
|
||||||
assert_eq!(psbt.outputs.len(), 1);
|
assert_eq!(psbt.outputs.len(), 1);
|
||||||
|
|
||||||
let tx = &psbt.global.unsigned_tx;
|
let tx = &psbt.unsigned_tx;
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
tx.txid(),
|
tx.txid(),
|
||||||
Txid::from_hex(
|
Txid::from_hex(
|
||||||
|
@ -793,37 +819,36 @@ mod tests {
|
||||||
|
|
||||||
// same vector as valid_vector_1 from BIPs with added
|
// same vector as valid_vector_1 from BIPs with added
|
||||||
let mut unserialized = PartiallySignedTransaction {
|
let mut unserialized = PartiallySignedTransaction {
|
||||||
global: Global {
|
unsigned_tx: Transaction {
|
||||||
unsigned_tx: Transaction {
|
version: 2,
|
||||||
version: 2,
|
lock_time: 1257139,
|
||||||
lock_time: 1257139,
|
input: vec![TxIn {
|
||||||
input: vec![TxIn {
|
previous_output: OutPoint {
|
||||||
previous_output: OutPoint {
|
txid: Txid::from_hex(
|
||||||
txid: Txid::from_hex(
|
"f61b1742ca13176464adb3cb66050c00787bb3a4eead37e985f2df1e37718126",
|
||||||
"f61b1742ca13176464adb3cb66050c00787bb3a4eead37e985f2df1e37718126",
|
).unwrap(),
|
||||||
).unwrap(),
|
vout: 0,
|
||||||
vout: 0,
|
},
|
||||||
},
|
script_sig: Script::new(),
|
||||||
script_sig: Script::new(),
|
sequence: 4294967294,
|
||||||
sequence: 4294967294,
|
witness: vec![],
|
||||||
witness: vec![],
|
}],
|
||||||
}],
|
output: vec![
|
||||||
output: vec![
|
TxOut {
|
||||||
TxOut {
|
value: 99999699,
|
||||||
value: 99999699,
|
script_pubkey: hex_script!("76a914d0c59903c5bac2868760e90fd521a4665aa7652088ac"),
|
||||||
script_pubkey: hex_script!("76a914d0c59903c5bac2868760e90fd521a4665aa7652088ac"),
|
},
|
||||||
},
|
TxOut {
|
||||||
TxOut {
|
value: 100000000,
|
||||||
value: 100000000,
|
script_pubkey: hex_script!("a9143545e6e33b832c47050f24d3eeb93c9c03948bc787"),
|
||||||
script_pubkey: hex_script!("a9143545e6e33b832c47050f24d3eeb93c9c03948bc787"),
|
},
|
||||||
},
|
],
|
||||||
],
|
|
||||||
},
|
|
||||||
version: 0,
|
|
||||||
xpub: Default::default(),
|
|
||||||
proprietary: Default::default(),
|
|
||||||
unknown: BTreeMap::new(),
|
|
||||||
},
|
},
|
||||||
|
version: 0,
|
||||||
|
xpub: Default::default(),
|
||||||
|
proprietary: Default::default(),
|
||||||
|
unknown: BTreeMap::new(),
|
||||||
|
|
||||||
inputs: vec![Input {
|
inputs: vec![Input {
|
||||||
non_witness_utxo: Some(Transaction {
|
non_witness_utxo: Some(Transaction {
|
||||||
version: 1,
|
version: 1,
|
||||||
|
@ -897,14 +922,14 @@ 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.global.proprietary.insert(ProprietaryKey {
|
psbt.proprietary.insert(ProprietaryKey {
|
||||||
prefix: b"test".to_vec(),
|
prefix: b"test".to_vec(),
|
||||||
subtype: 0u8,
|
subtype: 0u8,
|
||||||
key: b"test".to_vec(),
|
key: b"test".to_vec(),
|
||||||
}, b"test".to_vec());
|
}, b"test".to_vec());
|
||||||
assert!(!psbt.global.proprietary.is_empty());
|
assert!(!psbt.proprietary.is_empty());
|
||||||
let rtt : PartiallySignedTransaction = hex_psbt!(&serialize_hex(&psbt)).unwrap();
|
let rtt : PartiallySignedTransaction = hex_psbt!(&serialize_hex(&psbt)).unwrap();
|
||||||
assert!(!rtt.global.proprietary.is_empty());
|
assert!(!rtt.proprietary.is_empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue