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