referrerpolicy=no-referrer-when-downgrade

pallet_revive/evm/api/
rpc_types.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//! Utility impl for the RPC types.
18use super::*;
19use alloc::vec::Vec;
20use sp_core::{H160, U256};
21
22impl From<BlockNumberOrTag> for BlockNumberOrTagOrHash {
23	fn from(b: BlockNumberOrTag) -> Self {
24		match b {
25			BlockNumberOrTag::U256(n) => BlockNumberOrTagOrHash::BlockNumber(n),
26			BlockNumberOrTag::BlockTag(t) => BlockNumberOrTagOrHash::BlockTag(t),
27		}
28	}
29}
30
31impl From<TransactionSigned> for TransactionUnsigned {
32	fn from(tx: TransactionSigned) -> Self {
33		use TransactionSigned::*;
34		match tx {
35			Transaction7702Signed(tx) => tx.transaction_7702_unsigned.into(),
36			Transaction4844Signed(tx) => tx.transaction_4844_unsigned.into(),
37			Transaction1559Signed(tx) => tx.transaction_1559_unsigned.into(),
38			Transaction2930Signed(tx) => tx.transaction_2930_unsigned.into(),
39			TransactionLegacySigned(tx) => tx.transaction_legacy_unsigned.into(),
40		}
41	}
42}
43
44impl TransactionInfo {
45	/// Create a new [`TransactionInfo`] from a receipt and a signed transaction.
46	pub fn new(receipt: &ReceiptInfo, transaction_signed: TransactionSigned) -> Self {
47		Self {
48			block_hash: receipt.block_hash,
49			block_number: receipt.block_number,
50			from: receipt.from,
51			hash: receipt.transaction_hash,
52			transaction_index: receipt.transaction_index,
53			transaction_signed,
54		}
55	}
56}
57
58impl ReceiptInfo {
59	/// Initialize a new Receipt
60	pub fn new(
61		block_hash: H256,
62		block_number: U256,
63		contract_address: Option<Address>,
64		from: Address,
65		logs: Vec<Log>,
66		to: Option<Address>,
67		effective_gas_price: U256,
68		gas_used: U256,
69		success: bool,
70		transaction_hash: H256,
71		transaction_index: U256,
72		r#type: Byte,
73	) -> Self {
74		let logs_bloom = Self::logs_bloom(&logs);
75		ReceiptInfo {
76			block_hash,
77			block_number,
78			contract_address,
79			from,
80			logs,
81			logs_bloom,
82			to,
83			effective_gas_price,
84			gas_used,
85			status: Some(if success { U256::one() } else { U256::zero() }),
86			transaction_hash,
87			transaction_index,
88			r#type: Some(r#type),
89			..Default::default()
90		}
91	}
92
93	/// Returns `true` if the transaction was successful.
94	pub fn is_success(&self) -> bool {
95		self.status.map_or(false, |status| status == U256::one())
96	}
97
98	/// Calculate receipt logs bloom.
99	fn logs_bloom(logs: &[Log]) -> Bytes256 {
100		let mut bloom = [0u8; 256];
101		for log in logs {
102			m3_2048(&mut bloom, &log.address.as_ref());
103			for topic in &log.topics {
104				m3_2048(&mut bloom, topic.as_ref());
105			}
106		}
107		bloom.into()
108	}
109}
110/// Specialised Bloom filter that sets three bits out of 2048, given an
111/// arbitrary byte sequence.
112///
113/// See Section 4.4.1 "Transaction Receipt" of the [Ethereum Yellow Paper][ref].
114///
115/// [ref]: https://ethereum.github.io/yellowpaper/paper.pdf
116fn m3_2048(bloom: &mut [u8; 256], bytes: &[u8]) {
117	let hash = sp_core::keccak_256(bytes);
118	for i in [0, 2, 4] {
119		let bit = (hash[i + 1] as usize + ((hash[i] as usize) << 8)) & 0x7FF;
120		bloom[256 - 1 - bit / 8] |= 1 << (bit % 8);
121	}
122}
123
124#[test]
125fn can_deserialize_input_or_data_field_from_generic_transaction() {
126	let cases = [
127		("with input", r#"{"input": "0x01"}"#),
128		("with data", r#"{"data": "0x01"}"#),
129		("with both", r#"{"data": "0x01", "input": "0x01"}"#),
130	];
131
132	for (name, json) in cases {
133		let tx = serde_json::from_str::<GenericTransaction>(json).unwrap();
134		assert_eq!(tx.input.to_vec(), vec![1u8], "{}", name);
135	}
136
137	let err = serde_json::from_str::<GenericTransaction>(r#"{"data": "0x02", "input": "0x01"}"#)
138		.unwrap_err();
139	assert!(
140		err.to_string().starts_with(
141		"Both \"data\" and \"input\" are set and not equal. Please use \"input\" to pass transaction call data"
142		)
143	);
144}
145
146#[test]
147fn test_block_number_or_tag_or_hash_deserialization() {
148	let val: BlockNumberOrTagOrHash = serde_json::from_str("\"latest\"").unwrap();
149	assert_eq!(val, BlockTag::Latest.into());
150
151	for s in ["\"0x1a\"", r#"{ "blockNumber": "0x1a" }"#] {
152		let val: BlockNumberOrTagOrHash = serde_json::from_str(s).unwrap();
153		assert!(matches!(val, BlockNumberOrTagOrHash::BlockNumber(n) if n == 26u64.into()));
154	}
155
156	for s in [
157		"\"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\"",
158		r#"{ "blockHash": "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" }"#,
159	] {
160		let val: BlockNumberOrTagOrHash = serde_json::from_str(s).unwrap();
161		assert_eq!(val, BlockNumberOrTagOrHash::BlockHash(H256([0xaau8; 32])));
162	}
163}
164
165#[test]
166fn logs_bloom_works() {
167	let receipt: ReceiptInfo = serde_json::from_str(
168		r#"
169		{
170			"blockHash": "0x835ee379aaabf4802a22a93ad8164c02bbdde2cc03d4552d5c642faf4e09d1f3",
171			"blockNumber": "0x2",
172			"contractAddress": null,
173			"cumulativeGasUsed": "0x5d92",
174			"effectiveGasPrice": "0x2dcd5c2d",
175			"from": "0xb4f1f9ecfe5a28633a27f57300bda217e99b8969",
176			"gasUsed": "0x5d92",
177			"logs": [
178				{
179				"address": "0x82bdb002b9b1f36c42df15fbdc6886abcb2ab31d",
180				"topics": [
181					"0x1585375487296ff2f0370daeec4214074a032b31af827c12622fa9a58c16c7d0",
182					"0x000000000000000000000000b4f1f9ecfe5a28633a27f57300bda217e99b8969"
183				],
184				"data": "0x00000000000000000000000000000000000000000000000000000000000030390000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000b48656c6c6f20776f726c64000000000000000000000000000000000000000000",
185				"blockNumber": "0x2",
186				"transactionHash": "0xad0075127962bdf73d787f2944bdb5f351876f23c35e6a48c1f5b6463a100af4",
187				"transactionIndex": "0x0",
188				"blockHash": "0x835ee379aaabf4802a22a93ad8164c02bbdde2cc03d4552d5c642faf4e09d1f3",
189				"logIndex": "0x0",
190				"removed": false
191				}
192			],
193			"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000008000000000000000000000000000000000000000000000000800000000040000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000004000000000000000800000000000000000080000000000000000000000000000000000000000000",
194			"status": "0x1",
195			"to": "0x82bdb002b9b1f36c42df15fbdc6886abcb2ab31d",
196			"transactionHash": "0xad0075127962bdf73d787f2944bdb5f351876f23c35e6a48c1f5b6463a100af4",
197			"transactionIndex": "0x0",
198			"type": "0x2"
199		}
200		"#,
201	)
202	.unwrap();
203	assert_eq!(receipt.logs_bloom, ReceiptInfo::logs_bloom(&receipt.logs));
204}
205
206impl GenericTransaction {
207	/// Create a new [`GenericTransaction`] from a signed transaction.
208	pub fn from_signed(tx: TransactionSigned, base_gas_price: U256, from: Option<H160>) -> Self {
209		Self::from_unsigned(tx.into(), base_gas_price, from)
210	}
211
212	/// Create a new [`GenericTransaction`] from a unsigned transaction.
213	pub fn from_unsigned(
214		tx: TransactionUnsigned,
215		base_gas_price: U256,
216		from: Option<H160>,
217	) -> Self {
218		use TransactionUnsigned::*;
219		match tx {
220			TransactionLegacyUnsigned(tx) => GenericTransaction {
221				from,
222				r#type: Some(tx.r#type.as_byte()),
223				chain_id: tx.chain_id,
224				input: tx.input.into(),
225				nonce: Some(tx.nonce),
226				value: Some(tx.value),
227				to: tx.to,
228				gas: Some(tx.gas),
229				gas_price: Some(tx.gas_price),
230				..Default::default()
231			},
232			Transaction4844Unsigned(tx) => GenericTransaction {
233				from,
234				r#type: Some(tx.r#type.as_byte()),
235				chain_id: Some(tx.chain_id),
236				input: tx.input.into(),
237				nonce: Some(tx.nonce),
238				value: Some(tx.value),
239				to: Some(tx.to),
240				gas: Some(tx.gas),
241				gas_price: Some(
242					base_gas_price
243						.saturating_add(tx.max_priority_fee_per_gas)
244						.min(tx.max_fee_per_blob_gas),
245				),
246				access_list: Some(tx.access_list),
247				blob_versioned_hashes: tx.blob_versioned_hashes,
248				max_fee_per_blob_gas: Some(tx.max_fee_per_blob_gas),
249				max_fee_per_gas: Some(tx.max_fee_per_gas),
250				max_priority_fee_per_gas: Some(tx.max_priority_fee_per_gas),
251				..Default::default()
252			},
253			Transaction1559Unsigned(tx) => GenericTransaction {
254				from,
255				r#type: Some(tx.r#type.as_byte()),
256				chain_id: Some(tx.chain_id),
257				input: tx.input.into(),
258				nonce: Some(tx.nonce),
259				value: Some(tx.value),
260				to: tx.to,
261				gas: Some(tx.gas),
262				gas_price: Some(
263					base_gas_price
264						.saturating_add(tx.max_priority_fee_per_gas)
265						.min(tx.max_fee_per_gas),
266				),
267				access_list: Some(tx.access_list),
268				max_fee_per_gas: Some(tx.max_fee_per_gas),
269				max_priority_fee_per_gas: Some(tx.max_priority_fee_per_gas),
270				..Default::default()
271			},
272			Transaction2930Unsigned(tx) => GenericTransaction {
273				from,
274				r#type: Some(tx.r#type.as_byte()),
275				chain_id: Some(tx.chain_id),
276				input: tx.input.into(),
277				nonce: Some(tx.nonce),
278				value: Some(tx.value),
279				to: tx.to,
280				gas: Some(tx.gas),
281				gas_price: Some(tx.gas_price),
282				access_list: Some(tx.access_list),
283				..Default::default()
284			},
285			Transaction7702Unsigned(tx) => GenericTransaction {
286				from,
287				r#type: Some(tx.r#type.as_byte()),
288				chain_id: Some(tx.chain_id),
289				input: tx.input.into(),
290				nonce: Some(tx.nonce),
291				value: Some(tx.value),
292				to: tx.to,
293				gas: Some(tx.gas),
294				gas_price: Some(
295					base_gas_price
296						.saturating_add(tx.max_priority_fee_per_gas)
297						.min(tx.max_fee_per_gas),
298				),
299				access_list: Some(tx.access_list),
300				authorization_list: tx.authorization_list,
301				max_fee_per_gas: Some(tx.max_fee_per_gas),
302				max_priority_fee_per_gas: Some(tx.max_priority_fee_per_gas),
303				..Default::default()
304			},
305		}
306	}
307
308	/// Convert to a [`TransactionUnsigned`].
309	pub fn try_into_unsigned(self) -> Result<TransactionUnsigned, ()> {
310		match self.r#type.unwrap_or_default().0 {
311			TYPE_LEGACY => Ok(TransactionLegacyUnsigned {
312				r#type: TypeLegacy {},
313				chain_id: self.chain_id,
314				input: self.input.to_bytes(),
315				nonce: self.nonce.unwrap_or_default(),
316				value: self.value.unwrap_or_default(),
317				to: self.to,
318				gas: self.gas.unwrap_or_default(),
319				gas_price: self.gas_price.unwrap_or_default(),
320			}
321			.into()),
322			TYPE_EIP1559 => Ok(Transaction1559Unsigned {
323				r#type: TypeEip1559 {},
324				chain_id: self.chain_id.unwrap_or_default(),
325				input: self.input.to_bytes(),
326				nonce: self.nonce.unwrap_or_default(),
327				value: self.value.unwrap_or_default(),
328				to: self.to,
329				gas: self.gas.unwrap_or_default(),
330				gas_price: self.max_fee_per_gas.unwrap_or_default(),
331				access_list: self.access_list.unwrap_or_default(),
332				max_fee_per_gas: self.max_fee_per_gas.unwrap_or_default(),
333				max_priority_fee_per_gas: self.max_priority_fee_per_gas.unwrap_or_default(),
334			}
335			.into()),
336			TYPE_EIP2930 => Ok(Transaction2930Unsigned {
337				r#type: TypeEip2930 {},
338				chain_id: self.chain_id.unwrap_or_default(),
339				input: self.input.to_bytes(),
340				nonce: self.nonce.unwrap_or_default(),
341				value: self.value.unwrap_or_default(),
342				to: self.to,
343				gas: self.gas.unwrap_or_default(),
344				gas_price: self.gas_price.unwrap_or_default(),
345				access_list: self.access_list.unwrap_or_default(),
346			}
347			.into()),
348			TYPE_EIP4844 => Ok(Transaction4844Unsigned {
349				r#type: TypeEip4844 {},
350				chain_id: self.chain_id.unwrap_or_default(),
351				input: self.input.to_bytes(),
352				nonce: self.nonce.unwrap_or_default(),
353				value: self.value.unwrap_or_default(),
354				to: self.to.unwrap_or_default(),
355				gas: self.gas.unwrap_or_default(),
356				max_fee_per_gas: self.max_fee_per_gas.unwrap_or_default(),
357				max_fee_per_blob_gas: self.max_fee_per_blob_gas.unwrap_or_default(),
358				max_priority_fee_per_gas: self.max_priority_fee_per_gas.unwrap_or_default(),
359				access_list: self.access_list.unwrap_or_default(),
360				blob_versioned_hashes: self.blob_versioned_hashes,
361			}
362			.into()),
363			TYPE_EIP7702 => Ok(Transaction7702Unsigned {
364				r#type: TypeEip7702 {},
365				chain_id: self.chain_id.unwrap_or_default(),
366				input: self.input.to_bytes(),
367				nonce: self.nonce.unwrap_or_default(),
368				value: self.value.unwrap_or_default(),
369				to: self.to,
370				gas: self.gas.unwrap_or_default(),
371				gas_price: self.max_fee_per_gas.unwrap_or_default(),
372				max_fee_per_gas: self.max_fee_per_gas.unwrap_or_default(),
373				max_priority_fee_per_gas: self.max_priority_fee_per_gas.unwrap_or_default(),
374				access_list: self.access_list.unwrap_or_default(),
375				authorization_list: self.authorization_list,
376			}
377			.into()),
378			_ => Err(()),
379		}
380	}
381}
382
383#[test]
384fn from_unsigned_works_for_legacy() {
385	let base_gas_price = U256::from(10);
386	let tx = TransactionUnsigned::from(TransactionLegacyUnsigned {
387		chain_id: Some(U256::from(1)),
388		input: Bytes::from(vec![1u8]),
389		nonce: U256::from(1),
390		value: U256::from(1),
391		to: Some(H160::zero()),
392		gas: U256::from(1),
393		gas_price: U256::from(11),
394		..Default::default()
395	});
396
397	let generic = GenericTransaction::from_unsigned(tx.clone(), base_gas_price, None);
398	assert_eq!(generic.gas_price, Some(U256::from(11)));
399
400	let tx2 = generic.try_into_unsigned().unwrap();
401	assert_eq!(tx, tx2);
402}
403
404#[test]
405fn from_unsigned_works_for_1559() {
406	let base_gas_price = U256::from(10);
407	let tx = TransactionUnsigned::from(Transaction1559Unsigned {
408		chain_id: U256::from(1),
409		input: Bytes::from(vec![1u8]),
410		nonce: U256::from(1),
411		value: U256::from(1),
412		to: Some(H160::zero()),
413		gas: U256::from(1),
414		gas_price: U256::from(20),
415		max_fee_per_gas: U256::from(20),
416		max_priority_fee_per_gas: U256::from(1),
417		..Default::default()
418	});
419
420	let generic = GenericTransaction::from_unsigned(tx.clone(), base_gas_price, None);
421	assert_eq!(generic.gas_price, Some(U256::from(11)));
422
423	let tx2 = generic.try_into_unsigned().unwrap();
424	assert_eq!(tx, tx2);
425}
426
427#[test]
428fn from_unsigned_works_for_7702() {
429	let base_gas_price = U256::from(10);
430	let tx = TransactionUnsigned::from(Transaction7702Unsigned {
431		chain_id: U256::from(1),
432		input: Bytes::from(vec![1u8]),
433		nonce: U256::from(1),
434		value: U256::from(1),
435		to: Some(H160::zero()),
436		gas: U256::from(1),
437		gas_price: U256::from(20),
438		max_fee_per_gas: U256::from(20),
439		max_priority_fee_per_gas: U256::from(1),
440		authorization_list: vec![AuthorizationListEntry {
441			chain_id: U256::from(1),
442			address: H160::from_low_u64_be(42),
443			nonce: U256::from(0),
444			y_parity: U256::from(1),
445			r: U256::from(1),
446			s: U256::from(2),
447		}],
448		..Default::default()
449	});
450
451	let generic = GenericTransaction::from_unsigned(tx.clone(), base_gas_price, None);
452	assert_eq!(generic.gas_price, Some(U256::from(11)));
453
454	let tx2 = generic.try_into_unsigned().unwrap();
455	assert_eq!(tx, tx2);
456}