mwc_bch/messages/
tx.rs

1use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
2use linked_hash_map::LinkedHashMap;
3use messages::message::Payload;
4use messages::{OutPoint, TxIn, TxOut, COINBASE_OUTPOINT_HASH, COINBASE_OUTPOINT_INDEX};
5use script::{op_codes, Script, TransactionChecker};
6use std::fmt;
7use std::io;
8use std::io::{Read, Write};
9use transaction::sighash::SigHashCache;
10use util::{sha256d, var_int, Error, Hash256, Result, Serializable};
11
12/// Maximum number of satoshis possible
13pub const MAX_SATOSHIS: i64 = 21_000_000 * 100_000_000;
14
15/// Bitcoin transaction
16#[derive(Default, PartialEq, Eq, Hash, Clone)]
17pub struct Tx {
18    /// Transaction version
19    pub version: u32,
20    /// Transaction inputs
21    pub inputs: Vec<TxIn>,
22    /// Transaction outputs
23    pub outputs: Vec<TxOut>,
24    /// The block number or timestamp at which this transaction is unlocked
25    pub lock_time: u32,
26}
27
28impl Tx {
29    /// Calculates the hash of the transaction also known as the txid
30    pub fn hash(&self) -> Hash256 {
31        let mut b = Vec::with_capacity(self.size());
32        self.write(&mut b).unwrap();
33        sha256d(&b)
34    }
35
36    /// Validates a non-coinbase transaction
37    pub fn validate(
38        &self,
39        require_sighash_forkid: bool,
40        utxos: &LinkedHashMap<OutPoint, TxOut>,
41    ) -> Result<()> {
42        // Make sure neither in or out lists are empty
43        if self.inputs.len() == 0 {
44            return Err(Error::BadData("inputs empty".to_string()));
45        }
46        if self.outputs.len() == 0 {
47            return Err(Error::BadData("outputs empty".to_string()));
48        }
49
50        // Each output value, as well as the total, must be in legal money range
51        let mut total_out = 0;
52        for tx_out in self.outputs.iter() {
53            if tx_out.amount.0 < 0 {
54                return Err(Error::BadData("tx_out amount negative".to_string()));
55            }
56            total_out += tx_out.amount.0;
57        }
58        if total_out > MAX_SATOSHIS {
59            return Err(Error::BadData("Total out exceeds max satoshis".to_string()));
60        }
61
62        // Make sure none of the inputs are coinbase transactions
63        for tx_in in self.inputs.iter() {
64            if tx_in.prev_output.hash == COINBASE_OUTPOINT_HASH
65                && tx_in.prev_output.index == COINBASE_OUTPOINT_INDEX
66            {
67                return Err(Error::BadData("Unexpected coinbase".to_string()));
68            }
69        }
70
71        // Check that lock_time <= INT_MAX because some clients interpret this differently
72        if self.lock_time > 2_147_483_647 {
73            return Err(Error::BadData("Lock time too large".to_string()));
74        }
75
76        // Check that all inputs are in the utxo set and are in legal money range
77        let mut total_in = 0;
78        for tx_in in self.inputs.iter() {
79            let utxo = utxos.get(&tx_in.prev_output);
80            if let Some(tx_out) = utxo {
81                if tx_out.amount.0 < 0 {
82                    return Err(Error::BadData("tx_out amount negative".to_string()));
83                }
84                total_in += tx_out.amount.0;
85            } else {
86                return Err(Error::BadData("utxo not found".to_string()));
87            }
88        }
89        if total_in > MAX_SATOSHIS {
90            return Err(Error::BadData("Total in exceeds max satoshis".to_string()));
91        }
92
93        // Check inputs spent > outputs received
94        if total_in < total_out {
95            return Err(Error::BadData("Output total exceeds input".to_string()));
96        }
97
98        // Verify each script
99        let mut sighash_cache = SigHashCache::new();
100        for input in 0..self.inputs.len() {
101            let tx_in = &self.inputs[input];
102            let tx_out = utxos.get(&tx_in.prev_output).unwrap();
103
104            let mut script = Script::new();
105            script.append_slice(&tx_in.sig_script.0);
106            script.append(op_codes::OP_CODESEPARATOR);
107            script.append_slice(&tx_out.pk_script.0);
108
109            let mut tx_checker = TransactionChecker {
110                tx: self,
111                sig_hash_cache: &mut sighash_cache,
112                input: input,
113                amount: tx_out.amount,
114                require_sighash_forkid,
115            };
116
117            script.eval(&mut tx_checker)?;
118        }
119
120        Ok(())
121    }
122
123    /// Returns whether the transaction is the block reward
124    pub fn coinbase(&self) -> bool {
125        return self.inputs.len() == 1
126            && self.inputs[0].prev_output.hash == COINBASE_OUTPOINT_HASH
127            && self.inputs[0].prev_output.index == COINBASE_OUTPOINT_INDEX;
128    }
129}
130
131impl Serializable<Tx> for Tx {
132    fn read(reader: &mut dyn Read) -> Result<Tx> {
133        let version = reader.read_i32::<LittleEndian>()?;
134        let version = version as u32;
135        let n_inputs = var_int::read(reader)?;
136        let mut inputs = Vec::with_capacity(n_inputs as usize);
137        for _i in 0..n_inputs {
138            inputs.push(TxIn::read(reader)?);
139        }
140        let n_outputs = var_int::read(reader)?;
141        let mut outputs = Vec::with_capacity(n_outputs as usize);
142        for _i in 0..n_outputs {
143            outputs.push(TxOut::read(reader)?);
144        }
145        let lock_time = reader.read_u32::<LittleEndian>()?;
146        Ok(Tx {
147            version,
148            inputs,
149            outputs,
150            lock_time,
151        })
152    }
153
154    fn write(&self, writer: &mut dyn Write) -> io::Result<()> {
155        writer.write_u32::<LittleEndian>(self.version)?;
156        var_int::write(self.inputs.len() as u64, writer)?;
157        for tx_in in self.inputs.iter() {
158            tx_in.write(writer)?;
159        }
160        var_int::write(self.outputs.len() as u64, writer)?;
161        for tx_out in self.outputs.iter() {
162            tx_out.write(writer)?;
163        }
164        writer.write_u32::<LittleEndian>(self.lock_time)?;
165        Ok(())
166    }
167}
168
169impl Payload<Tx> for Tx {
170    fn size(&self) -> usize {
171        let mut size = 8;
172        size += var_int::size(self.inputs.len() as u64);
173        for tx_in in self.inputs.iter() {
174            size += tx_in.size();
175        }
176        size += var_int::size(self.outputs.len() as u64);
177        for tx_out in self.outputs.iter() {
178            size += tx_out.size();
179        }
180        size
181    }
182}
183
184impl fmt::Debug for Tx {
185    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
186        let inputs_str = format!("[<{} inputs>]", self.inputs.len());
187        let outputs_str = format!("[<{} outputs>]", self.outputs.len());
188
189        f.debug_struct("Tx")
190            .field("version", &self.version)
191            .field(
192                "inputs",
193                if self.inputs.len() <= 3 {
194                    &self.inputs
195                } else {
196                    &inputs_str
197                },
198            ).field(
199                "outputs",
200                if self.outputs.len() <= 3 {
201                    &self.outputs
202                } else {
203                    &outputs_str
204                },
205            ).field("lock_time", &self.lock_time)
206            .finish()
207    }
208}
209
210#[cfg(test)]
211mod tests {
212    use super::*;
213    use messages::OutPoint;
214    use std::io::Cursor;
215    use util::{Amount, Hash256};
216
217    #[test]
218    fn write_read() {
219        let mut v = Vec::new();
220        let t = Tx {
221            version: 1,
222            inputs: vec![
223                TxIn {
224                    prev_output: OutPoint {
225                        hash: Hash256([9; 32]),
226                        index: 9,
227                    },
228                    sig_script: Script(vec![1, 3, 5, 7, 9]),
229                    sequence: 100,
230                },
231                TxIn {
232                    prev_output: OutPoint {
233                        hash: Hash256([0; 32]),
234                        index: 8,
235                    },
236                    sig_script: Script(vec![3; 333]),
237                    sequence: 22,
238                },
239            ],
240            outputs: vec![
241                TxOut {
242                    amount: Amount(99),
243                    pk_script: Script(vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 100, 99, 98, 97, 96]),
244                },
245                TxOut {
246                    amount: Amount(199),
247                    pk_script: Script(vec![56, 78, 90, 90, 78, 56]),
248                },
249            ],
250            lock_time: 1000,
251        };
252        t.write(&mut v).unwrap();
253        assert!(v.len() == t.size());
254        assert!(Tx::read(&mut Cursor::new(&v)).unwrap() == t);
255    }
256
257    #[test]
258    fn hash() {
259        // The coinbase from block 2
260        let tx = Tx {
261            version: 1,
262            inputs: vec![TxIn {
263                prev_output: OutPoint {
264                    hash: Hash256([0; 32]),
265                    index: 4294967295,
266                },
267                sig_script: Script(vec![4, 255, 255, 0, 29, 1, 11]),
268                sequence: 4294967295,
269            }],
270            outputs: vec![TxOut {
271                amount: Amount(5000000000),
272                pk_script: Script(vec![
273                    65, 4, 114, 17, 168, 36, 245, 91, 80, 82, 40, 228, 195, 213, 25, 76, 31, 207,
274                    170, 21, 164, 86, 171, 223, 55, 249, 185, 217, 122, 64, 64, 175, 192, 115, 222,
275                    230, 200, 144, 100, 152, 79, 3, 56, 82, 55, 217, 33, 103, 193, 62, 35, 100, 70,
276                    180, 23, 171, 121, 160, 252, 174, 65, 42, 227, 49, 107, 119, 172,
277                ]),
278            }],
279            lock_time: 0,
280        };
281        let h = "9b0fc92260312ce44e74ef369f5c66bbb85848f2eddd5a7a1cde251e54ccfdd5";
282        assert!(tx.hash() == Hash256::decode(h).unwrap());
283        assert!(tx.coinbase());
284    }
285
286    #[test]
287    fn validate() {
288        let utxo = (
289            OutPoint {
290                hash: Hash256([5; 32]),
291                index: 3,
292            },
293            TxOut {
294                amount: Amount(100),
295                pk_script: Script(vec![]),
296            },
297        );
298        let mut utxos = LinkedHashMap::new();
299        utxos.insert(utxo.0.clone(), utxo.1.clone());
300
301        let tx = Tx {
302            version: 2,
303            inputs: vec![TxIn {
304                prev_output: utxo.0.clone(),
305                sig_script: Script(vec![op_codes::OP_1]),
306                sequence: 0,
307            }],
308            outputs: vec![
309                TxOut {
310                    amount: Amount(10),
311                    pk_script: Script(vec![]),
312                },
313                TxOut {
314                    amount: Amount(20),
315                    pk_script: Script(vec![]),
316                },
317            ],
318            lock_time: 0,
319        };
320        assert!(tx.validate(false, &utxos).is_ok());
321
322        let mut tx_test = tx.clone();
323        tx_test.inputs = vec![];
324        assert!(tx_test.validate(false, &utxos).is_err());
325
326        let mut tx_test = tx.clone();
327        tx_test.outputs = vec![];
328        assert!(tx_test.validate(false, &utxos).is_err());
329
330        let mut tx_test = tx.clone();
331        tx_test.outputs[0].amount = Amount(-1);
332        assert!(tx_test.validate(false, &utxos).is_err());
333
334        let mut tx_test = tx.clone();
335        tx_test.outputs[0].amount = Amount(0);
336        tx_test.outputs[0].amount = Amount(0);
337        assert!(tx_test.validate(false, &utxos).is_ok());
338
339        let mut tx_test = tx.clone();
340        tx_test.outputs[0].amount = Amount(MAX_SATOSHIS);
341        tx_test.outputs[1].amount = Amount(MAX_SATOSHIS);
342        assert!(tx_test.validate(false, &utxos).is_err());
343
344        let mut tx_test = tx.clone();
345        tx_test.outputs[1].amount = Amount(MAX_SATOSHIS + 1);
346        assert!(tx_test.validate(false, &utxos).is_err());
347
348        let mut tx_test = tx.clone();
349        tx_test.inputs[0].prev_output.hash = COINBASE_OUTPOINT_HASH;
350        tx_test.inputs[0].prev_output.index = COINBASE_OUTPOINT_INDEX;
351        assert!(tx_test.validate(false, &utxos).is_err());
352
353        let mut tx_test = tx.clone();
354        tx_test.lock_time = 4294967295;
355        assert!(tx_test.validate(false, &utxos).is_err());
356
357        let mut tx_test = tx.clone();
358        tx_test.inputs[0].prev_output.hash = Hash256([8; 32]);
359        assert!(tx_test.validate(false, &utxos).is_err());
360
361        let mut utxos_clone = utxos.clone();
362        let prev_output = &tx.inputs[0].prev_output;
363        utxos_clone.get_mut(prev_output).unwrap().amount = Amount(-1);
364        assert!(tx.validate(false, &utxos_clone).is_err());
365
366        let mut utxos_clone = utxos.clone();
367        let prev_output = &tx.inputs[0].prev_output;
368        utxos_clone.get_mut(prev_output).unwrap().amount = Amount(MAX_SATOSHIS + 1);
369        assert!(tx.validate(false, &utxos_clone).is_err());
370
371        let mut tx_test = tx.clone();
372        tx_test.outputs[0].amount = Amount(100);
373        assert!(tx_test.validate(false, &utxos).is_err());
374
375        let mut utxos_clone = utxos.clone();
376        let prev_output = &tx.inputs[0].prev_output;
377        utxos_clone.get_mut(prev_output).unwrap().pk_script = Script(vec![op_codes::OP_0]);
378        assert!(tx.validate(false, &utxos_clone).is_err());
379    }
380}