Added Hash Preimages to psbt

Added hash preimages to psbt as per updated bip174
This commit is contained in:
sanket1729 2020-08-31 15:06:21 -05:00
parent e8bcde4d38
commit c5204c001f
4 changed files with 147 additions and 4 deletions

View File

@ -18,6 +18,16 @@ use std::fmt;
use blockdata::transaction::Transaction;
use util::psbt::raw;
use hashes::{self, sha256, hash160, sha256d, ripemd160};
/// Support hash-preimages in psbt
#[derive(Debug)]
pub enum PsbtHash{
Ripemd160(ripemd160::Hash),
Sha256(sha256::Hash),
Hash256(sha256d::Hash),
Hash160(hash160::Hash),
}
/// Ways that a Partially Signed Transaction might fail.
#[derive(Debug)]
pub enum Error {
@ -48,6 +58,15 @@ pub enum Error {
},
/// Unable to parse as a standard SigHash type.
NonStandardSigHashType(u32),
/// Parsing errors from bitcoin_hashes
HashParseError(hashes::Error),
/// The pre-image must hash to the correponding psbt hash
InvalidPreimageHashPair{
/// Pre-image
preimage: Vec<u8>,
/// Hash value
hash: PsbtHash,
}
}
impl fmt::Display for Error {
@ -65,6 +84,11 @@ impl fmt::Display for Error {
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::HashParseError(e) => write!(f, "Hash Parse Error: {}", e),
Error::InvalidPreimageHashPair{ref preimage, ref hash} => {
// directly using debug forms of psbthash enums
write!(f, "Preimage {:?} does not match hash {:?}", preimage, hash )
}
}
}
}
@ -75,3 +99,10 @@ impl error::Error for Error {
"description() is deprecated; use Display"
}
}
#[doc(hidden)]
impl From<hashes::Error> for Error {
fn from(e: hashes::Error) -> Error {
Error::HashParseError(e)
}
}

View File

@ -130,6 +130,30 @@ macro_rules! impl_psbt_insert_pair {
};
}
#[cfg_attr(rustfmt, rustfmt_skip)]
macro_rules! impl_psbt_insert_hash_pair {
($slf:ident.$keyed_name:ident <= <$raw_key:ident: $keyed_key_type:ty>|<$raw_value:ident: $keyed_value_type:ty>; $err_name: ident ) => {
if !$raw_key.key.is_empty() {
let key_val: $keyed_key_type = $crate::util::psbt::serialize::Deserialize::deserialize(&$raw_key.key)?;
match $slf.$keyed_name.entry(key_val) {
::std::collections::btree_map::Entry::Vacant(empty_key) => {
let val: $keyed_value_type = $crate::util::psbt::serialize::Deserialize::deserialize(&$raw_value)?;
if <$keyed_key_type>::hash(&val) != key_val{
return Err($crate::util::psbt::Error::InvalidPreimageHashPair{
preimage: val,
hash: $crate::util::psbt::error::PsbtHash::$err_name(key_val),
}.into());
}
empty_key.insert(val);
}
::std::collections::btree_map::Entry::Occupied(_) => return Err($crate::util::psbt::Error::DuplicateKey($raw_key).into()),
}
} else {
return Err($crate::util::psbt::Error::InvalidKey($raw_key).into());
}
};
}
#[cfg_attr(rustfmt, rustfmt_skip)]
macro_rules! impl_psbt_get_pair {
($rv:ident.push($slf:ident.$unkeyed_name:ident as <$unkeyed_typeval:expr, _>|<$unkeyed_value_type:ty>)) => {
@ -155,3 +179,33 @@ macro_rules! impl_psbt_get_pair {
}
};
}
// macros for serde of hashes
macro_rules! impl_psbt_hash_de_serialize {
($thing:ty) => {
impl_psbt_hash_serialize!($thing);
impl_psbt_hash_deserialize!($thing);
};
}
macro_rules! impl_psbt_hash_deserialize {
($thing:ty) => {
impl $crate::util::psbt::serialize::Deserialize for $thing {
fn deserialize(bytes: &[u8]) -> Result<Self, $crate::consensus::encode::Error> {
<$thing>::from_slice(&bytes[..]).map_err(|e| {
$crate::util::psbt::Error::from(e).into()
})
}
}
};
}
macro_rules! impl_psbt_hash_serialize {
($thing:ty) => {
impl $crate::util::psbt::serialize::Serialize for $thing {
fn serialize(&self) -> Vec<u8> {
self.into_inner().to_vec()
}
}
};
}

View File

@ -17,13 +17,13 @@ use std::collections::BTreeMap;
use blockdata::script::Script;
use blockdata::transaction::{SigHashType, Transaction, TxOut};
use consensus::encode;
use hashes::{Hash, hash160, ripemd160, sha256, sha256d};
use util::bip32::{DerivationPath, Fingerprint};
use util::key::PublicKey;
use util::psbt;
use util::psbt::map::Map;
use util::psbt::raw;
use util::psbt::Error;
/// A key-value map for an input of the corresponding index in the unsigned
/// transaction.
#[derive(Clone, Default, Debug, PartialEq)]
@ -55,6 +55,15 @@ pub struct Input {
/// The finalized, fully-constructed scriptWitness with signatures and any
/// other scripts necessary for this input to pass validation.
pub final_script_witness: Option<Vec<Vec<u8>>>,
/// TODO: Proof of reserves commitment
/// RIPEMD hash to preimage map
pub ripemd_preimages: BTreeMap<ripemd160::Hash, Vec<u8>>,
/// SHA256 hash to preimage map
pub sha256_preimages: BTreeMap<sha256::Hash, Vec<u8>>,
/// HSAH160 hash to preimage map
pub hash160_preimages: BTreeMap<hash160::Hash, Vec<u8>>,
/// HAS256 hash to preimage map
pub hash256_preimages: BTreeMap<sha256d::Hash, Vec<u8>>,
/// Unknown key-value pairs for this input.
pub unknown: BTreeMap<raw::Key, Vec<u8>>,
}
@ -112,10 +121,34 @@ impl Map for Input {
self.hd_keypaths <= <raw_key: PublicKey>|<raw_value: (Fingerprint, DerivationPath)>
}
}
_ => match self.unknown.entry(raw_key) {
::std::collections::btree_map::Entry::Vacant(empty_key) => {empty_key.insert(raw_value);},
::std::collections::btree_map::Entry::Occupied(k) => return Err(Error::DuplicateKey(k.key().clone()).into()),
10u8 => {
impl_psbt_insert_hash_pair! {
self.ripemd_preimages <= <raw_key: ripemd160::Hash>|<raw_value: Vec<u8>>; Ripemd160
}
}
11u8 => {
impl_psbt_insert_hash_pair! {
self.sha256_preimages <= <raw_key: sha256::Hash>|<raw_value: Vec<u8>>; Sha256
}
}
12u8 => {
impl_psbt_insert_hash_pair! {
self.hash160_preimages <= <raw_key: hash160::Hash>|<raw_value: Vec<u8>>; Hash160
}
}
13u8 => {
impl_psbt_insert_hash_pair! {
self.hash256_preimages <= <raw_key: sha256d::Hash>|<raw_value: Vec<u8>>; Hash256
}
}
_ => match self.unknown.entry(raw_key) {
::std::collections::btree_map::Entry::Vacant(empty_key) => {
empty_key.insert(raw_value);
}
::std::collections::btree_map::Entry::Occupied(k) => {
return Err(Error::DuplicateKey(k.key().clone()).into())
}
},
}
Ok(())
@ -160,6 +193,22 @@ impl Map for Input {
rv.push(self.final_script_witness as <8u8, _>|<Script>)
}
impl_psbt_get_pair! {
rv.push(self.ripemd_preimages as <10u8, ripemd160::Hash>|<Vec<u8>>)
}
impl_psbt_get_pair! {
rv.push(self.sha256_preimages as <11u8, sha256::Hash>|<Vec<u8>>)
}
impl_psbt_get_pair! {
rv.push(self.hash160_preimages as <12u8, hash160::Hash>|<Vec<u8>>)
}
impl_psbt_get_pair! {
rv.push(self.hash256_preimages as <13u8, sha256d::Hash>|<Vec<u8>>)
}
for (key, value) in self.unknown.iter() {
rv.push(raw::Pair {
key: key.clone(),
@ -180,6 +229,10 @@ impl Map for Input {
self.partial_sigs.extend(other.partial_sigs);
self.hd_keypaths.extend(other.hd_keypaths);
self.ripemd_preimages.extend(other.ripemd_preimages);
self.sha256_preimages.extend(other.sha256_preimages);
self.hash160_preimages.extend(other.hash160_preimages);
self.hash256_preimages.extend(other.hash256_preimages);
self.unknown.extend(other.unknown);
merge!(redeem_script, self, other);

View File

@ -22,6 +22,7 @@ use std::io;
use blockdata::script::Script;
use blockdata::transaction::{SigHashType, Transaction, TxOut};
use consensus::encode::{self, serialize, Decodable};
use hashes::{hash160, ripemd160, sha256, sha256d, Hash};
use util::bip32::{ChildNumber, DerivationPath, Fingerprint};
use util::key::PublicKey;
use util::psbt;
@ -42,6 +43,10 @@ pub trait Deserialize: Sized {
impl_psbt_de_serialize!(Transaction);
impl_psbt_de_serialize!(TxOut);
impl_psbt_de_serialize!(Vec<Vec<u8>>); // scriptWitness
impl_psbt_hash_de_serialize!(ripemd160::Hash);
impl_psbt_hash_de_serialize!(sha256::Hash);
impl_psbt_hash_de_serialize!(hash160::Hash);
impl_psbt_hash_de_serialize!(sha256d::Hash);
impl Serialize for Script {
fn serialize(&self) -> Vec<u8> {