1use eth_valkyoth_primitives::ChainId;
4
5use super::encode::{
6 TransactionEncodeError, encode_list_envelope, encode_typed_envelope,
7 encoded_access_list_to_len, encoded_legacy_to_len, encoded_list_envelope_len,
8 encoded_typed_envelope_len, encoded_u64_len, encoded_u256_len, sum_lengths, write_access_list,
9 write_address, write_authorization_list, write_blob_hashes, write_legacy_to, write_scalar,
10 write_transaction_to, write_u64, write_u256, write_wei,
11};
12use super::{
13 ACCESS_LIST_TRANSACTION_TYPE, BLOB_TRANSACTION_TYPE, DYNAMIC_FEE_TRANSACTION_TYPE,
14 SET_CODE_TRANSACTION_TYPE, SetCodeAuthorization, UnvalidatedAccessListTransaction,
15 UnvalidatedBlobTransaction, UnvalidatedDynamicFeeTransaction, UnvalidatedLegacyTransaction,
16 UnvalidatedSetCodeTransaction,
17};
18
19pub const SET_CODE_AUTHORIZATION_MAGIC: u8 = 0x05;
21
22pub fn encoded_legacy_eip155_signing_preimage_len(
24 transaction: &UnvalidatedLegacyTransaction<'_>,
25 chain_id: ChainId,
26) -> Result<usize, TransactionEncodeError> {
27 encoded_list_envelope_len(legacy_eip155_payload_len(transaction, chain_id)?).map_err(Into::into)
28}
29
30pub fn encode_legacy_eip155_signing_preimage(
32 transaction: &UnvalidatedLegacyTransaction<'_>,
33 chain_id: ChainId,
34 output: &mut [u8],
35) -> Result<usize, TransactionEncodeError> {
36 let payload_len = legacy_eip155_payload_len(transaction, chain_id)?;
37 encode_list_envelope(payload_len, output, |fields| {
38 write_u64(transaction.nonce.get(), fields)?;
39 write_wei(transaction.gas_price.to_be_bytes(), fields)?;
40 write_u64(transaction.gas_limit.get(), fields)?;
41 write_legacy_to(transaction.to, fields)?;
42 write_wei(transaction.value.to_be_bytes(), fields)?;
43 write_scalar(transaction.input, fields)?;
44 write_u64(chain_id.get(), fields)?;
45 write_u64(0, fields)?;
46 write_u64(0, fields)
47 })
48 .map_err(Into::into)
49}
50
51pub fn encoded_access_list_signing_preimage_len(
53 transaction: &UnvalidatedAccessListTransaction<'_>,
54) -> Result<usize, TransactionEncodeError> {
55 encoded_typed_envelope_len(access_list_signing_payload_len(transaction)?).map_err(Into::into)
56}
57
58pub fn encode_access_list_signing_preimage(
60 transaction: &UnvalidatedAccessListTransaction<'_>,
61 output: &mut [u8],
62) -> Result<usize, TransactionEncodeError> {
63 let payload_len = access_list_signing_payload_len(transaction)?;
64 encode_typed_envelope(
65 ACCESS_LIST_TRANSACTION_TYPE,
66 payload_len,
67 output,
68 |fields| {
69 write_u64(transaction.chain_id.get(), fields)?;
70 write_u64(transaction.nonce.get(), fields)?;
71 write_wei(transaction.gas_price.to_be_bytes(), fields)?;
72 write_u64(transaction.gas_limit.get(), fields)?;
73 write_transaction_to(transaction.to, fields)?;
74 write_wei(transaction.value.to_be_bytes(), fields)?;
75 write_scalar(transaction.input, fields)?;
76 write_access_list(transaction.access_list, fields)
77 },
78 )
79 .map_err(Into::into)
80}
81
82pub fn encoded_dynamic_fee_signing_preimage_len(
84 transaction: &UnvalidatedDynamicFeeTransaction<'_>,
85) -> Result<usize, TransactionEncodeError> {
86 encoded_typed_envelope_len(dynamic_fee_signing_payload_len(transaction)?).map_err(Into::into)
87}
88
89pub fn encode_dynamic_fee_signing_preimage(
91 transaction: &UnvalidatedDynamicFeeTransaction<'_>,
92 output: &mut [u8],
93) -> Result<usize, TransactionEncodeError> {
94 let payload_len = dynamic_fee_signing_payload_len(transaction)?;
95 encode_typed_envelope(
96 DYNAMIC_FEE_TRANSACTION_TYPE,
97 payload_len,
98 output,
99 |fields| {
100 write_u64(transaction.chain_id.get(), fields)?;
101 write_u64(transaction.nonce.get(), fields)?;
102 write_wei(transaction.max_priority_fee_per_gas.to_be_bytes(), fields)?;
103 write_wei(transaction.max_fee_per_gas.to_be_bytes(), fields)?;
104 write_u64(transaction.gas_limit.get(), fields)?;
105 write_transaction_to(transaction.to, fields)?;
106 write_wei(transaction.value.to_be_bytes(), fields)?;
107 write_scalar(transaction.input, fields)?;
108 write_access_list(transaction.access_list, fields)
109 },
110 )
111 .map_err(Into::into)
112}
113
114pub fn encoded_blob_signing_preimage_len(
116 transaction: &UnvalidatedBlobTransaction<'_>,
117) -> Result<usize, TransactionEncodeError> {
118 encoded_typed_envelope_len(blob_signing_payload_len(transaction)?).map_err(Into::into)
119}
120
121pub fn encode_blob_signing_preimage(
123 transaction: &UnvalidatedBlobTransaction<'_>,
124 output: &mut [u8],
125) -> Result<usize, TransactionEncodeError> {
126 let payload_len = blob_signing_payload_len(transaction)?;
127 encode_typed_envelope(BLOB_TRANSACTION_TYPE, payload_len, output, |fields| {
128 write_u64(transaction.chain_id.get(), fields)?;
129 write_u64(transaction.nonce.get(), fields)?;
130 write_wei(transaction.max_priority_fee_per_gas.to_be_bytes(), fields)?;
131 write_wei(transaction.max_fee_per_gas.to_be_bytes(), fields)?;
132 write_u64(transaction.gas_limit.get(), fields)?;
133 write_scalar(&transaction.to.to_bytes(), fields)?;
134 write_wei(transaction.value.to_be_bytes(), fields)?;
135 write_scalar(transaction.input, fields)?;
136 write_access_list(transaction.access_list, fields)?;
137 write_wei(transaction.max_fee_per_blob_gas.to_be_bytes(), fields)?;
138 write_blob_hashes(transaction.blob_versioned_hashes, fields)
139 })
140 .map_err(Into::into)
141}
142
143pub fn encoded_set_code_signing_preimage_len(
145 transaction: &UnvalidatedSetCodeTransaction<'_>,
146) -> Result<usize, TransactionEncodeError> {
147 encoded_typed_envelope_len(set_code_signing_payload_len(transaction)?).map_err(Into::into)
148}
149
150pub fn encode_set_code_signing_preimage(
152 transaction: &UnvalidatedSetCodeTransaction<'_>,
153 output: &mut [u8],
154) -> Result<usize, TransactionEncodeError> {
155 let payload_len = set_code_signing_payload_len(transaction)?;
156 encode_typed_envelope(SET_CODE_TRANSACTION_TYPE, payload_len, output, |fields| {
157 write_u64(transaction.chain_id.get(), fields)?;
158 write_u64(transaction.nonce.get(), fields)?;
159 write_wei(transaction.max_priority_fee_per_gas.to_be_bytes(), fields)?;
160 write_wei(transaction.max_fee_per_gas.to_be_bytes(), fields)?;
161 write_u64(transaction.gas_limit.get(), fields)?;
162 write_address(transaction.to, fields)?;
163 write_wei(transaction.value.to_be_bytes(), fields)?;
164 write_scalar(transaction.input, fields)?;
165 write_access_list(transaction.access_list, fields)?;
166 write_authorization_list(transaction.authorization_list, fields)
167 })
168 .map_err(Into::into)
169}
170
171pub fn encoded_set_code_authorization_signing_preimage_len(
173 authorization: SetCodeAuthorization,
174) -> Result<usize, TransactionEncodeError> {
175 eth_valkyoth_codec::checked_len_add(
176 1,
177 encoded_list_envelope_len(set_code_authorization_signing_payload_len(authorization)?)?,
178 )
179 .map_err(Into::into)
180}
181
182pub fn encode_set_code_authorization_signing_preimage(
184 authorization: SetCodeAuthorization,
185 output: &mut [u8],
186) -> Result<usize, TransactionEncodeError> {
187 let payload_len = set_code_authorization_signing_payload_len(authorization)?;
188 let total_len = encoded_set_code_authorization_signing_preimage_len(authorization)?;
189 let payload = output
190 .get_mut(1..total_len)
191 .ok_or(eth_valkyoth_codec::DecodeError::OffsetOutOfBounds)?;
192 encode_list_envelope(payload_len, payload, |fields| {
193 write_u256(authorization.chain_id.to_be_bytes(), fields)?;
194 write_address(authorization.address, fields)?;
195 write_u64(authorization.nonce.get(), fields)
196 })?;
197 let magic = output
198 .first_mut()
199 .ok_or(eth_valkyoth_codec::DecodeError::OffsetOutOfBounds)?;
200 *magic = SET_CODE_AUTHORIZATION_MAGIC;
201 Ok(total_len)
202}
203
204fn legacy_eip155_payload_len(
205 tx: &UnvalidatedLegacyTransaction<'_>,
206 chain_id: ChainId,
207) -> Result<usize, TransactionEncodeError> {
208 sum_lengths(&[
209 encoded_u64_len(tx.nonce.get())?,
210 encoded_u256_len(tx.gas_price.to_be_bytes())?,
211 encoded_u64_len(tx.gas_limit.get())?,
212 encoded_legacy_to_len(tx.to)?,
213 encoded_u256_len(tx.value.to_be_bytes())?,
214 eth_valkyoth_codec::encoded_rlp_scalar_len(tx.input)?,
215 encoded_u64_len(chain_id.get())?,
216 encoded_u64_len(0)?,
217 encoded_u64_len(0)?,
218 ])
219 .map_err(Into::into)
220}
221
222fn access_list_signing_payload_len(
223 tx: &UnvalidatedAccessListTransaction<'_>,
224) -> Result<usize, TransactionEncodeError> {
225 sum_lengths(&[
226 encoded_u64_len(tx.chain_id.get())?,
227 encoded_u64_len(tx.nonce.get())?,
228 encoded_u256_len(tx.gas_price.to_be_bytes())?,
229 encoded_u64_len(tx.gas_limit.get())?,
230 encoded_access_list_to_len(tx.to)?,
231 encoded_u256_len(tx.value.to_be_bytes())?,
232 eth_valkyoth_codec::encoded_rlp_scalar_len(tx.input)?,
233 tx.access_list.encoded_rlp_len(),
234 ])
235 .map_err(Into::into)
236}
237
238fn dynamic_fee_signing_payload_len(
239 tx: &UnvalidatedDynamicFeeTransaction<'_>,
240) -> Result<usize, TransactionEncodeError> {
241 sum_lengths(&[
242 encoded_u64_len(tx.chain_id.get())?,
243 encoded_u64_len(tx.nonce.get())?,
244 encoded_u256_len(tx.max_priority_fee_per_gas.to_be_bytes())?,
245 encoded_u256_len(tx.max_fee_per_gas.to_be_bytes())?,
246 encoded_u64_len(tx.gas_limit.get())?,
247 encoded_access_list_to_len(tx.to)?,
248 encoded_u256_len(tx.value.to_be_bytes())?,
249 eth_valkyoth_codec::encoded_rlp_scalar_len(tx.input)?,
250 tx.access_list.encoded_rlp_len(),
251 ])
252 .map_err(Into::into)
253}
254
255fn blob_signing_payload_len(
256 tx: &UnvalidatedBlobTransaction<'_>,
257) -> Result<usize, TransactionEncodeError> {
258 sum_lengths(&[
259 encoded_u64_len(tx.chain_id.get())?,
260 encoded_u64_len(tx.nonce.get())?,
261 encoded_u256_len(tx.max_priority_fee_per_gas.to_be_bytes())?,
262 encoded_u256_len(tx.max_fee_per_gas.to_be_bytes())?,
263 encoded_u64_len(tx.gas_limit.get())?,
264 eth_valkyoth_codec::encoded_rlp_scalar_len(&tx.to.to_bytes())?,
265 encoded_u256_len(tx.value.to_be_bytes())?,
266 eth_valkyoth_codec::encoded_rlp_scalar_len(tx.input)?,
267 tx.access_list.encoded_rlp_len(),
268 encoded_u256_len(tx.max_fee_per_blob_gas.to_be_bytes())?,
269 tx.blob_versioned_hashes.encoded_rlp_len(),
270 ])
271 .map_err(Into::into)
272}
273
274fn set_code_signing_payload_len(
275 tx: &UnvalidatedSetCodeTransaction<'_>,
276) -> Result<usize, TransactionEncodeError> {
277 sum_lengths(&[
278 encoded_u64_len(tx.chain_id.get())?,
279 encoded_u64_len(tx.nonce.get())?,
280 encoded_u256_len(tx.max_priority_fee_per_gas.to_be_bytes())?,
281 encoded_u256_len(tx.max_fee_per_gas.to_be_bytes())?,
282 encoded_u64_len(tx.gas_limit.get())?,
283 eth_valkyoth_codec::encoded_rlp_scalar_len(&tx.to.to_bytes())?,
284 encoded_u256_len(tx.value.to_be_bytes())?,
285 eth_valkyoth_codec::encoded_rlp_scalar_len(tx.input)?,
286 tx.access_list.encoded_rlp_len(),
287 tx.authorization_list.encoded_rlp_len(),
288 ])
289 .map_err(Into::into)
290}
291
292fn set_code_authorization_signing_payload_len(
293 authorization: SetCodeAuthorization,
294) -> Result<usize, TransactionEncodeError> {
295 sum_lengths(&[
296 encoded_u256_len(authorization.chain_id.to_be_bytes())?,
297 eth_valkyoth_codec::encoded_rlp_scalar_len(&authorization.address.to_bytes())?,
298 encoded_u64_len(authorization.nonce.get())?,
299 ])
300 .map_err(Into::into)
301}
302
303#[cfg(test)]
304mod tests {
305 use super::*;
306 use eth_valkyoth_codec::DecodeLimits;
307 use eth_valkyoth_primitives::{Address, B256, Gas, Nonce, Wei};
308
309 const TEST_LIMITS: DecodeLimits = DecodeLimits {
310 max_input_bytes: 128,
311 max_list_items: 16,
312 max_nesting_depth: 8,
313 max_total_allocation: 128,
314 max_proof_nodes: 4,
315 max_total_items: 32,
316 };
317 const EIP155_SIGNING_DATA: &[u8] = &[
318 0xec, 0x09, 0x85, 0x04, 0xa8, 0x17, 0xc8, 0x00, 0x82, 0x52, 0x08, 0x94, 0x35, 0x35, 0x35,
319 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35,
320 0x35, 0x35, 0x88, 0x0d, 0xe0, 0xb6, 0xb3, 0xa7, 0x64, 0x00, 0x00, 0x80, 0x01, 0x80, 0x80,
321 ];
322
323 #[test]
324 fn encodes_eip155_published_signing_data() {
325 let tx = UnvalidatedLegacyTransaction {
326 nonce: Nonce::new(9),
327 gas_price: Wei::from_u128(20_000_000_000),
328 gas_limit: Gas::new(21_000),
329 to: super::super::LegacyTransactionTo::Call(Address::from_bytes([0x35; 20])),
330 value: Wei::from_u128(1_000_000_000_000_000_000),
331 input: &[],
332 v: [0_u8; 32],
333 r: [0_u8; 32],
334 s: [0_u8; 32],
335 };
336 let mut output = [0_u8; 64];
337
338 assert_eq!(
339 encoded_legacy_eip155_signing_preimage_len(&tx, ChainId::new(1)),
340 Ok(EIP155_SIGNING_DATA.len())
341 );
342 let written = encode_legacy_eip155_signing_preimage(&tx, ChainId::new(1), &mut output);
343 assert_eq!(written, Ok(EIP155_SIGNING_DATA.len()));
344 assert_eq!(
345 output.get(..EIP155_SIGNING_DATA.len()),
346 Some(EIP155_SIGNING_DATA)
347 );
348 }
349
350 #[test]
351 fn typed_preimages_drop_signature_fields() {
352 let access_list_tx = [
353 0x01, 0xcd, 0x01, 0x02, 0x03, 0x82, 0x52, 0x08, 0x80, 0x05, 0x80, 0xc0, 0x01, 0x01,
354 0x02,
355 ];
356 let tx = super::super::decode_access_list_transaction(&access_list_tx, TEST_LIMITS);
357 assert!(tx.is_ok());
358 if let Ok(tx) = tx {
359 let mut output = [0_u8; 32];
360 let expected = [
361 0x01, 0xca, 0x01, 0x02, 0x03, 0x82, 0x52, 0x08, 0x80, 0x05, 0x80, 0xc0,
362 ];
363 let written = encode_access_list_signing_preimage(&tx, &mut output);
364 assert_eq!(written, Ok(expected.len()));
365 assert_eq!(output.get(..expected.len()), Some(expected.as_slice()));
366 }
367 }
368
369 #[test]
370 fn dynamic_and_blob_preimages_include_type_byte() {
371 let dynamic_fee_tx = [
372 0x02, 0xce, 0x01, 0x02, 0x03, 0x04, 0x82, 0x52, 0x08, 0x80, 0x05, 0x80, 0xc0, 0x01,
373 0x01, 0x02,
374 ];
375 let blob_tx = [
376 0x03, 0xf8, 0x45, 0x01, 0x02, 0x03, 0x04, 0x82, 0x52, 0x08, 0x94, 0x11, 0x11, 0x11,
377 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
378 0x11, 0x11, 0x11, 0x05, 0x80, 0xc0, 0x06, 0xe1, 0xa0, 0x01, 0x01, 0x01, 0x01, 0x01,
379 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
380 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
381 0x01, 0x02,
382 ];
383 let dynamic = super::super::decode_dynamic_fee_transaction(&dynamic_fee_tx, TEST_LIMITS);
384 let blob = super::super::decode_blob_transaction(&blob_tx, TEST_LIMITS);
385 assert!(dynamic.is_ok());
386 assert!(blob.is_ok());
387 if let (Ok(dynamic), Ok(blob)) = (dynamic, blob) {
388 let mut dynamic_output = [0_u8; 32];
389 let mut blob_output = [0_u8; 96];
390 let dynamic_written =
391 encode_dynamic_fee_signing_preimage(&dynamic, &mut dynamic_output);
392 let blob_written = encode_blob_signing_preimage(&blob, &mut blob_output);
393
394 assert_eq!(dynamic_written, Ok(13));
395 assert_eq!(blob_written, Ok(69));
396 assert_eq!(dynamic_output.first(), Some(&DYNAMIC_FEE_TRANSACTION_TYPE));
397 assert_eq!(blob_output.first(), Some(&BLOB_TRANSACTION_TYPE));
398 assert_ne!(
399 dynamic_output.get(1..dynamic_written.unwrap_or(1)),
400 dynamic_fee_tx.get(1..dynamic_fee_tx.len())
401 );
402 assert_eq!(blob.blob_versioned_hashes.hashes().count(), 1);
403 assert_eq!(B256::from_bytes([1_u8; 32]).to_bytes()[0], 1);
404 }
405 }
406}