Skip to content

Commit

Permalink
fixup! SFT-UNKN: Add foundation-psbt crate.
Browse files Browse the repository at this point in the history
  • Loading branch information
jeandudey committed Nov 13, 2023
1 parent 4fb7571 commit 425fcee
Show file tree
Hide file tree
Showing 9 changed files with 264 additions and 194 deletions.
73 changes: 40 additions & 33 deletions psbt/src/parser/global.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,31 +23,34 @@ use crate::parser::keypair::key_pair;
use crate::parser::transaction::transaction;
use crate::transaction::Transaction;

#[rustfmt::skip]
pub fn global_map<'a, F, Error>(mut xpub_event: F) -> impl FnMut(&'a [u8]) -> IResult<&'a [u8], GlobalMap<'a>, Error>
pub fn global_map<'a, F, Error>(
mut xpub_event: F,
) -> impl FnMut(&'a [u8]) -> IResult<&'a [u8], GlobalMap<'a>, Error>
where
F: FnMut(ExtendedPublicKey, KeySource<&'a [u8]>),
Error: ContextError<&'a [u8]>,
Error: ParseError<&'a [u8]>,
Error: FromExternalError<&'a [u8], secp256k1::Error>,
Error: ContextError<&'a [u8]>
+ ParseError<&'a [u8]>
+ FromExternalError<&'a [u8], secp256k1::Error>
+ FromExternalError<&'a [u8], bitcoin_hashes::FromSliceError>,
{
let keypairs = fold_many0(
global_key_pair(),
GlobalMap::default,
move |mut map, key_pair| {
match key_pair {
KeyPair::UnsignedTx(v) => map.transaction = Some(v),
KeyPair::UnsignedTx(v) => map.transaction = Some(v),
KeyPair::Xpub { key, source } => xpub_event(key, source),
KeyPair::TxVersion(v) => map.transaction_version = Some(v),
KeyPair::FallbackLocktime(v) => map.fallback_locktime = Some(v),
KeyPair::InputCount(v) => map.input_count = Some(v),
KeyPair::OutputCount(v) => map.output_count = Some(v),
KeyPair::TxModifiable(v) => map.tx_modifiable = Some(v),
KeyPair::Version(v) => map.version = v,
KeyPair::TxVersion(v) => map.transaction_version = Some(v),
KeyPair::FallbackLocktime(v) => map.fallback_locktime = Some(v),
KeyPair::InputCount(v) => map.input_count = Some(v),
KeyPair::OutputCount(v) => map.output_count = Some(v),
KeyPair::TxModifiable(v) => map.tx_modifiable = Some(v),
KeyPair::Version(v) => map.version = v,
};

map
});
},
);

verify(
terminated(keypairs, context("separator", tag(b"\x00"))),
Expand All @@ -62,39 +65,43 @@ where
1 => true,
// Make sure that these fields exist and make sure that version 0 fields
// are excluded.
2 => map.transaction.is_none() && map.input_count.is_some() && map.output_count.is_some(),
2 => {
map.transaction.is_none()
&& map.input_count.is_some()
&& map.output_count.is_some()
}
// Don't verify what we don't know.
_ => true,
}
}
},
)
}

#[rustfmt::skip]
fn global_key_pair<'a, Error>() -> impl FnMut(&'a [u8]) -> IResult<&'a [u8], KeyPair, Error>
where
Error: ParseError<&'a [u8]>,
Error: FromExternalError<&'a [u8], secp256k1::Error>,
Error: ParseError<&'a [u8]>
+ FromExternalError<&'a [u8], secp256k1::Error>
+ FromExternalError<&'a [u8], bitcoin_hashes::FromSliceError>,
{
let unsigned_tx = key_pair(0x00, eof, transaction());
let xpub = key_pair(0x01, extended_public_key, key_source);
let xpub = verify(xpub, |(k, v)| usize::from(k.depth) == v.path.len());
let tx_version = key_pair(0x02, eof, le_u32);
let unsigned_tx = key_pair(0x00, eof, transaction());
let xpub = key_pair(0x01, extended_public_key, key_source);
let xpub = verify(xpub, |(k, v)| usize::from(k.depth) == v.path.len());
let tx_version = key_pair(0x02, eof, le_u32);
let fallback_locktime = key_pair(0x03, eof, le_u32);
let input_count = key_pair(0x04, eof, compact_size);
let output_count = key_pair(0x05, eof, compact_size);
let tx_modifiable = key_pair(0x06, eof, tx_modifiable);
let version = key_pair(0xFB, eof, le_u32);
let input_count = key_pair(0x04, eof, compact_size);
let output_count = key_pair(0x05, eof, compact_size);
let tx_modifiable = key_pair(0x06, eof, tx_modifiable);
let version = key_pair(0xFB, eof, le_u32);

alt((
map(unsigned_tx, |(_, v)| KeyPair::UnsignedTx(v)),
map(xpub, |(k, v)| KeyPair::Xpub { key: k, source: v }),
map(tx_version, |(_, v)| KeyPair::TxVersion(v)),
map(unsigned_tx, |(_, v)| KeyPair::UnsignedTx(v)),
map(xpub, |(k, v)| KeyPair::Xpub { key: k, source: v }),
map(tx_version, |(_, v)| KeyPair::TxVersion(v)),
map(fallback_locktime, |(_, v)| KeyPair::FallbackLocktime(v)),
map(input_count, |(_, v)| KeyPair::InputCount(v)),
map(output_count, |(_, v)| KeyPair::OutputCount(v)),
map(tx_modifiable, |(_, v)| KeyPair::TxModifiable(v)),
map(version, |(_, v)| KeyPair::Version(v)),
map(input_count, |(_, v)| KeyPair::InputCount(v)),
map(output_count, |(_, v)| KeyPair::OutputCount(v)),
map(tx_modifiable, |(_, v)| KeyPair::TxModifiable(v)),
map(version, |(_, v)| KeyPair::Version(v)),
))
}

Expand Down
82 changes: 55 additions & 27 deletions psbt/src/parser/hash.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,44 @@
use crate::{hash_types, taproot};
// SPDX-FileCopyrightText: © 2023 Foundation Devices, Inc. <[email protected]>
// SPDX-License-Identifier: GPL-3.0-or-later

use nom::{
bytes::complete::take,
combinator::map_res,
error::{FromExternalError, ParseError},
IResult,
error::ParseError, multi::fill, number::complete::u8, IResult, InputIter, InputLength, Slice,
};

use bitcoin_hashes::Hash;

use crate::{hash_types, taproot};

/// Parses a [`bitcoin_hashes::Hash`].
pub fn hash<'a, Hash, Error>(i: &'a [u8]) -> IResult<&'a [u8], Hash, Error>
///
/// # Why N instead of [`bitcoin_hashes::Hash::LEN`].
///
/// NOTE(jeandudey): Using `N` because on Rust 1.70.0 it somehow can't use the
/// associated constant `LEN` of `bitcoin_hashes::Hash`` trait and shows the
/// following error:
///
/// ```text
/// error: generic parameters may not be used in const operations
///
/// let mut buf = [0; Hash::LEN];
/// ```
///
/// Maybe a bug, perhaps report this upstream, I think rustc is broken in
/// this edge case.
///
/// Ideally one would use fill with the aforementioned `buf` variable to
/// avoid this verbose loop.
pub fn hash<Input, Hash, Error, const N: usize>(i: Input) -> IResult<Input, Hash, Error>
where
Input:
Clone + PartialEq + InputLength + InputIter<Item = u8> + Slice<core::ops::RangeFrom<usize>>,
Hash: bitcoin_hashes::Hash,
Error: ParseError<&'a [u8]>,
Error: FromExternalError<&'a [u8], bitcoin_hashes::FromSliceError>,
Error: ParseError<Input>,
{
map_res(take(Hash::LEN), Hash::from_slice)(i)
let mut buf = [0; N];
let (next_i, ()) = fill(u8, &mut buf)(i)?;
let hash = Hash::from_slice(&buf).expect("should have the correct length");
Ok((next_i, hash))
}

/// Define an alias for a hash parser.
Expand All @@ -25,14 +50,14 @@ where
macro_rules! define_hash_aliases {
($($name:ident),* $(,)?) => {
$(
pub fn $name<'a, Error>(
i: &'a [u8],
) -> IResult<&'a [u8], ::bitcoin_hashes::$name::Hash, Error>
pub fn $name<Input, Error>(
i: Input,
) -> IResult<Input, ::bitcoin_hashes::$name::Hash, Error>
where
Error: ParseError<&'a [u8]>,
Error: FromExternalError<&'a [u8], bitcoin_hashes::FromSliceError>,
Input: Clone + PartialEq + InputLength + InputIter<Item = u8> + Slice<core::ops::RangeFrom<usize>>,
Error: ParseError<Input>,
{
hash::<'a, ::bitcoin_hashes::$name::Hash, Error>(i)
hash::<_, ::bitcoin_hashes::$name::Hash, Error, { ::bitcoin_hashes::$name::Hash::LEN }>(i)
}
)*
};
Expand All @@ -45,26 +70,29 @@ define_hash_aliases! {
hash160,
}

pub fn taproot_leaf_hash<'a, Error>(i: &'a [u8]) -> IResult<&'a [u8], taproot::LeafHash, Error>
pub fn taproot_leaf_hash<Input, Error>(i: Input) -> IResult<Input, taproot::LeafHash, Error>
where
Error: ParseError<&'a [u8]>,
Error: FromExternalError<&'a [u8], bitcoin_hashes::FromSliceError>,
Input:
Clone + PartialEq + InputLength + InputIter<Item = u8> + Slice<core::ops::RangeFrom<usize>>,
Error: ParseError<Input>,
{
hash::<'a, taproot::LeafHash, Error>(i)
hash::<_, taproot::LeafHash, Error, { taproot::LeafHash::LEN }>(i)
}

pub fn taproot_node_hash<'a, Error>(i: &'a [u8]) -> IResult<&'a [u8], taproot::TapNodeHash, Error>
pub fn taproot_node_hash<Input, Error>(i: Input) -> IResult<Input, taproot::TapNodeHash, Error>
where
Error: ParseError<&'a [u8]>,
Error: FromExternalError<&'a [u8], bitcoin_hashes::FromSliceError>,
Input:
Clone + PartialEq + InputLength + InputIter<Item = u8> + Slice<core::ops::RangeFrom<usize>>,
Error: ParseError<Input>,
{
hash::<'a, taproot::TapNodeHash, Error>(i)
hash::<_, taproot::TapNodeHash, Error, { taproot::TapNodeHash::LEN }>(i)
}

pub fn txid<'a, Error>(i: &'a [u8]) -> IResult<&'a [u8], hash_types::Txid, Error>
pub fn txid<Input, Error>(i: Input) -> IResult<Input, hash_types::Txid, Error>
where
Error: ParseError<&'a [u8]>,
Error: FromExternalError<&'a [u8], bitcoin_hashes::FromSliceError>,
Input:
Clone + PartialEq + InputLength + InputIter<Item = u8> + Slice<core::ops::RangeFrom<usize>>,
Error: ParseError<Input>,
{
hash::<'a, hash_types::Txid, Error>(i)
hash::<_, hash_types::Txid, Error, { hash_types::Txid::LEN }>(i)
}
Loading

0 comments on commit 425fcee

Please sign in to comment.