Skip to content

Commit

Permalink
Merge commit '437d1e2c964faf182375169640f0561070dae346' into subtree-…
Browse files Browse the repository at this point in the history
…stuff
  • Loading branch information
thomaseizinger committed Dec 2, 2020
2 parents ad62453 + 437d1e2 commit 24e25d2
Show file tree
Hide file tree
Showing 4 changed files with 323 additions and 4 deletions.
194 changes: 194 additions & 0 deletions elements-fun/src/bip143.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
// Rust Bitcoin Library
// Written in 2018 by
// Andrew Poelstra <apoelstra@wpsoftware.net>
// 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/>.
//

//! BIP143 Implementation
//!
//! Implementation of BIP143 Segwit-style signatures. Should be sufficient
//! to create signatures for Segwit transactions (which should be pushed into
//! the appropriate place in the `Transaction::witness` array) or bcash
//! signatures, which are placed in the scriptSig.
//!

use crate::{
encode::{self, Encodable},
hash_types::SigHash,
script::Script,
transaction::{SigHashType, Transaction},
};
use bitcoin_hashes::{sha256d, Hash};
use std::{io, ops::Deref};

/// A replacement for SigHashComponents which supports all sighash modes
pub struct SigHashCache<R: Deref<Target = Transaction>> {
/// Access to transaction required for various introspection
tx: R,
/// Hash of all the previous outputs, computed as required
hash_prevouts: Option<sha256d::Hash>,
/// Hash of all the input sequence nos, computed as required
hash_sequence: Option<sha256d::Hash>,
/// Hash of all the outputs in this transaction, computed as required
hash_outputs: Option<sha256d::Hash>,
/// Hash of all the issunaces in this transaction, computed as required
hash_issuances: Option<sha256d::Hash>,
}

impl<R: Deref<Target = Transaction>> SigHashCache<R> {
/// Compute the sighash components from an unsigned transaction and auxiliary
/// in a lazy manner when required.
/// For the generated sighashes to be valid, no fields in the transaction may change except for
/// script_sig and witnesses.
pub fn new(tx: R) -> Self {
SigHashCache {
tx: tx,
hash_prevouts: None,
hash_sequence: None,
hash_outputs: None,
hash_issuances: None,
}
}

/// Calculate hash for prevouts
pub fn hash_prevouts(&mut self) -> sha256d::Hash {
let hash_prevout = &mut self.hash_prevouts;
let input = &self.tx.input;
*hash_prevout.get_or_insert_with(|| {
let mut enc = sha256d::Hash::engine();
for txin in input {
txin.previous_output.consensus_encode(&mut enc).unwrap();
}
sha256d::Hash::from_engine(enc)
})
}

/// Calculate hash for input sequence values
pub fn hash_sequence(&mut self) -> sha256d::Hash {
let hash_sequence = &mut self.hash_sequence;
let input = &self.tx.input;
*hash_sequence.get_or_insert_with(|| {
let mut enc = sha256d::Hash::engine();
for txin in input {
txin.sequence.consensus_encode(&mut enc).unwrap();
}
sha256d::Hash::from_engine(enc)
})
}

/// Calculate hash for issuances
pub fn hash_issuances(&mut self) -> sha256d::Hash {
let hash_issuance = &mut self.hash_issuances;
let input = &self.tx.input;
*hash_issuance.get_or_insert_with(|| {
let mut enc = sha256d::Hash::engine();
for txin in input {
if txin.has_issuance() {
txin.asset_issuance.consensus_encode(&mut enc).unwrap();
} else {
0u8.consensus_encode(&mut enc).unwrap();
}
}
sha256d::Hash::from_engine(enc)
})
}

/// Calculate hash for outputs
pub fn hash_outputs(&mut self) -> sha256d::Hash {
let hash_output = &mut self.hash_outputs;
let output = &self.tx.output;
*hash_output.get_or_insert_with(|| {
let mut enc = sha256d::Hash::engine();
for txout in output {
txout.consensus_encode(&mut enc).unwrap();
}
sha256d::Hash::from_engine(enc)
})
}

/// Encode the BIP143 signing data for any flag type into a given object implementing a
/// std::io::Write trait.
pub fn encode_signing_data_to<Write: io::Write>(
&mut self,
mut writer: Write,
input_index: usize,
script_code: &Script,
value: u64,
sighash_type: SigHashType,
) -> Result<(), encode::Error> {
let zero_hash = sha256d::Hash::default();

let (sighash, anyone_can_pay) = sighash_type.split_anyonecanpay_flag();

self.tx.version.consensus_encode(&mut writer)?;

if !anyone_can_pay {
self.hash_prevouts().consensus_encode(&mut writer)?;
} else {
zero_hash.consensus_encode(&mut writer)?;
}

if !anyone_can_pay && sighash != SigHashType::Single && sighash != SigHashType::None {
self.hash_sequence().consensus_encode(&mut writer)?;
} else {
zero_hash.consensus_encode(&mut writer)?;
}

// Elements: Push the hash issuance zero hash as required
// If required implement for issuance, but not necessary as of now
if !anyone_can_pay {
self.hash_issuances().consensus_encode(&mut writer)?;
} else {
zero_hash.consensus_encode(&mut writer)?;
}

// input specific values
{
let txin = &self.tx.input[input_index];

txin.previous_output.consensus_encode(&mut writer)?;
script_code.consensus_encode(&mut writer)?;
value.consensus_encode(&mut writer)?;
txin.sequence.consensus_encode(&mut writer)?;
if txin.has_issuance() {
txin.asset_issuance.consensus_encode(&mut writer)?;
}
}

// hashoutputs
if sighash != SigHashType::Single && sighash != SigHashType::None {
self.hash_outputs().consensus_encode(&mut writer)?;
} else if sighash == SigHashType::Single && input_index < self.tx.output.len() {
let mut single_enc = SigHash::engine();
self.tx.output[input_index].consensus_encode(&mut single_enc)?;
SigHash::from_engine(single_enc).consensus_encode(&mut writer)?;
} else {
zero_hash.consensus_encode(&mut writer)?;
}

self.tx.lock_time.consensus_encode(&mut writer)?;
sighash_type.as_u32().consensus_encode(&mut writer)?;
Ok(())
}

/// Compute the BIP143 sighash for any flag type.
pub fn signature_hash(
&mut self,
input_index: usize,
script_code: &Script,
value: u64,
sighash_type: SigHashType,
) -> SigHash {
let mut enc = SigHash::engine();
self.encode_signing_data_to(&mut enc, input_index, script_code, value, sighash_type)
.expect("engines don't error");
SigHash::from_engine(enc)
}
}
3 changes: 2 additions & 1 deletion elements-fun/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ extern crate serde_crate as serde;
#[macro_use]
mod internal_macros;
pub mod address;
pub mod bip143;
pub mod blech32;
mod block;
pub mod confidential;
Expand Down Expand Up @@ -62,5 +63,5 @@ pub use script::Script;
pub use transaction::{
AssetIssuance, ConfidentialAssetIssuance, ConfidentialTxOut, ExplicitAsset,
ExplicitAssetIssuance, ExplicitTxOut, ExplicitValue, OutPoint, PeginData, PegoutData,
Transaction, TxIn, TxInWitness, TxOut, TxOutWitness, UnblindedTxOut,
SigHashType, Transaction, TxIn, TxInWitness, TxOut, TxOutWitness, UnblindedTxOut,
};
75 changes: 72 additions & 3 deletions elements-fun/src/script.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,13 @@ use std::{default::Default, fmt, io, ops};
use serde;

use crate::{
bech32,
encode::{self, Decodable, Encodable},
opcodes, ScriptHash, WScriptHash,
opcodes, PubkeyHash, ScriptHash, WPubkeyHash, WScriptHash,
};

use bitcoin::PublicKey;
use bitcoin_hashes::Hash;

#[derive(Clone, Default, PartialOrd, Ord, PartialEq, Eq, Hash)]
/// A Bitcoin script
Expand Down Expand Up @@ -218,6 +220,75 @@ impl Script {
Script(vec![].into_boxed_slice())
}

/// Generates P2PK-type of scriptPubkey
pub fn new_p2pk(pubkey: &PublicKey) -> Script {
Builder::new()
.push_key(pubkey)
.push_opcode(opcodes::all::OP_CHECKSIG)
.into_script()
}

/// Generates P2PKH-type of scriptPubkey
pub fn new_p2pkh(pubkey_hash: &PubkeyHash) -> Script {
Builder::new()
.push_opcode(opcodes::all::OP_DUP)
.push_opcode(opcodes::all::OP_HASH160)
.push_slice(&pubkey_hash[..])
.push_opcode(opcodes::all::OP_EQUALVERIFY)
.push_opcode(opcodes::all::OP_CHECKSIG)
.into_script()
}

/// Generates P2SH-type of scriptPubkey with a given hash of the redeem script
pub fn new_p2sh(script_hash: &ScriptHash) -> Script {
Builder::new()
.push_opcode(opcodes::all::OP_HASH160)
.push_slice(&script_hash[..])
.push_opcode(opcodes::all::OP_EQUAL)
.into_script()
}

/// Generates P2WPKH-type of scriptPubkey
pub fn new_v0_wpkh(pubkey_hash: &WPubkeyHash) -> Script {
Script::new_witness_program(bech32::u5::try_from_u8(0).unwrap(), &pubkey_hash.to_vec())
}

/// Generates P2WSH-type of scriptPubkey with a given hash of the redeem script
pub fn new_v0_wsh(script_hash: &WScriptHash) -> Script {
Script::new_witness_program(bech32::u5::try_from_u8(0).unwrap(), &script_hash.to_vec())
}

/// Generates P2WSH-type of scriptPubkey with a given hash of the redeem script
pub fn new_witness_program(ver: bech32::u5, program: &[u8]) -> Script {
let mut verop = ver.to_u8();
assert!(verop <= 16, "incorrect witness version provided: {}", verop);
if verop > 0 {
verop = 0x50 + verop;
}
Builder::new()
.push_opcode(verop.into())
.push_slice(&program)
.into_script()
}

/// Generates OP_RETURN-type of scriptPubkey for a given data
pub fn new_op_return(data: &[u8]) -> Script {
Builder::new()
.push_opcode(opcodes::all::OP_RETURN)
.push_slice(data)
.into_script()
}

/// Returns 160-bit hash of the script
pub fn script_hash(&self) -> ScriptHash {
ScriptHash::hash(&self.as_bytes())
}

/// Returns 256-bit hash of the script for P2WSH outputs
pub fn wscript_hash(&self) -> WScriptHash {
WScriptHash::hash(&self.as_bytes())
}

/// The length in bytes of the script
pub fn len(&self) -> usize {
self.0.len()
Expand Down Expand Up @@ -245,7 +316,6 @@ impl Script {

/// Compute the P2SH output corresponding to this redeem script
pub fn to_p2sh(&self) -> Script {
use bitcoin::hashes::Hash;
Builder::new()
.push_opcode(opcodes::all::OP_HASH160)
.push_slice(&ScriptHash::hash(&self.0)[..])
Expand All @@ -256,7 +326,6 @@ impl Script {
/// Compute the P2WSH output corresponding to this witnessScript (aka the "witness redeem
/// script")
pub fn to_v0_p2wsh(&self) -> Script {
use bitcoin::hashes::Hash;
Builder::new()
.push_int(0)
.push_slice(&WScriptHash::hash(&self.0)[..])
Expand Down
55 changes: 55 additions & 0 deletions elements-fun/src/transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1435,6 +1435,61 @@ impl Decodable for Transaction {
}
}
}
/// Hashtype of a transaction, encoded in the last byte of a signature
/// Fixed values so they can be casted as integer types for encoding
#[derive(PartialEq, Eq, Debug, Copy, Clone)]
pub enum SigHashType {
/// 0x1: Sign all outputs
All = 0x01,
/// 0x2: Sign no outputs --- anyone can choose the destination
None = 0x02,
/// 0x3: Sign the output whose index matches this input's index. If none exists,
/// sign the hash `0000000000000000000000000000000000000000000000000000000000000001`.
/// (This rule is probably an unintentional C++ism, but it's consensus so we have
/// to follow it.)
Single = 0x03,
/// 0x81: Sign all outputs but only this input
AllPlusAnyoneCanPay = 0x81,
/// 0x82: Sign no outputs and only this input
NonePlusAnyoneCanPay = 0x82,
/// 0x83: Sign one output and only this input (see `Single` for what "one output" means)
SinglePlusAnyoneCanPay = 0x83,
}

impl SigHashType {
/// Break the sighash flag into the "real" sighash flag and the ANYONECANPAY boolean
pub(crate) fn split_anyonecanpay_flag(self) -> (SigHashType, bool) {
match self {
SigHashType::All => (SigHashType::All, false),
SigHashType::None => (SigHashType::None, false),
SigHashType::Single => (SigHashType::Single, false),
SigHashType::AllPlusAnyoneCanPay => (SigHashType::All, true),
SigHashType::NonePlusAnyoneCanPay => (SigHashType::None, true),
SigHashType::SinglePlusAnyoneCanPay => (SigHashType::Single, true),
}
}

/// Reads a 4-byte uint32 as a sighash type
pub fn from_u32(n: u32) -> SigHashType {
match n & 0x9f {
// "real" sighashes
0x01 => SigHashType::All,
0x02 => SigHashType::None,
0x03 => SigHashType::Single,
0x81 => SigHashType::AllPlusAnyoneCanPay,
0x82 => SigHashType::NonePlusAnyoneCanPay,
0x83 => SigHashType::SinglePlusAnyoneCanPay,
// catchalls
x if x & 0x80 == 0x80 => SigHashType::AllPlusAnyoneCanPay,
_ => SigHashType::All,
}
}

/// Converts to a u32
pub fn as_u32(self) -> u32 {
self as u32
}
}

#[cfg(test)]
mod tests {
Expand Down

0 comments on commit 24e25d2

Please sign in to comment.