ab_core_primitives/transaction/
owned.rs1use crate::transaction::{
4 SerializedTransactionLengths, Transaction, TransactionHeader, TransactionSlot,
5};
6use ab_aligned_buffer::{OwnedAlignedBuffer, SharedAlignedBuffer};
7use ab_io_type::trivial_type::TrivialType;
8use core::slice;
9
10#[derive(Debug, thiserror::Error)]
12pub enum OwnedTransactionError {
13 #[error("Too many read slots")]
15 TooManyReadSlots,
16 #[error("Too many write slots")]
18 TooManyWriteSlots,
19 #[error("The payload is too large")]
21 PayloadTooLarge,
22 #[error("The payload is not a multiple of `u128`")]
24 PayloadIsNotMultipleOfU128,
25 #[error("The leal too large")]
27 SealTooLarge,
28 #[error("The transaction is too large")]
30 TransactionTooLarge,
31 #[error("Not enough bytes")]
33 NotEnoughBytes,
34 #[error("Invalid padding")]
36 InvalidPadding,
37 #[error("Expected number of bytes: {actual} != {expected}")]
39 UnexpectedNumberOfBytes {
40 actual: u32,
42 expected: u32,
44 },
45}
46
47#[derive(Debug, Clone)]
52pub struct OwnedTransaction {
53 buffer: SharedAlignedBuffer,
54}
55
56impl OwnedTransaction {
57 pub fn from_parts(
59 header: &TransactionHeader,
60 read_slots: &[TransactionSlot],
61 write_slots: &[TransactionSlot],
62 payload: &[u128],
63 seal: &[u8],
64 ) -> Result<Self, OwnedTransactionError> {
65 let mut buffer = OwnedAlignedBuffer::with_capacity(
66 (TransactionHeader::SIZE + SerializedTransactionLengths::SIZE)
67 .saturating_add(size_of_val(read_slots) as u32)
68 .saturating_add(size_of_val(write_slots) as u32)
69 .saturating_add(size_of_val(payload) as u32)
70 .saturating_add(size_of_val(seal) as u32),
71 );
72
73 Self::from_parts_into(header, read_slots, write_slots, payload, seal, &mut buffer)?;
74
75 Ok(Self {
76 buffer: buffer.into_shared(),
77 })
78 }
79
80 pub fn from_parts_into(
82 header: &TransactionHeader,
83 read_slots: &[TransactionSlot],
84 write_slots: &[TransactionSlot],
85 payload: &[u128],
86 seal: &[u8],
87 buffer: &mut OwnedAlignedBuffer,
88 ) -> Result<(), OwnedTransactionError> {
89 const _: () = {
90 assert!(
92 size_of::<TransactionHeader>()
93 .is_multiple_of(align_of::<SerializedTransactionLengths>())
94 );
95 };
96
97 let transaction_lengths = SerializedTransactionLengths {
98 read_slots: read_slots
99 .len()
100 .try_into()
101 .map_err(|_error| OwnedTransactionError::TooManyReadSlots)?,
102 write_slots: read_slots
103 .len()
104 .try_into()
105 .map_err(|_error| OwnedTransactionError::TooManyWriteSlots)?,
106 payload: size_of_val(payload)
107 .try_into()
108 .map_err(|_error| OwnedTransactionError::PayloadTooLarge)?,
109 seal: seal
110 .len()
111 .try_into()
112 .map_err(|_error| OwnedTransactionError::SealTooLarge)?,
113 padding: [0; _],
114 };
115
116 let true = buffer.append(header.as_bytes()) else {
117 unreachable!("Always fits into `u32`");
118 };
119 let true = buffer.append(transaction_lengths.as_bytes()) else {
120 unreachable!("Always fits into `u32`");
121 };
122
123 const _: () = {
124 assert!(
127 (size_of::<TransactionHeader>() + size_of::<SerializedTransactionLengths>())
128 .is_multiple_of(align_of::<TransactionSlot>())
129 );
130 };
131 if transaction_lengths.read_slots > 0 {
132 if !buffer.append(unsafe {
134 slice::from_raw_parts(read_slots.as_ptr().cast::<u8>(), size_of_val(read_slots))
135 }) {
136 return Err(OwnedTransactionError::TransactionTooLarge);
137 }
138 }
139 if transaction_lengths.write_slots > 0 {
140 if !buffer.append(unsafe {
142 slice::from_raw_parts(write_slots.as_ptr().cast::<u8>(), size_of_val(write_slots))
143 }) {
144 return Err(OwnedTransactionError::TransactionTooLarge);
145 }
146 }
147
148 const _: () = {
149 assert!(
152 (size_of::<TransactionHeader>() + size_of::<SerializedTransactionLengths>())
153 .is_multiple_of(align_of::<u128>())
154 );
155 assert!(
156 (size_of::<TransactionHeader>()
157 + size_of::<SerializedTransactionLengths>()
158 + size_of::<TransactionSlot>())
159 .is_multiple_of(align_of::<u128>())
160 );
161 };
162 if transaction_lengths.payload > 0 {
163 if !transaction_lengths.payload.is_multiple_of(u128::SIZE) {
164 return Err(OwnedTransactionError::PayloadIsNotMultipleOfU128);
165 }
166
167 if !buffer.append(unsafe {
169 slice::from_raw_parts(payload.as_ptr().cast::<u8>(), size_of_val(payload))
170 }) {
171 return Err(OwnedTransactionError::TransactionTooLarge);
172 }
173 }
174
175 const _: () = {
176 assert!(
179 (size_of::<TransactionHeader>() + size_of::<SerializedTransactionLengths>())
180 .is_multiple_of(align_of::<u128>())
181 );
182 assert!(
183 (size_of::<TransactionHeader>()
184 + size_of::<SerializedTransactionLengths>()
185 + size_of::<TransactionSlot>())
186 .is_multiple_of(align_of::<u128>())
187 );
188 };
189 if transaction_lengths.seal > 0 && !buffer.append(seal) {
190 return Err(OwnedTransactionError::TransactionTooLarge);
191 }
192
193 Ok(())
194 }
195
196 #[inline(always)]
198 pub fn from_transaction(transaction: Transaction<'_>) -> Result<Self, OwnedTransactionError> {
199 Self::from_parts(
200 transaction.header,
201 transaction.read_slots,
202 transaction.write_slots,
203 transaction.payload,
204 transaction.seal,
205 )
206 }
207
208 pub fn from_buffer(buffer: SharedAlignedBuffer) -> Result<Self, OwnedTransactionError> {
210 if (buffer.len() as usize)
211 < size_of::<TransactionHeader>() + size_of::<SerializedTransactionLengths>()
212 {
213 return Err(OwnedTransactionError::NotEnoughBytes);
214 }
215
216 let lengths = unsafe {
218 buffer
219 .as_ptr()
220 .add(size_of::<TransactionHeader>())
221 .cast::<SerializedTransactionLengths>()
222 .read()
223 };
224 let SerializedTransactionLengths {
225 read_slots,
226 write_slots,
227 payload,
228 seal,
229 padding,
230 } = lengths;
231
232 if padding != [0; _] {
233 return Err(OwnedTransactionError::InvalidPadding);
234 }
235
236 if !payload.is_multiple_of(u128::SIZE) {
237 return Err(OwnedTransactionError::PayloadIsNotMultipleOfU128);
238 }
239
240 let expected = (size_of::<TransactionHeader>() as u32
241 + size_of::<SerializedTransactionLengths>() as u32)
242 .saturating_add(u32::from(read_slots))
243 .saturating_add(u32::from(write_slots))
244 .saturating_add(payload)
245 .saturating_add(seal);
246
247 if buffer.len() != expected {
248 return Err(OwnedTransactionError::UnexpectedNumberOfBytes {
249 actual: buffer.len(),
250 expected,
251 });
252 }
253
254 Ok(Self { buffer })
255 }
256
257 pub fn buffer(&self) -> &SharedAlignedBuffer {
259 &self.buffer
260 }
261
262 pub fn transaction(&self) -> Transaction<'_> {
264 unsafe { Transaction::from_bytes_unchecked(self.buffer.as_slice()) }
266 }
267}