bitcoin-cash 1.0.0-beta.0

A library for creating and parsing Bitcoin Cash trasactions
Documentation
use crate::{error::Result, BitcoinCode, ByteArray, ByteArrayError};

pub fn decode_bitcoin_code<T: BitcoinCode>(byte_array: impl Into<ByteArray>) -> Result<T> {
    let (item, leftover) = T::deser(byte_array.into())?;
    if !leftover.is_empty() {
        return Err(ByteArrayError::LeftoverBytes { bytes: leftover }.into());
    }
    return Ok(item);
}

#[cfg(test)]
mod tests {
    use crate::error::Result;
    use crate::{decode_bitcoin_code, BitcoinCode, ByteArray, Hashed, Sha256d};

    #[bitcoin_code(crate = "crate")]
    #[derive(BitcoinCode, PartialEq, Debug)]
    struct TxInput {
        pub prev_tx_hash: Sha256d,
        pub prev_vout: u32,
        pub script: Vec<u8>,
        pub sequence: u32,
    }

    #[bitcoin_code(crate = "crate")]
    #[derive(BitcoinCode, PartialEq, Debug)]
    struct TxOutput {
        pub value: u64,
        pub script: Vec<u8>,
    }

    #[bitcoin_code(crate = "crate")]
    #[derive(BitcoinCode, PartialEq, Debug)]
    struct Tx {
        pub version: u32,
        pub inputs: Vec<TxInput>,
        pub outputs: Vec<TxOutput>,
        pub locktime: u32,
    }

    #[test]
    fn test_struct() -> Result<()> {
        use hex_literal::hex;
        #[bitcoin_code(crate = "crate")]
        #[derive(BitcoinCode, PartialEq, Debug)]
        struct Test {
            int: u32,
            seq: Vec<Vec<u8>>,
        }

        let j = hex!("010000000201770199");
        let expected = Test {
            int: 1,
            seq: vec![b"\x77".to_vec(), b"\x99".to_vec()],
        };
        assert_eq!(expected, decode_bitcoin_code(&j)?);
        Ok(())
    }

    #[test]
    fn test_fields() -> Result<()> {
        #[bitcoin_code(crate = "crate")]
        #[derive(PartialEq, Debug, BitcoinCode)]
        struct TestPack {
            a: u8,
            b: u16,
            c: u32,
            d: u64,
            e: i8,
            f: i16,
            g: i32,
            h: i64,
        }

        #[bitcoin_code(crate = "crate")]
        #[derive(PartialEq, Debug, BitcoinCode)]
        struct Test {
            int: u32,
            seq: Vec<ByteArray>,
            relay: bool,
            pack: TestPack,
        }

        let sample = Test {
            int: 1,
            seq: vec![b"\x77".into(), b"\x99".into()],
            relay: true,
            pack: TestPack {
                a: 1,
                b: 2,
                c: 3,
                d: 4,
                e: 5,
                f: 6,
                g: 7,
                h: 8,
            },
        };
        let sample_encoded = hex::decode(format!(
            "01000000\
             0201770199\
             01\
             010200030000000400000000000000\
             050600070000000800000000000000",
        ))?;
        assert_eq!(sample, decode_bitcoin_code(sample.ser())?);
        assert_eq!(sample, decode_bitcoin_code(sample_encoded.as_slice())?);
        assert_eq!(sample.ser().as_slice(), sample_encoded.as_slice());
        Ok(())
    }

    #[test]
    fn test_encode_lengths() -> Result<()> {
        #[bitcoin_code(crate = "crate")]
        #[derive(BitcoinCode, PartialEq, Debug)]
        struct Test {
            array: ByteArray,
        };
        fn make(vec: Vec<u8>) -> Test {
            Test { array: vec.into() }
        }
        let encoded = make(vec![0x77; 0xfc]).ser();
        assert_eq!(encoded[0], 0xfc);
        assert_eq!(&encoded[1..], &[0x77; 0xfc][..]);
        let encoded = make(vec![0x88; 0xfd]).ser();
        assert_eq!(&encoded[0..3], &[0xfd, 0xfd, 0x00][..]);
        assert_eq!(&encoded[3..], &[0x88; 0xfd][..]);
        let encoded = make(vec![0x99; 0x103]).ser();
        assert_eq!(&encoded[0..3], &[0xfd, 0x03, 0x01][..]);
        assert_eq!(&encoded[3..], &[0x99; 0x103][..]);
        let encoded = make(vec![0xaa; 0xffff]).ser();
        assert_eq!(&encoded[0..3], &[0xfd, 0xff, 0xff][..]);
        assert_eq!(&encoded[3..], &[0xaa; 0xffff][..]);
        let encoded = make(vec![0xbb; 0x10000]).ser();
        assert_eq!(&encoded[0..5], &[0xfe, 0x00, 0x00, 0x01, 0x00][..]);
        assert_eq!(&encoded[5..], &[0xbb; 0x10000][..]);
        let encoded = make(vec![0xbb; 0x123456]).ser();
        assert_eq!(&encoded[0..5], &[0xfe, 0x56, 0x34, 0x12, 0x00][..]);
        assert_eq!(&encoded[5..], &[0xbb; 0x123456][..]);
        Ok(())
    }

    #[test]
    fn test_decode_lengths() -> Result<()> {
        #[bitcoin_code(crate = "crate")]
        #[derive(BitcoinCode, PartialEq, Debug)]
        struct Test {
            array: ByteArray,
        };
        fn make(vec: Vec<u8>) -> Test {
            Test { array: vec.into() }
        }
        let t: Test = decode_bitcoin_code([&[0xfc][..], &vec![0x77; 0xfc][..]].concat())?;
        assert_eq!(t, make(vec![0x77; 0xfc]));
        let t: Test =
            decode_bitcoin_code([&[0xfd, 0xfd, 0x00][..], &vec![0x88; 0xfd][..]].concat())?;
        assert_eq!(t, make(vec![0x88; 0xfd]));
        let t: Test =
            decode_bitcoin_code([&[0xfd, 0x03, 0x01][..], &vec![0x99; 0x103][..]].concat())?;
        assert_eq!(t, make(vec![0x99; 0x103]));
        let t: Test =
            decode_bitcoin_code([&[0xfd, 0xff, 0xff][..], &vec![0xaa; 0xffff][..]].concat())?;
        assert_eq!(t, make(vec![0xaa; 0xffff]));
        let t: Test = decode_bitcoin_code(
            [
                &[0xfe, 0x00, 0x00, 0x01, 0x00][..],
                &vec![0xbb; 0x10000][..],
            ]
            .concat(),
        )?;
        assert_eq!(t, make(vec![0xbb; 0x10000]));
        let t: Test = decode_bitcoin_code(
            [
                &[0xfe, 0x56, 0x34, 0x12, 0x00][..],
                &vec![0xcc; 0x123456][..],
            ]
            .concat(),
        )?;
        assert_eq!(t, make(vec![0xcc; 0x123456]));
        Ok(())
    }

    #[test]
    fn test_tx() -> Result<()> {
        let tx_raw = hex::decode(
            "0100000002b1c5c527d23f2f559ccac3748568806e617b38d76894b1e36c5e795e10ebe29400000000fc0047\
            304402207a8ed9b57865ce56935b60794526c9c48833f752394ba920faddb08a14cbd02502200879a7fe141b\
            dab57d28fc778f85fa0dc5df1f6af2bcd1d793387e2f4ef7492a414730440220623322db152ba053ed861fcd\
            148d5fc0cc49158019911cf2a8411693e0bb95c602201f04cf4f81ec37e32aa030b89b359e3c49491014bac2\
            d70a201e14932647ef11414c6952210257be20743c0bc14d33e6c0fe5b887b6cf47883b8924282a6948db577\
            4502acd4210280b4d5ca10b008b757999dae9bdad11a2c856490c3582bcdc9ff8ca458529bd12103ec98d577\
            ea245b65ca8c77f94463920ca53356b99b5ce49691c65e26f5b5683153aeffffffff3cbac2af90aa4e622214\
            8238ef3d5e268fa577ff01ecc67b8b534039c7403b8206000000fdfd000047304402207ac1f3a87aeef786e1\
            550b7832ca355da2195cff83bb5546569511920b2a2b5c02207d28e7b02d57f59bfad26c681071a88b4e3903\
            b07243735a9be5149bb28be2ed41483045022100a8a8af3a437c0dfa9c5bab36230d9e72727c972859a6facc\
            c76b506a4ae3294702206b924799bdc4880a707ec4b15e76e2503e2693dc8c1694fde237b60ad71cb637414c\
            69522102a9c79875e2de1a769831dba1a9cf20b7bddc48fbcbdb9c5eeab67d7fb682a01c21031187abcee948\
            d3c93c065fe5e560f7ae9cb7735b4e70507cebdf18ac7860a793210314d67175239913da79a0c905e086ad84\
            2a0470fcb774929eec0c9cc1222a6ef153aeffffffff01a0d90800000000001976a914f450f83dd8d1b09326\
            ae64857c3e9dfaa8a34ee688ac00000000"
        ).unwrap();
        assert_eq!(
            Sha256d::digest(tx_raw.as_slice()),
            Sha256d::from_hex_le(
                "fff9979f9c7afb3cbe7fe34083e6dd206e33b19df176772feefd55d71667bae1"
            )?,
        );
        let (tx, _) = Tx::deser(tx_raw.clone().into()).unwrap();
        assert_eq!(tx.version, 1);
        assert_eq!(tx.locktime, 0);
        let tx_in0 = &tx.inputs[0];
        let tx_in1 = &tx.inputs[1];
        assert_eq!(tx.inputs.len(), 2);
        assert_eq!(
            tx_in0.prev_tx_hash,
            Sha256d::from_hex_le(
                "94e2eb105e795e6ce3b19468d7387b616e80688574c3ca9c552f3fd227c5c5b1"
            )?
        );
        assert_eq!(tx_in0.prev_vout, 0);
        assert_eq!(
            hex::encode(&tx_in0.script),
            "0047304402207a8ed9b57865ce56935b60794526c9c48833f752394ba920faddb08a14cbd02502200879a\
             7fe141bdab57d28fc778f85fa0dc5df1f6af2bcd1d793387e2f4ef7492a414730440220623322db152ba05\
             3ed861fcd148d5fc0cc49158019911cf2a8411693e0bb95c602201f04cf4f81ec37e32aa030b89b359e3c4\
             9491014bac2d70a201e14932647ef11414c6952210257be20743c0bc14d33e6c0fe5b887b6cf47883b8924\
             282a6948db5774502acd4210280b4d5ca10b008b757999dae9bdad11a2c856490c3582bcdc9ff8ca458529\
             bd12103ec98d577ea245b65ca8c77f94463920ca53356b99b5ce49691c65e26f5b5683153ae",
        );
        assert_eq!(tx_in0.sequence, 0xffff_ffff);

        assert_eq!(
            tx_in1.prev_tx_hash,
            Sha256d::from_hex_le(
                "823b40c73940538b7bc6ec01ff77a58f265e3def38821422624eaa90afc2ba3c"
            )?
        );
        assert_eq!(tx_in1.prev_vout, 6);
        assert_eq!(
            hex::encode(&tx_in1.script),
            "0047304402207ac1f3a87aeef786e1550b7832ca355da2195cff83bb5546569511920b2a2b5c02207d28e\
             7b02d57f59bfad26c681071a88b4e3903b07243735a9be5149bb28be2ed41483045022100a8a8af3a437c0\
             dfa9c5bab36230d9e72727c972859a6faccc76b506a4ae3294702206b924799bdc4880a707ec4b15e76e25\
             03e2693dc8c1694fde237b60ad71cb637414c69522102a9c79875e2de1a769831dba1a9cf20b7bddc48fbc\
             bdb9c5eeab67d7fb682a01c21031187abcee948d3c93c065fe5e560f7ae9cb7735b4e70507cebdf18ac786\
             0a793210314d67175239913da79a0c905e086ad842a0470fcb774929eec0c9cc1222a6ef153ae",
        );
        assert_eq!(tx_in1.sequence, 0xffff_ffff);

        let tx_out0 = &tx.outputs[0];
        assert_eq!(tx.outputs.len(), 1);
        assert_eq!(tx_out0.value, 5_800_00);
        assert_eq!(
            hex::encode(&tx_out0.script),
            "76a914f450f83dd8d1b09326ae64857c3e9dfaa8a34ee688ac",
        );

        assert_eq!(tx.ser().as_slice(), tx_raw.as_slice());

        Ok(())
    }
}