use std::cmp;
use std::collections::btree_map::Entry;
use std::{
collections::BTreeMap,
io::{self, Cursor, Read},
};
use crate::encode::{self, VarInt};
use crate::encode::Decodable;
use crate::endian::u32_to_array_le;
use crate::pset::{self, map::Map, raw, Error};
use crate::LockTime;
use bitcoin::bip32::{ChildNumber, DerivationPath, Xpub, Fingerprint, KeySource};
use secp256k1_zkp::Tweak;
const PSET_GLOBAL_UNSIGNED_TX: u8 = 0x00;
const PSET_GLOBAL_XPUB: u8 = 0x01;
const PSET_GLOBAL_TX_VERSION: u8 = 0x02;
const PSET_GLOBAL_FALLBACK_LOCKTIME: u8 = 0x03;
const PSET_GLOBAL_INPUT_COUNT: u8 = 0x04;
const PSET_GLOBAL_OUTPUT_COUNT: u8 = 0x05;
const PSET_GLOBAL_TX_MODIFIABLE: u8 = 0x06;
const PSET_GLOBAL_VERSION: u8 = 0xFB;
const PSET_GLOBAL_PROPRIETARY: u8 = 0xFC;
const PSBT_ELEMENTS_GLOBAL_SCALAR: u8 = 0x00;
const PSBT_ELEMENTS_GLOBAL_TX_MODIFIABLE: u8 = 0x01;
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(crate = "actual_serde"))]
pub struct TxData {
pub version: u32,
pub fallback_locktime: Option<LockTime>,
pub(crate) input_count: usize,
pub(crate) output_count: usize,
pub tx_modifiable: Option<u8>,
}
impl Default for TxData {
fn default() -> Self {
Self {
version: 2,
fallback_locktime: None,
input_count: 0,
output_count: 0,
tx_modifiable: None,
}
}
}
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(crate = "actual_serde"))]
pub struct Global {
#[cfg_attr(feature = "serde", serde(flatten))]
pub tx_data: TxData,
pub version: u32,
pub xpub: BTreeMap<Xpub, KeySource>,
pub scalars: Vec<Tweak>,
pub elements_tx_modifiable_flag: Option<u8>,
#[cfg_attr(
feature = "serde",
serde(with = "crate::serde_utils::btreemap_as_seq_byte_values")
)]
pub proprietary: BTreeMap<raw::ProprietaryKey, Vec<u8>>,
#[cfg_attr(
feature = "serde",
serde(with = "crate::serde_utils::btreemap_as_seq_byte_values")
)]
pub unknown: BTreeMap<raw::Key, Vec<u8>>,
}
impl Default for Global {
fn default() -> Self {
Self {
tx_data: TxData::default(),
version: 2,
xpub: BTreeMap::new(),
scalars: Vec::new(),
elements_tx_modifiable_flag: None,
proprietary: BTreeMap::new(),
unknown: BTreeMap::new(),
}
}
}
impl Global {
pub fn n_inputs(&self) -> usize {
self.tx_data.input_count
}
pub fn n_outputs(&self) -> usize {
self.tx_data.output_count
}
}
impl Map for Global {
fn insert_pair(&mut self, pair: raw::Pair) -> Result<(), encode::Error> {
let raw::Pair {
key: raw_key,
value: raw_value,
} = pair;
match raw_key.type_value {
PSET_GLOBAL_UNSIGNED_TX => return Err(Error::ExpiredPsbtv0Field)?,
PSET_GLOBAL_VERSION
| PSET_GLOBAL_FALLBACK_LOCKTIME
| PSET_GLOBAL_INPUT_COUNT
| PSET_GLOBAL_OUTPUT_COUNT
| PSET_GLOBAL_TX_MODIFIABLE
| PSET_GLOBAL_TX_VERSION => return Err(Error::DuplicateKey(raw_key).into()),
PSET_GLOBAL_PROPRIETARY => {
let prop_key = raw::ProprietaryKey::from_key(&raw_key)?;
if prop_key.is_pset_key() && prop_key.subtype == PSBT_ELEMENTS_GLOBAL_SCALAR {
if raw_value.is_empty() && prop_key.key.len() == 32 {
let scalar = Tweak::from_slice(&prop_key.key)?;
if self.scalars.contains(&scalar) {
return Err(Error::DuplicateKey(raw_key).into());
}
self.scalars.push(scalar);
} else {
return Err(Error::InvalidKey(raw_key))?;
}
} else if prop_key.is_pset_key()
&& prop_key.subtype == PSBT_ELEMENTS_GLOBAL_TX_MODIFIABLE
{
if prop_key.key.is_empty() && raw_value.len() == 1 {
self.elements_tx_modifiable_flag = Some(raw_value[0]);
} else {
return Err(Error::InvalidKey(raw_key))?;
}
} else {
match self.proprietary.entry(prop_key) {
Entry::Vacant(empty_key) => {
empty_key.insert(raw_value);
}
Entry::Occupied(_) => return Err(Error::DuplicateKey(raw_key).into()),
}
}
}
_ => match self.unknown.entry(raw_key) {
Entry::Vacant(empty_key) => {
empty_key.insert(raw_value);
}
Entry::Occupied(k) => return Err(Error::DuplicateKey(k.key().clone()).into()),
},
}
Ok(())
}
fn get_pairs(&self) -> Result<Vec<raw::Pair>, encode::Error> {
let mut rv: Vec<raw::Pair> = Vec::default();
let TxData {
version,
fallback_locktime,
input_count,
output_count,
tx_modifiable,
} = self.tx_data;
let input_count_vint = VarInt(input_count as u64);
let output_count_vint = VarInt(output_count as u64);
impl_pset_get_pair! {
rv.push_mandatory(version as <PSET_GLOBAL_TX_VERSION, _>)
}
impl_pset_get_pair! {
rv.push(fallback_locktime as <PSET_GLOBAL_FALLBACK_LOCKTIME, _>)
}
impl_pset_get_pair! {
rv.push_mandatory(input_count_vint as <PSET_GLOBAL_INPUT_COUNT, _>)
}
impl_pset_get_pair! {
rv.push_mandatory(output_count_vint as <PSET_GLOBAL_OUTPUT_COUNT, _>)
}
impl_pset_get_pair! {
rv.push(tx_modifiable as <PSET_GLOBAL_TX_MODIFIABLE, _>)
}
for (xpub, (fingerprint, derivation)) in &self.xpub {
rv.push(raw::Pair {
key: raw::Key {
type_value: PSET_GLOBAL_XPUB,
key: xpub.encode().to_vec(),
},
value: {
let mut ret = Vec::with_capacity(4 + derivation.len() * 4);
ret.extend(fingerprint.as_bytes());
derivation
.into_iter()
.for_each(|n| ret.extend(&u32_to_array_le((*n).into())));
ret
},
});
}
let ver = self.version; impl_pset_get_pair!(
rv.push_mandatory(ver as <PSET_GLOBAL_VERSION, _>)
);
for scalar in &self.scalars {
let key = raw::ProprietaryKey::from_pset_pair(
PSBT_ELEMENTS_GLOBAL_SCALAR,
scalar.as_ref().to_vec(),
);
rv.push(raw::Pair {
key: key.to_key(),
value: vec![], });
}
impl_pset_get_pair! {
rv.push_prop(self.elements_tx_modifiable_flag as <PSBT_ELEMENTS_GLOBAL_TX_MODIFIABLE, _>)
}
for (key, value) in &self.proprietary {
rv.push(raw::Pair {
key: key.to_key(),
value: value.clone(),
});
}
for (key, value) in &self.unknown {
rv.push(raw::Pair {
key: key.clone(),
value: value.clone(),
});
}
Ok(rv)
}
#[allow(clippy::if_same_then_else)] fn merge(&mut self, other: Self) -> Result<(), pset::Error> {
self.tx_data.tx_modifiable = Some(
self.tx_data.tx_modifiable.unwrap_or(0) | other.tx_data.tx_modifiable.unwrap_or(0),
);
self.version = cmp::max(self.version, other.version);
for (xpub, (fingerprint1, derivation1)) in other.xpub {
match self.xpub.entry(xpub) {
Entry::Vacant(entry) => {
entry.insert((fingerprint1, derivation1));
}
Entry::Occupied(mut entry) => {
let (fingerprint2, derivation2) = entry.get().clone();
if derivation1 == derivation2 && fingerprint1 == fingerprint2 {
continue;
} else if derivation1.len() < derivation2.len()
&& derivation1[..] == derivation2[derivation2.len() - derivation1.len()..]
{
continue;
} else if derivation2[..]
== derivation1[derivation1.len() - derivation2.len()..]
{
entry.insert((fingerprint1, derivation1));
continue;
}
return Err(pset::Error::MergeConflict(
format!("global xpub {} has inconsistent key sources", xpub)
));
}
}
}
self.scalars.extend(other.scalars);
self.scalars.sort();
self.scalars.dedup();
merge!(elements_tx_modifiable_flag, self, other);
self.proprietary.extend(other.proprietary);
self.unknown.extend(other.unknown);
Ok(())
}
}
impl_psetmap_consensus_encoding!(Global);
impl Decodable for Global {
fn consensus_decode<D: io::Read>(mut d: D) -> Result<Self, encode::Error> {
let mut version: Option<u32> = None;
let mut unknowns: BTreeMap<raw::Key, Vec<u8>> = BTreeMap::new();
let mut xpub_map: BTreeMap<Xpub, (Fingerprint, DerivationPath)> =
BTreeMap::new();
let mut proprietary = BTreeMap::new();
let mut scalars = Vec::new();
let mut tx_version: Option<u32> = None;
let mut input_count: Option<VarInt> = None;
let mut output_count: Option<VarInt> = None;
let mut fallback_locktime: Option<LockTime> = None;
let mut tx_modifiable: Option<u8> = None;
let mut elements_tx_modifiable_flag: Option<u8> = None;
loop {
match raw::Pair::consensus_decode(&mut d) {
Ok(pair) => {
let raw::Pair {
key: raw_key,
value: raw_value,
} = pair;
match raw_key.type_value {
PSET_GLOBAL_TX_VERSION => {
impl_pset_insert_pair! {
tx_version <= <raw_key: _>|<raw_value: u32>
}
}
PSET_GLOBAL_FALLBACK_LOCKTIME => {
impl_pset_insert_pair! {
fallback_locktime <= <raw_key: _>|<raw_value: LockTime>
}
}
PSET_GLOBAL_INPUT_COUNT => {
impl_pset_insert_pair! {
input_count <= <raw_key: _>|<raw_value: VarInt>
}
}
PSET_GLOBAL_OUTPUT_COUNT => {
impl_pset_insert_pair! {
output_count <= <raw_key: _>|<raw_value: VarInt>
}
}
PSET_GLOBAL_TX_MODIFIABLE => {
impl_pset_insert_pair! {
tx_modifiable <= <raw_key: _>|<raw_value: u8>
}
}
PSET_GLOBAL_XPUB => {
if raw_key.key.is_empty() {
return Err(encode::Error::ParseFailed(
"Xpub global key must contain serialized Xpub data",
));
}
let xpub = Xpub::decode(&raw_key.key)
.map_err(|_| encode::Error::ParseFailed(
"Can't deserialize Xpub from global XPUB key data"
))?;
if raw_value.is_empty() || raw_value.len() % 4 != 0 {
return Err(encode::Error::ParseFailed(
"Incorrect length of global xpub derivation data",
));
}
let child_count = raw_value.len() / 4 - 1;
let mut decoder = Cursor::new(raw_value);
let mut fingerprint = [0u8; 4];
decoder.read_exact(&mut fingerprint[..])?;
let mut path = Vec::<ChildNumber>::with_capacity(child_count);
while let Ok(index) = u32::consensus_decode(&mut decoder) {
path.push(ChildNumber::from(index));
}
let derivation = DerivationPath::from(path);
if xpub_map
.insert(xpub, (Fingerprint::from(fingerprint), derivation))
.is_some()
{
return Err(encode::Error::ParseFailed(
"Repeated global xpub key",
));
}
}
PSET_GLOBAL_VERSION => {
impl_pset_insert_pair! {
version <= <raw_key: _>|<raw_value: u32>
}
}
PSET_GLOBAL_PROPRIETARY => {
let prop_key = raw::ProprietaryKey::from_key(&raw_key)?;
if prop_key.is_pset_key()
&& prop_key.subtype == PSBT_ELEMENTS_GLOBAL_SCALAR
{
if raw_value.is_empty() && prop_key.key.len() == 32 {
let scalar = Tweak::from_slice(&prop_key.key)?;
if scalars.contains(&scalar) {
return Err(Error::DuplicateKey(raw_key).into());
}
scalars.push(scalar);
} else {
return Err(Error::InvalidKey(raw_key))?;
}
} else if prop_key.is_pset_key()
&& prop_key.subtype == PSBT_ELEMENTS_GLOBAL_TX_MODIFIABLE
{
if prop_key.key.is_empty() && raw_value.len() == 1 {
elements_tx_modifiable_flag = Some(raw_value[0]);
} else {
return Err(Error::InvalidKey(raw_key))?;
}
} else {
match proprietary.entry(prop_key) {
Entry::Vacant(empty_key) => {
empty_key.insert(raw_value);
}
Entry::Occupied(_) => {
return Err(Error::DuplicateKey(raw_key).into())
}
}
}
}
_ => match unknowns.entry(raw_key) {
Entry::Vacant(empty_key) => {
empty_key.insert(raw_value);
}
Entry::Occupied(k) => {
return Err(Error::DuplicateKey(k.key().clone()).into())
}
},
}
}
Err(crate::encode::Error::PsetError(crate::pset::Error::NoMorePairs)) => break,
Err(e) => return Err(e),
}
}
let version = version.ok_or(Error::IncorrectPsetVersion)?;
if version != 2 {
return Err(Error::IncorrectPsetVersion)?;
}
let tx_version = tx_version.ok_or(Error::MissingTxVersion)?;
let input_count = input_count.ok_or(Error::MissingInputCount)?.0 as usize;
let output_count = output_count.ok_or(Error::MissingOutputCount)?.0 as usize;
let global = Global {
tx_data: TxData {
version: tx_version,
fallback_locktime,
input_count,
output_count,
tx_modifiable,
},
version,
xpub: xpub_map,
proprietary,
unknown: unknowns,
scalars,
elements_tx_modifiable_flag,
};
Ok(global)
}
}