bsv_wasm/transaction/
sighash.rs

1use crate::BSVErrors;
2use crate::ECDSA;
3use std::convert::TryFrom;
4use std::io::Write;
5
6use crate::{transaction::*, Hash, PrivateKey, PublicKey, Script, Signature};
7use byteorder::{LittleEndian, WriteBytesExt};
8use num_traits::{FromPrimitive, ToPrimitive};
9use strum_macros::EnumString;
10#[cfg(target_arch = "wasm32")]
11use wasm_bindgen::throw_str;
12
13#[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-transaction"), wasm_bindgen)]
14#[derive(Debug, Clone, Copy, Eq, PartialEq, PartialOrd, Ord, FromPrimitive, ToPrimitive, EnumString)]
15#[allow(non_camel_case_types)]
16pub enum SigHash {
17    FORKID = 0x40,
18    ALL = 0x01,
19    NONE = 0x02,
20    SINGLE = 0x03,
21    ANYONECANPAY = 0x80,
22    // MAGIC = 0x21e8, - Idea for the future
23    /**
24     * ALL | FORKID
25     */
26    InputsOutputs = 0x41,
27    /**
28     * NONE | FORKID
29     */
30    Inputs = 0x42,
31    /**
32     * SINGLE | FORKID
33     */
34    InputsOutput = 0x43,
35    /**
36     * ALL | ANYONECANPAY | FORKID
37     */
38    InputOutputs = 0xc1,
39    /**
40     * NONE | ANYONECANPAY | FORKID
41     */
42    Input = 0xc2,
43    /**
44     * SINGLE | ANYONECANPAY | FORKID
45     */
46    InputOutput = 0xc3,
47
48    /**
49     * ALL | ANYONECANPAY
50     */
51    Legacy_InputOutputs = 0x81,
52    /**
53     * NONE | ANYONECANPAY
54     */
55    Legacy_Input = 0x82,
56    /**
57     * SINGLE | ANYONECANPAY
58     */
59    Legacy_InputOutput = 0x83,
60}
61
62impl TryFrom<u8> for SigHash {
63    type Error = BSVErrors;
64
65    fn try_from(value: u8) -> Result<Self, Self::Error> {
66        FromPrimitive::from_u8(value).ok_or_else(|| BSVErrors::ToSighash(format!("Could not convert {} into a valid SigHash value", value)))
67    }
68}
69
70impl std::ops::BitOr for SigHash {
71    type Output = u8;
72
73    fn bitor(self, rhs: Self) -> Self::Output {
74        let lhs = self.to_u8().unwrap();
75        lhs | rhs.to_u8().unwrap()
76    }
77}
78
79impl std::ops::BitAnd for SigHash {
80    type Output = u8;
81
82    fn bitand(self, rhs: Self) -> Self::Output {
83        let lhs = self.to_u8().unwrap();
84        lhs & rhs.to_u8().unwrap()
85    }
86}
87
88#[derive(Debug, Clone, PartialEq, Default)]
89pub struct HashCache {
90    pub(super) hash_inputs: Option<Hash>,
91    pub(super) hash_sequence: Option<Hash>,
92    pub(super) hash_outputs: Option<Hash>,
93}
94
95impl HashCache {
96    /// Creates a new cache
97    pub fn new() -> Self {
98        HashCache {
99            hash_inputs: None,
100            hash_sequence: None,
101            hash_outputs: None,
102        }
103    }
104}
105
106impl Transaction {
107    /**
108     * Calculates the SIGHASH buffer and then signs it
109     */
110    pub(crate) fn sign_impl(&mut self, priv_key: &PrivateKey, sighash: SigHash, n_tx_in: usize, unsigned_script: &Script, value: u64) -> Result<SighashSignature, BSVErrors> {
111        let buffer = self.sighash_preimage_impl(n_tx_in, sighash, unsigned_script, value)?;
112
113        let signature = ECDSA::sign_with_deterministic_k_impl(priv_key, &buffer, crate::SigningHash::Sha256d, true)?;
114
115        Ok(SighashSignature {
116            signature,
117            sighash_type: sighash,
118            sighash_buffer: buffer,
119        })
120    }
121
122    /**
123     * Calculates the SIGHASH buffer and then signs it with a specific ephemeral key. I hope you know what you're doing!
124     */
125    pub(crate) fn sign_with_k_impl(
126        &mut self,
127        priv_key: &PrivateKey,
128        ephemeral_key: &PrivateKey,
129        sighash: SigHash,
130        n_tx_in: usize,
131        unsigned_script: &Script,
132        value: u64,
133    ) -> Result<SighashSignature, BSVErrors> {
134        let buffer = self.sighash_preimage_impl(n_tx_in, sighash, unsigned_script, value)?;
135
136        let signature = ECDSA::sign_with_k_impl(priv_key, ephemeral_key, &buffer, crate::SigningHash::Sha256d)?;
137
138        Ok(SighashSignature {
139            signature,
140            sighash_type: sighash,
141            sighash_buffer: buffer,
142        })
143    }
144
145    /**
146     * Calculates the SIGHASH Buffer to be signed
147     */
148    pub(crate) fn sighash_preimage_impl(&mut self, n_tx_in: usize, sighash: SigHash, unsigned_script: &Script, value: u64) -> Result<Vec<u8>, BSVErrors> {
149        // If uses any of the FORK_ID sighash variants
150        // Gross, fix this. Maybe a nice method on SigHash enum to check if contains another SigHash type
151        match sighash {
152            SigHash::Input | SigHash::InputOutput | SigHash::InputOutputs | SigHash::Inputs | SigHash::InputsOutput | SigHash::InputsOutputs => {
153                self.sighash_bip143(n_tx_in, sighash, unsigned_script, value)
154            }
155            _ => self.sighash_legacy(n_tx_in, sighash, unsigned_script),
156        }
157    }
158
159    pub(crate) fn sighash_legacy(&mut self, n_tx_in: usize, sighash: SigHash, unsigned_script: &Script) -> Result<Vec<u8>, BSVErrors> {
160        let mut tx = self.clone();
161        let mut script = unsigned_script.clone();
162        script.remove_codeseparators();
163
164        // Empty scripts
165        tx.inputs.iter_mut().for_each(|txin| txin.set_script(&Script::default()));
166
167        let mut prev_txin = tx.get_input(n_tx_in).ok_or_else(|| BSVErrors::OutOfBounds(format!("Could not get TxIn at index {}", n_tx_in)))?;
168        prev_txin.set_script(&script);
169        tx.set_input(n_tx_in, &prev_txin);
170
171        match sighash {
172            SigHash::SINGLE | SigHash::Legacy_InputOutput => {
173                // Not supporting the SIGHASH_SINGLE bug. Sue me craig.
174                // // This if statement is needed because of Consensus SIGHASH_SINGLE bug
175                // // https://bitcoinfiles.org/t/9a3a165cc7881bb2e37567dec5eaab64568a889e83e6b850b42f347e1d96a555
176                // if n_tx_in >= tx.outputs.len() {
177                //   return Ok(hex::decode("0000000000000000000000000000000000000000000000000000000000000001").map_err(|e| anyhow!(e))?)
178                // }
179
180                let txout = tx.get_output(n_tx_in).ok_or_else(|| BSVErrors::OutOfBounds(format!("Could not get TxOut at index {}", n_tx_in)))?;
181                tx.outputs = vec![txout];
182
183                for i in 0..tx.outputs.len() {
184                    if i < n_tx_in {
185                        tx.set_output(i, &TxOut::new(0xffffffffffffffff, &Script::default()));
186                    }
187                }
188
189                for i in 0..tx.inputs.len() {
190                    if i == n_tx_in {
191                        continue;
192                    }
193
194                    tx.inputs[i].set_sequence(0x00000000);
195                }
196            }
197
198            SigHash::NONE | SigHash::Legacy_Input => {
199                tx.outputs.clear();
200
201                for i in 0..tx.inputs.len() {
202                    if i == n_tx_in {
203                        continue;
204                    }
205
206                    tx.inputs[i].set_sequence(0x00000000);
207                }
208            }
209            _ => {}
210        }
211
212        if sighash.ge(&SigHash::ANYONECANPAY) {
213            let input = tx.inputs[n_tx_in].clone();
214            tx.inputs = vec![];
215            tx.add_input(&input);
216        }
217
218        let mut buffer = tx.to_bytes_impl()?;
219        let sighash_i32 = sighash.to_i32().ok_or_else(|| BSVErrors::FromSighash(format!("Cannot convert SigHash {:?} into i32", sighash)))?;
220        buffer.write_i32::<LittleEndian>(sighash_i32)?;
221
222        Ok(buffer)
223    }
224
225    pub(crate) fn sighash_bip143(&mut self, n_tx_in: usize, sighash: SigHash, unsigned_script: &Script, value: u64) -> Result<Vec<u8>, BSVErrors> {
226        let mut buffer: Vec<u8> = vec![];
227
228        let input = self.get_input(n_tx_in).ok_or_else(|| BSVErrors::OutOfBounds(format!("Could not get TxIn at index {}", n_tx_in)))?;
229
230        let hashed_outputs = self.hash_outputs(sighash, n_tx_in)?;
231
232        buffer.write_u32::<LittleEndian>(self.version)?;
233        buffer.write_all(&self.hash_inputs(sighash))?;
234        buffer.write_all(&self.hash_sequence(sighash))?;
235        buffer.write_all(&input.get_outpoint_bytes(Some(true)))?;
236        buffer.write_varint(unsigned_script.to_bytes().len() as u64)?;
237        buffer.write_all(&unsigned_script.to_bytes())?;
238        buffer.write_u64::<LittleEndian>(value)?;
239        buffer.write_u32::<LittleEndian>(input.get_sequence())?;
240        buffer.write_all(&hashed_outputs)?;
241        buffer.write_u32::<LittleEndian>(self.n_locktime)?;
242
243        let sighash_u32 = sighash.to_u32().ok_or_else(|| BSVErrors::FromSighash(format!("Cannot convert SigHash {:?} into u32", sighash)))?;
244        buffer.write_u32::<LittleEndian>(sighash_u32)?;
245
246        Ok(buffer)
247    }
248
249    /**
250     * Checks the hash cache to see if there already are hashed sequence, otherwise calculates the hash and adds it to the cache
251     */
252    fn hash_sequence(&mut self, sighash: SigHash) -> Vec<u8> {
253        match sighash {
254            SigHash::ALL | SigHash::InputsOutputs => {
255                if let Some(x) = &self.hash_cache.hash_sequence {
256                    return x.to_bytes();
257                }
258                let input_sequences: Vec<u8> = self.inputs.iter().flat_map(|x| x.get_sequence_as_bytes()).collect();
259                let hash = Hash::sha_256d(&input_sequences);
260                self.hash_cache.hash_sequence = Some(hash.clone());
261                hash.to_bytes()
262            }
263            _ => [0; 32].to_vec(),
264        }
265    }
266
267    /**
268     * Checks the hash cache to see if there already are hashed outputs, otherwise calculates the hash and adds it to the cache
269     */
270    fn hash_outputs(&mut self, sighash: SigHash, n_tx_in: usize) -> Result<Vec<u8>, BSVErrors> {
271        match sighash {
272            // Only sign the output at the same index as the given txin
273            SigHash::SINGLE | SigHash::InputOutput | SigHash::Legacy_InputOutput | SigHash::InputsOutput => {
274                if n_tx_in > self.get_noutputs() as usize {
275                    return Err(BSVErrors::OutOfBounds("Cannot sign with SIGHASH_SINGLE given input index greater than number of outputs".into()));
276                }
277
278                let output = self.get_output(n_tx_in).ok_or_else(|| BSVErrors::OutOfBounds(format!("Could not find output at index {}", n_tx_in)))?;
279                let output_bytes = output.to_bytes_impl()?;
280                Ok(Hash::sha_256d(&output_bytes).to_bytes())
281            }
282            // Sign all outputs
283            SigHash::ALL | SigHash::InputOutputs | SigHash::Legacy_InputOutputs | SigHash::InputsOutputs => {
284                if let Some(x) = &self.hash_cache.hash_outputs {
285                    return Ok(x.to_bytes());
286                }
287                let mut txout_bytes = Vec::new();
288                for output in &self.outputs {
289                    txout_bytes.write_all(&output.to_bytes_impl()?)?;
290                }
291                let hash = Hash::sha_256d(&txout_bytes);
292                self.hash_cache.hash_outputs = Some(hash.clone());
293                Ok(hash.to_bytes())
294            }
295            _ => Ok([0; 32].to_vec()),
296        }
297    }
298
299    /**
300     * (hashPrevouts) https://github.com/bitcoincashorg/bitcoincash.org/blob/master/spec/replay-protected-sighash.md
301     * Checks the hash cache to see if there already are hashed inputs, otherwise calculates the hash and adds it to the cache.
302     *
303     * Logic:
304     * - If SigHash does not contain ANYONECANPAY, SHA256d all input outpoints
305     * - Else 32 bytes of zeroes
306     */
307    pub fn hash_inputs(&mut self, sighash: SigHash) -> Vec<u8> {
308        match sighash {
309            SigHash::ANYONECANPAY | SigHash::Input | SigHash::InputOutput | SigHash::Legacy_Input | SigHash::Legacy_InputOutput | SigHash::InputOutputs => [0; 32].to_vec(),
310            _ => {
311                if let Some(x) = &self.hash_cache.hash_inputs {
312                    return x.to_bytes();
313                }
314                let input_bytes: Vec<u8> = self.inputs.iter().flat_map(|txin| txin.get_outpoint_bytes(Some(true))).collect();
315
316                let hash = Hash::sha_256d(&input_bytes);
317                self.hash_cache.hash_inputs = Some(hash.clone());
318
319                hash.to_bytes()
320            }
321        }
322    }
323}
324
325#[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-transaction"), wasm_bindgen)]
326impl Transaction {
327    pub fn verify(&self, pub_key: &PublicKey, sig: &SighashSignature) -> bool {
328        ECDSA::verify_digest_impl(&sig.sighash_buffer, pub_key, &sig.signature, crate::SigningHash::Sha256d).unwrap_or(false)
329    }
330}
331
332#[cfg(not(all(target_arch = "wasm32", feature = "wasm-bindgen-transaction")))]
333impl Transaction {
334    pub fn sign(&mut self, priv_key: &PrivateKey, sighash: SigHash, n_tx_in: usize, unsigned_script: &Script, value: u64) -> Result<SighashSignature, BSVErrors> {
335        Transaction::sign_impl(self, priv_key, sighash, n_tx_in, unsigned_script, value)
336    }
337
338    pub fn sign_with_k(&mut self, priv_key: &PrivateKey, ephemeral_key: &PrivateKey, sighash: SigHash, n_tx_in: usize, unsigned_script: &Script, value: u64) -> Result<SighashSignature, BSVErrors> {
339        Transaction::sign_with_k_impl(self, priv_key, ephemeral_key, sighash, n_tx_in, unsigned_script, value)
340    }
341
342    pub fn sighash_preimage(&mut self, sighash: SigHash, n_tx_in: usize, unsigned_script: &Script, value: u64) -> Result<Vec<u8>, BSVErrors> {
343        Transaction::sighash_preimage_impl(self, n_tx_in, sighash, unsigned_script, value)
344    }
345}
346
347#[cfg(all(target_arch = "wasm32", feature = "wasm-bindgen-transaction"))]
348#[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-transaction"), wasm_bindgen)]
349impl Transaction {
350    #[cfg_attr(target_arch = "wasm32", wasm_bindgen(js_name = sign))]
351    pub fn sign(&mut self, priv_key: &PrivateKey, sighash: SigHash, n_tx_in: usize, unsigned_script: &Script, value: u64) -> Result<SighashSignature, JsValue> {
352        match Transaction::sign_impl(self, priv_key, sighash, n_tx_in, unsigned_script, value) {
353            Ok(v) => Ok(v),
354            Err(e) => Err(JsValue::from_str(&e.to_string())),
355        }
356    }
357
358    #[cfg_attr(target_arch = "wasm32", wasm_bindgen(js_name = signWithK))]
359    pub fn sign_with_k(&mut self, priv_key: &PrivateKey, ephemeral_key: &PrivateKey, sighash: SigHash, n_tx_in: usize, unsigned_script: &Script, value: u64) -> Result<SighashSignature, JsValue> {
360        match Transaction::sign_with_k_impl(self, priv_key, ephemeral_key, sighash, n_tx_in, unsigned_script, value) {
361            Ok(v) => Ok(v),
362            Err(e) => Err(JsValue::from_str(&e.to_string())),
363        }
364    }
365
366    #[cfg_attr(target_arch = "wasm32", wasm_bindgen(js_name = sighashPreimage))]
367    pub fn sighash_preimage(&mut self, sighash: SigHash, n_tx_in: usize, unsigned_script: &Script, value: u64) -> Result<Vec<u8>, JsValue> {
368        match Transaction::sighash_preimage_impl(self, n_tx_in, sighash, unsigned_script, value) {
369            Ok(v) => Ok(v),
370            Err(e) => Err(JsValue::from_str(&e.to_string())),
371        }
372    }
373}
374
375#[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-transaction"), wasm_bindgen)]
376pub struct SighashSignature {
377    pub(crate) signature: Signature,
378    pub(crate) sighash_type: SigHash,
379    pub(crate) sighash_buffer: Vec<u8>,
380}
381
382impl SighashSignature {
383    pub(crate) fn to_hex_impl(&self) -> Result<String, BSVErrors> {
384        Ok(hex::encode(self.to_bytes_impl()?))
385    }
386
387    pub(crate) fn to_bytes_impl(&self) -> Result<Vec<u8>, BSVErrors> {
388        let mut sig_bytes = self.signature.to_der_bytes();
389        let sighash_u8 = self
390            .sighash_type
391            .to_u8()
392            .ok_or_else(|| BSVErrors::FromSighash(format!("Cannot convert SigHash {:?} into u8", self.sighash_type)))?;
393
394        sig_bytes.push(sighash_u8);
395        Ok(sig_bytes)
396    }
397}
398
399#[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-transaction"), wasm_bindgen)]
400impl SighashSignature {
401    #[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-transaction"), wasm_bindgen(constructor))]
402    pub fn new(signature: &Signature, sighash_type: SigHash, sighash_buffer: &[u8]) -> SighashSignature {
403        SighashSignature {
404            signature: signature.clone(),
405            sighash_type,
406            sighash_buffer: sighash_buffer.to_vec(),
407        }
408    }
409}
410
411#[cfg(not(all(target_arch = "wasm32", feature = "wasm-bindgen-transaction")))]
412impl SighashSignature {
413    pub fn to_hex(&self) -> Result<String, BSVErrors> {
414        self.to_hex_impl()
415    }
416
417    pub fn to_bytes(&self) -> Result<Vec<u8>, BSVErrors> {
418        self.to_bytes_impl()
419    }
420}
421
422#[cfg(all(target_arch = "wasm32", feature = "wasm-bindgen-transaction"))]
423#[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-transaction"), wasm_bindgen)]
424impl SighashSignature {
425    #[cfg_attr(target_arch = "wasm32", wasm_bindgen(js_name = toHex))]
426    pub fn to_hex(&self) -> Result<String, JsValue> {
427        match self.to_hex_impl() {
428            Ok(v) => Ok(v),
429            Err(e) => Err(JsValue::from_str(&e.to_string())),
430        }
431    }
432
433    #[cfg_attr(target_arch = "wasm32", wasm_bindgen(js_name = toBytes))]
434    pub fn to_bytes(&self) -> Result<Vec<u8>, JsValue> {
435        match self.to_bytes_impl() {
436            Ok(v) => Ok(v),
437            Err(e) => Err(JsValue::from_str(&e.to_string())),
438        }
439    }
440}