Skip to content

Commit 24e25d2

Browse files
Merge commit '437d1e2c964faf182375169640f0561070dae346' into subtree-stuff
2 parents ad62453 + 437d1e2 commit 24e25d2

File tree

4 files changed

+323
-4
lines changed

4 files changed

+323
-4
lines changed

elements-fun/src/bip143.rs

Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
// Rust Bitcoin Library
2+
// Written in 2018 by
3+
// Andrew Poelstra <[email protected]>
4+
// To the extent possible under law, the author(s) have dedicated all
5+
// copyright and related and neighboring rights to this software to
6+
// the public domain worldwide. This software is distributed without
7+
// any warranty.
8+
//
9+
// You should have received a copy of the CC0 Public Domain Dedication
10+
// along with this software.
11+
// If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
12+
//
13+
14+
//! BIP143 Implementation
15+
//!
16+
//! Implementation of BIP143 Segwit-style signatures. Should be sufficient
17+
//! to create signatures for Segwit transactions (which should be pushed into
18+
//! the appropriate place in the `Transaction::witness` array) or bcash
19+
//! signatures, which are placed in the scriptSig.
20+
//!
21+
22+
use crate::{
23+
encode::{self, Encodable},
24+
hash_types::SigHash,
25+
script::Script,
26+
transaction::{SigHashType, Transaction},
27+
};
28+
use bitcoin_hashes::{sha256d, Hash};
29+
use std::{io, ops::Deref};
30+
31+
/// A replacement for SigHashComponents which supports all sighash modes
32+
pub struct SigHashCache<R: Deref<Target = Transaction>> {
33+
/// Access to transaction required for various introspection
34+
tx: R,
35+
/// Hash of all the previous outputs, computed as required
36+
hash_prevouts: Option<sha256d::Hash>,
37+
/// Hash of all the input sequence nos, computed as required
38+
hash_sequence: Option<sha256d::Hash>,
39+
/// Hash of all the outputs in this transaction, computed as required
40+
hash_outputs: Option<sha256d::Hash>,
41+
/// Hash of all the issunaces in this transaction, computed as required
42+
hash_issuances: Option<sha256d::Hash>,
43+
}
44+
45+
impl<R: Deref<Target = Transaction>> SigHashCache<R> {
46+
/// Compute the sighash components from an unsigned transaction and auxiliary
47+
/// in a lazy manner when required.
48+
/// For the generated sighashes to be valid, no fields in the transaction may change except for
49+
/// script_sig and witnesses.
50+
pub fn new(tx: R) -> Self {
51+
SigHashCache {
52+
tx: tx,
53+
hash_prevouts: None,
54+
hash_sequence: None,
55+
hash_outputs: None,
56+
hash_issuances: None,
57+
}
58+
}
59+
60+
/// Calculate hash for prevouts
61+
pub fn hash_prevouts(&mut self) -> sha256d::Hash {
62+
let hash_prevout = &mut self.hash_prevouts;
63+
let input = &self.tx.input;
64+
*hash_prevout.get_or_insert_with(|| {
65+
let mut enc = sha256d::Hash::engine();
66+
for txin in input {
67+
txin.previous_output.consensus_encode(&mut enc).unwrap();
68+
}
69+
sha256d::Hash::from_engine(enc)
70+
})
71+
}
72+
73+
/// Calculate hash for input sequence values
74+
pub fn hash_sequence(&mut self) -> sha256d::Hash {
75+
let hash_sequence = &mut self.hash_sequence;
76+
let input = &self.tx.input;
77+
*hash_sequence.get_or_insert_with(|| {
78+
let mut enc = sha256d::Hash::engine();
79+
for txin in input {
80+
txin.sequence.consensus_encode(&mut enc).unwrap();
81+
}
82+
sha256d::Hash::from_engine(enc)
83+
})
84+
}
85+
86+
/// Calculate hash for issuances
87+
pub fn hash_issuances(&mut self) -> sha256d::Hash {
88+
let hash_issuance = &mut self.hash_issuances;
89+
let input = &self.tx.input;
90+
*hash_issuance.get_or_insert_with(|| {
91+
let mut enc = sha256d::Hash::engine();
92+
for txin in input {
93+
if txin.has_issuance() {
94+
txin.asset_issuance.consensus_encode(&mut enc).unwrap();
95+
} else {
96+
0u8.consensus_encode(&mut enc).unwrap();
97+
}
98+
}
99+
sha256d::Hash::from_engine(enc)
100+
})
101+
}
102+
103+
/// Calculate hash for outputs
104+
pub fn hash_outputs(&mut self) -> sha256d::Hash {
105+
let hash_output = &mut self.hash_outputs;
106+
let output = &self.tx.output;
107+
*hash_output.get_or_insert_with(|| {
108+
let mut enc = sha256d::Hash::engine();
109+
for txout in output {
110+
txout.consensus_encode(&mut enc).unwrap();
111+
}
112+
sha256d::Hash::from_engine(enc)
113+
})
114+
}
115+
116+
/// Encode the BIP143 signing data for any flag type into a given object implementing a
117+
/// std::io::Write trait.
118+
pub fn encode_signing_data_to<Write: io::Write>(
119+
&mut self,
120+
mut writer: Write,
121+
input_index: usize,
122+
script_code: &Script,
123+
value: u64,
124+
sighash_type: SigHashType,
125+
) -> Result<(), encode::Error> {
126+
let zero_hash = sha256d::Hash::default();
127+
128+
let (sighash, anyone_can_pay) = sighash_type.split_anyonecanpay_flag();
129+
130+
self.tx.version.consensus_encode(&mut writer)?;
131+
132+
if !anyone_can_pay {
133+
self.hash_prevouts().consensus_encode(&mut writer)?;
134+
} else {
135+
zero_hash.consensus_encode(&mut writer)?;
136+
}
137+
138+
if !anyone_can_pay && sighash != SigHashType::Single && sighash != SigHashType::None {
139+
self.hash_sequence().consensus_encode(&mut writer)?;
140+
} else {
141+
zero_hash.consensus_encode(&mut writer)?;
142+
}
143+
144+
// Elements: Push the hash issuance zero hash as required
145+
// If required implement for issuance, but not necessary as of now
146+
if !anyone_can_pay {
147+
self.hash_issuances().consensus_encode(&mut writer)?;
148+
} else {
149+
zero_hash.consensus_encode(&mut writer)?;
150+
}
151+
152+
// input specific values
153+
{
154+
let txin = &self.tx.input[input_index];
155+
156+
txin.previous_output.consensus_encode(&mut writer)?;
157+
script_code.consensus_encode(&mut writer)?;
158+
value.consensus_encode(&mut writer)?;
159+
txin.sequence.consensus_encode(&mut writer)?;
160+
if txin.has_issuance() {
161+
txin.asset_issuance.consensus_encode(&mut writer)?;
162+
}
163+
}
164+
165+
// hashoutputs
166+
if sighash != SigHashType::Single && sighash != SigHashType::None {
167+
self.hash_outputs().consensus_encode(&mut writer)?;
168+
} else if sighash == SigHashType::Single && input_index < self.tx.output.len() {
169+
let mut single_enc = SigHash::engine();
170+
self.tx.output[input_index].consensus_encode(&mut single_enc)?;
171+
SigHash::from_engine(single_enc).consensus_encode(&mut writer)?;
172+
} else {
173+
zero_hash.consensus_encode(&mut writer)?;
174+
}
175+
176+
self.tx.lock_time.consensus_encode(&mut writer)?;
177+
sighash_type.as_u32().consensus_encode(&mut writer)?;
178+
Ok(())
179+
}
180+
181+
/// Compute the BIP143 sighash for any flag type.
182+
pub fn signature_hash(
183+
&mut self,
184+
input_index: usize,
185+
script_code: &Script,
186+
value: u64,
187+
sighash_type: SigHashType,
188+
) -> SigHash {
189+
let mut enc = SigHash::engine();
190+
self.encode_signing_data_to(&mut enc, input_index, script_code, value, sighash_type)
191+
.expect("engines don't error");
192+
SigHash::from_engine(enc)
193+
}
194+
}

elements-fun/src/lib.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ extern crate serde_crate as serde;
3434
#[macro_use]
3535
mod internal_macros;
3636
pub mod address;
37+
pub mod bip143;
3738
pub mod blech32;
3839
mod block;
3940
pub mod confidential;
@@ -62,5 +63,5 @@ pub use script::Script;
6263
pub use transaction::{
6364
AssetIssuance, ConfidentialAssetIssuance, ConfidentialTxOut, ExplicitAsset,
6465
ExplicitAssetIssuance, ExplicitTxOut, ExplicitValue, OutPoint, PeginData, PegoutData,
65-
Transaction, TxIn, TxInWitness, TxOut, TxOutWitness, UnblindedTxOut,
66+
SigHashType, Transaction, TxIn, TxInWitness, TxOut, TxOutWitness, UnblindedTxOut,
6667
};

elements-fun/src/script.rs

Lines changed: 72 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,13 @@ use std::{default::Default, fmt, io, ops};
3030
use serde;
3131

3232
use crate::{
33+
bech32,
3334
encode::{self, Decodable, Encodable},
34-
opcodes, ScriptHash, WScriptHash,
35+
opcodes, PubkeyHash, ScriptHash, WPubkeyHash, WScriptHash,
3536
};
3637

3738
use bitcoin::PublicKey;
39+
use bitcoin_hashes::Hash;
3840

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

223+
/// Generates P2PK-type of scriptPubkey
224+
pub fn new_p2pk(pubkey: &PublicKey) -> Script {
225+
Builder::new()
226+
.push_key(pubkey)
227+
.push_opcode(opcodes::all::OP_CHECKSIG)
228+
.into_script()
229+
}
230+
231+
/// Generates P2PKH-type of scriptPubkey
232+
pub fn new_p2pkh(pubkey_hash: &PubkeyHash) -> Script {
233+
Builder::new()
234+
.push_opcode(opcodes::all::OP_DUP)
235+
.push_opcode(opcodes::all::OP_HASH160)
236+
.push_slice(&pubkey_hash[..])
237+
.push_opcode(opcodes::all::OP_EQUALVERIFY)
238+
.push_opcode(opcodes::all::OP_CHECKSIG)
239+
.into_script()
240+
}
241+
242+
/// Generates P2SH-type of scriptPubkey with a given hash of the redeem script
243+
pub fn new_p2sh(script_hash: &ScriptHash) -> Script {
244+
Builder::new()
245+
.push_opcode(opcodes::all::OP_HASH160)
246+
.push_slice(&script_hash[..])
247+
.push_opcode(opcodes::all::OP_EQUAL)
248+
.into_script()
249+
}
250+
251+
/// Generates P2WPKH-type of scriptPubkey
252+
pub fn new_v0_wpkh(pubkey_hash: &WPubkeyHash) -> Script {
253+
Script::new_witness_program(bech32::u5::try_from_u8(0).unwrap(), &pubkey_hash.to_vec())
254+
}
255+
256+
/// Generates P2WSH-type of scriptPubkey with a given hash of the redeem script
257+
pub fn new_v0_wsh(script_hash: &WScriptHash) -> Script {
258+
Script::new_witness_program(bech32::u5::try_from_u8(0).unwrap(), &script_hash.to_vec())
259+
}
260+
261+
/// Generates P2WSH-type of scriptPubkey with a given hash of the redeem script
262+
pub fn new_witness_program(ver: bech32::u5, program: &[u8]) -> Script {
263+
let mut verop = ver.to_u8();
264+
assert!(verop <= 16, "incorrect witness version provided: {}", verop);
265+
if verop > 0 {
266+
verop = 0x50 + verop;
267+
}
268+
Builder::new()
269+
.push_opcode(verop.into())
270+
.push_slice(&program)
271+
.into_script()
272+
}
273+
274+
/// Generates OP_RETURN-type of scriptPubkey for a given data
275+
pub fn new_op_return(data: &[u8]) -> Script {
276+
Builder::new()
277+
.push_opcode(opcodes::all::OP_RETURN)
278+
.push_slice(data)
279+
.into_script()
280+
}
281+
282+
/// Returns 160-bit hash of the script
283+
pub fn script_hash(&self) -> ScriptHash {
284+
ScriptHash::hash(&self.as_bytes())
285+
}
286+
287+
/// Returns 256-bit hash of the script for P2WSH outputs
288+
pub fn wscript_hash(&self) -> WScriptHash {
289+
WScriptHash::hash(&self.as_bytes())
290+
}
291+
221292
/// The length in bytes of the script
222293
pub fn len(&self) -> usize {
223294
self.0.len()
@@ -245,7 +316,6 @@ impl Script {
245316

246317
/// Compute the P2SH output corresponding to this redeem script
247318
pub fn to_p2sh(&self) -> Script {
248-
use bitcoin::hashes::Hash;
249319
Builder::new()
250320
.push_opcode(opcodes::all::OP_HASH160)
251321
.push_slice(&ScriptHash::hash(&self.0)[..])
@@ -256,7 +326,6 @@ impl Script {
256326
/// Compute the P2WSH output corresponding to this witnessScript (aka the "witness redeem
257327
/// script")
258328
pub fn to_v0_p2wsh(&self) -> Script {
259-
use bitcoin::hashes::Hash;
260329
Builder::new()
261330
.push_int(0)
262331
.push_slice(&WScriptHash::hash(&self.0)[..])

elements-fun/src/transaction.rs

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1435,6 +1435,61 @@ impl Decodable for Transaction {
14351435
}
14361436
}
14371437
}
1438+
/// Hashtype of a transaction, encoded in the last byte of a signature
1439+
/// Fixed values so they can be casted as integer types for encoding
1440+
#[derive(PartialEq, Eq, Debug, Copy, Clone)]
1441+
pub enum SigHashType {
1442+
/// 0x1: Sign all outputs
1443+
All = 0x01,
1444+
/// 0x2: Sign no outputs --- anyone can choose the destination
1445+
None = 0x02,
1446+
/// 0x3: Sign the output whose index matches this input's index. If none exists,
1447+
/// sign the hash `0000000000000000000000000000000000000000000000000000000000000001`.
1448+
/// (This rule is probably an unintentional C++ism, but it's consensus so we have
1449+
/// to follow it.)
1450+
Single = 0x03,
1451+
/// 0x81: Sign all outputs but only this input
1452+
AllPlusAnyoneCanPay = 0x81,
1453+
/// 0x82: Sign no outputs and only this input
1454+
NonePlusAnyoneCanPay = 0x82,
1455+
/// 0x83: Sign one output and only this input (see `Single` for what "one output" means)
1456+
SinglePlusAnyoneCanPay = 0x83,
1457+
}
1458+
1459+
impl SigHashType {
1460+
/// Break the sighash flag into the "real" sighash flag and the ANYONECANPAY boolean
1461+
pub(crate) fn split_anyonecanpay_flag(self) -> (SigHashType, bool) {
1462+
match self {
1463+
SigHashType::All => (SigHashType::All, false),
1464+
SigHashType::None => (SigHashType::None, false),
1465+
SigHashType::Single => (SigHashType::Single, false),
1466+
SigHashType::AllPlusAnyoneCanPay => (SigHashType::All, true),
1467+
SigHashType::NonePlusAnyoneCanPay => (SigHashType::None, true),
1468+
SigHashType::SinglePlusAnyoneCanPay => (SigHashType::Single, true),
1469+
}
1470+
}
1471+
1472+
/// Reads a 4-byte uint32 as a sighash type
1473+
pub fn from_u32(n: u32) -> SigHashType {
1474+
match n & 0x9f {
1475+
// "real" sighashes
1476+
0x01 => SigHashType::All,
1477+
0x02 => SigHashType::None,
1478+
0x03 => SigHashType::Single,
1479+
0x81 => SigHashType::AllPlusAnyoneCanPay,
1480+
0x82 => SigHashType::NonePlusAnyoneCanPay,
1481+
0x83 => SigHashType::SinglePlusAnyoneCanPay,
1482+
// catchalls
1483+
x if x & 0x80 == 0x80 => SigHashType::AllPlusAnyoneCanPay,
1484+
_ => SigHashType::All,
1485+
}
1486+
}
1487+
1488+
/// Converts to a u32
1489+
pub fn as_u32(self) -> u32 {
1490+
self as u32
1491+
}
1492+
}
14381493

14391494
#[cfg(test)]
14401495
mod tests {

0 commit comments

Comments
 (0)