From a4a7035a947998c8d0d69dab206e97253fd8e048 Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Thu, 8 Oct 2020 18:19:48 +0200 Subject: [PATCH 1/3] String and serde de/serialization for SigHashType --- src/blockdata/transaction.rs | 63 +++++++++++++++++++++++++++++++++++- 1 file changed, 62 insertions(+), 1 deletion(-) diff --git a/src/blockdata/transaction.rs b/src/blockdata/transaction.rs index f713f39d..dd7f9697 100644 --- a/src/blockdata/transaction.rs +++ b/src/blockdata/transaction.rs @@ -24,7 +24,7 @@ //! use std::default::Default; -use std::{error, fmt, io}; +use std::{error, fmt, io, str}; use hashes::{self, Hash, sha256d}; use hashes::hex::FromHex; @@ -616,6 +616,37 @@ pub enum SigHashType { /// 0x83: Sign one output and only this input (see `Single` for what "one output" means) SinglePlusAnyoneCanPay = 0x83 } +serde_string_impl!(SigHashType, "a SigHashType data"); + +impl fmt::Display for SigHashType { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let s = match self { + SigHashType::All => "SIGHASH_ALL", + SigHashType::None => "SIGHASH_NONE", + SigHashType::Single => "SIGHASH_SINGLE", + SigHashType::AllPlusAnyoneCanPay => "SIGHASH_ALL|SIGHASH_ANYONECANPAY", + SigHashType::NonePlusAnyoneCanPay => "SIGHASH_NONE|SIGHASH_ANYONECANPAY", + SigHashType::SinglePlusAnyoneCanPay => "SIGHASH_SINGLE|SIGHASH_ANYONECANPAY", + }; + f.write_str(s) + } +} + +impl str::FromStr for SigHashType { + type Err = String; + + fn from_str(s: &str) -> Result { + match s.replace(' ', "").to_ascii_uppercase().as_ref() { + "SIGHASH_ALL" => Ok(SigHashType::All), + "SIGHASH_NONE" => Ok(SigHashType::None), + "SIGHASH_SINGLE" => Ok(SigHashType::Single), + "SIGHASH_ALL|SIGHASH_ANYONECANPAY" => Ok(SigHashType::AllPlusAnyoneCanPay), + "SIGHASH_NONE|SIGHASH_ANYONECANPAY" => Ok(SigHashType::NonePlusAnyoneCanPay), + "SIGHASH_SINGLE|SIGHASH_ANYONECANPAY" => Ok(SigHashType::SinglePlusAnyoneCanPay), + _ => Err("can't recognize SIGHASH string".to_string()) + } + } +} impl SigHashType { /// Break the sighash flag into the "real" sighash flag and the ANYONECANPAY boolean @@ -665,6 +696,7 @@ mod tests { use hashes::hex::FromHex; use hash_types::*; + use SigHashType; #[test] fn test_outpoint() { @@ -916,6 +948,35 @@ mod tests { assert_eq!(consensus_encoded, tx_bytes); } + #[test] + fn test_sighashtype_fromstr_display() { + let sighashtypes = vec![("SIGHASH_ALL", SigHashType::All), + ("SIGHASH_NONE", SigHashType::None), + ("SIGHASH_SINGLE", SigHashType::Single), + ("SIGHASH_ALL|SIGHASH_ANYONECANPAY", SigHashType::AllPlusAnyoneCanPay), + ("SIGHASH_NONE|SIGHASH_ANYONECANPAY", SigHashType::NonePlusAnyoneCanPay), + ("SIGHASH_SINGLE|SIGHASH_ANYONECANPAY", SigHashType::SinglePlusAnyoneCanPay)]; + for (s, sht) in sighashtypes { + assert_eq!(sht.to_string(), s); + assert_eq!(SigHashType::from_str(s).unwrap(), sht); + } + let sht_spaces = vec![ + ("SIGHASH_ALL | SIGHASH_ANYONECANPAY", SigHashType::AllPlusAnyoneCanPay), + ("SIGHASH_NONE |SIGHASH_ANYONECANPAY", SigHashType::NonePlusAnyoneCanPay), + ("SIGHASH_SINGLE| SIGHASH_ANYONECANPAY", SigHashType::SinglePlusAnyoneCanPay) + ]; + for (s, sht) in sht_spaces { + assert_eq!(SigHashType::from_str(s).unwrap(), sht); + } + let sht_mistakes = vec![ + "SIGHASH_ALL SIGHASH_ANYONECANPAY", + "SIGHASH_NONE |", + "SIGHASH_SIGNLE" + ]; + for s in sht_mistakes { + assert_eq!(SigHashType::from_str(s).unwrap_err(), "can't recognize SIGHASH string"); + } + } // These test vectors were stolen from libbtc, which is Copyright 2014 Jonas Schnelli MIT // They were transformed by replacing {...} with run_test_sighash(...), then the ones containing From 00c34663638a28cbae534a8f2effe4d64bc5565e Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Sun, 11 Oct 2020 04:25:39 +0200 Subject: [PATCH 2/3] Serde implementation for PSBT --- src/util/psbt/map/global.rs | 1 + src/util/psbt/map/input.rs | 5 +++++ src/util/psbt/map/output.rs | 3 +++ src/util/psbt/mod.rs | 1 + src/util/psbt/raw.rs | 2 ++ 5 files changed, 12 insertions(+) diff --git a/src/util/psbt/map/global.rs b/src/util/psbt/map/global.rs index f6b411d4..681a3e1b 100644 --- a/src/util/psbt/map/global.rs +++ b/src/util/psbt/map/global.rs @@ -32,6 +32,7 @@ pub struct Global { /// Unknown global key-value pairs. pub unknown: BTreeMap>, } +serde_struct_impl!(Global, unsigned_tx, unknown); impl Global { /// Create a Global from an unsigned transaction, error if not unsigned diff --git a/src/util/psbt/map/input.rs b/src/util/psbt/map/input.rs index 72f33942..4481f755 100644 --- a/src/util/psbt/map/input.rs +++ b/src/util/psbt/map/input.rs @@ -58,6 +58,11 @@ pub struct Input { /// Unknown key-value pairs for this input. pub unknown: BTreeMap>, } +serde_struct_impl!( + Input, non_witness_utxo, witness_utxo, partial_sigs, + sighash_type, redeem_script, witness_script, hd_keypaths, + final_script_sig, final_script_witness, unknown +); impl Map for Input { fn insert_pair(&mut self, pair: raw::Pair) -> Result<(), encode::Error> { diff --git a/src/util/psbt/map/output.rs b/src/util/psbt/map/output.rs index 856396aa..a089fdbc 100644 --- a/src/util/psbt/map/output.rs +++ b/src/util/psbt/map/output.rs @@ -38,6 +38,9 @@ pub struct Output { /// Unknown key-value pairs for this output. pub unknown: BTreeMap>, } +serde_struct_impl!( + Output, redeem_script, witness_script, hd_keypaths, unknown +); impl Map for Output { fn insert_pair(&mut self, pair: raw::Pair) -> Result<(), encode::Error> { diff --git a/src/util/psbt/mod.rs b/src/util/psbt/mod.rs index 1fe55d04..cbf47ab7 100644 --- a/src/util/psbt/mod.rs +++ b/src/util/psbt/mod.rs @@ -49,6 +49,7 @@ pub struct PartiallySignedTransaction { /// transaction. pub outputs: Vec, } +serde_struct_impl!(PartiallySignedTransaction, global, inputs, outputs); impl PartiallySignedTransaction { /// Create a PartiallySignedTransaction from an unsigned transaction, error diff --git a/src/util/psbt/raw.rs b/src/util/psbt/raw.rs index 9bf73ff9..16185715 100644 --- a/src/util/psbt/raw.rs +++ b/src/util/psbt/raw.rs @@ -31,6 +31,7 @@ pub struct Key { /// The key itself in raw byte form. pub key: Vec, } +serde_struct_impl!(Key, type_value, key); /// A PSBT key-value pair in its raw byte form. #[derive(Debug, PartialEq)] @@ -40,6 +41,7 @@ pub struct Pair { /// The value of this key-value pair in raw byte form. pub value: Vec, } +serde_struct_impl!(Pair, key, value); impl fmt::Display for Key { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { From 8e0b9921ae2cb56a07df87ae47d4e1c21e9b6c9b Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Wed, 14 Oct 2020 16:46:48 +0200 Subject: [PATCH 3/3] No space- and case insensitivity for SigHashType string serialization --- src/blockdata/transaction.rs | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/src/blockdata/transaction.rs b/src/blockdata/transaction.rs index dd7f9697..c083b712 100644 --- a/src/blockdata/transaction.rs +++ b/src/blockdata/transaction.rs @@ -636,7 +636,7 @@ impl str::FromStr for SigHashType { type Err = String; fn from_str(s: &str) -> Result { - match s.replace(' ', "").to_ascii_uppercase().as_ref() { + match s.as_ref() { "SIGHASH_ALL" => Ok(SigHashType::All), "SIGHASH_NONE" => Ok(SigHashType::None), "SIGHASH_SINGLE" => Ok(SigHashType::Single), @@ -960,18 +960,17 @@ mod tests { assert_eq!(sht.to_string(), s); assert_eq!(SigHashType::from_str(s).unwrap(), sht); } - let sht_spaces = vec![ - ("SIGHASH_ALL | SIGHASH_ANYONECANPAY", SigHashType::AllPlusAnyoneCanPay), - ("SIGHASH_NONE |SIGHASH_ANYONECANPAY", SigHashType::NonePlusAnyoneCanPay), - ("SIGHASH_SINGLE| SIGHASH_ANYONECANPAY", SigHashType::SinglePlusAnyoneCanPay) - ]; - for (s, sht) in sht_spaces { - assert_eq!(SigHashType::from_str(s).unwrap(), sht); - } let sht_mistakes = vec![ + "SIGHASH_ALL | SIGHASH_ANYONECANPAY", + "SIGHASH_NONE |SIGHASH_ANYONECANPAY", + "SIGHASH_SINGLE| SIGHASH_ANYONECANPAY", "SIGHASH_ALL SIGHASH_ANYONECANPAY", "SIGHASH_NONE |", - "SIGHASH_SIGNLE" + "SIGHASH_SIGNLE", + "sighash_none", + "Sighash_none", + "SigHash_None", + "SigHash_NONE", ]; for s in sht_mistakes { assert_eq!(SigHashType::from_str(s).unwrap_err(), "can't recognize SIGHASH string");