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
24pub trait PrepareSign {
26 fn prepare_sign(&mut self);
28}
29
30pub trait UniqueIdentifier {
32 fn id(&self, chain_id: &ChainId) -> Bytes32;
34
35 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
64pub trait Signable: UniqueIdentifier {
68 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 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 {
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}