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;
12
13#[derive(Debug, thiserror::Error)]
15pub enum OwnedTransactionError {
16 #[error("Too many read slots")]
18 TooManyReadSlots,
19 #[error("Too many write slots")]
21 TooManyWriteSlots,
22 #[error("The payload is too large")]
24 PayloadTooLarge,
25 #[error("The payload is not a multiple of `u128`")]
27 PayloadIsNotMultipleOfU128,
28 #[error("The leal too large")]
30 SealTooLarge,
31 #[error("The transaction is too large")]
33 TransactionTooLarge,
34 #[error("Not enough bytes")]
36 NotEnoughBytes,
37 #[error("Invalid padding")]
39 InvalidPadding,
40 #[error("Expected number of bytes: {actual} != {expected}")]
42 UnexpectedNumberOfBytes {
43 actual: u32,
45 expected: u32,
47 },
48}
49
50#[derive(Debug, Clone)]
55pub struct OwnedTransaction {
56 buffer: SharedAlignedBuffer,
57}
58
59impl OwnedTransaction {
60 pub fn from_parts(
62 header: &TransactionHeader,
63 read_slots: &[TransactionSlot],
64 write_slots: &[TransactionSlot],
65 payload: &[u128],
66 seal: &[u8],
67 ) -> Result<Self, OwnedTransactionError> {
68 let mut buffer = OwnedAlignedBuffer::with_capacity(
69 (TransactionHeader::SIZE + SerializedTransactionLengths::SIZE)
70 .saturating_add(size_of_val(read_slots) as u32)
71 .saturating_add(size_of_val(write_slots) as u32)
72 .saturating_add(size_of_val(payload) as u32)
73 .saturating_add(size_of_val(seal) as u32),
74 );
75
76 Self::from_parts_into(header, read_slots, write_slots, payload, seal, &mut buffer)?;
77
78 Ok(Self {
79 buffer: buffer.into_shared(),
80 })
81 }
82
83 pub fn from_parts_into(
87 header: &TransactionHeader,
88 read_slots: &[TransactionSlot],
89 write_slots: &[TransactionSlot],
90 payload: &[u128],
91 seal: &[u8],
92 buffer: &mut OwnedAlignedBuffer,
93 ) -> Result<(), OwnedTransactionError> {
94 const {
95 assert!(
97 size_of::<TransactionHeader>()
98 .is_multiple_of(align_of::<SerializedTransactionLengths>())
99 );
100 }
101
102 let transaction_lengths = SerializedTransactionLengths {
103 read_slots: read_slots
104 .len()
105 .try_into()
106 .map_err(|_error| OwnedTransactionError::TooManyReadSlots)?,
107 write_slots: write_slots
108 .len()
109 .try_into()
110 .map_err(|_error| OwnedTransactionError::TooManyWriteSlots)?,
111 payload: size_of_val(payload)
112 .try_into()
113 .map_err(|_error| OwnedTransactionError::PayloadTooLarge)?,
114 seal: seal
115 .len()
116 .try_into()
117 .map_err(|_error| OwnedTransactionError::SealTooLarge)?,
118 padding: [0; _],
119 };
120
121 if !buffer.append(header.as_bytes()) {
122 return Err(OwnedTransactionError::TransactionTooLarge);
123 }
124 if !buffer.append(transaction_lengths.as_bytes()) {
125 return Err(OwnedTransactionError::TransactionTooLarge);
126 }
127
128 const {
129 assert!(
132 (size_of::<TransactionHeader>() + size_of::<SerializedTransactionLengths>())
133 .is_multiple_of(align_of::<TransactionSlot>())
134 );
135 }
136 if transaction_lengths.read_slots > 0
137 && !buffer.append(TransactionSlot::slice_as_bytes(read_slots))
138 {
139 return Err(OwnedTransactionError::TransactionTooLarge);
140 }
141 if transaction_lengths.write_slots > 0
142 && !buffer.append(TransactionSlot::slice_as_bytes(write_slots))
143 {
144 return Err(OwnedTransactionError::TransactionTooLarge);
145 }
146
147 const {
148 assert!(
151 (size_of::<TransactionHeader>() + size_of::<SerializedTransactionLengths>())
152 .is_multiple_of(usize::from(Alignment::MAX.get()))
153 );
154 assert!(
155 (size_of::<TransactionHeader>()
156 + size_of::<SerializedTransactionLengths>()
157 + size_of::<TransactionSlot>())
158 .is_multiple_of(usize::from(Alignment::MAX.get()))
159 );
160 }
161 if transaction_lengths.payload > 0 {
162 if !transaction_lengths.payload.is_multiple_of(u128::SIZE) {
163 return Err(OwnedTransactionError::PayloadIsNotMultipleOfU128);
164 }
165
166 if !buffer.append(u128::slice_as_bytes(payload)) {
167 return Err(OwnedTransactionError::TransactionTooLarge);
168 }
169 }
170
171 const {
172 assert!(
175 (size_of::<TransactionHeader>() + size_of::<SerializedTransactionLengths>())
176 .is_multiple_of(usize::from(Alignment::MAX.get()))
177 );
178 assert!(
179 (size_of::<TransactionHeader>()
180 + size_of::<SerializedTransactionLengths>()
181 + size_of::<TransactionSlot>())
182 .is_multiple_of(usize::from(Alignment::MAX.get()))
183 );
184 }
185 if transaction_lengths.seal > 0 && !buffer.append(seal) {
186 return Err(OwnedTransactionError::TransactionTooLarge);
187 }
188
189 Ok(())
190 }
191
192 #[inline(always)]
194 pub fn from_transaction(transaction: Transaction<'_>) -> Result<Self, OwnedTransactionError> {
195 Self::from_parts(
196 transaction.header,
197 transaction.read_slots,
198 transaction.write_slots,
199 transaction.payload,
200 transaction.seal,
201 )
202 }
203
204 pub fn from_buffer(buffer: SharedAlignedBuffer) -> Result<Self, OwnedTransactionError> {
210 let (lengths, _remainder) = buffer
212 .as_slice()
213 .get(size_of::<TransactionHeader>()..)
214 .and_then(SerializedTransactionLengths::from_bytes)
215 .ok_or(OwnedTransactionError::NotEnoughBytes)?;
216 let SerializedTransactionLengths {
217 read_slots: _,
218 write_slots: _,
219 payload,
220 seal: _,
221 padding,
222 } = *lengths;
223
224 if padding != [0; _] {
227 return Err(OwnedTransactionError::InvalidPadding);
228 }
229
230 if !payload.is_multiple_of(u128::SIZE) {
231 return Err(OwnedTransactionError::PayloadIsNotMultipleOfU128);
232 }
233
234 let Some((transaction, remainder)) = Transaction::try_from_bytes(buffer.as_slice()) else {
235 return Err(OwnedTransactionError::NotEnoughBytes);
238 };
239
240 if !remainder.is_empty() {
241 return Err(OwnedTransactionError::UnexpectedNumberOfBytes {
242 actual: buffer.len(),
243 expected: u32::try_from(transaction.encoded_size())
244 .expect("Smaller than the buffer, whose length is `u32`; qed"),
245 });
246 }
247
248 Ok(Self { buffer })
249 }
250
251 pub fn buffer(&self) -> &SharedAlignedBuffer {
253 &self.buffer
254 }
255
256 pub fn transaction(&self) -> Transaction<'_> {
258 let (transaction, _remainder) = Transaction::try_from_bytes(self.buffer.as_slice())
259 .expect("Constructors either write a valid transaction or check it; qed");
260 transaction
261 }
262}