-
Notifications
You must be signed in to change notification settings - Fork 169
[Draft] BIP322 Implementation #240
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,337 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // BIP322 Generic Signature Algorithm | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Written in 2019 by | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Rajarshi Maitra <rajarshi149@protonmail.com>] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // To the extent possible under law, the author(s) have dedicated all | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // copyright and related and neighboring rights to this software to | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // the public domain worldwide. This software is distributed without | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // any warranty. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // You should have received a copy of the CC0 Public Domain Dedication | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // along with this software. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // If not, see <http://creativecommons.org/publicdomain/zero/1.0/>. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| //! # BIP322 Generic Signed Message Structure | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| //! | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| //! This module implements the BIP322 Generic Message Signer and Validator | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| //! | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| //! `https://github.com/bitcoin/bips/blob/master/bip-0322.mediawiki` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| //! | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| use crate::{Descriptor, DescriptorTrait, MiniscriptKey, ToPublicKey}; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| use bitcoin::blockdata::{opcodes, script::Builder}; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| use bitcoin::hashes::{ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| borrow_slice_impl, hex_fmt_impl, index_impl, serde_impl, sha256t_hash_newtype, Hash, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| use bitcoin::secp256k1::{Secp256k1, Signature}; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| use bitcoin::{OutPoint, PublicKey, SigHashType, Transaction, TxIn, TxOut}; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| use crate::interpreter::{Error as InterpreterError, Interpreter}; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| use std::convert::From; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // BIP322 message tag = sha256("BIP0322-signed-message") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| static MIDSTATE: [u8; 32] = [ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 116, 101, 132, 161, 135, 47, 161, 0, 65, 85, 78, 255, 160, 56, 214, 18, 73, 66, 221, 121, 180, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 229, 138, 76, 218, 24, 78, 19, 219, 230, 44, 73, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ]; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // BIP322 Tagged Hash | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| sha256t_hash_newtype!( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| MessageHash, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| MessageTag, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| MIDSTATE, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 64, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| doc = "test hash", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| true | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// BIP322 Error types | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| #[derive(Debug)] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would be great to derive all possible things that we can derive.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There's not much to drive here as rust-miniscript/src/interpreter/error.rs Lines 20 to 21 in 86a63d8
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| pub enum BIP322Error { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// BIP322 Internal Error | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| InternalError(String), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// Signature Validation Error | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ValidationError(InterpreterError), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| #[doc(hidden)] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| impl From<InterpreterError> for BIP322Error { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fn from(e: InterpreterError) -> BIP322Error { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| BIP322Error::ValidationError(e) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// Bip322 Signatures | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| #[derive(Debug, Clone, Eq, PartialEq)] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| pub enum Bip322Signature { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// Legacy style. Only applicable for P2PKH message_challenge | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Legacy(Signature, PublicKey), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// Simple witness structure | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Simple(Vec<Vec<u8>>), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// Full `to_sign` transaction structure | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Full(Transaction), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// BIP322 validator structure | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// A standard for interoperable signed messages based on the Bitcoin Script format, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// either for proving fund availability, or committing to a message as the intended | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// recipient of funds sent to the invoice address. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| #[derive(Debug, Clone, Eq, PartialEq)] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| pub struct Bip322<T: MiniscriptKey + ToPublicKey> { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// Message to be signed | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| message: Vec<u8>, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// Signature to verify the message | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// Optional value is used here because a validator structure can be | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// created without a BIP322Signature. Such structure can only produce | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// to_spend (or empty to_sign) transaction, but cannot validate them. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| signature: Option<Bip322Signature>, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// script_pubkey to define the challenge script inside to_spend transaction | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// here we take in descriptors to derive the resulting script_pubkey | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| message_challenge: Descriptor<T>, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| impl<T: MiniscriptKey + ToPublicKey> Bip322<T> { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// Create a new BIP322 validator | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| pub fn new(msg: &[u8], sig: Option<Bip322Signature>, addr: Descriptor<T>) -> Self { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Bip322 { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| message: msg.to_vec(), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| signature: sig, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| message_challenge: addr, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// Insert Signature inside BIP322 structure | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| pub fn insert_sig(&mut self, sig: Bip322Signature) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| self.signature = Some(sig) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// create the to_spend transaction | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| pub fn to_spend(&self) -> Transaction { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // create default input and output | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let mut vin = TxIn::default(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let mut vout = TxOut::default(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // calculate the message tagged hash | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let msg_hash = MessageHash::hash(&self.message[..]).into_inner(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // mutate the input with appropriate script_sig and sequence | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| vin.script_sig = Builder::new() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .push_int(0) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .push_slice(&msg_hash[..]) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .into_script(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| vin.sequence = 0; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // mutate the value and script_pubkey as appropriate | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| vout.value = 0; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| vout.script_pubkey = self.message_challenge.script_pubkey(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // create and return final transaction | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Transaction { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| version: 0, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I have made a comment to BIP322 to make this always 2 and it was positively received. I think we should keep it always 2.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done.. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| lock_time: 0, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| input: vec![vin], | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| output: vec![vout], | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// Create to_sign transaction | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// This will create a transaction structure with empty signature and witness field | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// its up to the user of the library to fill the Tx with appropriate signature and witness | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| pub fn to_sign(&self) -> Transaction { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Bip322Signature::Full(to_sign) => self.tx_validation(to_sign), | |
| // If Simple Signature is provided, the resulting `to_sign` Tx will be computed | |
| Bip322Signature::Simple(witness) => { | |
| // create empty to_sign transaction | |
| let mut to_sign = self.to_sign(); | |
| to_sign.input[0].witness = witness.to_owned(); | |
| self.tx_validation(&to_sign) | |
| } | |
| // Legacy Signature can only be used to validate against P2PKH message_challenge | |
| Bip322Signature::Legacy(sig, pubkey) => { | |
| if !self.message_challenge.script_pubkey().is_p2pkh() { | |
| return Err(BIP322Error::InternalError("Legacy style signature is only applicable for P2PKH message_challenge".to_string())); | |
| } else { | |
| let mut sig_ser = sig.serialize_der()[..].to_vec(); | |
| // By default SigHashType is ALL | |
| sig_ser.push(SigHashType::All as u8); | |
| let script_sig = Builder::new() | |
| .push_slice(&sig_ser[..]) | |
| .push_key(&pubkey) | |
| .into_script(); | |
| let mut to_sign = self.to_sign(); | |
| to_sign.input[0].script_sig = script_sig; | |
| self.tx_validation(&to_sign) |
Here we are mutating the empty to_sign transaction with corresponding signatures. Which can be done with a satisfier too. But because a Bip322Signature::Full type signature will contain the entire to_sign transaction itself (which I think will be most of the cases, the other two cases are rather simplistic and more specific so can be hardcoded easily), using a separate satisfier to create the same to_sign feels like more roundabout than necessary.
Bip322Signer probably needs to use a satisfier in some way, but we can flesh that detail out later.
For age and height, I think it's better to provide these values in the validator structure itself. Though I am not sure if I have applied these values correctly in the empty_to_spend creation, please check.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should reproduce the errors states from the BIP. This should return Result<(T, S), BIP322Error> where BIP322Error should either be Inconclusive(..) or Invalid(..)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it possible to somehow derive these error types from the IterpreterErrors?
Also if I understand it right, neither to_spend nor to_sign transactions are supposed to be mined. They are not validated against the chain. So what the purpose of T and S here and how the validator is supposed to validate Time and Age of an unmined transaction? Also, what does it mean to have a signature against a message only valid "after x time"? These are not transactions, but simple generic messages.
Uh oh!
There was an error while loading. Please reload this page.