ab_transaction/lib.rs
1#![feature(ptr_as_ref_unchecked)]
2#![no_std]
3
4#[cfg(feature = "alloc")]
5pub mod owned;
6
7use ab_contracts_common::Address;
8use ab_contracts_common::block::BlockHash;
9use ab_contracts_common::env::Blake3Hash;
10use ab_contracts_io_type::trivial_type::TrivialType;
11use blake3::Hasher;
12use core::slice;
13
14/// A measure of compute resources, 1 Gas == 1 ns of compute on reference hardware
15#[derive(Debug, Default, Copy, Clone, TrivialType)]
16#[repr(C)]
17pub struct Gas(u64);
18
19#[derive(Debug, Default, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, TrivialType)]
20#[repr(C)]
21pub struct TransactionHash(Blake3Hash);
22
23#[derive(Debug, Copy, Clone, TrivialType)]
24#[repr(C)]
25pub struct TransactionHeader {
26 /// Block hash at which transaction was created
27 pub block_hash: BlockHash,
28 pub gas_limit: Gas,
29 /// Contract implementing `TxHandler` trait to use for transaction verification and execution
30 pub contract: Address,
31}
32
33#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, TrivialType)]
34#[repr(C)]
35pub struct TransactionSlot {
36 pub owner: Address,
37 pub contract: Address,
38}
39
40/// Similar to `Transaction`, but doesn't require `allow` or data ownership.
41///
42/// Can be created with `Transaction::as_ref()` call.
43#[derive(Debug, Copy, Clone)]
44pub struct Transaction<'a> {
45 pub header: &'a TransactionHeader,
46 /// Slots in the form of [`TransactionSlot`] that may be read during transaction processing.
47 ///
48 /// These are the only slots that can be used in authorization code.
49 ///
50 /// The code slot of the contract that is being executed and balance of native token are
51 /// implicitly included and doesn't need to be specified (see [`Transaction::read_slots()`].
52 /// Also slots that may also be written to do not need to be repeated in the read slots.
53 pub read_slots: &'a [TransactionSlot],
54 /// Slots in the form of [`TransactionSlot`] that may be written during transaction processing
55 pub write_slots: &'a [TransactionSlot],
56 pub payload: &'a [u128],
57 pub seal: &'a [u8],
58}
59
60impl Transaction<'_> {
61 /// Compute transaction hash.
62 ///
63 /// Note: this computes transaction hash on every call, so worth caching if it is expected to be
64 /// called often.
65 pub fn hash(&self) -> TransactionHash {
66 let mut hasher = Hasher::new();
67
68 hasher.update(self.header.as_bytes());
69 // SAFETY: `TransactionSlot` is `TrivialType` and can be treated as bytes
70 hasher.update(unsafe {
71 slice::from_raw_parts(
72 self.read_slots.as_ptr().cast::<u8>(),
73 size_of_val(self.read_slots),
74 )
75 });
76 // SAFETY: `TransactionSlot` is `TrivialType` and can be treated as bytes
77 hasher.update(unsafe {
78 slice::from_raw_parts(
79 self.write_slots.as_ptr().cast::<u8>(),
80 size_of_val(self.write_slots),
81 )
82 });
83 // SAFETY: `u128` and can be treated as bytes
84 hasher.update(unsafe {
85 slice::from_raw_parts(
86 self.payload.as_ptr().cast::<u8>(),
87 size_of_val(self.payload),
88 )
89 });
90 hasher.update(self.seal);
91
92 TransactionHash(*hasher.finalize().as_bytes())
93 }
94
95 /// Read slots touched by the transaction.
96 ///
97 /// In contrast to `read_slots` property, this includes implicitly used slots.
98 pub fn read_slots(&self) -> impl Iterator<Item = TransactionSlot> {
99 // Slots included implicitly that are always used
100 let implicit_slots = [
101 TransactionSlot {
102 owner: self.header.contract,
103 contract: Address::SYSTEM_CODE,
104 },
105 // TODO: Uncomment once system token contract exists
106 // TransactionSlot {
107 // owner: self.header.contract,
108 // contract: Address::SYSTEM_TOKEN,
109 // },
110 ];
111
112 implicit_slots
113 .into_iter()
114 .chain(self.read_slots.iter().copied())
115 }
116
117 /// All slots touched by the transaction.
118 ///
119 /// In contrast to `read_slots` and `write_slots` properties, this includes implicitly used
120 /// slots.
121 pub fn slots(&self) -> impl Iterator<Item = TransactionSlot> {
122 self.read_slots().chain(self.write_slots.iter().copied())
123 }
124}