bitcointx 0.0.15

Bitcoin TX utility crate
Documentation
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;

/// Signs all of the outputs
pub const SIGHASH_ALL: SighashType = 0x01;
/// Sign none of the outputs so that they may be spent anywhere
pub const SIGHASH_NONE: SighashType = 0x02;
/// Sign only the output paired with the the input
pub const SIGHASH_SINGLE: SighashType = 0x03;
/// Sign only the input so others may inputs to the transaction
pub const SIGHASH_ANYONECANPAY: SighashType = 0x80;
/// Bitcoin Cash / SV sighash flag for use on outputs after the fork
pub const SIGHASH_FORKID: SighashType = 0x40;

// Iterates through a provided vector of structs
// and executes an implemented method on it
// collects the resultant vector after that
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);
        }
    }
}