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
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 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 {
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}