fuel_tx/transaction/
id.rs

1use crate::{
2    field,
3    input::{
4        coin::CoinSigned,
5        message::{
6            MessageCoinSigned,
7            MessageDataSigned,
8        },
9    },
10    Input,
11    Transaction,
12};
13use fuel_crypto::{
14    Message,
15    PublicKey,
16    SecretKey,
17    Signature,
18};
19use fuel_types::{
20    Bytes32,
21    ChainId,
22};
23
24/// Prepares transaction for signing.
25pub trait PrepareSign {
26    /// Prepares transaction for signing zeroing required fields.
27    fn prepare_sign(&mut self);
28}
29
30/// Means that transaction has a unique identifier.
31pub trait UniqueIdentifier {
32    /// The unique identifier of the transaction is based on its content.
33    fn id(&self, chain_id: &ChainId) -> Bytes32;
34
35    /// The cached unique identifier of the transaction.
36    /// Returns None if transaction was not precomputed.
37    fn cached_id(&self) -> Option<Bytes32>;
38}
39
40impl UniqueIdentifier for Transaction {
41    fn id(&self, chain_id: &ChainId) -> Bytes32 {
42        match self {
43            Self::Script(tx) => tx.id(chain_id),
44            Self::Create(tx) => tx.id(chain_id),
45            Self::Mint(tx) => tx.id(chain_id),
46            Self::Upgrade(tx) => tx.id(chain_id),
47            Self::Upload(tx) => tx.id(chain_id),
48            Self::Blob(tx) => tx.id(chain_id),
49        }
50    }
51
52    fn cached_id(&self) -> Option<Bytes32> {
53        match self {
54            Self::Script(tx) => tx.cached_id(),
55            Self::Create(tx) => tx.cached_id(),
56            Self::Mint(tx) => tx.cached_id(),
57            Self::Upgrade(tx) => tx.cached_id(),
58            Self::Upload(tx) => tx.cached_id(),
59            Self::Blob(tx) => tx.cached_id(),
60        }
61    }
62}
63
64/// Means that transaction can be singed.
65///
66/// # Note: Autogenerated transactions are not signable.
67pub trait Signable: UniqueIdentifier {
68    /// Signs inputs of the transaction.
69    fn sign_inputs(&mut self, secret: &SecretKey, chain_id: &ChainId);
70}
71
72impl<T> Signable for T
73where
74    T: UniqueIdentifier + field::Witnesses + field::Inputs,
75{
76    /// For all inputs of type `coin` or `message`, check if its `owner` equals the public
77    /// counterpart of the provided key. Sign all matches.
78    fn sign_inputs(&mut self, secret: &SecretKey, chain_id: &ChainId) {
79        use itertools::Itertools;
80
81        let pk = PublicKey::from(secret);
82        let pk = Input::owner(&pk);
83        let id = self.id(chain_id);
84
85        let message = Message::from_bytes_ref(&id);
86
87        let signature = Signature::sign(secret, message);
88
89        let inputs = self.inputs();
90
91        let witness_indexes = inputs
92            .iter()
93            .filter_map(|input| match input {
94                Input::CoinSigned(CoinSigned {
95                    owner,
96                    witness_index,
97                    ..
98                })
99                | Input::MessageCoinSigned(MessageCoinSigned {
100                    recipient: owner,
101                    witness_index,
102                    ..
103                })
104                | Input::MessageDataSigned(MessageDataSigned {
105                    recipient: owner,
106                    witness_index,
107                    ..
108                }) if owner == &pk => Some(*witness_index as usize),
109                _ => None,
110            })
111            .sorted()
112            .dedup()
113            .collect_vec();
114
115        for w in witness_indexes {
116            if let Some(w) = self.witnesses_mut().get_mut(w) {
117                *w = signature.as_ref().into();
118            }
119        }
120    }
121}
122
123#[cfg(test)]
124mod tests {
125    use crate::{
126        field::*,
127        input,
128        input::{
129            coin::{
130                CoinPredicate,
131                CoinSigned,
132            },
133            message::{
134                MessageCoinPredicate,
135                MessageCoinSigned,
136                MessageDataPredicate,
137                MessageDataSigned,
138            },
139        },
140        output,
141        test_helper::{
142            generate_bytes,
143            generate_nonempty_padded_bytes,
144        },
145        Buildable,
146        Input,
147        Output,
148        StorageSlot,
149        Transaction,
150        UpgradePurpose as UpgradePurposeType,
151        UploadBody,
152        UtxoId,
153    };
154    use core::{
155        mem,
156        ops::Not,
157    };
158    use fuel_types::{
159        canonical::{
160            Deserialize,
161            Serialize,
162        },
163        ChainId,
164    };
165    use rand::{
166        rngs::StdRng,
167        Rng,
168        RngCore,
169        SeedableRng,
170    };
171
172    fn invert<B>(mut bytes: B)
173    where
174        B: AsMut<[u8]>,
175    {
176        bytes.as_mut().iter_mut().for_each(|b| *b = b.not());
177    }
178
179    fn invert_utxo_id(utxo_id: &mut UtxoId) {
180        let mut tx_id = *utxo_id.tx_id();
181        invert(&mut tx_id);
182        let out_idx = utxo_id.output_index().not();
183
184        *utxo_id = UtxoId::new(tx_id, out_idx)
185    }
186
187    fn invert_storage_slot(storage_slot: &mut StorageSlot) {
188        let mut data = storage_slot.to_bytes();
189        invert(&mut data);
190        *storage_slot =
191            StorageSlot::from_bytes(&data).expect("Failed to decode storage slot");
192    }
193
194    fn inv_v(bytes: &mut Vec<u8>) {
195        if bytes.is_empty() {
196            bytes.push(0xfb);
197        } else {
198            invert(bytes.as_mut_slice());
199        }
200    }
201
202    fn not<T>(t: &mut T)
203    where
204        T: Copy + Not<Output = T>,
205    {
206        let mut t_p = t.not();
207        mem::swap(t, &mut t_p);
208    }
209
210    fn assert_id_eq<Tx: Buildable, F>(tx: &Tx, mut f: F)
211    where
212        F: FnMut(&mut Tx),
213    {
214        let mut tx_p = tx.clone();
215
216        let tx_q = tx.clone();
217
218        f(&mut tx_p);
219
220        let chain_id = ChainId::default();
221
222        assert_eq!(tx.id(&chain_id), tx_p.id(&chain_id));
223        assert_eq!(tx.id(&chain_id), tx_q.id(&chain_id));
224    }
225
226    fn assert_id_ne<Tx: Buildable, F>(tx: &Tx, mut f: F)
227    where
228        F: FnMut(&mut Tx),
229    {
230        let mut tx_p = tx.clone();
231
232        f(&mut tx_p);
233
234        let tx_q = tx_p.clone();
235
236        let chain_id = ChainId::default();
237
238        assert_ne!(tx.id(&chain_id), tx_p.id(&chain_id));
239        assert_ne!(tx.id(&chain_id), tx_q.id(&chain_id));
240    }
241
242    macro_rules! assert_io_ne {
243        ($tx:expr, $t:ident, $i:path, $a:ident, $inv:expr) => {
244            assert_id_ne($tx, |t| {
245                t.$t().iter_mut().for_each(|x| match x {
246                    $i { $a, .. } => $inv($a),
247                    _ => (),
248                })
249            });
250        };
251        ($tx:expr, $t:ident, $i:path[$it:path], $a:ident, $inv:expr) => {
252            assert_id_ne($tx, |t| {
253                t.$t().iter_mut().for_each(|x| match x {
254                    $i($it { $a, .. }) => $inv($a),
255                    _ => (),
256                })
257            });
258        };
259    }
260
261    macro_rules! assert_io_eq {
262        ($tx:expr, $t:ident, $i:path, $a:ident, $inv:expr) => {
263            assert_id_eq($tx, |t| {
264                t.$t().iter_mut().for_each(|x| match x {
265                    $i { $a, .. } => $inv($a),
266                    _ => (),
267                })
268            });
269        };
270        ($tx:expr, $t:ident, $i:path[$it:path], $a:ident, $inv:expr) => {
271            assert_id_eq($tx, |t| {
272                t.$t().iter_mut().for_each(|x| match x {
273                    $i($it { $a, .. }) => $inv($a),
274                    _ => (),
275                })
276            });
277        };
278    }
279
280    fn assert_id_common_attrs<Tx: Buildable>(tx: &Tx) {
281        use core::ops::Deref;
282        assert_id_ne(tx, |t| t.set_tip(t.tip().not()));
283        assert_id_ne(tx, |t| t.set_maturity((t.maturity().deref().not()).into()));
284
285        if !tx.inputs().is_empty() {
286            assert_io_ne!(
287                tx,
288                inputs_mut,
289                Input::CoinSigned[CoinSigned],
290                utxo_id,
291                invert_utxo_id
292            );
293            assert_io_ne!(tx, inputs_mut, Input::CoinSigned[CoinSigned], owner, invert);
294            assert_io_ne!(tx, inputs_mut, Input::CoinSigned[CoinSigned], amount, not);
295            assert_io_ne!(
296                tx,
297                inputs_mut,
298                Input::CoinSigned[CoinSigned],
299                asset_id,
300                invert
301            );
302            assert_io_ne!(
303                tx,
304                inputs_mut,
305                Input::CoinSigned[CoinSigned],
306                witness_index,
307                not
308            );
309
310            assert_io_ne!(
311                tx,
312                inputs_mut,
313                Input::CoinPredicate[CoinPredicate],
314                utxo_id,
315                invert_utxo_id
316            );
317            assert_io_ne!(
318                tx,
319                inputs_mut,
320                Input::CoinPredicate[CoinPredicate],
321                owner,
322                invert
323            );
324            assert_io_ne!(
325                tx,
326                inputs_mut,
327                Input::CoinPredicate[CoinPredicate],
328                amount,
329                not
330            );
331            assert_io_ne!(
332                tx,
333                inputs_mut,
334                Input::CoinPredicate[CoinPredicate],
335                asset_id,
336                invert
337            );
338            assert_io_ne!(
339                tx,
340                inputs_mut,
341                Input::CoinPredicate[CoinPredicate],
342                predicate,
343                inv_v
344            );
345            assert_io_ne!(
346                tx,
347                inputs_mut,
348                Input::CoinPredicate[CoinPredicate],
349                predicate_data,
350                inv_v
351            );
352
353            assert_io_eq!(
354                tx,
355                inputs_mut,
356                Input::Contract[input::contract::Contract],
357                utxo_id,
358                invert_utxo_id
359            );
360            assert_io_eq!(
361                tx,
362                inputs_mut,
363                Input::Contract[input::contract::Contract],
364                balance_root,
365                invert
366            );
367            assert_io_eq!(
368                tx,
369                inputs_mut,
370                Input::Contract[input::contract::Contract],
371                state_root,
372                invert
373            );
374            assert_io_ne!(
375                tx,
376                inputs_mut,
377                Input::Contract[input::contract::Contract],
378                contract_id,
379                invert
380            );
381
382            assert_io_ne!(
383                tx,
384                inputs_mut,
385                Input::MessageCoinSigned[MessageCoinSigned],
386                sender,
387                invert
388            );
389            assert_io_ne!(
390                tx,
391                inputs_mut,
392                Input::MessageCoinSigned[MessageCoinSigned],
393                recipient,
394                invert
395            );
396            assert_io_ne!(
397                tx,
398                inputs_mut,
399                Input::MessageCoinSigned[MessageCoinSigned],
400                amount,
401                not
402            );
403            assert_io_ne!(
404                tx,
405                inputs_mut,
406                Input::MessageCoinSigned[MessageCoinSigned],
407                nonce,
408                invert
409            );
410            assert_io_ne!(
411                tx,
412                inputs_mut,
413                Input::MessageCoinSigned[MessageCoinSigned],
414                witness_index,
415                not
416            );
417
418            assert_io_ne!(
419                tx,
420                inputs_mut,
421                Input::MessageDataSigned[MessageDataSigned],
422                sender,
423                invert
424            );
425            assert_io_ne!(
426                tx,
427                inputs_mut,
428                Input::MessageDataSigned[MessageDataSigned],
429                recipient,
430                invert
431            );
432            assert_io_ne!(
433                tx,
434                inputs_mut,
435                Input::MessageDataSigned[MessageDataSigned],
436                amount,
437                not
438            );
439            assert_io_ne!(
440                tx,
441                inputs_mut,
442                Input::MessageDataSigned[MessageDataSigned],
443                nonce,
444                invert
445            );
446            assert_io_ne!(
447                tx,
448                inputs_mut,
449                Input::MessageDataSigned[MessageDataSigned],
450                witness_index,
451                not
452            );
453            assert_io_ne!(
454                tx,
455                inputs_mut,
456                Input::MessageDataSigned[MessageDataSigned],
457                data,
458                inv_v
459            );
460
461            assert_io_ne!(
462                tx,
463                inputs_mut,
464                Input::MessageDataPredicate[MessageDataPredicate],
465                sender,
466                invert
467            );
468            assert_io_ne!(
469                tx,
470                inputs_mut,
471                Input::MessageCoinPredicate[MessageCoinPredicate],
472                recipient,
473                invert
474            );
475            assert_io_ne!(
476                tx,
477                inputs_mut,
478                Input::MessageCoinPredicate[MessageCoinPredicate],
479                amount,
480                not
481            );
482            assert_io_ne!(
483                tx,
484                inputs_mut,
485                Input::MessageCoinPredicate[MessageCoinPredicate],
486                nonce,
487                invert
488            );
489            assert_io_ne!(
490                tx,
491                inputs_mut,
492                Input::MessageCoinPredicate[MessageCoinPredicate],
493                predicate,
494                inv_v
495            );
496            assert_io_ne!(
497                tx,
498                inputs_mut,
499                Input::MessageCoinPredicate[MessageCoinPredicate],
500                predicate_data,
501                inv_v
502            );
503
504            assert_io_ne!(
505                tx,
506                inputs_mut,
507                Input::MessageDataPredicate[MessageDataPredicate],
508                sender,
509                invert
510            );
511            assert_io_ne!(
512                tx,
513                inputs_mut,
514                Input::MessageDataPredicate[MessageDataPredicate],
515                recipient,
516                invert
517            );
518            assert_io_ne!(
519                tx,
520                inputs_mut,
521                Input::MessageDataPredicate[MessageDataPredicate],
522                amount,
523                not
524            );
525            assert_io_ne!(
526                tx,
527                inputs_mut,
528                Input::MessageDataPredicate[MessageDataPredicate],
529                data,
530                inv_v
531            );
532            assert_io_ne!(
533                tx,
534                inputs_mut,
535                Input::MessageDataPredicate[MessageDataPredicate],
536                nonce,
537                invert
538            );
539            assert_io_ne!(
540                tx,
541                inputs_mut,
542                Input::MessageDataPredicate[MessageDataPredicate],
543                data,
544                inv_v
545            );
546            assert_io_ne!(
547                tx,
548                inputs_mut,
549                Input::MessageDataPredicate[MessageDataPredicate],
550                predicate,
551                inv_v
552            );
553            assert_io_ne!(
554                tx,
555                inputs_mut,
556                Input::MessageDataPredicate[MessageDataPredicate],
557                predicate_data,
558                inv_v
559            );
560        }
561
562        if !tx.outputs().is_empty() {
563            assert_io_ne!(tx, outputs_mut, Output::Coin, to, invert);
564            assert_io_ne!(tx, outputs_mut, Output::Coin, amount, not);
565            assert_io_ne!(tx, outputs_mut, Output::Coin, asset_id, invert);
566
567            assert_io_ne!(
568                tx,
569                outputs_mut,
570                Output::Contract[output::contract::Contract],
571                input_index,
572                not
573            );
574            assert_io_eq!(
575                tx,
576                outputs_mut,
577                Output::Contract[output::contract::Contract],
578                balance_root,
579                invert
580            );
581            assert_io_eq!(
582                tx,
583                outputs_mut,
584                Output::Contract[output::contract::Contract],
585                state_root,
586                invert
587            );
588
589            assert_io_ne!(tx, outputs_mut, Output::Change, to, invert);
590            assert_io_eq!(tx, outputs_mut, Output::Change, amount, not);
591            assert_io_ne!(tx, outputs_mut, Output::Change, asset_id, invert);
592
593            assert_io_eq!(tx, outputs_mut, Output::Variable, to, invert);
594            assert_io_eq!(tx, outputs_mut, Output::Variable, amount, not);
595            assert_io_eq!(tx, outputs_mut, Output::Variable, asset_id, invert);
596
597            assert_io_ne!(
598                tx,
599                outputs_mut,
600                Output::ContractCreated,
601                contract_id,
602                invert
603            );
604        }
605
606        if !tx.witnesses().is_empty() {
607            assert_id_eq(tx, |t| {
608                inv_v(t.witnesses_mut().first_mut().unwrap().as_vec_mut())
609            });
610        }
611    }
612
613    #[test]
614    fn id() {
615        let rng = &mut StdRng::seed_from_u64(8586);
616
617        let inputs = [
618            vec![],
619            vec![
620                Input::coin_signed(
621                    rng.gen(),
622                    rng.gen(),
623                    rng.next_u64(),
624                    rng.gen(),
625                    rng.gen(),
626                    rng.gen(),
627                ),
628                Input::coin_predicate(
629                    rng.gen(),
630                    rng.gen(),
631                    rng.next_u64(),
632                    rng.gen(),
633                    rng.gen(),
634                    rng.gen(),
635                    generate_nonempty_padded_bytes(rng),
636                    generate_bytes(rng),
637                ),
638                Input::contract(rng.gen(), rng.gen(), rng.gen(), rng.gen(), rng.gen()),
639                Input::message_coin_signed(
640                    rng.gen(),
641                    rng.gen(),
642                    rng.next_u64(),
643                    rng.gen(),
644                    rng.gen(),
645                ),
646                Input::message_coin_predicate(
647                    rng.gen(),
648                    rng.gen(),
649                    rng.next_u64(),
650                    rng.gen(),
651                    rng.gen(),
652                    generate_nonempty_padded_bytes(rng),
653                    generate_bytes(rng),
654                ),
655                Input::message_data_signed(
656                    rng.gen(),
657                    rng.gen(),
658                    rng.next_u64(),
659                    rng.gen(),
660                    rng.gen(),
661                    generate_nonempty_padded_bytes(rng),
662                ),
663                Input::message_data_predicate(
664                    rng.gen(),
665                    rng.gen(),
666                    rng.next_u64(),
667                    rng.gen(),
668                    rng.gen(),
669                    generate_nonempty_padded_bytes(rng),
670                    generate_nonempty_padded_bytes(rng),
671                    generate_bytes(rng),
672                ),
673            ],
674        ];
675
676        let outputs = [
677            vec![],
678            vec![
679                Output::coin(rng.gen(), rng.next_u64(), rng.gen()),
680                Output::contract(rng.gen(), rng.gen(), rng.gen()),
681                Output::change(rng.gen(), rng.next_u64(), rng.gen()),
682                Output::variable(rng.gen(), rng.next_u64(), rng.gen()),
683                Output::contract_created(rng.gen(), rng.gen()),
684            ],
685        ];
686
687        let witnesses = [
688            vec![],
689            vec![generate_bytes(rng).into(), generate_bytes(rng).into()],
690        ];
691
692        let scripts = [vec![], generate_bytes(rng), generate_bytes(rng)];
693        let script_data = [vec![], generate_bytes(rng), generate_bytes(rng)];
694        let storage_slots = [vec![], vec![rng.gen(), rng.gen()]];
695        let purposes = [
696            UpgradePurposeType::ConsensusParameters {
697                witness_index: rng.gen(),
698                checksum: rng.gen(),
699            },
700            UpgradePurposeType::StateTransition { root: rng.gen() },
701        ];
702
703        for inputs in inputs.iter() {
704            for outputs in outputs.iter() {
705                for witnesses in witnesses.iter() {
706                    for script in scripts.iter() {
707                        for script_data in script_data.iter() {
708                            let tx = Transaction::script(
709                                rng.next_u64(),
710                                script.clone(),
711                                script_data.clone(),
712                                rng.gen(),
713                                inputs.clone(),
714                                outputs.clone(),
715                                witnesses.clone(),
716                            );
717
718                            assert_id_common_attrs(&tx);
719                            assert_id_ne(&tx, |t| {
720                                t.set_script_gas_limit(t.script_gas_limit().not())
721                            });
722                            assert_id_ne(&tx, |t| inv_v(t.script_mut()));
723                            assert_id_ne(&tx, |t| inv_v(t.script_data_mut()));
724                        }
725                    }
726
727                    for storage_slots in storage_slots.iter() {
728                        let tx = Transaction::create(
729                            rng.gen(),
730                            rng.gen(),
731                            rng.gen(),
732                            storage_slots.clone(),
733                            inputs.clone(),
734                            outputs.clone(),
735                            witnesses.clone(),
736                        );
737
738                        assert_id_ne(&tx, |t| not(t.bytecode_witness_index_mut()));
739                        assert_id_ne(&tx, |t| invert(t.salt_mut()));
740                        assert_id_ne(&tx, |t| invert(t.salt_mut()));
741
742                        if !storage_slots.is_empty() {
743                            assert_id_ne(&tx, |t| {
744                                invert_storage_slot(
745                                    t.storage_slots_mut().first_mut().unwrap(),
746                                )
747                            });
748                        }
749
750                        assert_id_common_attrs(&tx);
751                    }
752
753                    for purpose in purposes.iter() {
754                        let tx = Transaction::upgrade(
755                            *purpose,
756                            rng.gen(),
757                            inputs.clone(),
758                            outputs.clone(),
759                            witnesses.clone(),
760                        );
761
762                        assert_id_common_attrs(&tx);
763                        assert_id_ne(&tx, |t| match t.upgrade_purpose_mut() {
764                            UpgradePurposeType::ConsensusParameters {
765                                witness_index,
766                                checksum,
767                            } => {
768                                *witness_index = witness_index.not();
769                                invert(checksum);
770                            }
771                            UpgradePurposeType::StateTransition { root } => {
772                                invert(root);
773                            }
774                        });
775                    }
776
777                    // Upload
778                    {
779                        let tx = Transaction::upload(
780                            UploadBody {
781                                root: rng.gen(),
782                                witness_index: rng.gen(),
783                                subsection_index: rng.gen(),
784                                subsections_number: rng.gen(),
785                                proof_set: vec![rng.gen(), rng.gen(), rng.gen()],
786                            },
787                            rng.gen(),
788                            inputs.clone(),
789                            outputs.clone(),
790                            witnesses.clone(),
791                        );
792
793                        assert_id_common_attrs(&tx);
794                        assert_id_ne(&tx, |t| invert(t.bytecode_root_mut()));
795                        assert_id_ne(&tx, |t| not(t.bytecode_witness_index_mut()));
796                        assert_id_ne(&tx, |t| not(t.subsection_index_mut()));
797                        assert_id_ne(&tx, |t| not(t.subsections_number_mut()));
798                        assert_id_ne(&tx, |t| {
799                            t.proof_set_mut().iter_mut().for_each(invert)
800                        });
801                    }
802                }
803            }
804        }
805    }
806}