bitcoin_cash/
tx_preimage.rs

1use crate::{
2    encode_bitcoin_code, BitcoinByteArray, BitcoinDataType, ByteArray, DataType, Hashed, Op,
3    Script, Sha256d, ToPreimages, TxOutpoint,
4};
5use bitflags::bitflags;
6use serde_derive::{Deserialize, Serialize};
7
8bitflags! {
9    #[derive(Deserialize, Serialize, Default)]
10    pub struct SigHashFlags: u32 {
11        const ALL          = 0x01;
12        const NONE         = 0x02;
13        const SINGLE       = 0x03;
14        const FORKID       = 0x40;
15        const ANYONECANPAY = 0x80;
16        const MASK         = 0x1f;
17        const DEFAULT      = Self::ALL.bits | Self::FORKID.bits;
18    }
19}
20
21#[derive(Clone, Debug, Default)]
22pub struct TxPreimage {
23    pub version: i32,
24    pub hash_prevouts: Sha256d,
25    pub hash_sequence: Sha256d,
26    pub outpoint: TxOutpoint,
27    pub script_code: ByteArray,
28    pub value: u64,
29    pub sequence: u32,
30    pub hash_outputs: Sha256d,
31    pub lock_time: u32,
32    pub sig_hash_type: u32,
33}
34
35impl SigHashFlags {
36    pub fn from_u8(flags: u8) -> Self {
37        let mut sig_hash_flags = Self::DEFAULT;
38        sig_hash_flags.bits = flags as u32;
39        sig_hash_flags
40    }
41}
42
43impl TxPreimage {
44    pub fn build_preimages(tx: &impl ToPreimages) -> Vec<Vec<TxPreimage>> {
45        let hash_all_prevouts = {
46            let mut outpoints_serialized = ByteArray::from_slice_unnamed(&[]);
47            for input_idx in 0..tx.num_inputs() {
48                outpoints_serialized = outpoints_serialized.concat(ByteArray::new(
49                    format!("outpoint_{}", input_idx),
50                    encode_bitcoin_code(tx.input_outpoint_at(input_idx))
51                        .expect("Cannot encode outpoint"),
52                ));
53            }
54            Sha256d::digest(outpoints_serialized.named("prevouts")).named("hashPrevouts")
55        };
56        let hash_all_sequences = {
57            let mut sequences_serialized = ByteArray::from_slice_unnamed(&[]);
58            for input_idx in 0..tx.num_inputs() {
59                sequences_serialized = sequences_serialized.concat(ByteArray::new(
60                    format!("sequence_{}", input_idx),
61                    encode_bitcoin_code(&tx.input_sequence_at(input_idx))
62                        .expect("Cannot encode sequence"),
63                ));
64            }
65            Sha256d::digest(sequences_serialized.named("sequences")).named("hashSequence")
66        };
67
68        let hash_all_outputs = {
69            let mut outputs_serialized = ByteArray::from_slice_unnamed(&[]);
70            for output_idx in 0..tx.num_outputs() {
71                let byte_array = tx
72                    .output_at(output_idx)
73                    .serialize()
74                    .expect("Cannot encode output")
75                    .named(format!("output_{}", output_idx));
76                outputs_serialized = outputs_serialized.concat(byte_array);
77            }
78            Sha256d::digest(outputs_serialized.named("outputs")).named("hashOutputs")
79        };
80        let mut inputs_preimages = Vec::with_capacity(tx.num_inputs());
81        for input_idx in 0..tx.num_inputs() {
82            let sig_hash_flags = tx.input_sig_hash_flags_at(input_idx);
83            let mut preimages = Vec::with_capacity(sig_hash_flags.len());
84            for &sig_hash_flags in sig_hash_flags {
85                let hash_prevouts = if !sig_hash_flags.contains(SigHashFlags::ANYONECANPAY) {
86                    hash_all_prevouts.clone()
87                } else {
88                    Sha256d::new([0; 32]).named("hashPrevouts")
89                };
90                let masked_flags = sig_hash_flags & SigHashFlags::MASK;
91                let hash_sequence = if !sig_hash_flags.contains(SigHashFlags::ANYONECANPAY)
92                    && masked_flags != SigHashFlags::SINGLE
93                    && masked_flags != SigHashFlags::NONE
94                {
95                    hash_all_sequences.clone()
96                } else {
97                    Sha256d::new([0; 32]).named("hashSequence")
98                };
99                let hash_outputs =
100                    if masked_flags != SigHashFlags::SINGLE && masked_flags != SigHashFlags::NONE {
101                        hash_all_outputs.clone()
102                    } else if masked_flags == SigHashFlags::SINGLE && input_idx < tx.num_outputs() {
103                        Sha256d::digest(
104                            tx.output_at(input_idx)
105                                .serialize()
106                                .expect("Cannot encode output"),
107                        )
108                    } else {
109                        Sha256d::new([0; 32]).named("hashOutputs")
110                    };
111                preimages.push(TxPreimage {
112                    version: tx.version(),
113                    hash_prevouts,
114                    hash_sequence,
115                    outpoint: tx.input_outpoint_at(input_idx).clone(),
116                    script_code: encode_bitcoin_code(
117                        &tx.input_lock_script_at(input_idx).to_script_code_first(),
118                    )
119                    .unwrap()
120                    .into(),
121                    value: tx.input_value_at(input_idx),
122                    sequence: tx.input_sequence_at(input_idx),
123                    hash_outputs,
124                    lock_time: tx.lock_time(),
125                    sig_hash_type: sig_hash_flags.bits(),
126                });
127            }
128            inputs_preimages.push(preimages);
129        }
130        inputs_preimages
131    }
132
133    pub fn size_with_script(script_code: &Script) -> usize {
134        struct TxPreimageWithoutScript {
135            _version: i32,
136            _hash_prevouts: Sha256d,
137            _hash_sequence: Sha256d,
138            _outpoint: TxOutpoint,
139            _value: u64,
140            _sequence: u32,
141            _hash_outputs: Sha256d,
142            _lock_time: u32,
143            _sighash_flags: u32,
144        }
145        #[derive(Serialize)]
146        struct TxPreimageOnlyScript<'a> {
147            script_code: &'a Script,
148        }
149        let script_size = encode_bitcoin_code(&TxPreimageOnlyScript { script_code })
150            .expect("Couldn't encode script")
151            .len();
152        let rest_size = std::mem::size_of::<TxPreimageWithoutScript>();
153        script_size + rest_size
154    }
155
156    pub fn empty_with_script(script_code: &Script) -> TxPreimage {
157        TxPreimage {
158            version: 0,
159            hash_prevouts: Sha256d::new([0; 32]).named("hashPrevouts"),
160            hash_sequence: Sha256d::new([0; 32]).named("hashSequence"),
161            outpoint: TxOutpoint {
162                tx_hash: Sha256d::new([0; 32]),
163                vout: 0,
164            },
165            script_code: script_code.serialize().expect("Cannot encode script_code"),
166            value: 0,
167            sequence: 0,
168            hash_outputs: Sha256d::new([0; 32]).named("hashOutputs"),
169            lock_time: 0,
170            sig_hash_type: SigHashFlags::ALL.bits(),
171        }
172    }
173
174    pub fn to_byte_array(&self) -> ByteArray {
175        ByteArray::new("nVersion", self.version.to_le_bytes().to_vec())
176            .concat(self.hash_prevouts.as_byte_array().clone())
177            .concat(self.hash_sequence.as_byte_array().clone())
178            .concat(ByteArray::new(
179                "scriptCode",
180                encode_bitcoin_code(&self.outpoint).unwrap(),
181            ))
182            .concat(self.script_code.clone())
183            .concat(ByteArray::new("value", self.value.to_le_bytes().to_vec()))
184            .concat(ByteArray::new(
185                "nSequence",
186                self.sequence.to_le_bytes().to_vec(),
187            ))
188            .concat(self.hash_outputs.as_byte_array().clone())
189            .concat(ByteArray::new(
190                "nLocktime",
191                self.lock_time.to_le_bytes().to_vec(),
192            ))
193            .concat(ByteArray::new(
194                "sighash",
195                self.sig_hash_type.to_le_bytes().to_vec(),
196            ))
197    }
198}
199
200impl BitcoinDataType for TxPreimage {
201    type Type = BitcoinByteArray;
202    fn to_data(&self) -> Self::Type {
203        BitcoinByteArray(self.to_byte_array())
204    }
205    fn to_pushop(&self) -> Op {
206        self.to_byte_array().into()
207    }
208    fn to_data_type(&self) -> DataType {
209        DataType::ByteArray(None)
210    }
211}