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