feat: impl send_transaction (#1774)

Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
This commit is contained in:
chirag-bgh
2023-03-22 16:55:10 +05:30
committed by GitHub
parent 29e798662c
commit f858379511
5 changed files with 119 additions and 10 deletions

View File

@ -3,7 +3,9 @@
//! transaction deserialized from the json input of an RPC call. Depending on what fields are set,
//! it can be converted into the container type [`TypedTransactionRequest`].
use reth_primitives::{AccessList, Address, Bytes, U128, U256};
use reth_primitives::{
AccessList, Address, Bytes, Transaction, TxEip1559, TxEip2930, TxLegacy, U128, U256,
};
use reth_rlp::{BufMut, Decodable, DecodeError, Encodable, RlpDecodable, RlpEncodable};
use serde::{Deserialize, Serialize};
@ -20,6 +22,46 @@ pub enum TypedTransactionRequest {
EIP1559(EIP1559TransactionRequest),
}
impl TypedTransactionRequest {
/// coverts a typed transaction request into a primitive transaction
pub fn into_transaction(self) -> Transaction {
match self {
TypedTransactionRequest::Legacy(tx) => Transaction::Legacy(TxLegacy {
chain_id: tx.chain_id,
nonce: u64::from_be_bytes(tx.nonce.to_be_bytes()),
gas_price: u128::from_be_bytes(tx.gas_price.to_be_bytes()),
gas_limit: u64::from_be_bytes(tx.gas_limit.to_be_bytes()),
to: tx.kind.into(),
value: u128::from_be_bytes(tx.value.to_be_bytes()),
input: tx.input,
}),
TypedTransactionRequest::EIP2930(tx) => Transaction::Eip2930(TxEip2930 {
chain_id: tx.chain_id,
nonce: u64::from_be_bytes(tx.nonce.to_be_bytes()),
gas_price: u128::from_be_bytes(tx.gas_price.to_be_bytes()),
gas_limit: u64::from_be_bytes(tx.gas_limit.to_be_bytes()),
to: tx.kind.into(),
value: u128::from_be_bytes(tx.value.to_be_bytes()),
input: tx.input,
access_list: tx.access_list,
}),
TypedTransactionRequest::EIP1559(tx) => Transaction::Eip1559(TxEip1559 {
chain_id: tx.chain_id,
nonce: u64::from_be_bytes(tx.nonce.to_be_bytes()),
max_fee_per_gas: u128::from_be_bytes(tx.max_fee_per_gas.to_be_bytes()),
gas_limit: u64::from_be_bytes(tx.gas_limit.to_be_bytes()),
to: tx.kind.into(),
value: u128::from_be_bytes(tx.value.to_be_bytes()),
input: tx.input,
access_list: tx.access_list,
max_priority_fee_per_gas: u128::from_be_bytes(
tx.max_priority_fee_per_gas.to_be_bytes(),
),
}),
}
}
}
/// Represents a legacy transaction request
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct LegacyTransactionRequest {
@ -112,3 +154,12 @@ impl Decodable for TransactionKind {
}
}
}
impl From<TransactionKind> for reth_primitives::TransactionKind {
fn from(kind: TransactionKind) -> Self {
match kind {
TransactionKind::Call(to) => reth_primitives::TransactionKind::Call(to),
TransactionKind::Create => reth_primitives::TransactionKind::Create,
}
}
}

View File

@ -28,6 +28,7 @@ reth-tasks = { path = "../../tasks" }
revm = { version = "3.0.0", features = ["optional_block_gas_limit"] }
ethers-core = { git = "https://github.com/gakonst/ethers-rs", features = ["eip712"] }
# rpc
jsonrpsee = { version = "0.16" }
http = "0.2.8"

View File

@ -7,13 +7,16 @@ use crate::{
EthApi,
};
use async_trait::async_trait;
use crate::eth::error::SignError;
use reth_primitives::{
BlockId, BlockNumberOrTag, Bytes, FromRecoveredTransaction, IntoRecoveredTransaction,
TransactionSignedEcRecovered, H256, U256,
Address, BlockId, BlockNumberOrTag, Bytes, FromRecoveredTransaction, IntoRecoveredTransaction,
TransactionSigned, TransactionSignedEcRecovered, H256, U256,
};
use reth_provider::{providers::ChainState, BlockProvider, EvmEnvProvider, StateProviderFactory};
use reth_rpc_types::{Index, Transaction, TransactionInfo, TransactionRequest};
use reth_rpc_types::{
Index, Transaction, TransactionInfo, TransactionRequest, TypedTransactionRequest,
};
use reth_transaction_pool::{TransactionOrigin, TransactionPool};
use revm::primitives::{BlockEnv, CfgEnv};
@ -150,8 +153,53 @@ where
Client: BlockProvider + StateProviderFactory + EvmEnvProvider + 'static,
Network: 'static,
{
pub(crate) async fn send_transaction(&self, _request: TransactionRequest) -> EthResult<H256> {
unimplemented!()
/// Send a transaction to the pool
///
/// This will sign the transaction and submit it to the pool
pub(crate) async fn send_transaction(&self, request: TransactionRequest) -> EthResult<H256> {
let from = match request.from {
Some(from) => from,
None => return Err(SignError::NoAccount.into()),
};
let transaction = match request.into_typed_request() {
Some(tx) => tx,
None => {
return Err(EthApiError::Unsupported(
"both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified",
))
}
};
// TODO we need to update additional settings in the transaction: nonce, gaslimit, chainid,
// gasprice
let signed_tx = self.sign_request(&from, transaction)?;
let recovered =
signed_tx.into_ecrecovered().ok_or(EthApiError::InvalidTransactionSignature)?;
let pool_transaction = <Pool::Transaction>::from_recovered_transaction(recovered);
// submit the transaction to the pool with a `Local` origin
let hash = self.pool().add_transaction(TransactionOrigin::Local, pool_transaction).await?;
Ok(hash)
}
pub(crate) fn sign_request(
&self,
from: &Address,
request: TypedTransactionRequest,
) -> EthResult<TransactionSigned> {
for signer in self.inner.signers.iter() {
if signer.is_signer_for(from) {
return match signer.sign_transaction(request, from) {
Ok(tx) => Ok(tx),
Err(e) => Err(e.into()),
}
}
}
Err(EthApiError::InvalidTransactionSignature)
}
/// Get Transaction by [BlockId] and the index of the transaction within that Block.

View File

@ -374,6 +374,9 @@ pub enum SignError {
/// TypedData has invalid format.
#[error("Given typed data is not valid")]
TypedData,
/// No chainid
#[error("No chainid")]
NoChainId,
}
/// Converts the evm [ExecutionResult] into a result where `Ok` variant is the output bytes if it is

View File

@ -7,6 +7,7 @@ use ethers_core::{
};
use reth_primitives::{sign_message, Address, Signature, TransactionSigned, H256};
use reth_rpc_types::TypedTransactionRequest;
use secp256k1::SecretKey;
use std::collections::HashMap;
@ -72,10 +73,15 @@ impl EthSigner for DevSigner {
fn sign_transaction(
&self,
_request: TypedTransactionRequest,
_address: &Address,
request: TypedTransactionRequest,
address: &Address,
) -> Result<TransactionSigned> {
todo!()
// convert to primitive transaction
let transaction = request.into_transaction();
let tx_signature_hash = transaction.signature_hash();
let signature = self.sign_hash(tx_signature_hash, *address)?;
Ok(TransactionSigned::from_transaction_and_signature(transaction, signature))
}
fn sign_typed_data(&self, address: Address, payload: &TypedData) -> Result<Signature> {