ab_system_contract_simple_wallet_base/
seal.rs1use crate::{SIGNING_CONTEXT, Seal};
4use ab_contracts_common::ContractError;
5use ab_core_primitives::transaction::{TransactionHeader, TransactionSlot};
6use ab_io_type::trivial_type::TrivialType;
7use blake3::{Hasher, OUT_LEN};
8use core::slice;
9use schnorrkel::context::SigningContext;
10use schnorrkel::{Keypair, PublicKey, Signature};
11
12#[cfg_attr(feature = "no-panic", no_panic::no_panic)]
16pub fn hash_transaction(
17 header: &TransactionHeader,
18 read_slots: &[TransactionSlot],
19 write_slots: &[TransactionSlot],
20 payload: &[u128],
21 nonce: u64,
22) -> [u8; OUT_LEN] {
23 let mut hasher = Hasher::new();
24 hasher.update(header.as_bytes());
25 for slot in read_slots {
26 hasher.update(slot.as_bytes());
27 }
28 for slot in write_slots {
29 hasher.update(slot.as_bytes());
30 }
31 let payload_bytes =
33 unsafe { slice::from_raw_parts(payload.as_ptr().cast::<u8>(), size_of_val(payload)) };
34 hasher.update(payload_bytes);
35 hasher.update(&nonce.to_le_bytes());
36 hasher.finalize().into()
37}
38
39#[cfg_attr(feature = "no-panic", no_panic::no_panic)]
44pub fn sign(keypair: &Keypair, tx_hash: &[u8; OUT_LEN]) -> Signature {
45 let signing_context = SigningContext::new(SIGNING_CONTEXT);
46 keypair.sign(signing_context.bytes(tx_hash))
47}
48
49#[cfg_attr(feature = "no-panic", no_panic::no_panic)]
51pub fn hash_and_sign(
52 keypair: &Keypair,
53 header: &TransactionHeader,
54 read_slots: &[TransactionSlot],
55 write_slots: &[TransactionSlot],
56 payload: &[u128],
57 nonce: u64,
58) -> Seal {
59 let tx_hash = hash_transaction(header, read_slots, write_slots, payload, nonce);
60 let signature = sign(keypair, &tx_hash).to_bytes();
61
62 Seal { signature, nonce }
63}
64
65#[cfg_attr(feature = "no-panic", no_panic::no_panic)]
70pub fn verify(
71 public_key: &PublicKey,
72 expected_nonce: u64,
73 tx_hash: &[u8; OUT_LEN],
74 signature: &Signature,
75 nonce: u64,
76) -> Result<(), ContractError> {
77 if nonce != expected_nonce {
78 return Err(ContractError::BadInput);
79 }
80
81 let signing_context = SigningContext::new(SIGNING_CONTEXT);
82 public_key
83 .verify(signing_context.bytes(tx_hash.as_bytes()), signature)
84 .map_err(|_error| ContractError::Forbidden)
85}
86
87#[cfg_attr(feature = "no-panic", no_panic::no_panic)]
89pub fn hash_and_verify(
90 public_key: &PublicKey,
91 expected_nonce: u64,
92 header: &TransactionHeader,
93 read_slots: &[TransactionSlot],
94 write_slots: &[TransactionSlot],
95 payload: &[u128],
96 seal: &Seal,
97) -> Result<(), ContractError> {
98 if seal.nonce != expected_nonce {
99 return Err(ContractError::BadInput);
100 }
101
102 let tx_hash = hash_transaction(header, read_slots, write_slots, payload, seal.nonce);
103 let signature =
104 Signature::from_bytes(&seal.signature).map_err(|_error| ContractError::BadInput)?;
105 let signing_context = SigningContext::new(SIGNING_CONTEXT);
106 public_key
107 .verify(signing_context.bytes(tx_hash.as_bytes()), &signature)
108 .map_err(|_error| ContractError::Forbidden)
109}