Skip to main content

fuel_tx/transaction/
id.rs

1use crate::{
2    Input,
3    Transaction,
4    field,
5    input::{
6        coin::CoinSigned,
7        message::{
8            MessageCoinSigned,
9            MessageDataSigned,
10        },
11    },
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        Buildable,
127        Input,
128        Output,
129        StorageSlot,
130        Transaction,
131        UpgradePurpose as UpgradePurposeType,
132        UploadBody,
133        UtxoId,
134        field::*,
135        input,
136        input::{
137            coin::{
138                CoinPredicate,
139                CoinSigned,
140            },
141            message::{
142                MessageCoinPredicate,
143                MessageCoinSigned,
144                MessageDataPredicate,
145                MessageDataSigned,
146            },
147        },
148        output,
149        test_helper::{
150            generate_bytes,
151            generate_nonempty_padded_bytes,
152        },
153    };
154    use core::{
155        mem,
156        ops::Not,
157    };
158    use fuel_types::{
159        ChainId,
160        canonical::{
161            Deserialize,
162            Serialize,
163        },
164    };
165    use rand::{
166        Rng,
167        RngCore,
168        SeedableRng,
169        rngs::StdRng,
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.r#gen(),
622                    rng.r#gen(),
623                    rng.next_u64(),
624                    rng.r#gen(),
625                    rng.r#gen(),
626                    rng.r#gen(),
627                ),
628                Input::coin_predicate(
629                    rng.r#gen(),
630                    rng.r#gen(),
631                    rng.next_u64(),
632                    rng.r#gen(),
633                    rng.r#gen(),
634                    rng.r#gen(),
635                    generate_nonempty_padded_bytes(rng),
636                    generate_bytes(rng),
637                ),
638                Input::contract(
639                    rng.r#gen(),
640                    rng.r#gen(),
641                    rng.r#gen(),
642                    rng.r#gen(),
643                    rng.r#gen(),
644                ),
645                Input::message_coin_signed(
646                    rng.r#gen(),
647                    rng.r#gen(),
648                    rng.next_u64(),
649                    rng.r#gen(),
650                    rng.r#gen(),
651                ),
652                Input::message_coin_predicate(
653                    rng.r#gen(),
654                    rng.r#gen(),
655                    rng.next_u64(),
656                    rng.r#gen(),
657                    rng.r#gen(),
658                    generate_nonempty_padded_bytes(rng),
659                    generate_bytes(rng),
660                ),
661                Input::message_data_signed(
662                    rng.r#gen(),
663                    rng.r#gen(),
664                    rng.next_u64(),
665                    rng.r#gen(),
666                    rng.r#gen(),
667                    generate_nonempty_padded_bytes(rng),
668                ),
669                Input::message_data_predicate(
670                    rng.r#gen(),
671                    rng.r#gen(),
672                    rng.next_u64(),
673                    rng.r#gen(),
674                    rng.r#gen(),
675                    generate_nonempty_padded_bytes(rng),
676                    generate_nonempty_padded_bytes(rng),
677                    generate_bytes(rng),
678                ),
679            ],
680        ];
681
682        let outputs = [
683            vec![],
684            vec![
685                Output::coin(rng.r#gen(), rng.next_u64(), rng.r#gen()),
686                Output::contract(rng.r#gen(), rng.r#gen(), rng.r#gen()),
687                Output::change(rng.r#gen(), rng.next_u64(), rng.r#gen()),
688                Output::variable(rng.r#gen(), rng.next_u64(), rng.r#gen()),
689                Output::contract_created(rng.r#gen(), rng.r#gen()),
690            ],
691        ];
692
693        let witnesses = [
694            vec![],
695            vec![generate_bytes(rng).into(), generate_bytes(rng).into()],
696        ];
697
698        let scripts = [vec![], generate_bytes(rng), generate_bytes(rng)];
699        let script_data = [vec![], generate_bytes(rng), generate_bytes(rng)];
700        let storage_slots = [vec![], vec![rng.r#gen(), rng.r#gen()]];
701        let purposes = [
702            UpgradePurposeType::ConsensusParameters {
703                witness_index: rng.r#gen(),
704                checksum: rng.r#gen(),
705            },
706            UpgradePurposeType::StateTransition { root: rng.r#gen() },
707        ];
708
709        for inputs in inputs.iter() {
710            for outputs in outputs.iter() {
711                for witnesses in witnesses.iter() {
712                    for script in scripts.iter() {
713                        for script_data in script_data.iter() {
714                            let tx = Transaction::script(
715                                rng.next_u64(),
716                                script.clone(),
717                                script_data.clone(),
718                                rng.r#gen(),
719                                inputs.clone(),
720                                outputs.clone(),
721                                witnesses.clone(),
722                            );
723
724                            assert_id_common_attrs(&tx);
725                            assert_id_ne(&tx, |t| {
726                                t.set_script_gas_limit(t.script_gas_limit().not())
727                            });
728                            assert_id_ne(&tx, |t| inv_v(t.script_mut()));
729                            assert_id_ne(&tx, |t| inv_v(t.script_data_mut()));
730                        }
731                    }
732
733                    for storage_slots in storage_slots.iter() {
734                        let tx = Transaction::create(
735                            rng.r#gen(),
736                            rng.r#gen(),
737                            rng.r#gen(),
738                            storage_slots.clone(),
739                            inputs.clone(),
740                            outputs.clone(),
741                            witnesses.clone(),
742                        );
743
744                        assert_id_ne(&tx, |t| not(t.bytecode_witness_index_mut()));
745                        assert_id_ne(&tx, |t| invert(t.salt_mut()));
746                        assert_id_ne(&tx, |t| invert(t.salt_mut()));
747
748                        if !storage_slots.is_empty() {
749                            assert_id_ne(&tx, |t| {
750                                invert_storage_slot(
751                                    t.storage_slots_mut().first_mut().unwrap(),
752                                )
753                            });
754                        }
755
756                        assert_id_common_attrs(&tx);
757                    }
758
759                    for purpose in purposes.iter() {
760                        let tx = Transaction::upgrade(
761                            *purpose,
762                            rng.r#gen(),
763                            inputs.clone(),
764                            outputs.clone(),
765                            witnesses.clone(),
766                        );
767
768                        assert_id_common_attrs(&tx);
769                        assert_id_ne(&tx, |t| match t.upgrade_purpose_mut() {
770                            UpgradePurposeType::ConsensusParameters {
771                                witness_index,
772                                checksum,
773                            } => {
774                                *witness_index = witness_index.not();
775                                invert(checksum);
776                            }
777                            UpgradePurposeType::StateTransition { root } => {
778                                invert(root);
779                            }
780                        });
781                    }
782
783                    // Upload
784                    {
785                        let tx = Transaction::upload(
786                            UploadBody {
787                                root: rng.r#gen(),
788                                witness_index: rng.r#gen(),
789                                subsection_index: rng.r#gen(),
790                                subsections_number: rng.r#gen(),
791                                proof_set: vec![rng.r#gen(), rng.r#gen(), rng.r#gen()],
792                            },
793                            rng.r#gen(),
794                            inputs.clone(),
795                            outputs.clone(),
796                            witnesses.clone(),
797                        );
798
799                        assert_id_common_attrs(&tx);
800                        assert_id_ne(&tx, |t| invert(t.bytecode_root_mut()));
801                        assert_id_ne(&tx, |t| not(t.bytecode_witness_index_mut()));
802                        assert_id_ne(&tx, |t| not(t.subsection_index_mut()));
803                        assert_id_ne(&tx, |t| not(t.subsections_number_mut()));
804                        assert_id_ne(&tx, |t| {
805                            t.proof_set_mut().iter_mut().for_each(invert)
806                        });
807                    }
808                }
809            }
810        }
811    }
812}