Skip to content

Commit 2ec9af3

Browse files
committed
Merge rust-bitcoin/rust-bitcoin#669: PSBT: partial sig data type
14ace92 Fix SchnorrSig type references in PSBT serialization macros (Dr Maxim Orlovsky) 2b53000 Use EcdsaSig in PSBT partial signatures instead of Vec<u8> (Dr Maxim Orlovsky) 141dbbd Add serde impl for EcdsaSig (Dr Maxim Orlovsky) c92057d PSBT serialize/deserialize impl for EcdsaSig type (Dr Maxim Orlovsky) 0af1c3f Add Display and FromStr for EcdsaSig (Dr Maxim Orlovsky) daf0eac Improve NonStandardSigHashType (Dr Maxim Orlovsky) c36a3da Add EcdsaSig::sighash_all convenience constructor (Dr Maxim Orlovsky) Pull request description: Previously signatures were kept in PSBT as raw byte vec, without processing. This adds specific data type, capable of holding & serializing/deserializing partial signature with sighash flag information. ACKs for top commit: apoelstra: ACK 14ace92 Kixunil: ACK 14ace92 Tree-SHA512: f505df9d1990735fe3941092174a61e067a4e3db30d2d1b94b136da4607865b3b78b274e674b2cfb877aaf0ab58e007c1ab1738f3927d60a4a3ecc12cadc5a48
2 parents f332a19 + 14ace92 commit 2ec9af3

File tree

5 files changed

+91
-23
lines changed

5 files changed

+91
-23
lines changed

src/blockdata/transaction.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -666,11 +666,11 @@ impl Decodable for Transaction {
666666
/// This type is consensus valid but an input including it would prevent the transaction from
667667
/// being relayed on today's Bitcoin network.
668668
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
669-
pub struct NonStandardSigHashType;
669+
pub struct NonStandardSigHashType(pub u32);
670670

671671
impl fmt::Display for NonStandardSigHashType {
672672
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
673-
write!(f, "Non standard sighash type")
673+
write!(f, "Non standard sighash type {}", self.0)
674674
}
675675
}
676676

@@ -789,7 +789,7 @@ impl EcdsaSigHashType {
789789
0x81 => Ok(EcdsaSigHashType::AllPlusAnyoneCanPay),
790790
0x82 => Ok(EcdsaSigHashType::NonePlusAnyoneCanPay),
791791
0x83 => Ok(EcdsaSigHashType::SinglePlusAnyoneCanPay),
792-
_ => Err(NonStandardSigHashType)
792+
non_standard => Err(NonStandardSigHashType(non_standard))
793793
}
794794
}
795795

@@ -1151,7 +1151,7 @@ mod tests {
11511151
assert_eq!(EcdsaSigHashType::from_u32(nonstandard_hashtype), EcdsaSigHashType::All);
11521152
assert_eq!(EcdsaSigHashType::from_u32_consensus(nonstandard_hashtype), EcdsaSigHashType::All);
11531153
// But it's policy-invalid to use it!
1154-
assert_eq!(EcdsaSigHashType::from_u32_standard(nonstandard_hashtype), Err(NonStandardSigHashType));
1154+
assert_eq!(EcdsaSigHashType::from_u32_standard(nonstandard_hashtype), Err(NonStandardSigHashType(0x04)));
11551155
}
11561156

11571157
// These test vectors were stolen from libbtc, which is Copyright 2014 Jonas Schnelli MIT

src/util/ecdsa.rs

Lines changed: 49 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,12 @@ use io;
2525

2626
use secp256k1::{self, Secp256k1};
2727
use network::constants::Network;
28-
use hashes::{Hash, hash160};
28+
use hashes::{Hash, hash160, hex};
29+
use hashes::hex::FromHex;
2930
use hash_types::{PubkeyHash, WPubkeyHash};
3031
use util::base58;
3132
use util::key::Error;
32-
use blockdata::transaction::EcdsaSigHashType;
33+
use blockdata::transaction::{EcdsaSigHashType, NonStandardSigHashType};
3334

3435

3536
/// A Bitcoin ECDSA public key
@@ -417,6 +418,7 @@ impl<'de> ::serde::Deserialize<'de> for PublicKey {
417418

418419
/// An ECDSA signature with the corresponding hash type.
419420
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
421+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
420422
pub struct EcdsaSig {
421423
/// The underlying ECDSA Signature
422424
pub sig: secp256k1::ecdsa::Signature,
@@ -425,13 +427,20 @@ pub struct EcdsaSig {
425427
}
426428

427429
impl EcdsaSig {
430+
/// Constructs ECDSA bitcoin signature for [`EcdsaSigHashType::All`]
431+
pub fn sighash_all(sig: secp256k1::ecdsa::Signature) -> EcdsaSig {
432+
EcdsaSig {
433+
sig,
434+
hash_ty: EcdsaSigHashType::All
435+
}
436+
}
428437

429438
/// Deserialize from slice
430439
pub fn from_slice(sl: &[u8]) -> Result<Self, EcdsaSigError> {
431440
let (hash_ty, sig) = sl.split_last()
432441
.ok_or(EcdsaSigError::EmptySignature)?;
433442
let hash_ty = EcdsaSigHashType::from_u32_standard(*hash_ty as u32)
434-
.map_err(|_| EcdsaSigError::NonStandardSigHashType(*hash_ty))?;
443+
.map_err(|_| EcdsaSigError::NonStandardSigHashType(*hash_ty as u32))?;
435444
let sig = secp256k1::ecdsa::Signature::from_der(sig)
436445
.map_err(EcdsaSigError::Secp256k1)?;
437446
Ok(EcdsaSig { sig, hash_ty })
@@ -446,11 +455,34 @@ impl EcdsaSig {
446455
}
447456
}
448457

458+
impl fmt::Display for EcdsaSig {
459+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
460+
hex::format_hex(&self.sig.serialize_der(), f)?;
461+
hex::format_hex(&[self.hash_ty as u8], f)
462+
}
463+
}
464+
465+
impl FromStr for EcdsaSig {
466+
type Err = EcdsaSigError;
467+
468+
fn from_str(s: &str) -> Result<Self, Self::Err> {
469+
let bytes = Vec::from_hex(s)?;
470+
let (sighash_byte, signature) = bytes.split_last()
471+
.ok_or(EcdsaSigError::EmptySignature)?;
472+
Ok(EcdsaSig {
473+
sig: secp256k1::ecdsa::Signature::from_der(signature)?,
474+
hash_ty: EcdsaSigHashType::from_u32_standard(*sighash_byte as u32)?
475+
})
476+
}
477+
}
478+
449479
/// A key-related error.
450480
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
451481
pub enum EcdsaSigError {
482+
/// Hex encoding error
483+
HexEncoding(hex::Error),
452484
/// Base58 encoding error
453-
NonStandardSigHashType(u8),
485+
NonStandardSigHashType(u32),
454486
/// Empty Signature
455487
EmptySignature,
456488
/// secp256k1-related error
@@ -467,6 +499,7 @@ impl fmt::Display for EcdsaSigError {
467499
write!(f, "Invalid Ecdsa signature: {}", e),
468500
EcdsaSigError::EmptySignature =>
469501
write!(f, "Empty ECDSA signature"),
502+
EcdsaSigError::HexEncoding(e) => write!(f, "EcdsaSig hex encoding error: {}", e)
470503
}
471504
}
472505
}
@@ -481,6 +514,18 @@ impl From<secp256k1::Error> for EcdsaSigError {
481514
}
482515
}
483516

517+
impl From<NonStandardSigHashType> for EcdsaSigError {
518+
fn from(err: NonStandardSigHashType) -> Self {
519+
EcdsaSigError::NonStandardSigHashType(err.0)
520+
}
521+
}
522+
523+
impl From<hex::Error> for EcdsaSigError {
524+
fn from(err: hex::Error) -> Self {
525+
EcdsaSigError::HexEncoding(err)
526+
}
527+
}
528+
484529
#[cfg(test)]
485530
mod tests {
486531
use io;

src/util/psbt/map/input.rs

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414

1515
use prelude::*;
1616

17-
use io;
17+
use ::{EcdsaSig, io};
1818

1919
use blockdata::script::Script;
2020
use blockdata::transaction::{EcdsaSigHashType, Transaction, TxOut};
@@ -29,7 +29,7 @@ use util::psbt::raw;
2929
use util::psbt::serialize::Deserialize;
3030
use util::psbt::{Error, error};
3131

32-
use schnorr;
32+
use ::{SchnorrSig};
3333
use util::taproot::{ControlBlock, LeafVersion, TapLeafHash, TapBranchHash};
3434

3535
/// Type: Non-Witness UTXO PSBT_IN_NON_WITNESS_UTXO = 0x00
@@ -87,9 +87,8 @@ pub struct Input {
8787
/// including P2SH embedded ones.
8888
pub witness_utxo: Option<TxOut>,
8989
/// A map from public keys to their corresponding signature as would be
90-
/// pushed to the stack from a scriptSig or witness.
91-
#[cfg_attr(feature = "serde", serde(with = "::serde_utils::btreemap_byte_values"))]
92-
pub partial_sigs: BTreeMap<PublicKey, Vec<u8>>,
90+
/// pushed to the stack from a scriptSig or witness for a non-taproot inputs.
91+
pub partial_sigs: BTreeMap<PublicKey, EcdsaSig>,
9392
/// The sighash type to be used for this input. Signatures for this input
9493
/// must use the sighash type.
9594
pub sighash_type: Option<EcdsaSigHashType>,
@@ -121,10 +120,10 @@ pub struct Input {
121120
#[cfg_attr(feature = "serde", serde(with = "::serde_utils::btreemap_byte_values"))]
122121
pub hash256_preimages: BTreeMap<sha256d::Hash, Vec<u8>>,
123122
/// Serialized schnorr signature with sighash type for key spend
124-
pub tap_key_sig: Option<schnorr::SchnorrSig>,
123+
pub tap_key_sig: Option<SchnorrSig>,
125124
/// Map of <xonlypubkey>|<leafhash> with signature
126125
#[cfg_attr(feature = "serde", serde(with = "::serde_utils::btreemap_as_seq"))]
127-
pub tap_script_sigs: BTreeMap<(XOnlyPublicKey, TapLeafHash), schnorr::SchnorrSig>,
126+
pub tap_script_sigs: BTreeMap<(XOnlyPublicKey, TapLeafHash), SchnorrSig>,
128127
/// Map of Control blocks to Script version pair
129128
#[cfg_attr(feature = "serde", serde(with = "::serde_utils::btreemap_as_seq"))]
130129
pub tap_scripts: BTreeMap<ControlBlock, (Script, LeafVersion)>,
@@ -163,7 +162,7 @@ impl Map for Input {
163162
}
164163
PSBT_IN_PARTIAL_SIG => {
165164
impl_psbt_insert_pair! {
166-
self.partial_sigs <= <raw_key: PublicKey>|<raw_value: Vec<u8>>
165+
self.partial_sigs <= <raw_key: PublicKey>|<raw_value: EcdsaSig>
167166
}
168167
}
169168
PSBT_IN_SIGHASH_TYPE => {
@@ -210,12 +209,12 @@ impl Map for Input {
210209
}
211210
PSBT_IN_TAP_KEY_SIG => {
212211
impl_psbt_insert_pair! {
213-
self.tap_key_sig <= <raw_key: _>|<raw_value: schnorr::SchnorrSig>
212+
self.tap_key_sig <= <raw_key: _>|<raw_value: SchnorrSig>
214213
}
215214
}
216215
PSBT_IN_TAP_SCRIPT_SIG => {
217216
impl_psbt_insert_pair! {
218-
self.tap_script_sigs <= <raw_key: (XOnlyPublicKey, TapLeafHash)>|<raw_value: schnorr::SchnorrSig>
217+
self.tap_script_sigs <= <raw_key: (XOnlyPublicKey, TapLeafHash)>|<raw_value: SchnorrSig>
219218
}
220219
}
221220
PSBT_IN_TAP_LEAF_SCRIPT=> {
@@ -267,7 +266,7 @@ impl Map for Input {
267266
}
268267

269268
impl_psbt_get_pair! {
270-
rv.push(self.partial_sigs as <PSBT_IN_PARTIAL_SIG, PublicKey>|<Vec<u8>>)
269+
rv.push(self.partial_sigs as <PSBT_IN_PARTIAL_SIG, PublicKey>|<EcdsaSig>)
271270
}
272271

273272
impl_psbt_get_pair! {
@@ -311,11 +310,11 @@ impl Map for Input {
311310
}
312311

313312
impl_psbt_get_pair! {
314-
rv.push(self.tap_key_sig as <PSBT_IN_TAP_KEY_SIG, _>|<Vec<u8>>)
313+
rv.push(self.tap_key_sig as <PSBT_IN_TAP_KEY_SIG, _>|<SchnorrSig>)
315314
}
316315

317316
impl_psbt_get_pair! {
318-
rv.push(self.tap_script_sigs as <PSBT_IN_TAP_SCRIPT_SIG, (XOnlyPublicKey, TapLeafHash)>|<Vec<u8>>)
317+
rv.push(self.tap_script_sigs as <PSBT_IN_TAP_SCRIPT_SIG, (XOnlyPublicKey, TapLeafHash)>|<SchnorrSig>)
319318
}
320319

321320
impl_psbt_get_pair! {

src/util/psbt/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -477,7 +477,7 @@ mod tests {
477477
witness_script: None,
478478
partial_sigs: vec![(
479479
"0339880dc92394b7355e3d0439fa283c31de7590812ea011c4245c0674a685e883".parse().unwrap(),
480-
vec![8, 5, 4],
480+
"304402204f67e2afb76142d44fae58a2495d33a3419daa26cd0db8d04f3452b63289ac0f022010762a9fb67e94cc5cad9026f6dc99ff7f070f4278d30fbc7d0c869dd38c7fe701".parse().unwrap(),
481481
)].into_iter().collect(),
482482
bip32_derivation: keypaths.clone(),
483483
final_script_witness: Some(vec![vec![1, 3], vec![5]]),

src/util/psbt/serialize.rs

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ use consensus::encode::{self, serialize, Decodable, Encodable, deserialize_parti
2828
use secp256k1::{self, XOnlyPublicKey};
2929
use util::bip32::{ChildNumber, Fingerprint, KeySource};
3030
use hashes::{hash160, ripemd160, sha256, sha256d, Hash};
31-
use util::ecdsa::PublicKey;
31+
use util::ecdsa::{PublicKey, EcdsaSig};
3232
use util::psbt;
3333
use util::taproot::{TapBranchHash, TapLeafHash, ControlBlock, LeafVersion};
3434
use schnorr;
@@ -89,6 +89,30 @@ impl Deserialize for PublicKey {
8989
}
9090
}
9191

92+
impl Serialize for EcdsaSig {
93+
fn serialize(&self) -> Vec<u8> {
94+
let mut buf = Vec::with_capacity(72);
95+
buf.extend(self.sig.serialize_der().iter());
96+
buf.push(self.hash_ty as u8);
97+
buf
98+
}
99+
}
100+
101+
impl Deserialize for EcdsaSig {
102+
fn deserialize(bytes: &[u8]) -> Result<Self, encode::Error> {
103+
let (sighash_byte, signature) = bytes.split_last()
104+
.ok_or(encode::Error::ParseFailed("empty partial signature data"))?;
105+
Ok(EcdsaSig {
106+
sig: secp256k1::ecdsa::Signature::from_der(signature)
107+
.map_err(|_| encode::Error::ParseFailed("non-DER encoded signature"))?,
108+
// NB: Since BIP-174 says "the signature as would be pushed to the stack from
109+
// a scriptSig or witness" we should use a consensus deserialization and do
110+
// not error on a non-standard values.
111+
hash_ty: EcdsaSigHashType::from_u32_consensus(*sighash_byte as u32)
112+
})
113+
}
114+
}
115+
92116
impl Serialize for KeySource {
93117
fn serialize(&self) -> Vec<u8> {
94118
let mut rv: Vec<u8> = Vec::with_capacity(key_source_len(&self));

0 commit comments

Comments
 (0)