mod txin;
mod txout;
use crate::{sha256d, Script, Serializable, VarInt};
use std::convert::TryInto;
pub use txin::TxIn;
pub use txout::TxOut;
type SighashType = u32;
pub const SIGHASH_ALL: SighashType = 0x01;
pub const SIGHASH_NONE: SighashType = 0x02;
pub const SIGHASH_SINGLE: SighashType = 0x03;
pub const SIGHASH_ANYONECANPAY: SighashType = 0x80;
pub const SIGHASH_FORKID: SighashType = 0x40;
macro_rules! iterate_collect {
($vec:expr, $method:ident) => {
($vec
.iter()
.map(|a| a.$method())
.flatten()
.collect::<Vec<u8>>())
};
}
pub struct Tx {
pub version: u32,
pub inputs: Vec<TxIn>,
pub outputs: Vec<TxOut>,
pub locktime: u32,
}
impl Tx {
pub fn new() -> Tx {
return Tx {
version: 0,
inputs: vec![],
outputs: vec![],
locktime: 0,
};
}
pub fn hash_prevouts(&self) -> Vec<u8> {
return sha256d(iterate_collect!((self.inputs), get_outpoint));
}
pub fn hash_sequence(&self) -> Vec<u8> {
return sha256d(iterate_collect!((self.inputs), get_seq_no));
}
pub fn hash_outputs(&self) -> Vec<u8> {
return sha256d(iterate_collect!((self.outputs), get_vout));
}
pub fn sighash(
&self,
index: usize,
pubkey: Script,
value: u64,
sighash_type: SighashType,
) -> Vec<u8> {
let mut v = vec![];
let has_anyone_can_pay = sighash_type & SIGHASH_ANYONECANPAY != 0;
let base_type = sighash_type & 0x1f;
let hash_prevouts = if !has_anyone_can_pay {
self.hash_prevouts()
} else {
vec![0; 32]
};
let hash_sequence = if !has_anyone_can_pay
&& (base_type != SIGHASH_SINGLE)
&& (base_type != SIGHASH_NONE)
{
self.hash_sequence()
} else {
vec![0; 32]
};
let hash_outputs = if (base_type != SIGHASH_SINGLE) && (base_type != SIGHASH_NONE) {
self.hash_outputs()
} else if (base_type != SIGHASH_SINGLE) && (index < self.outputs.len()) {
sha256d(self.outputs[index].get_vout())
} else {
vec![0; 32]
};
v.extend(self.version.to_le_bytes().to_vec());
v.extend(hash_prevouts);
v.extend(hash_sequence);
v.extend(self.inputs[index].get_outpoint());
v.extend(pubkey.serialize());
v.extend(value.to_le_bytes().to_vec());
v.extend(self.inputs[index].get_seq_no());
v.extend(hash_outputs);
v.extend(self.locktime.to_le_bytes().to_vec());
v.extend(sighash_type.to_le_bytes().to_vec());
return sha256d(v);
}
}
impl Serializable<Tx> for Tx {
fn serialize(&self) -> Vec<u8> {
panic!("Unimplemented!");
}
fn deserialize(cur: &mut [u8]) -> (Tx, &mut [u8]) {
let mut tx = Self::new();
let (version_bytes, cur) = cur.split_at_mut(4);
tx.version = u32::from_le_bytes((*version_bytes).try_into().unwrap());
let (num_inputs, mut cur) = VarInt::deserialize(cur);
for _ in 0..num_inputs {
let (tx_in, _cur) = TxIn::deserialize(cur);
tx.inputs.push(tx_in);
cur = _cur;
}
let (num_outputs, mut cur) = VarInt::deserialize(cur);
for _ in 0..num_outputs {
let (tx_out, _cur) = TxOut::deserialize(cur);
tx.outputs.push(tx_out);
cur = _cur;
}
let (locktime_bytes, cur) = cur.split_at_mut(4);
tx.locktime = u32::from_le_bytes((*locktime_bytes).try_into().unwrap());
return (tx, cur);
}
}
#[cfg(test)]
pub mod test {
use super::*;
struct TestCase {
pub deserialized_tx: Vec<u8>,
pub hash_prevouts: Vec<u8>,
pub hash_sequence: Vec<u8>,
pub hash_outputs: Vec<u8>,
pub sighash_case: Option<SighashCase>,
}
struct SighashCase {
pub sighash_type: u32,
pub index: usize,
pub value: u64,
pub pubkey: Vec<u8>,
pub sighash: Vec<u8>,
}
fn gen_test_cases() -> Vec<TestCase> {
return vec![
TestCase{
deserialized_tx: hex::decode("0100000002fff7f7881a8099afa6940d42d1e7f6362bec38171ea3edf433541db4e4ad969f0000000000eeffffffef51e1b804cc89d182d279655c3aa89e815b1b309fe287d9b2b55d57b90ec68a0100000000ffffffff02202cb206000000001976a9148280b37df378db99f66f85c95a783a76ac7a6d5988ac9093510d000000001976a9143bde42dbee7e4dbe6a21b2d50ce2f0167faa815988ac11000000").unwrap(),
hash_prevouts: hex::decode("96b827c8483d4e9b96712b6713a7b68d6e8003a781feba36c31143470b4efd37").unwrap(),
hash_sequence: hex::decode("52b0a642eea2fb7ae638c36f6252b6750293dbe574a806984b8e4d8548339a3b").unwrap(),
hash_outputs: hex::decode("863ef3e1a92afbfdb97f31ad0fc7683ee943e9abcf2501590ff8f6551f47e5e5").unwrap(),
sighash_case: Some(SighashCase{
sighash: hex::decode("c37af31116d1b27caf68aae9e3ac82f1477929014d5b917657d0eb49478cb670").unwrap(),
pubkey: hex::decode("76a9141d0f172a0ecb48aee1be1f2687d2963ae33f71a188ac").unwrap(),
sighash_type: 1,
index: 1,
value: 600000000,
}),
},
TestCase{
deserialized_tx: hex::decode("0100000001db6b1b20aa0fd7b23880be2ecbd4a98130974cf4748fb66092ac4d3ceb1a54770100000000feffffff02b8b4eb0b000000001976a914a457b684d7f0d539a46a45bbc043f35b59d0d96388ac0008af2f000000001976a914fd270b1ee6abcaea97fea7ad0402e8bd8ad6d77c88ac92040000").unwrap(),
hash_prevouts: hex::decode("b0287b4a252ac05af83d2dcef00ba313af78a3e9c329afa216eb3aa2a7b4613a").unwrap(),
hash_sequence: hex::decode("18606b350cd8bf565266bc352f0caddcf01e8fa789dd8a15386327cf8cabe198").unwrap(),
hash_outputs: hex::decode("de984f44532e2173ca0d64314fcefe6d30da6f8cf27bafa706da61df8a226c83").unwrap(),
sighash_case: None,
},
];
}
#[test]
fn test_sighash_prehashes() {
for case in &mut gen_test_cases() {
let (tx, _) = Tx::deserialize(case.deserialized_tx.as_mut_slice());
println!("Testing Hash Prevouts");
assert_eq!(tx.hash_prevouts(), case.hash_prevouts);
println!("Testing Hash Outputs");
assert_eq!(tx.hash_outputs(), case.hash_outputs);
println!("Testing Hash Sequence");
assert_eq!(tx.hash_sequence(), case.hash_sequence);
}
}
#[test]
fn test_simple_sighash() {
for case in &mut gen_test_cases() {
let s_case = match &case.sighash_case {
Some(case) => case,
None => continue,
};
let (tx, _) = Tx::deserialize(case.deserialized_tx.as_mut_slice());
let got_sighash = tx.sighash(
s_case.index,
Script::from_vec(s_case.pubkey.clone()),
s_case.value,
s_case.sighash_type,
);
assert_eq!(got_sighash, s_case.sighash);
}
}
}