ab_core_primitives/transaction/
owned.rs1#[cfg(test)]
4mod tests;
5
6use crate::transaction::{
7 SerializedTransactionLengths, Transaction, TransactionHeader, TransactionSlot,
8};
9use ab_aligned_buffer::{OwnedAlignedBuffer, SharedAlignedBuffer};
10use ab_io_type::alignment::Alignment;
11use ab_io_type::trivial_type::TrivialType;
12use core::slice;
13
14#[derive(Debug, thiserror::Error)]
16pub enum OwnedTransactionError {
17 #[error("Too many read slots")]
19 TooManyReadSlots,
20 #[error("Too many write slots")]
22 TooManyWriteSlots,
23 #[error("The payload is too large")]
25 PayloadTooLarge,
26 #[error("The payload is not a multiple of `u128`")]
28 PayloadIsNotMultipleOfU128,
29 #[error("The leal too large")]
31 SealTooLarge,
32 #[error("The transaction is too large")]
34 TransactionTooLarge,
35 #[error("Not enough bytes")]
37 NotEnoughBytes,
38 #[error("Invalid padding")]
40 InvalidPadding,
41 #[error("Expected number of bytes: {actual} != {expected}")]
43 UnexpectedNumberOfBytes {
44 actual: u32,
46 expected: u32,
48 },
49}
50
51#[derive(Debug, Clone)]
56pub struct OwnedTransaction {
57 buffer: SharedAlignedBuffer,
58}
59
60impl OwnedTransaction {
61 pub fn from_parts(
63 header: &TransactionHeader,
64 read_slots: &[TransactionSlot],
65 write_slots: &[TransactionSlot],
66 payload: &[u128],
67 seal: &[u8],
68 ) -> Result<Self, OwnedTransactionError> {
69 let mut buffer = OwnedAlignedBuffer::with_capacity(
70 (TransactionHeader::SIZE + SerializedTransactionLengths::SIZE)
71 .saturating_add(size_of_val(read_slots) as u32)
72 .saturating_add(size_of_val(write_slots) as u32)
73 .saturating_add(size_of_val(payload) as u32)
74 .saturating_add(size_of_val(seal) as u32),
75 );
76
77 Self::from_parts_into(header, read_slots, write_slots, payload, seal, &mut buffer)?;
78
79 Ok(Self {
80 buffer: buffer.into_shared(),
81 })
82 }
83
84 pub fn from_parts_into(
88 header: &TransactionHeader,
89 read_slots: &[TransactionSlot],
90 write_slots: &[TransactionSlot],
91 payload: &[u128],
92 seal: &[u8],
93 buffer: &mut OwnedAlignedBuffer,
94 ) -> Result<(), OwnedTransactionError> {
95 const {
96 assert!(
98 size_of::<TransactionHeader>()
99 .is_multiple_of(align_of::<SerializedTransactionLengths>())
100 );
101 }
102
103 let transaction_lengths = SerializedTransactionLengths {
104 read_slots: read_slots
105 .len()
106 .try_into()
107 .map_err(|_error| OwnedTransactionError::TooManyReadSlots)?,
108 write_slots: write_slots
109 .len()
110 .try_into()
111 .map_err(|_error| OwnedTransactionError::TooManyWriteSlots)?,
112 payload: size_of_val(payload)
113 .try_into()
114 .map_err(|_error| OwnedTransactionError::PayloadTooLarge)?,
115 seal: seal
116 .len()
117 .try_into()
118 .map_err(|_error| OwnedTransactionError::SealTooLarge)?,
119 padding: [0; _],
120 };
121
122 if !buffer.append(header.as_bytes()) {
123 return Err(OwnedTransactionError::TransactionTooLarge);
124 }
125 if !buffer.append(transaction_lengths.as_bytes()) {
126 return Err(OwnedTransactionError::TransactionTooLarge);
127 }
128
129 const {
130 assert!(
133 (size_of::<TransactionHeader>() + size_of::<SerializedTransactionLengths>())
134 .is_multiple_of(align_of::<TransactionSlot>())
135 );
136 }
137 if transaction_lengths.read_slots > 0 {
138 if !buffer.append(unsafe {
140 slice::from_raw_parts(read_slots.as_ptr().cast::<u8>(), size_of_val(read_slots))
141 }) {
142 return Err(OwnedTransactionError::TransactionTooLarge);
143 }
144 }
145 if transaction_lengths.write_slots > 0 {
146 if !buffer.append(unsafe {
148 slice::from_raw_parts(write_slots.as_ptr().cast::<u8>(), size_of_val(write_slots))
149 }) {
150 return Err(OwnedTransactionError::TransactionTooLarge);
151 }
152 }
153
154 const {
155 assert!(
158 (size_of::<TransactionHeader>() + size_of::<SerializedTransactionLengths>())
159 .is_multiple_of(usize::from(Alignment::MAX.get()))
160 );
161 assert!(
162 (size_of::<TransactionHeader>()
163 + size_of::<SerializedTransactionLengths>()
164 + size_of::<TransactionSlot>())
165 .is_multiple_of(usize::from(Alignment::MAX.get()))
166 );
167 }
168 if transaction_lengths.payload > 0 {
169 if !transaction_lengths.payload.is_multiple_of(u128::SIZE) {
170 return Err(OwnedTransactionError::PayloadIsNotMultipleOfU128);
171 }
172
173 if !buffer.append(unsafe {
175 slice::from_raw_parts(payload.as_ptr().cast::<u8>(), size_of_val(payload))
176 }) {
177 return Err(OwnedTransactionError::TransactionTooLarge);
178 }
179 }
180
181 const {
182 assert!(
185 (size_of::<TransactionHeader>() + size_of::<SerializedTransactionLengths>())
186 .is_multiple_of(usize::from(Alignment::MAX.get()))
187 );
188 assert!(
189 (size_of::<TransactionHeader>()
190 + size_of::<SerializedTransactionLengths>()
191 + size_of::<TransactionSlot>())
192 .is_multiple_of(usize::from(Alignment::MAX.get()))
193 );
194 }
195 if transaction_lengths.seal > 0 && !buffer.append(seal) {
196 return Err(OwnedTransactionError::TransactionTooLarge);
197 }
198
199 Ok(())
200 }
201
202 #[inline(always)]
204 pub fn from_transaction(transaction: Transaction<'_>) -> Result<Self, OwnedTransactionError> {
205 Self::from_parts(
206 transaction.header,
207 transaction.read_slots,
208 transaction.write_slots,
209 transaction.payload,
210 transaction.seal,
211 )
212 }
213
214 pub fn from_buffer(buffer: SharedAlignedBuffer) -> Result<Self, OwnedTransactionError> {
220 if buffer.len() < TransactionHeader::SIZE + SerializedTransactionLengths::SIZE {
221 return Err(OwnedTransactionError::NotEnoughBytes);
222 }
223
224 let lengths = unsafe {
227 buffer
228 .as_ptr()
229 .byte_add(size_of::<TransactionHeader>())
230 .cast::<SerializedTransactionLengths>()
231 .read()
232 };
233 let SerializedTransactionLengths {
234 read_slots: _,
235 write_slots: _,
236 payload,
237 seal: _,
238 padding,
239 } = lengths;
240
241 if padding != [0; _] {
244 return Err(OwnedTransactionError::InvalidPadding);
245 }
246
247 if !payload.is_multiple_of(u128::SIZE) {
248 return Err(OwnedTransactionError::PayloadIsNotMultipleOfU128);
249 }
250
251 let Some((transaction, remainder)) = Transaction::try_from_bytes(buffer.as_slice()) else {
252 return Err(OwnedTransactionError::NotEnoughBytes);
255 };
256
257 if !remainder.is_empty() {
258 return Err(OwnedTransactionError::UnexpectedNumberOfBytes {
259 actual: buffer.len(),
260 expected: u32::try_from(transaction.encoded_size())
261 .expect("Smaller than the buffer, whose length is `u32`; qed"),
262 });
263 }
264
265 Ok(Self { buffer })
266 }
267
268 pub fn buffer(&self) -> &SharedAlignedBuffer {
270 &self.buffer
271 }
272
273 pub fn transaction(&self) -> Transaction<'_> {
275 unsafe { Transaction::from_bytes_unchecked(self.buffer.as_slice()) }
277 }
278}