use core::convert::TryFrom;
use core::fmt;
use bitcoin::bip32::KeySource;
use bitcoin::io::Read;
use bitcoin::key::{PublicKey, XOnlyPublicKey};
use bitcoin::taproot::{TapLeafHash, TapTree};
use bitcoin::{Amount, ScriptBuf, TxOut};
use crate::consts::{
PSBT_OUT_AMOUNT, PSBT_OUT_BIP32_DERIVATION, PSBT_OUT_PROPRIETARY, PSBT_OUT_REDEEM_SCRIPT,
PSBT_OUT_SCRIPT, PSBT_OUT_TAP_BIP32_DERIVATION, PSBT_OUT_TAP_INTERNAL_KEY, PSBT_OUT_TAP_TREE,
PSBT_OUT_WITNESS_SCRIPT,
};
#[cfg(feature = "silent-payments")]
use crate::consts::{PSBT_OUT_SP_V0_INFO, PSBT_OUT_SP_V0_LABEL};
use crate::error::write_err;
use crate::prelude::*;
use crate::serialize::{Deserialize, Serialize};
use crate::v2::map::Map;
use crate::{raw, serialize};
#[derive(Debug, Default, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Output {
pub amount: Amount,
pub script_pubkey: ScriptBuf,
pub redeem_script: Option<ScriptBuf>,
pub witness_script: Option<ScriptBuf>,
#[cfg_attr(feature = "serde", serde(with = "crate::serde_utils::btreemap_as_seq"))]
pub bip32_derivations: BTreeMap<PublicKey, KeySource>,
pub tap_internal_key: Option<XOnlyPublicKey>,
pub tap_tree: Option<TapTree>,
#[cfg_attr(feature = "serde", serde(with = "crate::serde_utils::btreemap_as_seq"))]
pub tap_key_origins: BTreeMap<XOnlyPublicKey, (Vec<TapLeafHash>, KeySource)>,
#[cfg(feature = "silent-payments")]
pub sp_v0_info: Option<Vec<u8>>,
#[cfg(feature = "silent-payments")]
pub sp_v0_label: Option<u32>,
#[cfg_attr(feature = "serde", serde(with = "crate::serde_utils::btreemap_as_seq_byte_values"))]
pub proprietaries: BTreeMap<raw::ProprietaryKey, Vec<u8>>,
#[cfg_attr(feature = "serde", serde(with = "crate::serde_utils::btreemap_as_seq_byte_values"))]
pub unknowns: BTreeMap<raw::Key, Vec<u8>>,
}
impl Output {
pub fn new(utxo: TxOut) -> Self {
Output {
amount: utxo.value,
script_pubkey: utxo.script_pubkey,
redeem_script: None,
witness_script: None,
bip32_derivations: BTreeMap::new(),
tap_internal_key: None,
tap_tree: None,
tap_key_origins: BTreeMap::new(),
#[cfg(feature = "silent-payments")]
sp_v0_info: None,
#[cfg(feature = "silent-payments")]
sp_v0_label: None,
proprietaries: BTreeMap::new(),
unknowns: BTreeMap::new(),
}
}
pub(crate) fn tx_out(&self) -> TxOut {
TxOut { value: self.amount, script_pubkey: self.script_pubkey.clone() }
}
pub(in crate::v2) fn decode<R: Read + ?Sized>(r: &mut R) -> Result<Self, DecodeError> {
let invalid = TxOut { value: Amount::ZERO, script_pubkey: ScriptBuf::default() };
let mut rv = Self::new(invalid);
loop {
match raw::Pair::decode(r) {
Ok(pair) => rv.insert_pair(pair)?,
Err(serialize::Error::NoMorePairs) => break,
Err(e) => return Err(DecodeError::DeserPair(e)),
}
}
if rv.amount == Amount::ZERO {
return Err(DecodeError::MissingValue);
}
#[cfg(not(feature = "silent-payments"))]
if rv.script_pubkey == ScriptBuf::default() {
return Err(DecodeError::MissingScriptPubkey);
}
#[cfg(feature = "silent-payments")]
if rv.script_pubkey == ScriptBuf::default() && rv.sp_v0_info.is_none() {
return Err(DecodeError::MissingScriptPubkey);
}
#[cfg(feature = "silent-payments")]
if rv.sp_v0_label.is_some() && rv.sp_v0_info.is_none() {
return Err(DecodeError::LabelWithoutInfo);
}
Ok(rv)
}
fn insert_pair(&mut self, pair: raw::Pair) -> Result<(), InsertPairError> {
let raw::Pair { key: raw_key, value: raw_value } = pair;
match raw_key.type_value {
PSBT_OUT_AMOUNT => {
if self.amount != Amount::ZERO {
return Err(InsertPairError::DuplicateKey(raw_key));
}
let amount: Amount = Deserialize::deserialize(&raw_value)?;
self.amount = amount;
}
PSBT_OUT_SCRIPT => {
if self.script_pubkey != ScriptBuf::default() {
return Err(InsertPairError::DuplicateKey(raw_key));
}
let script: ScriptBuf = Deserialize::deserialize(&raw_value)?;
self.script_pubkey = script;
}
PSBT_OUT_REDEEM_SCRIPT => {
v2_impl_psbt_insert_pair! {
self.redeem_script <= <raw_key: _>|<raw_value: ScriptBuf>
}
}
PSBT_OUT_WITNESS_SCRIPT => {
v2_impl_psbt_insert_pair! {
self.witness_script <= <raw_key: _>|<raw_value: ScriptBuf>
}
}
PSBT_OUT_BIP32_DERIVATION => {
v2_impl_psbt_insert_pair! {
self.bip32_derivations <= <raw_key: PublicKey>|<raw_value: KeySource>
}
}
PSBT_OUT_PROPRIETARY => {
let key = raw::ProprietaryKey::try_from(raw_key.clone())?;
match self.proprietaries.entry(key) {
btree_map::Entry::Vacant(empty_key) => {
empty_key.insert(raw_value);
}
btree_map::Entry::Occupied(_) =>
return Err(InsertPairError::DuplicateKey(raw_key)),
}
}
PSBT_OUT_TAP_INTERNAL_KEY => {
v2_impl_psbt_insert_pair! {
self.tap_internal_key <= <raw_key: _>|<raw_value: XOnlyPublicKey>
}
}
PSBT_OUT_TAP_TREE => {
v2_impl_psbt_insert_pair! {
self.tap_tree <= <raw_key: _>|<raw_value: TapTree>
}
}
PSBT_OUT_TAP_BIP32_DERIVATION => {
v2_impl_psbt_insert_pair! {
self.tap_key_origins <= <raw_key: XOnlyPublicKey>|< raw_value: (Vec<TapLeafHash>, KeySource)>
}
}
#[cfg(feature = "silent-payments")]
PSBT_OUT_SP_V0_INFO => {
if self.sp_v0_info.is_some() {
return Err(InsertPairError::DuplicateKey(raw_key));
}
if !raw_key.key.is_empty() {
return Err(InsertPairError::InvalidKeyDataNotEmpty(raw_key));
}
if raw_value.len() != 66 {
return Err(InsertPairError::ValueWrongLength(raw_value.len(), 66));
}
self.sp_v0_info = Some(raw_value);
}
#[cfg(feature = "silent-payments")]
PSBT_OUT_SP_V0_LABEL => {
if self.sp_v0_label.is_some() {
return Err(InsertPairError::DuplicateKey(raw_key));
}
if !raw_key.key.is_empty() {
return Err(InsertPairError::InvalidKeyDataNotEmpty(raw_key));
}
if raw_value.len() != 4 {
return Err(InsertPairError::ValueWrongLength(raw_value.len(), 4));
}
let label =
u32::from_le_bytes([raw_value[0], raw_value[1], raw_value[2], raw_value[3]]);
self.sp_v0_label = Some(label);
}
_ => match self.unknowns.entry(raw_key) {
btree_map::Entry::Vacant(empty_key) => {
empty_key.insert(raw_value);
}
btree_map::Entry::Occupied(k) =>
return Err(InsertPairError::DuplicateKey(k.key().clone())),
},
}
Ok(())
}
pub fn combine(&mut self, other: Self) -> Result<(), CombineError> {
if self.amount != other.amount {
return Err(CombineError::AmountMismatch { this: self.amount, that: other.amount });
}
if self.script_pubkey != other.script_pubkey {
return Err(CombineError::ScriptPubkeyMismatch {
this: self.script_pubkey.clone(),
that: other.script_pubkey,
});
}
v2_combine_option!(redeem_script, self, other);
v2_combine_option!(witness_script, self, other);
v2_combine_map!(bip32_derivations, self, other);
v2_combine_option!(tap_internal_key, self, other);
v2_combine_option!(tap_tree, self, other);
v2_combine_map!(tap_key_origins, self, other);
#[cfg(feature = "silent-payments")]
v2_combine_option!(sp_v0_info, self, other);
#[cfg(feature = "silent-payments")]
v2_combine_option!(sp_v0_label, self, other);
v2_combine_map!(proprietaries, self, other);
v2_combine_map!(unknowns, self, other);
Ok(())
}
}
impl Map for Output {
fn get_pairs(&self) -> Vec<raw::Pair> {
let mut rv: Vec<raw::Pair> = Default::default();
rv.push(raw::Pair {
key: raw::Key { type_value: PSBT_OUT_AMOUNT, key: vec![] },
value: self.amount.serialize(),
});
rv.push(raw::Pair {
key: raw::Key { type_value: PSBT_OUT_SCRIPT, key: vec![] },
value: self.script_pubkey.serialize(),
});
v2_impl_psbt_get_pair! {
rv.push(self.redeem_script, PSBT_OUT_REDEEM_SCRIPT)
}
v2_impl_psbt_get_pair! {
rv.push(self.witness_script, PSBT_OUT_WITNESS_SCRIPT)
}
v2_impl_psbt_get_pair! {
rv.push_map(self.bip32_derivations, PSBT_OUT_BIP32_DERIVATION)
}
v2_impl_psbt_get_pair! {
rv.push(self.tap_internal_key, PSBT_OUT_TAP_INTERNAL_KEY)
}
v2_impl_psbt_get_pair! {
rv.push(self.tap_tree, PSBT_OUT_TAP_TREE)
}
v2_impl_psbt_get_pair! {
rv.push_map(self.tap_key_origins, PSBT_OUT_TAP_BIP32_DERIVATION)
}
#[cfg(feature = "silent-payments")]
if let Some(sp_info) = &self.sp_v0_info {
rv.push(raw::Pair {
key: raw::Key { type_value: PSBT_OUT_SP_V0_INFO, key: vec![] },
value: sp_info.clone(),
});
}
#[cfg(feature = "silent-payments")]
if let Some(label) = self.sp_v0_label {
rv.push(raw::Pair {
key: raw::Key { type_value: PSBT_OUT_SP_V0_LABEL, key: vec![] },
value: label.to_le_bytes().to_vec(),
});
}
for (key, value) in self.proprietaries.iter() {
rv.push(raw::Pair { key: key.to_key(), value: value.clone() });
}
for (key, value) in self.unknowns.iter() {
rv.push(raw::Pair { key: key.clone(), value: value.clone() });
}
rv
}
}
pub struct OutputBuilder(Output);
impl OutputBuilder {
pub fn new(utxo: TxOut) -> Self { OutputBuilder(Output::new(utxo)) }
pub fn build(self) -> Output { self.0 }
}
#[derive(Debug)]
#[non_exhaustive]
pub enum DecodeError {
InsertPair(InsertPairError),
DeserPair(serialize::Error),
MissingValue,
MissingScriptPubkey,
LabelWithoutInfo,
}
impl fmt::Display for DecodeError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use DecodeError::*;
match *self {
InsertPair(ref e) => write_err!(f, "error inserting a pair"; e),
DeserPair(ref e) => write_err!(f, "error deserializing a pair"; e),
MissingValue => write!(f, "encoded output is missing a value"),
MissingScriptPubkey => write!(f, "encoded output is missing a script pubkey"),
LabelWithoutInfo => write!(f, "output has a sp_v0_label without a sp_v0_info"),
}
}
}
#[cfg(feature = "std")]
impl std::error::Error for DecodeError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
use DecodeError::*;
match *self {
InsertPair(ref e) => Some(e),
DeserPair(ref e) => Some(e),
MissingValue | MissingScriptPubkey | LabelWithoutInfo => None,
}
}
}
impl From<InsertPairError> for DecodeError {
fn from(e: InsertPairError) -> Self { Self::InsertPair(e) }
}
#[derive(Debug)]
pub enum InsertPairError {
DuplicateKey(raw::Key),
Deser(serialize::Error),
InvalidKeyDataEmpty(raw::Key),
InvalidKeyDataNotEmpty(raw::Key),
ValueWrongLength(usize, usize),
}
impl fmt::Display for InsertPairError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use InsertPairError::*;
match *self {
DuplicateKey(ref key) => write!(f, "duplicate key: {}", key),
Deser(ref e) => write_err!(f, "error deserializing raw value"; e),
InvalidKeyDataEmpty(ref key) => write!(f, "key should contain data: {}", key),
InvalidKeyDataNotEmpty(ref key) => write!(f, "key should not contain data: {}", key),
ValueWrongLength(got, expected) => {
write!(f, "value wrong length (got: {}, expected: {})", got, expected)
}
}
}
}
#[cfg(feature = "std")]
impl std::error::Error for InsertPairError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
use InsertPairError::*;
match *self {
Deser(ref e) => Some(e),
DuplicateKey(_)
| InvalidKeyDataEmpty(_)
| InvalidKeyDataNotEmpty(_)
| ValueWrongLength(..) => None,
}
}
}
impl From<serialize::Error> for InsertPairError {
fn from(e: serialize::Error) -> Self { Self::Deser(e) }
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub enum CombineError {
AmountMismatch {
this: Amount,
that: Amount,
},
ScriptPubkeyMismatch {
this: ScriptBuf,
that: ScriptBuf,
},
}
impl fmt::Display for CombineError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use CombineError::*;
match *self {
AmountMismatch { ref this, ref that } => {
write!(f, "combine two PSBTs with different amounts: {} {}", this, that)
}
ScriptPubkeyMismatch { ref this, ref that } => {
write!(f, "combine two PSBTs with different script_pubkeys: {:x} {:x}", this, that)
}
}
}
}
#[cfg(feature = "std")]
impl std::error::Error for CombineError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
use CombineError::*;
match *self {
AmountMismatch { .. } | ScriptPubkeyMismatch { .. } => None,
}
}
}
#[cfg(test)]
#[cfg(feature = "std")]
mod tests {
use bitcoin::io::Cursor;
use super::*;
fn tx_out() -> TxOut {
let script = ScriptBuf::from_hex("76a914162c5ea71c0b23f5b9022ef047c4a86470a5b07088ac")
.expect("failed to parse script form hex");
let value = Amount::from_sat(123_456_789);
TxOut { value, script_pubkey: script }
}
#[test]
fn serialize_roundtrip() {
let output = Output::new(tx_out());
let ser = output.serialize_map();
let mut d = Cursor::new(ser);
let decoded = Output::decode(&mut d).expect("failed to decode");
assert_eq!(decoded, output);
}
}