feat: add transaction request

This commit is contained in:
Matthias Seitz
2022-10-01 14:15:43 +02:00
parent 2717dd643a
commit 841c87830c
7 changed files with 260 additions and 2 deletions

16
Cargo.lock generated
View File

@ -1143,11 +1143,25 @@ dependencies = [
"ethers-core",
]
[[package]]
name = "reth-rpc"
version = "0.1.0"
dependencies = [
"reth-primitives",
"reth-rpc-api",
"reth-rpc-types",
"serde",
"serde_json",
"thiserror",
]
[[package]]
name = "reth-rpc-api"
version = "0.1.0"
dependencies = [
"jsonrpsee",
"reth-primitives",
"reth-rpc-types",
"serde",
"serde_json",
"thiserror",
@ -1157,6 +1171,8 @@ dependencies = [
name = "reth-rpc-types"
version = "0.1.0"
dependencies = [
"fastrlp",
"reth-primitives",
"serde",
"serde_json",
"thiserror",

View File

@ -12,6 +12,9 @@ Reth RPC types
# reth
reth-primitives = { path = "../primitives" }
# eth
fastrlp = { version = "0.1" }
# misc
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"

View File

@ -1 +1,7 @@
//! Ethereum related types
//! Ethereum related types
pub use reth_primitives::{BlockId, BlockNumber};
mod transaction;
pub use transaction::*;

View File

@ -0,0 +1,5 @@
mod request;
mod typed;
pub use request::TransactionRequest;
pub use typed::*;

View File

@ -0,0 +1,115 @@
use crate::eth::transaction::typed::{
EIP1559TransactionRequest, EIP2930TransactionRequest, LegacyTransactionRequest,
TransactionKind, TypedTransactionRequest,
};
use reth_primitives::{
transaction::eip2930::AccessListItem, Address, Bytes, U256,
};
use serde::{Deserialize, Serialize};
/// Represents _all_ transaction requests received from RPC
#[derive(Clone, Debug, PartialEq, Eq, Default, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
#[serde(rename_all = "camelCase")]
pub struct TransactionRequest {
/// from address
pub from: Option<Address>,
/// to address
pub to: Option<Address>,
/// legacy, gas Price
#[serde(default)]
pub gas_price: Option<U256>,
/// max base fee per gas sender is willing to pay
#[serde(default)]
pub max_fee_per_gas: Option<U256>,
/// miner tip
#[serde(default)]
pub max_priority_fee_per_gas: Option<U256>,
/// gas
pub gas: Option<U256>,
/// value of th tx in wei
pub value: Option<U256>,
/// Any additional data sent
pub data: Option<Bytes>,
/// Transaction nonce
pub nonce: Option<U256>,
/// warm storage access pre-payment
#[serde(default)]
pub access_list: Option<Vec<AccessListItem>>,
/// EIP-2718 type
#[serde(rename = "type")]
pub transaction_type: Option<U256>,
}
// == impl TransactionRequest ==
impl TransactionRequest {
/// Converts the request into a [TypedTransactionRequest]
pub fn into_typed_request(self) -> Option<TypedTransactionRequest> {
let TransactionRequest {
to,
gas_price,
max_fee_per_gas,
max_priority_fee_per_gas,
gas,
value,
data,
nonce,
mut access_list,
..
} = self;
match (gas_price, max_fee_per_gas, access_list.take()) {
// legacy transaction
(Some(_), None, None) => {
Some(TypedTransactionRequest::Legacy(LegacyTransactionRequest {
nonce: nonce.unwrap_or(U256::zero()),
gas_price: gas_price.unwrap_or_default(),
gas_limit: gas.unwrap_or_default(),
value: value.unwrap_or(U256::zero()),
input: data.unwrap_or_default(),
kind: match to {
Some(to) => TransactionKind::Call(to),
None => TransactionKind::Create,
},
chain_id: None,
}))
}
// EIP2930
(_, None, Some(access_list)) => {
Some(TypedTransactionRequest::EIP2930(EIP2930TransactionRequest {
nonce: nonce.unwrap_or(U256::zero()),
gas_price: gas_price.unwrap_or_default(),
gas_limit: gas.unwrap_or_default(),
value: value.unwrap_or(U256::zero()),
input: data.unwrap_or_default(),
kind: match to {
Some(to) => TransactionKind::Call(to),
None => TransactionKind::Create,
},
chain_id: 0,
access_list,
}))
}
// EIP1559
(None, Some(_), access_list) | (None, None, access_list @ None) => {
// Empty fields fall back to the canonical transaction schema.
Some(TypedTransactionRequest::EIP1559(EIP1559TransactionRequest {
nonce: nonce.unwrap_or(U256::zero()),
max_fee_per_gas: max_fee_per_gas.unwrap_or_default(),
max_priority_fee_per_gas: max_priority_fee_per_gas.unwrap_or(U256::zero()),
gas_limit: gas.unwrap_or_default(),
value: value.unwrap_or(U256::zero()),
input: data.unwrap_or_default(),
kind: match to {
Some(to) => TransactionKind::Call(to),
None => TransactionKind::Create,
},
chain_id: 0,
access_list: access_list.unwrap_or_default(),
}))
}
_ => None,
}
}
}

View File

@ -0,0 +1,111 @@
#![allow(missing_docs)]
use fastrlp::{RlpDecodable, RlpEncodable};
use reth_primitives::{transaction::eip2930::AccessListItem, Address, Bytes, U256};
use serde::{Deserialize, Serialize};
/// Container type for various Ethereum transaction requests
///
/// Its variants correspond to specific allowed transactions:
/// 1. Legacy (pre-EIP2718) [`LegacyTransactionRequest`]
/// 2. EIP2930 (state access lists) [`EIP2930TransactionRequest`]
/// 3. EIP1559 [`EIP1559TransactionRequest`]
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum TypedTransactionRequest {
Legacy(LegacyTransactionRequest),
EIP2930(EIP2930TransactionRequest),
EIP1559(EIP1559TransactionRequest),
}
/// Represents a legacy transaction request
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct LegacyTransactionRequest {
pub nonce: U256,
pub gas_price: U256,
pub gas_limit: U256,
pub kind: TransactionKind,
pub value: U256,
pub input: Bytes,
pub chain_id: Option<u64>,
}
/// Represents an EIP-2930 transaction request
#[derive(Debug, Clone, PartialEq, Eq, RlpEncodable, RlpDecodable)]
pub struct EIP2930TransactionRequest {
pub chain_id: u64,
pub nonce: U256,
pub gas_price: U256,
pub gas_limit: U256,
pub kind: TransactionKind,
pub value: U256,
pub input: Bytes,
pub access_list: Vec<AccessListItem>,
}
/// Represents an EIP-1559 transaction request
#[derive(Debug, Clone, PartialEq, Eq, RlpEncodable, RlpDecodable)]
pub struct EIP1559TransactionRequest {
pub chain_id: u64,
pub nonce: U256,
pub max_priority_fee_per_gas: U256,
pub max_fee_per_gas: U256,
pub gas_limit: U256,
pub kind: TransactionKind,
pub value: U256,
pub input: Bytes,
pub access_list: Vec<AccessListItem>,
}
/// Represents the `to` field of a transaction request
///
/// This determines what kind of transaction this is
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum TransactionKind {
/// Transaction will call this address or transfer funds to this address
Call(Address),
/// No `to` field set, this transaction will create a contract
Create,
}
// == impl TransactionKind ==
impl TransactionKind {
/// If this transaction is a call this returns the address of the callee
pub fn as_call(&self) -> Option<&Address> {
match self {
TransactionKind::Call(to) => Some(to),
TransactionKind::Create => None,
}
}
}
impl fastrlp::Encodable for TransactionKind {
fn length(&self) -> usize {
match self {
TransactionKind::Call(to) => to.length(),
TransactionKind::Create => ([]).length(),
}
}
fn encode(&self, out: &mut dyn fastrlp::BufMut) {
match self {
TransactionKind::Call(to) => to.encode(out),
TransactionKind::Create => ([]).encode(out),
}
}
}
impl fastrlp::Decodable for TransactionKind {
fn decode(buf: &mut &[u8]) -> Result<Self, fastrlp::DecodeError> {
if let Some(&first) = buf.first() {
if first == 0x80 {
*buf = &buf[1..];
Ok(TransactionKind::Create)
} else {
let addr = <Address as fastrlp::Decodable>::decode(buf)?;
Ok(TransactionKind::Call(addr))
}
} else {
Err(fastrlp::DecodeError::InputTooShort)
}
}
}

View File

@ -9,4 +9,6 @@
//!
//! Provides all relevant types for the various RPC endpoints, grouped by namespace.
mod eth;
mod eth;
pub use eth::*;