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}