fc_rpc_v2_types/
txpool.rs

1// This file is part of Frontier.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
5
6// This program is free software: you can redistribute it and/or modify
7// it under the terms of the GNU General Public License as published by
8// the Free Software Foundation, either version 3 of the License, or
9// (at your option) any later version.
10
11// This program is distributed in the hope that it will be useful,
12// but WITHOUT ANY WARRANTY; without even the implied warranty of
13// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14// GNU General Public License for more details.
15
16// You should have received a copy of the GNU General Public License
17// along with this program. If not, see <https://www.gnu.org/licenses/>.
18
19use std::{collections::BTreeMap, fmt};
20
21use ethereum_types::{Address, U256, U64};
22use serde::{de, Deserialize, Serialize};
23
24use crate::transaction::Transaction;
25
26pub type TxpoolInspect = TxpoolResult<AddressMapping<NonceMapping<Summary>>>;
27pub type TxpoolContent = TxpoolResult<AddressMapping<NonceMapping<Transaction>>>;
28pub type TxpoolContentFrom = TxpoolResult<NonceMapping<Transaction>>;
29pub type TxpoolStatus = TxpoolResult<U64>;
30
31pub type NonceMapping<T> = BTreeMap<u64, T>;
32pub type AddressMapping<T> = BTreeMap<Address, T>;
33
34/// The txpool result type.
35#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
36pub struct TxpoolResult<T> {
37	/// Pending transactions.
38	pub pending: T,
39	/// Queued transactions.
40	pub queued: T,
41}
42
43/// The textual summary of all the transactions currently pending for inclusion in the next block(s).
44#[derive(Clone, Debug, Eq, PartialEq)]
45pub struct Summary {
46	/// Recipient.
47	pub to: Option<Address>,
48	/// Transferred value.
49	pub value: U256,
50	/// Gas limit.
51	pub gas: u128,
52	/// Gas price.
53	pub gas_price: u128,
54}
55
56impl serde::Serialize for Summary {
57	fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
58	where
59		S: serde::Serializer,
60	{
61		let formatted_to = if let Some(to) = self.to {
62			format!("{to:?}")
63		} else {
64			"contract creation".to_string()
65		};
66		let formatted = format!(
67			"{}: {} wei + {} gas × {} wei",
68			formatted_to, self.value, self.gas, self.gas_price
69		);
70		serializer.serialize_str(&formatted)
71	}
72}
73
74impl<'de> serde::Deserialize<'de> for Summary {
75	fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
76	where
77		D: serde::Deserializer<'de>,
78	{
79		struct SummaryVisitor;
80		impl de::Visitor<'_> for SummaryVisitor {
81			type Value = Summary;
82
83			fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
84				formatter.write_str("{{to}}: {{value}} wei + {{gas}} gas × {{gas_price}} wei")
85			}
86
87			fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
88			where
89				E: de::Error,
90			{
91				let addr_split: Vec<&str> = v.split(": ").collect();
92				if addr_split.len() != 2 {
93					return Err(de::Error::custom("invalid `to` format"));
94				}
95
96				let value_split: Vec<&str> = addr_split[1].split(" wei + ").collect();
97				if value_split.len() != 2 {
98					return Err(de::Error::custom("invalid `value` format"));
99				}
100
101				let gas_split: Vec<&str> = value_split[1].split(" gas × ").collect();
102				if gas_split.len() != 2 {
103					return Err(de::Error::custom("invalid `gas` format"));
104				}
105
106				let gas_price_split: Vec<&str> = gas_split[1].split(" wei").collect();
107				if gas_price_split.len() != 2 {
108					return Err(de::Error::custom("invalid `gas_price` format"));
109				}
110
111				let to = match addr_split[0] {
112					"contract creation" => None,
113					addr => {
114						let addr = addr
115							.trim_start_matches("0x")
116							.parse::<Address>()
117							.map_err(de::Error::custom)?;
118						Some(addr)
119					}
120				};
121				let value = U256::from_dec_str(value_split[0]).map_err(de::Error::custom)?;
122				let gas = gas_split[0].parse::<u128>().map_err(de::Error::custom)?;
123				let gas_price = gas_price_split[0]
124					.parse::<u128>()
125					.map_err(de::Error::custom)?;
126
127				Ok(Summary {
128					to,
129					value,
130					gas,
131					gas_price,
132				})
133			}
134
135			fn visit_string<E>(self, v: String) -> Result<Self::Value, E>
136			where
137				E: de::Error,
138			{
139				self.visit_str(&v)
140			}
141		}
142
143		deserializer.deserialize_str(SummaryVisitor)
144	}
145}
146
147#[cfg(test)]
148mod tests {
149	use super::*;
150
151	#[test]
152	fn inspect_summary_serde_impl() {
153		let valid_cases = [
154			(
155				r#""contract creation: 2472666000 wei + 21000 gas × 1000 wei""#,
156				Summary {
157					to: None,
158					value: U256::from(2472666000u64),
159					gas: 21000,
160					gas_price: 1000,
161				},
162			),
163			(
164				r#""0x1111111111111111111111111111111111111111: 2472666000 wei + 21000 gas × 1000 wei""#,
165				Summary {
166					to: Some(
167						"0x1111111111111111111111111111111111111111"
168							.parse::<Address>()
169							.unwrap(),
170					),
171					value: U256::from(2472666000u64),
172					gas: 21000,
173					gas_price: 1000,
174				},
175			),
176		];
177		for (raw, typed) in valid_cases {
178			let deserialized = serde_json::from_str::<Summary>(raw).unwrap();
179			assert_eq!(deserialized, typed);
180
181			let serialized = serde_json::to_string(&typed).unwrap();
182			assert_eq!(serialized, raw);
183		}
184
185		let invalid_cases = [
186			r#"": ""#,
187			r#"" : 2472666000 wei + 21000 gas × 1000 wei""#,
188			r#""0x: 2472666000 wei + 21000 gas × 1000 wei""#,
189			r#""0x1111111111111111111111111111111111111111: 2472666000 wei""#,
190			r#""0x1111111111111111111111111111111111111111: 2472666000 wei + 21000 gas × ""#,
191			r#""0x1111111111111111111111111111111111111111: 2472666000 wei + 21000 gas × 1000""#,
192		];
193		for raw in invalid_cases {
194			let summary: Result<Summary, _> = serde_json::from_str(raw);
195			assert!(summary.is_err());
196		}
197	}
198}