referrerpolicy=no-referrer-when-downgrade

pallet_revive/evm/api/
rlp_codec.rs

1// This file is part of Substrate.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: Apache-2.0
5
6// Licensed under the Apache License, Version 2.0 (the "License");
7// you may not use this file except in compliance with the License.
8// You may obtain a copy of the License at
9//
10// 	http://www.apache.org/licenses/LICENSE-2.0
11//
12// Unless required by applicable law or agreed to in writing, software
13// distributed under the License is distributed on an "AS IS" BASIS,
14// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15// See the License for the specific language governing permissions and
16// limitations under the License.
17//! RLP encoding and decoding for Ethereum transactions.
18//! See <https://ethereum.org/en/developers/docs/data-structures-and-encoding/rlp/> for more information about RLP encoding.
19
20use super::*;
21use alloc::vec::Vec;
22use rlp::{Decodable, Encodable};
23
24impl TransactionUnsigned {
25	/// Return the bytes to be signed by the private key.
26	pub fn unsigned_payload(&self) -> Vec<u8> {
27		use TransactionUnsigned::*;
28		let mut s = rlp::RlpStream::new();
29		match self {
30			Transaction7702Unsigned(ref tx) => {
31				s.append(&tx.r#type.value());
32				s.append(tx);
33			},
34			Transaction2930Unsigned(ref tx) => {
35				s.append(&tx.r#type.value());
36				s.append(tx);
37			},
38			Transaction1559Unsigned(ref tx) => {
39				s.append(&tx.r#type.value());
40				s.append(tx);
41			},
42			Transaction4844Unsigned(ref tx) => {
43				s.append(&tx.r#type.value());
44				s.append(tx);
45			},
46			TransactionLegacyUnsigned(ref tx) => {
47				s.append(tx);
48			},
49		}
50
51		s.out().to_vec()
52	}
53}
54
55impl TransactionSigned {
56	/// Extract the unsigned transaction from a signed transaction.
57	pub fn unsigned(self) -> TransactionUnsigned {
58		use TransactionSigned::*;
59		use TransactionUnsigned::*;
60		match self {
61			Transaction7702Signed(tx) => Transaction7702Unsigned(tx.transaction_7702_unsigned),
62			Transaction2930Signed(tx) => Transaction2930Unsigned(tx.transaction_2930_unsigned),
63			Transaction1559Signed(tx) => Transaction1559Unsigned(tx.transaction_1559_unsigned),
64			Transaction4844Signed(tx) => Transaction4844Unsigned(tx.transaction_4844_unsigned),
65			TransactionLegacySigned(tx) =>
66				TransactionLegacyUnsigned(tx.transaction_legacy_unsigned),
67		}
68	}
69
70	/// Encode the Ethereum transaction into bytes.
71	pub fn signed_payload(&self) -> Vec<u8> {
72		use TransactionSigned::*;
73		let mut s = rlp::RlpStream::new();
74		match self {
75			Transaction7702Signed(ref tx) => {
76				s.append(&tx.transaction_7702_unsigned.r#type.value());
77				s.append(tx);
78			},
79			Transaction2930Signed(ref tx) => {
80				s.append(&tx.transaction_2930_unsigned.r#type.value());
81				s.append(tx);
82			},
83			Transaction1559Signed(ref tx) => {
84				s.append(&tx.transaction_1559_unsigned.r#type.value());
85				s.append(tx);
86			},
87			Transaction4844Signed(ref tx) => {
88				s.append(&tx.transaction_4844_unsigned.r#type.value());
89				s.append(tx);
90			},
91			TransactionLegacySigned(ref tx) => {
92				s.append(tx);
93			},
94		}
95
96		s.out().to_vec()
97	}
98
99	/// Decode the Ethereum transaction from bytes.
100	pub fn decode(data: &[u8]) -> Result<Self, rlp::DecoderError> {
101		if data.len() < 1 {
102			return Err(rlp::DecoderError::RlpIsTooShort);
103		}
104		match data[0] {
105			TYPE_EIP2930 => rlp::decode::<Transaction2930Signed>(&data[1..]).map(Into::into),
106			TYPE_EIP1559 => rlp::decode::<Transaction1559Signed>(&data[1..]).map(Into::into),
107			TYPE_EIP4844 => rlp::decode::<Transaction4844Signed>(&data[1..]).map(Into::into),
108			_ => rlp::decode::<TransactionLegacySigned>(data).map(Into::into),
109		}
110	}
111}
112
113impl TransactionUnsigned {
114	/// Get a signed transaction payload with a dummy 65 bytes signature.
115	pub fn dummy_signed_payload(self) -> Vec<u8> {
116		const DUMMY_SIGNATURE: [u8; 65] = [1u8; 65];
117		self.with_signature(DUMMY_SIGNATURE).signed_payload()
118	}
119}
120
121/// See <https://eips.ethereum.org/EIPS/eip-155>
122impl Encodable for TransactionLegacyUnsigned {
123	fn rlp_append(&self, s: &mut rlp::RlpStream) {
124		if let Some(chain_id) = self.chain_id {
125			s.begin_list(9);
126			s.append(&self.nonce);
127			s.append(&self.gas_price);
128			s.append(&self.gas);
129			match self.to {
130				Some(ref to) => s.append(to),
131				None => s.append_empty_data(),
132			};
133			s.append(&self.value);
134			s.append(&self.input.0);
135			s.append(&chain_id);
136			s.append(&0u8);
137			s.append(&0u8);
138		} else {
139			s.begin_list(6);
140			s.append(&self.nonce);
141			s.append(&self.gas_price);
142			s.append(&self.gas);
143			match self.to {
144				Some(ref to) => s.append(to),
145				None => s.append_empty_data(),
146			};
147			s.append(&self.value);
148			s.append(&self.input.0);
149		}
150	}
151}
152
153impl Decodable for TransactionLegacyUnsigned {
154	fn decode(rlp: &rlp::Rlp) -> Result<Self, rlp::DecoderError> {
155		Ok(TransactionLegacyUnsigned {
156			nonce: rlp.val_at(0)?,
157			gas_price: rlp.val_at(1)?,
158			gas: rlp.val_at(2)?,
159			to: {
160				let to = rlp.at(3)?;
161				if to.is_empty() {
162					None
163				} else {
164					Some(to.as_val()?)
165				}
166			},
167			value: rlp.val_at(4)?,
168			input: Bytes(rlp.val_at(5)?),
169			chain_id: rlp.val_at(6).ok(),
170			..Default::default()
171		})
172	}
173}
174
175impl Encodable for TransactionLegacySigned {
176	fn rlp_append(&self, s: &mut rlp::RlpStream) {
177		let tx = &self.transaction_legacy_unsigned;
178
179		s.begin_list(9);
180		s.append(&tx.nonce);
181		s.append(&tx.gas_price);
182		s.append(&tx.gas);
183		match tx.to {
184			Some(ref to) => s.append(to),
185			None => s.append_empty_data(),
186		};
187		s.append(&tx.value);
188		s.append(&tx.input.0);
189
190		s.append(&self.v);
191		s.append(&self.r);
192		s.append(&self.s);
193	}
194}
195
196impl Encodable for AccessListEntry {
197	fn rlp_append(&self, s: &mut rlp::RlpStream) {
198		s.begin_list(2);
199		s.append(&self.address);
200		s.append_list(&self.storage_keys);
201	}
202}
203
204impl Decodable for AccessListEntry {
205	fn decode(rlp: &rlp::Rlp) -> Result<Self, rlp::DecoderError> {
206		Ok(AccessListEntry { address: rlp.val_at(0)?, storage_keys: rlp.list_at(1)? })
207	}
208}
209
210impl Encodable for AuthorizationListEntry {
211	fn rlp_append(&self, s: &mut rlp::RlpStream) {
212		s.begin_list(6);
213		s.append(&self.chain_id);
214		s.append(&self.address);
215		s.append(&self.nonce);
216		s.append(&self.y_parity);
217		s.append(&self.r);
218		s.append(&self.s);
219	}
220}
221
222impl Decodable for AuthorizationListEntry {
223	fn decode(rlp: &rlp::Rlp) -> Result<Self, rlp::DecoderError> {
224		Ok(AuthorizationListEntry {
225			chain_id: rlp.val_at(0)?,
226			address: rlp.val_at(1)?,
227			nonce: rlp.val_at(2)?,
228			y_parity: rlp.val_at(3)?,
229			r: rlp.val_at(4)?,
230			s: rlp.val_at(5)?,
231		})
232	}
233}
234
235/// See <https://eips.ethereum.org/EIPS/eip-1559>
236impl Encodable for Transaction1559Unsigned {
237	fn rlp_append(&self, s: &mut rlp::RlpStream) {
238		s.begin_list(9);
239		s.append(&self.chain_id);
240		s.append(&self.nonce);
241		s.append(&self.max_priority_fee_per_gas);
242		s.append(&self.max_fee_per_gas);
243		s.append(&self.gas);
244		match self.to {
245			Some(ref to) => s.append(to),
246			None => s.append_empty_data(),
247		};
248		s.append(&self.value);
249		s.append(&self.input.0);
250		s.append_list(&self.access_list);
251	}
252}
253
254/// See <https://eips.ethereum.org/EIPS/eip-1559>
255impl Encodable for Transaction1559Signed {
256	fn rlp_append(&self, s: &mut rlp::RlpStream) {
257		let tx = &self.transaction_1559_unsigned;
258		s.begin_list(12);
259		s.append(&tx.chain_id);
260		s.append(&tx.nonce);
261		s.append(&tx.max_priority_fee_per_gas);
262		s.append(&tx.max_fee_per_gas);
263		s.append(&tx.gas);
264		match tx.to {
265			Some(ref to) => s.append(to),
266			None => s.append_empty_data(),
267		};
268		s.append(&tx.value);
269		s.append(&tx.input.0);
270		s.append_list(&tx.access_list);
271
272		s.append(&self.y_parity);
273		s.append(&self.r);
274		s.append(&self.s);
275	}
276}
277
278impl Decodable for Transaction1559Signed {
279	fn decode(rlp: &rlp::Rlp) -> Result<Self, rlp::DecoderError> {
280		Ok(Transaction1559Signed {
281			transaction_1559_unsigned: {
282				Transaction1559Unsigned {
283					chain_id: rlp.val_at(0)?,
284					nonce: rlp.val_at(1)?,
285					max_priority_fee_per_gas: rlp.val_at(2)?,
286					max_fee_per_gas: rlp.val_at(3)?,
287					gas: rlp.val_at(4)?,
288					to: {
289						let to = rlp.at(5)?;
290						if to.is_empty() {
291							None
292						} else {
293							Some(to.as_val()?)
294						}
295					},
296					value: rlp.val_at(6)?,
297					input: Bytes(rlp.val_at(7)?),
298					access_list: rlp.list_at(8)?,
299					..Default::default()
300				}
301			},
302			y_parity: rlp.val_at(9)?,
303			r: rlp.val_at(10)?,
304			s: rlp.val_at(11)?,
305			..Default::default()
306		})
307	}
308}
309
310//See https://eips.ethereum.org/EIPS/eip-2930
311impl Encodable for Transaction2930Unsigned {
312	fn rlp_append(&self, s: &mut rlp::RlpStream) {
313		s.begin_list(8);
314		s.append(&self.chain_id);
315		s.append(&self.nonce);
316		s.append(&self.gas_price);
317		s.append(&self.gas);
318		match self.to {
319			Some(ref to) => s.append(to),
320			None => s.append_empty_data(),
321		};
322		s.append(&self.value);
323		s.append(&self.input.0);
324		s.append_list(&self.access_list);
325	}
326}
327
328//See https://eips.ethereum.org/EIPS/eip-2930
329impl Encodable for Transaction2930Signed {
330	fn rlp_append(&self, s: &mut rlp::RlpStream) {
331		let tx = &self.transaction_2930_unsigned;
332		s.begin_list(11);
333		s.append(&tx.chain_id);
334		s.append(&tx.nonce);
335		s.append(&tx.gas_price);
336		s.append(&tx.gas);
337		match tx.to {
338			Some(ref to) => s.append(to),
339			None => s.append_empty_data(),
340		};
341		s.append(&tx.value);
342		s.append(&tx.input.0);
343		s.append_list(&tx.access_list);
344		s.append(&self.y_parity);
345		s.append(&self.r);
346		s.append(&self.s);
347	}
348}
349
350impl Decodable for Transaction2930Signed {
351	fn decode(rlp: &rlp::Rlp) -> Result<Self, rlp::DecoderError> {
352		Ok(Transaction2930Signed {
353			transaction_2930_unsigned: {
354				Transaction2930Unsigned {
355					chain_id: rlp.val_at(0)?,
356					nonce: rlp.val_at(1)?,
357					gas_price: rlp.val_at(2)?,
358					gas: rlp.val_at(3)?,
359					to: {
360						let to = rlp.at(4)?;
361						if to.is_empty() {
362							None
363						} else {
364							Some(to.as_val()?)
365						}
366					},
367					value: rlp.val_at(5)?,
368					input: Bytes(rlp.val_at(6)?),
369					access_list: rlp.list_at(7)?,
370					..Default::default()
371				}
372			},
373			y_parity: rlp.val_at(8)?,
374			r: rlp.val_at(9)?,
375			s: rlp.val_at(10)?,
376			..Default::default()
377		})
378	}
379}
380
381//See https://eips.ethereum.org/EIPS/eip-7702
382impl Encodable for Transaction7702Unsigned {
383	fn rlp_append(&self, s: &mut rlp::RlpStream) {
384		s.begin_list(10);
385		s.append(&self.chain_id);
386		s.append(&self.nonce);
387		s.append(&self.max_priority_fee_per_gas);
388		s.append(&self.max_fee_per_gas);
389		s.append(&self.gas);
390		s.append(&self.to);
391		s.append(&self.value);
392		s.append(&self.input.0);
393		s.append_list(&self.access_list);
394		s.append_list(&self.authorization_list);
395	}
396}
397
398impl Encodable for Transaction4844Unsigned {
399	fn rlp_append(&self, s: &mut rlp::RlpStream) {
400		s.begin_list(11);
401		s.append(&self.chain_id);
402		s.append(&self.nonce);
403		s.append(&self.max_priority_fee_per_gas);
404		s.append(&self.max_fee_per_gas);
405		s.append(&self.gas);
406		s.append(&self.to);
407		s.append(&self.value);
408		s.append(&self.input.0);
409		s.append_list(&self.access_list);
410		s.append(&self.max_fee_per_blob_gas);
411		s.append_list(&self.blob_versioned_hashes);
412	}
413}
414
415//See https://eips.ethereum.org/EIPS/eip-7702
416impl Encodable for Transaction7702Signed {
417	fn rlp_append(&self, s: &mut rlp::RlpStream) {
418		let tx = &self.transaction_7702_unsigned;
419		s.begin_list(13);
420		s.append(&tx.chain_id);
421		s.append(&tx.nonce);
422		s.append(&tx.max_priority_fee_per_gas);
423		s.append(&tx.max_fee_per_gas);
424		s.append(&tx.gas);
425		s.append(&tx.to);
426		s.append(&tx.value);
427		s.append(&tx.input.0);
428		s.append_list(&tx.access_list);
429		s.append_list(&tx.authorization_list);
430		s.append(&self.y_parity);
431		s.append(&self.r);
432		s.append(&self.s);
433	}
434}
435
436//See https://eips.ethereum.org/EIPS/eip-4844
437impl Encodable for Transaction4844Signed {
438	fn rlp_append(&self, s: &mut rlp::RlpStream) {
439		let tx = &self.transaction_4844_unsigned;
440		s.begin_list(14);
441		s.append(&tx.chain_id);
442		s.append(&tx.nonce);
443		s.append(&tx.max_priority_fee_per_gas);
444		s.append(&tx.max_fee_per_gas);
445		s.append(&tx.gas);
446		s.append(&tx.to);
447		s.append(&tx.value);
448		s.append(&tx.input.0);
449		s.append_list(&tx.access_list);
450		s.append(&tx.max_fee_per_blob_gas);
451		s.append_list(&tx.blob_versioned_hashes);
452		s.append(&self.y_parity);
453		s.append(&self.r);
454		s.append(&self.s);
455	}
456}
457
458impl Decodable for Transaction4844Signed {
459	fn decode(rlp: &rlp::Rlp) -> Result<Self, rlp::DecoderError> {
460		Ok(Transaction4844Signed {
461			transaction_4844_unsigned: {
462				Transaction4844Unsigned {
463					chain_id: rlp.val_at(0)?,
464					nonce: rlp.val_at(1)?,
465					max_priority_fee_per_gas: rlp.val_at(2)?,
466					max_fee_per_gas: rlp.val_at(3)?,
467					gas: rlp.val_at(4)?,
468					to: rlp.val_at(5)?,
469					value: rlp.val_at(6)?,
470					input: Bytes(rlp.val_at(7)?),
471					access_list: rlp.list_at(8)?,
472					max_fee_per_blob_gas: rlp.val_at(9)?,
473					blob_versioned_hashes: rlp.list_at(10)?,
474					..Default::default()
475				}
476			},
477			y_parity: rlp.val_at(11)?,
478			r: rlp.val_at(12)?,
479			s: rlp.val_at(13)?,
480		})
481	}
482}
483
484/// See <https://eips.ethereum.org/EIPS/eip-155>
485impl Decodable for TransactionLegacySigned {
486	fn decode(rlp: &rlp::Rlp) -> Result<Self, rlp::DecoderError> {
487		let v: U256 = rlp.val_at(6)?;
488
489		let extract_chain_id = |v: U256| {
490			if v.ge(&35u32.into()) {
491				Some((v - 35) / 2)
492			} else {
493				None
494			}
495		};
496
497		Ok(TransactionLegacySigned {
498			transaction_legacy_unsigned: {
499				TransactionLegacyUnsigned {
500					nonce: rlp.val_at(0)?,
501					gas_price: rlp.val_at(1)?,
502					gas: rlp.val_at(2)?,
503					to: {
504						let to = rlp.at(3)?;
505						if to.is_empty() {
506							None
507						} else {
508							Some(to.as_val()?)
509						}
510					},
511					value: rlp.val_at(4)?,
512					input: Bytes(rlp.val_at(5)?),
513					chain_id: extract_chain_id(v).map(|v| v.into()),
514					r#type: TypeLegacy {},
515				}
516			},
517			v,
518			r: rlp.val_at(7)?,
519			s: rlp.val_at(8)?,
520		})
521	}
522}
523
524#[cfg(test)]
525mod test {
526	use super::*;
527
528	#[test]
529	fn encode_decode_tx_works() {
530		let txs = [
531			// Legacy
532			(
533				"f86080808301e24194095e7baea6a6c7c4c2dfeb977efac326af552d87808025a0fe38ca4e44a30002ac54af7cf922a6ac2ba11b7d22f548e8ecb3f51f41cb31b0a06de6a5cbae13c0c856e33acf021b51819636cfc009d39eafb9f606d546e305a8",
534				r#"
535				{
536					"chainId": "0x1",
537					"gas": "0x1e241",
538					"gasPrice": "0x0",
539					"input": "0x",
540					"nonce": "0x0",
541					"to": "0x095e7baea6a6c7c4c2dfeb977efac326af552d87",
542					"type": "0x0",
543					"value": "0x0",
544					"r": "0xfe38ca4e44a30002ac54af7cf922a6ac2ba11b7d22f548e8ecb3f51f41cb31b0",
545					"s": "0x6de6a5cbae13c0c856e33acf021b51819636cfc009d39eafb9f606d546e305a8",
546					"v": "0x25"
547				}
548				"#
549			),
550			// type 1: EIP2930
551			(
552				"01f89b0180808301e24194095e7baea6a6c7c4c2dfeb977efac326af552d878080f838f7940000000000000000000000000000000000000001e1a0000000000000000000000000000000000000000000000000000000000000000080a0fe38ca4e44a30002ac54af7cf922a6ac2ba11b7d22f548e8ecb3f51f41cb31b0a06de6a5cbae13c0c856e33acf021b51819636cfc009d39eafb9f606d546e305a8",
553				r#"
554				{
555					"accessList": [
556						{
557						"address": "0x0000000000000000000000000000000000000001",
558						"storageKeys": ["0x0000000000000000000000000000000000000000000000000000000000000000"]
559						}
560					],
561					"chainId": "0x1",
562					"gas": "0x1e241",
563					"gasPrice": "0x0",
564					"input": "0x",
565					"nonce": "0x0",
566					"to": "0x095e7baea6a6c7c4c2dfeb977efac326af552d87",
567					"type": "0x1",
568					"value": "0x0",
569					"r": "0xfe38ca4e44a30002ac54af7cf922a6ac2ba11b7d22f548e8ecb3f51f41cb31b0",
570					"s": "0x6de6a5cbae13c0c856e33acf021b51819636cfc009d39eafb9f606d546e305a8",
571					"yParity": "0x0"
572				}
573				"#
574			),
575			// type 2: EIP1559
576			(
577				"02f89c018080018301e24194095e7baea6a6c7c4c2dfeb977efac326af552d878080f838f7940000000000000000000000000000000000000001e1a0000000000000000000000000000000000000000000000000000000000000000080a0fe38ca4e44a30002ac54af7cf922a6ac2ba11b7d22f548e8ecb3f51f41cb31b0a06de6a5cbae13c0c856e33acf021b51819636cfc009d39eafb9f606d546e305a8",
578				r#"
579				{
580					"accessList": [
581						{
582							"address": "0x0000000000000000000000000000000000000001",
583							"storageKeys": ["0x0000000000000000000000000000000000000000000000000000000000000000"]
584						}
585					],
586					"chainId": "0x1",
587					"gas": "0x1e241",
588					"gasPrice": "0x0",
589					"input": "0x",
590					"maxFeePerGas": "0x1",
591					"maxPriorityFeePerGas": "0x0",
592					"nonce": "0x0",
593					"to": "0x095e7baea6a6c7c4c2dfeb977efac326af552d87",
594					"type": "0x2",
595					"value": "0x0",
596					"r": "0xfe38ca4e44a30002ac54af7cf922a6ac2ba11b7d22f548e8ecb3f51f41cb31b0",
597					"s": "0x6de6a5cbae13c0c856e33acf021b51819636cfc009d39eafb9f606d546e305a8",
598					"yParity": "0x0"
599
600				}
601				"#
602			),
603			// type 3: EIP4844
604			(
605
606				"03f8bf018002018301e24194095e7baea6a6c7c4c2dfeb977efac326af552d878080f838f7940000000000000000000000000000000000000001e1a0000000000000000000000000000000000000000000000000000000000000000080e1a0000000000000000000000000000000000000000000000000000000000000000080a0fe38ca4e44a30002ac54af7cf922a6ac2ba11b7d22f548e8ecb3f51f41cb31b0a06de6a5cbae13c0c856e33acf021b51819636cfc009d39eafb9f606d546e305a8",
607				r#"
608				{
609					"accessList": [
610						{
611						"address": "0x0000000000000000000000000000000000000001",
612						"storageKeys": ["0x0000000000000000000000000000000000000000000000000000000000000000"]
613						}
614					],
615					"blobVersionedHashes": ["0x0000000000000000000000000000000000000000000000000000000000000000"],
616					"chainId": "0x1",
617					"gas": "0x1e241",
618					"input": "0x",
619					"maxFeePerBlobGas": "0x0",
620					"maxFeePerGas": "0x1",
621					"maxPriorityFeePerGas": "0x2",
622					"nonce": "0x0",
623					"to": "0x095e7baea6a6c7c4c2dfeb977efac326af552d87",
624					"type": "0x3",
625					"value": "0x0",
626					"r": "0xfe38ca4e44a30002ac54af7cf922a6ac2ba11b7d22f548e8ecb3f51f41cb31b0",
627					"s": "0x6de6a5cbae13c0c856e33acf021b51819636cfc009d39eafb9f606d546e305a8",
628					"yParity": "0x0"
629				}
630				"#
631			)
632		];
633
634		for (tx, json) in txs {
635			let raw_tx = alloy_core::hex::decode(tx).unwrap();
636			let tx = TransactionSigned::decode(&raw_tx).unwrap();
637			assert_eq!(tx.signed_payload(), raw_tx);
638			let expected_tx = serde_json::from_str(json).unwrap();
639			assert_eq!(tx, expected_tx);
640		}
641	}
642
643	#[test]
644	fn dummy_signed_payload_works() {
645		let tx: TransactionUnsigned = TransactionLegacyUnsigned {
646			chain_id: Some(596.into()),
647			gas: U256::from(21000),
648			nonce: U256::from(1),
649			gas_price: U256::from("0x640000006a"),
650			to: Some(Account::from(subxt_signer::eth::dev::baltathar()).address()),
651			value: U256::from(123123),
652			input: Bytes(vec![]),
653			r#type: TypeLegacy,
654		}
655		.into();
656
657		let dummy_signed_payload = tx.clone().dummy_signed_payload();
658		let payload = Account::default().sign_transaction(tx).signed_payload();
659		assert_eq!(dummy_signed_payload.len(), payload.len());
660	}
661}