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