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 InputsOutputs = 0x41,
27 Inputs = 0x42,
31 InputsOutput = 0x43,
35 InputOutputs = 0xc1,
39 Input = 0xc2,
43 InputOutput = 0xc3,
47
48 Legacy_InputOutputs = 0x81,
52 Legacy_Input = 0x82,
56 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 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 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 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 pub(crate) fn sighash_preimage_impl(&mut self, n_tx_in: usize, sighash: SigHash, unsigned_script: &Script, value: u64) -> Result<Vec<u8>, BSVErrors> {
149 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 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 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 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 fn hash_outputs(&mut self, sighash: SigHash, n_tx_in: usize) -> Result<Vec<u8>, BSVErrors> {
271 match sighash {
272 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 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 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}