Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: update the Transaction RPC type and fix response of txpool_content RPC #1234

Merged
merged 2 commits into from
Nov 2, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 7 additions & 4 deletions client/rpc-core/src/txpool.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,10 @@ pub trait TxPoolApi {
/// associative arrays, in which each entry maps an origin-address to a batch of scheduled
/// transactions. These batches themselves are maps associating nonces with actual transactions.
///
/// For details, see [txpool_content](https://geth.ethereum.org/docs/interacting-with-geth/rpc/ns-txpool#txpool-content)
/// For details, see [txpool_content (geth)](https://geth.ethereum.org/docs/interacting-with-geth/rpc/ns-txpool#txpool-content)
/// or [txpool_content (nethermind)](https://docs.nethermind.io/nethermind/ethereum-client/json-rpc/txpool#txpool_content).
#[method(name = "txpool_content")]
fn content(&self) -> RpcResult<TxPoolResult<TransactionMap<TxPoolTransaction>>>;
fn content(&self) -> RpcResult<TxPoolResult<TransactionMap<Transaction>>>;

/// The inspect inspection property can be queried to list a textual summary of all the
/// transactions currently pending for inclusion in the next block(s), as well as the ones that
Expand All @@ -48,7 +49,8 @@ pub trait TxPoolApi {
/// transactions. These batches themselves are maps associating nonces with transactions
/// summary strings.
///
/// For details, see [txpool_inspect](https://geth.ethereum.org/docs/interacting-with-geth/rpc/ns-txpool#txpool-content)
/// For details, see [txpool_inspect (geth)](https://geth.ethereum.org/docs/interacting-with-geth/rpc/ns-txpool#txpool-inspect)
/// or [txpool_inspect (nethermind)](https://docs.nethermind.io/nethermind/ethereum-client/json-rpc/txpool#txpool_inspect).
#[method(name = "txpool_inspect")]
fn inspect(&self) -> RpcResult<TxPoolResult<TransactionMap<Summary>>>;

Expand All @@ -59,7 +61,8 @@ pub trait TxPoolApi {
/// The result is an object with two fields pending and queued, each of which is a counter
/// representing the number of transactions in that particular state.
///
/// For details, see [txpool_status](https://geth.ethereum.org/docs/interacting-with-geth/rpc/ns-txpool#txpool-status)
/// For details, see [txpool_status (geth)](https://geth.ethereum.org/docs/interacting-with-geth/rpc/ns-txpool#txpool-status)
/// or [txpool_status (nethermind)](https://docs.nethermind.io/nethermind/ethereum-client/json-rpc/txpool#txpool_status).
#[method(name = "txpool_status")]
fn status(&self) -> RpcResult<TxPoolResult<U256>>;
}
9 changes: 8 additions & 1 deletion client/rpc-core/src/types/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,12 @@ mod work;

pub mod pubsub;

use ethereum::TransactionV2 as EthereumTransaction;
use ethereum_types::H160;
use serde::{de::Error, Deserialize, Deserializer};

#[cfg(feature = "txpool")]
pub use self::txpool::{Get, Summary, TransactionMap, TxPoolResult, TxPoolTransaction};
pub use self::txpool::{Summary, TransactionMap, TxPoolResult};
pub use self::{
account_info::{AccountInfo, EthAccount, ExtAccountInfo, RecoveredAccount, StorageProof},
block::{Block, BlockTransactions, Header, Rich, RichBlock, RichHeader},
Expand Down Expand Up @@ -89,3 +91,8 @@ pub(crate) fn deserialize_data_or_input<'d, D: Deserializer<'d>>(
(_, _) => Ok(data.or(input)),
}
}

/// The trait that used to build types from the `from` address and ethereum `transaction`.
pub trait BuildFrom {
fn build_from(from: H160, transaction: &EthereumTransaction) -> Self;
}
116 changes: 60 additions & 56 deletions client/rpc-core/src/types/transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,19 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.

use ethereum::{AccessListItem, TransactionV2};
use ethereum_types::{H160, H256, H512, U256, U64};
use ethereum::{AccessListItem, TransactionAction, TransactionV2 as EthereumTransaction};
use ethereum_types::{H160, H256, U256, U64};
use serde::{ser::SerializeStruct, Serialize, Serializer};

use crate::types::Bytes;
use crate::types::{BuildFrom, Bytes};

/// Transaction
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Transaction {
/// EIP-2718 transaction type
#[serde(rename = "type", skip_serializing_if = "Option::is_none")]
pub transaction_type: Option<U256>,
/// Hash
pub hash: H256,
/// Nonce
Expand All @@ -42,6 +45,8 @@ pub struct Transaction {
pub to: Option<H160>,
/// Transferred value
pub value: U256,
/// Gas
pub gas: U256,
/// Gas Price
#[serde(skip_serializing_if = "Option::is_none")]
pub gas_price: Option<U256>,
Expand All @@ -51,114 +56,113 @@ pub struct Transaction {
/// The miner's tip.
#[serde(skip_serializing_if = "Option::is_none")]
pub max_priority_fee_per_gas: Option<U256>,
/// Gas
pub gas: U256,
/// Data
pub input: Bytes,
/// Creates contract
pub creates: Option<H160>,
/// Raw transaction data
pub raw: Bytes,
/// Public key of the signer.
pub public_key: Option<H512>,
/// The network id of the transaction, if any.
#[serde(skip_serializing_if = "Option::is_none")]
pub chain_id: Option<U64>,
/// The standardised V field of the signature (0 or 1).
pub standard_v: U256,
/// Pre-pay to warm storage access.
#[serde(skip_serializing_if = "Option::is_none")]
pub access_list: Option<Vec<AccessListItem>>,
/// The parity (0 for even, 1 for odd) of the y-value of the secp256k1 signature.
#[serde(skip_serializing_if = "Option::is_none")]
pub y_parity: Option<U256>,
/// The standardised V field of the signature.
pub v: U256,
///
/// For backwards compatibility, `v` is optionally provided as an alternative to `yParity`.
/// This field is DEPRECATED and all use of it should migrate to `yParity`.
#[serde(skip_serializing_if = "Option::is_none")]
pub v: Option<U256>,
/// The R field of the signature.
pub r: U256,
/// The S field of the signature.
pub s: U256,
/// Pre-pay to warm storage access.
#[cfg_attr(feature = "std", serde(skip_serializing_if = "Option::is_none"))]
pub access_list: Option<Vec<AccessListItem>>,
Comment on lines -75 to -76
Copy link
Collaborator Author

@koushiro koushiro Oct 27, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know why we used #[cfg_attr(feature = "std", ...) here before, maybe it was an error caused by copy c + v

/// EIP-2718 type
#[serde(rename = "type", skip_serializing_if = "Option::is_none")]
pub transaction_type: Option<U256>,
}

impl From<TransactionV2> for Transaction {
fn from(transaction: TransactionV2) -> Self {
let serialized = ethereum::EnvelopedEncodable::encode(&transaction);
impl BuildFrom for Transaction {
fn build_from(from: H160, transaction: &EthereumTransaction) -> Self {
let hash = transaction.hash();
let raw = Bytes(serialized.to_vec());
match transaction {
TransactionV2::Legacy(t) => Transaction {
EthereumTransaction::Legacy(t) => Self {
transaction_type: Some(U256::from(0)),
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if we should use transaction_type : None for Legacy transaction

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should be 0x0.

From what I see in the RPC spec, this field is never None, so we can probably remove the Option.

hash,
nonce: t.nonce,
block_hash: None,
block_number: None,
transaction_index: None,
from: H160::default(),
to: None,
from,
to: match t.action {
TransactionAction::Call(to) => Some(to),
TransactionAction::Create => None,
},
value: t.value,
gas: t.gas_limit,
gas_price: Some(t.gas_price),
max_fee_per_gas: None,
max_priority_fee_per_gas: None,
gas: t.gas_limit,
input: Bytes(t.clone().input),
input: Bytes(t.input.clone()),
creates: None,
raw,
public_key: None,
chain_id: t.signature.chain_id().map(U64::from),
standard_v: U256::from(t.signature.standard_v()),
v: U256::from(t.signature.v()),
access_list: None,
y_parity: None,
v: Some(U256::from(t.signature.v())),
r: U256::from(t.signature.r().as_bytes()),
s: U256::from(t.signature.s().as_bytes()),
access_list: None,
transaction_type: Some(U256::from(0)),
},
TransactionV2::EIP2930(t) => Transaction {
EthereumTransaction::EIP2930(t) => Self {
transaction_type: Some(U256::from(1)),
hash,
nonce: t.nonce,
block_hash: None,
block_number: None,
transaction_index: None,
from: H160::default(),
to: None,
from,
to: match t.action {
TransactionAction::Call(to) => Some(to),
TransactionAction::Create => None,
},
value: t.value,
gas: t.gas_limit,
gas_price: Some(t.gas_price),
max_fee_per_gas: None,
max_priority_fee_per_gas: None,
gas: t.gas_limit,
input: Bytes(t.clone().input),
input: Bytes(t.input.clone()),
creates: None,
raw,
public_key: None,
chain_id: Some(U64::from(t.chain_id)),
standard_v: U256::from(t.odd_y_parity as u8),
v: U256::from(t.odd_y_parity as u8),
access_list: Some(t.access_list.clone()),
y_parity: Some(U256::from(t.odd_y_parity as u8)),
v: Some(U256::from(t.odd_y_parity as u8)),
r: U256::from(t.r.as_bytes()),
s: U256::from(t.s.as_bytes()),
access_list: Some(t.access_list),
transaction_type: Some(U256::from(1)),
},
TransactionV2::EIP1559(t) => Transaction {
EthereumTransaction::EIP1559(t) => Self {
transaction_type: Some(U256::from(2)),
hash,
nonce: t.nonce,
block_hash: None,
block_number: None,
transaction_index: None,
from: H160::default(),
to: None,
from,
to: match t.action {
TransactionAction::Call(to) => Some(to),
TransactionAction::Create => None,
},
value: t.value,
gas_price: None,
gas: t.gas_limit,
// If transaction is not mined yet, gas price is considered just max fee per gas.
gas_price: Some(t.max_fee_per_gas),
max_fee_per_gas: Some(t.max_fee_per_gas),
max_priority_fee_per_gas: Some(t.max_priority_fee_per_gas),
gas: t.gas_limit,
input: Bytes(t.clone().input),
input: Bytes(t.input.clone()),
creates: None,
raw,
public_key: None,
chain_id: Some(U64::from(t.chain_id)),
standard_v: U256::from(t.odd_y_parity as u8),
v: U256::from(t.odd_y_parity as u8),
access_list: Some(t.access_list.clone()),
y_parity: Some(U256::from(t.odd_y_parity as u8)),
v: Some(U256::from(t.odd_y_parity as u8)),
r: U256::from(t.r.as_bytes()),
s: U256::from(t.s.as_bytes()),
access_list: Some(t.access_list),
transaction_type: Some(U256::from(2)),
},
}
}
Expand Down
107 changes: 6 additions & 101 deletions client/rpc-core/src/types/txpool.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,19 +19,15 @@
use std::collections::HashMap;

use ethereum::{TransactionAction, TransactionV2 as EthereumTransaction};
use ethereum_types::{H160, H256, U256};
use ethereum_types::{H160, U256};
use serde::{Serialize, Serializer};

use crate::types::Bytes;
use crate::types::BuildFrom;

/// The entry maps an origin-address to a batch of scheduled transactions.
/// These batches themselves are maps associating nonces with actual transactions.
pub type TransactionMap<T> = HashMap<H160, HashMap<U256, T>>;

pub trait Get {
fn get(hash: H256, from_address: H160, txn: &EthereumTransaction) -> Self;
}

/// The result type of `txpool` API.
#[derive(Debug, Serialize)]
pub struct TxPoolResult<T: Serialize> {
Expand Down Expand Up @@ -68,9 +64,9 @@ impl Serialize for Summary {
}
}

impl Get for Summary {
fn get(_hash: H256, _from_address: H160, txn: &EthereumTransaction) -> Self {
let (action, value, gas_price, gas_limit) = match txn {
impl BuildFrom for Summary {
fn build_from(_from: H160, transaction: &EthereumTransaction) -> Self {
let (action, value, gas_price, gas) = match transaction {
EthereumTransaction::Legacy(t) => (t.action, t.value, t.gas_price, t.gas_limit),
EthereumTransaction::EIP2930(t) => (t.action, t.value, t.gas_price, t.gas_limit),
EthereumTransaction::EIP1559(t) => (t.action, t.value, t.max_fee_per_gas, t.gas_limit),
Expand All @@ -82,98 +78,7 @@ impl Get for Summary {
},
value,
gas_price,
gas: gas_limit,
}
}
}

/// The exact details of all the transactions currently pending for inclusion in the next block(s)
#[derive(Debug, Default, Clone, PartialEq, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct TxPoolTransaction {
/// Hash
pub hash: H256,
/// Nonce
pub nonce: U256,
/// Block hash
#[serde(serialize_with = "block_hash_serialize")]
pub block_hash: Option<H256>,
/// Block number
pub block_number: Option<U256>,
/// Sender
pub from: H160,
/// Recipient
#[serde(serialize_with = "to_serialize")]
pub to: Option<H160>,
/// Transferred value
pub value: U256,
/// Gas Price
pub gas_price: U256,
/// Gas
pub gas: U256,
/// Data
pub input: Bytes,
/// Transaction Index
pub transaction_index: Option<U256>,
}

fn block_hash_serialize<S>(hash: &Option<H256>, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(&format!("0x{:x}", hash.unwrap_or_default()))
}

fn to_serialize<S>(hash: &Option<H160>, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(&format!("0x{:x}", hash.unwrap_or_default()))
}

impl Get for TxPoolTransaction {
fn get(hash: H256, from_address: H160, txn: &EthereumTransaction) -> Self {
let (nonce, action, value, gas_price, gas_limit, input) = match txn {
EthereumTransaction::Legacy(t) => (
t.nonce,
t.action,
t.value,
t.gas_price,
t.gas_limit,
t.input.clone(),
),
EthereumTransaction::EIP2930(t) => (
t.nonce,
t.action,
t.value,
t.gas_price,
t.gas_limit,
t.input.clone(),
),
EthereumTransaction::EIP1559(t) => (
t.nonce,
t.action,
t.value,
t.max_fee_per_gas,
t.gas_limit,
t.input.clone(),
),
};
Self {
hash,
nonce,
block_hash: None,
block_number: None,
from: from_address,
to: match action {
TransactionAction::Call(to) => Some(to),
_ => None,
},
value,
gas_price,
gas: gas_limit,
input: Bytes(input),
transaction_index: None,
gas,
}
}
}
Loading
Loading