fix: make into_transaction fallible (#4710)

This commit is contained in:
DaniPopes
2023-09-21 16:38:21 +02:00
committed by GitHub
parent 5c3d7d0b2a
commit 6a601755c9
9 changed files with 54 additions and 43 deletions

View File

@ -108,7 +108,7 @@ fn fill(
Transaction {
hash: signed_tx.hash(),
nonce: U256::from(signed_tx.nonce()),
nonce: U64::from(signed_tx.nonce()),
from: signer,
to,
value: U256::from(signed_tx.value()),

View File

@ -110,7 +110,7 @@ pub struct CallRequest {
#[serde(default, flatten)]
pub input: CallInput,
/// Nonce
pub nonce: Option<U256>,
pub nonce: Option<U64>,
/// chain id
pub chain_id: Option<U64>,
/// AccessList

View File

@ -19,7 +19,7 @@ pub struct Transaction {
/// Hash
pub hash: H256,
/// Nonce
pub nonce: U256,
pub nonce: U64,
/// Block hash
pub block_hash: Option<H256>,
/// Block number
@ -80,7 +80,7 @@ mod tests {
fn serde_transaction() {
let transaction = Transaction {
hash: H256::from_low_u64_be(1),
nonce: U256::from(2),
nonce: U64::from(2),
block_hash: Some(H256::from_low_u64_be(3)),
block_number: Some(U256::from(4)),
transaction_index: Some(U256::from(5)),
@ -117,7 +117,7 @@ mod tests {
fn serde_transaction_with_parity_bit() {
let transaction = Transaction {
hash: H256::from_low_u64_be(1),
nonce: U256::from(2),
nonce: U64::from(2),
block_hash: Some(H256::from_low_u64_be(3)),
block_number: Some(U256::from(4)),
transaction_index: Some(U256::from(5)),

View File

@ -2,7 +2,7 @@ use crate::eth::transaction::typed::{
EIP1559TransactionRequest, EIP2930TransactionRequest, LegacyTransactionRequest,
TransactionKind, TypedTransactionRequest,
};
use reth_primitives::{AccessList, Address, Bytes, U128, U256, U8};
use reth_primitives::{AccessList, Address, Bytes, U128, U256, U64, U8};
use serde::{Deserialize, Serialize};
/// Represents _all_ transaction requests received from RPC
@ -31,7 +31,7 @@ pub struct TransactionRequest {
#[serde(alias = "input")]
pub data: Option<Bytes>,
/// Transaction nonce
pub nonce: Option<U256>,
pub nonce: Option<U64>,
/// warm storage access pre-payment
#[serde(default)]
pub access_list: Option<AccessList>,
@ -64,10 +64,10 @@ impl TransactionRequest {
// legacy transaction
(Some(_), None, None) => {
Some(TypedTransactionRequest::Legacy(LegacyTransactionRequest {
nonce: nonce.unwrap_or(U256::ZERO),
nonce: nonce.unwrap_or_default(),
gas_price: gas_price.unwrap_or_default(),
gas_limit: gas.unwrap_or_default(),
value: value.unwrap_or(U256::ZERO),
value: value.unwrap_or_default(),
input: data.unwrap_or_default(),
kind: match to {
Some(to) => TransactionKind::Call(to),
@ -79,10 +79,10 @@ impl TransactionRequest {
// EIP2930
(_, None, Some(access_list)) => {
Some(TypedTransactionRequest::EIP2930(EIP2930TransactionRequest {
nonce: nonce.unwrap_or(U256::ZERO),
nonce: nonce.unwrap_or_default(),
gas_price: gas_price.unwrap_or_default(),
gas_limit: gas.unwrap_or_default(),
value: value.unwrap_or(U256::ZERO),
value: value.unwrap_or_default(),
input: data.unwrap_or_default(),
kind: match to {
Some(to) => TransactionKind::Call(to),
@ -96,11 +96,11 @@ impl TransactionRequest {
(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),
nonce: nonce.unwrap_or_default(),
max_fee_per_gas: max_fee_per_gas.unwrap_or_default(),
max_priority_fee_per_gas: max_priority_fee_per_gas.unwrap_or(U128::ZERO),
max_priority_fee_per_gas: max_priority_fee_per_gas.unwrap_or_default(),
gas_limit: gas.unwrap_or_default(),
value: value.unwrap_or(U256::ZERO),
value: value.unwrap_or_default(),
input: data.unwrap_or_default(),
kind: match to {
Some(to) => TransactionKind::Call(to),

View File

@ -4,7 +4,7 @@
//! it can be converted into the container type [`TypedTransactionRequest`].
use reth_primitives::{
AccessList, Address, Bytes, Transaction, TxEip1559, TxEip2930, TxLegacy, U128, U256,
AccessList, Address, Bytes, Transaction, TxEip1559, TxEip2930, TxLegacy, U128, U256, U64,
};
use reth_rlp::{BufMut, Decodable, DecodeError, Encodable, RlpDecodable, RlpEncodable};
use serde::{Deserialize, Serialize};
@ -23,49 +23,52 @@ pub enum TypedTransactionRequest {
}
impl TypedTransactionRequest {
/// coverts a typed transaction request into a primitive transaction
pub fn into_transaction(self) -> Transaction {
match self {
/// Converts a typed transaction request into a primitive transaction.
///
/// Returns `None` if any of the following are true:
/// - `nonce` is greater than [`u64::MAX`]
/// - `gas_limit` is greater than [`u64::MAX`]
/// - `value` is greater than [`u128::MAX`]
pub fn into_transaction(self) -> Option<Transaction> {
Some(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()),
nonce: tx.nonce.as_u64(),
gas_price: tx.gas_price.to(),
gas_limit: tx.gas_limit.try_into().ok()?,
to: tx.kind.into(),
value: u128::from_be_bytes(tx.value.to_be_bytes()),
value: tx.value.try_into().ok()?,
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()),
nonce: tx.nonce.as_u64(),
gas_price: tx.gas_price.to(),
gas_limit: tx.gas_limit.try_into().ok()?,
to: tx.kind.into(),
value: u128::from_be_bytes(tx.value.to_be_bytes()),
value: tx.value.try_into().ok()?,
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()),
nonce: tx.nonce.as_u64(),
max_fee_per_gas: tx.max_fee_per_gas.to(),
gas_limit: tx.gas_limit.try_into().ok()?,
to: tx.kind.into(),
value: u128::from_be_bytes(tx.value.to_be_bytes()),
value: tx.value.try_into().ok()?,
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(),
),
max_priority_fee_per_gas: tx.max_priority_fee_per_gas.to(),
}),
}
})
}
}
/// Represents a legacy transaction request
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct LegacyTransactionRequest {
pub nonce: U256,
pub nonce: U64,
pub gas_price: U128,
pub gas_limit: U256,
pub kind: TransactionKind,
@ -78,7 +81,7 @@ pub struct LegacyTransactionRequest {
#[derive(Debug, Clone, PartialEq, Eq, RlpEncodable, RlpDecodable)]
pub struct EIP2930TransactionRequest {
pub chain_id: u64,
pub nonce: U256,
pub nonce: U64,
pub gas_price: U128,
pub gas_limit: U256,
pub kind: TransactionKind,
@ -91,7 +94,7 @@ pub struct EIP2930TransactionRequest {
#[derive(Debug, Clone, PartialEq, Eq, RlpEncodable, RlpDecodable)]
pub struct EIP1559TransactionRequest {
pub chain_id: u64,
pub nonce: U256,
pub nonce: U64,
pub max_priority_fee_per_gas: U128,
pub max_fee_per_gas: U128,
pub gas_limit: U256,

View File

@ -21,7 +21,8 @@ impl<Provider, Pool, Network> EthApi<Provider, Pool, Network> {
pub(crate) async fn sign_typed_data(&self, data: Value, account: Address) -> EthResult<Bytes> {
let signer = self.find_signer(&account)?;
let data = serde_json::from_value::<TypedData>(data).map_err(|_| SignError::TypedData)?;
let data =
serde_json::from_value::<TypedData>(data).map_err(|_| SignError::InvalidTypedData)?;
let signature = signer.sign_typed_data(account, &data)?;
let bytes = hex::encode(signature.to_bytes()).as_bytes().into();
Ok(bytes)

View File

@ -446,7 +446,8 @@ where
if request.nonce.is_none() {
let nonce =
self.get_transaction_count(from, Some(BlockId::Number(BlockNumberOrTag::Pending)))?;
request.nonce = Some(nonce);
// note: `.to()` can't panic because the nonce is constructed from a `u64`
request.nonce = Some(U64::from(nonce.to::<u64>()));
}
let chain_id = self.chain_id();

View File

@ -600,8 +600,11 @@ pub enum SignError {
NoAccount,
/// TypedData has invalid format.
#[error("Given typed data is not valid")]
TypedData,
/// No chainid
InvalidTypedData,
/// Invalid transaction request in `sign_transaction`.
#[error("Invalid transaction request")]
InvalidTransactionRequest,
/// No chain ID was given.
#[error("No chainid")]
NoChainId,
}

View File

@ -48,12 +48,14 @@ impl DevSigner {
fn get_key(&self, account: Address) -> Result<&SecretKey> {
self.accounts.get(&account).ok_or(SignError::NoAccount)
}
fn sign_hash(&self, hash: H256, account: Address) -> Result<Signature> {
let secret = self.get_key(account)?;
let signature = sign_message(H256::from_slice(secret.as_ref()), hash);
signature.map_err(|_| SignError::CouldNotSign)
}
}
#[async_trait::async_trait]
impl EthSigner for DevSigner {
fn accounts(&self) -> Vec<Address> {
@ -77,7 +79,7 @@ impl EthSigner for DevSigner {
address: &Address,
) -> Result<TransactionSigned> {
// convert to primitive transaction
let transaction = request.into_transaction();
let transaction = request.into_transaction().ok_or(SignError::InvalidTransactionRequest)?;
let tx_signature_hash = transaction.signature_hash();
let signature = self.sign_hash(tx_signature_hash, *address)?;
@ -85,7 +87,8 @@ impl EthSigner for DevSigner {
}
fn sign_typed_data(&self, address: Address, payload: &TypedData) -> Result<Signature> {
let encoded: H256 = payload.encode_eip712().map_err(|_| SignError::TypedData)?.into();
let encoded: H256 =
payload.encode_eip712().map_err(|_| SignError::InvalidTypedData)?.into();
self.sign_hash(encoded, address)
}
}