mirror of
https://github.com/hl-archive-node/nanoreth.git
synced 2025-12-06 10:59:55 +00:00
fix: use unchecked sender recovery in rpc (#5721)
This commit is contained in:
@ -749,6 +749,12 @@ impl ChainSpec {
|
||||
.unwrap_or_else(|| self.is_fork_active_at_timestamp(Hardfork::Cancun, timestamp))
|
||||
}
|
||||
|
||||
/// Convenience method to check if [Hardfork::Homestead] is active at a given block number.
|
||||
#[inline]
|
||||
pub fn is_homestead_active_at_block(&self, block_number: u64) -> bool {
|
||||
self.fork(Hardfork::Homestead).active_at_block(block_number)
|
||||
}
|
||||
|
||||
/// Creates a [`ForkFilter`] for the block described by [Head].
|
||||
pub fn fork_filter(&self, head: Head) -> ForkFilter {
|
||||
let forks = self.forks_iter().filter_map(|(_, condition)| {
|
||||
|
||||
@ -1037,6 +1037,16 @@ impl TransactionSigned {
|
||||
Some(TransactionSignedEcRecovered { signed_transaction: self, signer })
|
||||
}
|
||||
|
||||
/// Consumes the type, recover signer and return [`TransactionSignedEcRecovered`] _without
|
||||
/// ensuring that the signature has a low `s` value_ (EIP-2).
|
||||
///
|
||||
/// Returns `None` if the transaction's signature is invalid, see also
|
||||
/// [Self::recover_signer_unchecked].
|
||||
pub fn into_ecrecovered_unchecked(self) -> Option<TransactionSignedEcRecovered> {
|
||||
let signer = self.recover_signer_unchecked()?;
|
||||
Some(TransactionSignedEcRecovered { signed_transaction: self, signer })
|
||||
}
|
||||
|
||||
/// Tries to recover signer and return [`TransactionSignedEcRecovered`] by cloning the type.
|
||||
pub fn try_ecrecovered(&self) -> Option<TransactionSignedEcRecovered> {
|
||||
let signer = self.recover_signer()?;
|
||||
@ -1054,6 +1064,18 @@ impl TransactionSigned {
|
||||
}
|
||||
}
|
||||
|
||||
/// Tries to recover signer and return [`TransactionSignedEcRecovered`]. _without ensuring that
|
||||
/// the signature has a low `s` value_ (EIP-2).
|
||||
///
|
||||
/// Returns `Err(Self)` if the transaction's signature is invalid, see also
|
||||
/// [Self::recover_signer_unchecked].
|
||||
pub fn try_into_ecrecovered_unchecked(self) -> Result<TransactionSignedEcRecovered, Self> {
|
||||
match self.recover_signer_unchecked() {
|
||||
None => Err(self),
|
||||
Some(signer) => Ok(TransactionSignedEcRecovered { signed_transaction: self, signer }),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the enveloped encoded transactions.
|
||||
///
|
||||
/// See also [TransactionSigned::encode_enveloped]
|
||||
|
||||
@ -19,9 +19,11 @@ use reth_primitives::{
|
||||
db::{DatabaseCommit, DatabaseRef},
|
||||
BlockEnv, CfgEnv,
|
||||
},
|
||||
Address, Block, BlockId, BlockNumberOrTag, Bytes, TransactionSigned, B256,
|
||||
Address, Block, BlockId, BlockNumberOrTag, Bytes, TransactionSignedEcRecovered, B256,
|
||||
};
|
||||
use reth_provider::{
|
||||
BlockReaderIdExt, ChainSpecProvider, HeaderProvider, StateProviderBox, TransactionVariant,
|
||||
};
|
||||
use reth_provider::{BlockReaderIdExt, HeaderProvider, StateProviderBox, TransactionVariant};
|
||||
use reth_revm::{
|
||||
database::{StateProviderDatabase, SubState},
|
||||
tracing::{
|
||||
@ -73,7 +75,7 @@ impl<Provider, Eth> DebugApi<Provider, Eth> {
|
||||
|
||||
impl<Provider, Eth> DebugApi<Provider, Eth>
|
||||
where
|
||||
Provider: BlockReaderIdExt + HeaderProvider + 'static,
|
||||
Provider: BlockReaderIdExt + HeaderProvider + ChainSpecProvider + 'static,
|
||||
Eth: EthTransactions + 'static,
|
||||
{
|
||||
/// Acquires a permit to execute a tracing call.
|
||||
@ -85,7 +87,7 @@ where
|
||||
async fn trace_block_with(
|
||||
&self,
|
||||
at: BlockId,
|
||||
transactions: Vec<TransactionSigned>,
|
||||
transactions: Vec<TransactionSignedEcRecovered>,
|
||||
cfg: CfgEnv,
|
||||
block_env: BlockEnv,
|
||||
opts: GethDebugTracingOptions,
|
||||
@ -100,7 +102,6 @@ where
|
||||
|
||||
let mut transactions = transactions.into_iter().peekable();
|
||||
while let Some(tx) = transactions.next() {
|
||||
let tx = tx.into_ecrecovered().ok_or(BlockError::InvalidSignature)?;
|
||||
let tx = tx_env_with_recovered(&tx);
|
||||
let env = Env { cfg: cfg.clone(), block: block_env.clone(), tx };
|
||||
let (result, state_changes) =
|
||||
@ -133,10 +134,32 @@ where
|
||||
Block::decode(&mut rlp_block.as_ref()).map_err(BlockError::RlpDecodeRawBlock)?;
|
||||
|
||||
let (cfg, block_env) = self.inner.eth_api.evm_env_for_raw_block(&block.header).await?;
|
||||
|
||||
// we trace on top the block's parent block
|
||||
let parent = block.parent_hash;
|
||||
self.trace_block_with(parent.into(), block.body, cfg, block_env, opts).await
|
||||
|
||||
// Depending on EIP-2 we need to recover the transactions differently
|
||||
let transactions =
|
||||
if self.inner.provider.chain_spec().is_homestead_active_at_block(block.number) {
|
||||
block
|
||||
.body
|
||||
.into_iter()
|
||||
.map(|tx| {
|
||||
tx.into_ecrecovered()
|
||||
.ok_or_else(|| EthApiError::InvalidTransactionSignature)
|
||||
})
|
||||
.collect::<EthResult<Vec<_>>>()?
|
||||
} else {
|
||||
block
|
||||
.body
|
||||
.into_iter()
|
||||
.map(|tx| {
|
||||
tx.into_ecrecovered_unchecked()
|
||||
.ok_or_else(|| EthApiError::InvalidTransactionSignature)
|
||||
})
|
||||
.collect::<EthResult<Vec<_>>>()?
|
||||
};
|
||||
|
||||
self.trace_block_with(parent.into(), transactions, cfg, block_env, opts).await
|
||||
}
|
||||
|
||||
/// Replays a block and returns the trace of each transaction.
|
||||
@ -153,7 +176,7 @@ where
|
||||
|
||||
let ((cfg, block_env, _), block) = futures::try_join!(
|
||||
self.inner.eth_api.evm_env_at(block_hash.into()),
|
||||
self.inner.eth_api.block_by_id(block_id),
|
||||
self.inner.eth_api.block_by_id_with_senders(block_id),
|
||||
)?;
|
||||
|
||||
let block = block.ok_or_else(|| EthApiError::UnknownBlockNumber)?;
|
||||
@ -161,7 +184,14 @@ where
|
||||
// its parent block's state
|
||||
let state_at = block.parent_hash;
|
||||
|
||||
self.trace_block_with(state_at.into(), block.body, cfg, block_env, opts).await
|
||||
self.trace_block_with(
|
||||
state_at.into(),
|
||||
block.into_transactions_ecrecovered().collect(),
|
||||
cfg,
|
||||
block_env,
|
||||
opts,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
/// Trace the transaction according to the provided options.
|
||||
@ -354,11 +384,10 @@ where
|
||||
let StateContext { transaction_index, block_number } = state_context.unwrap_or_default();
|
||||
let transaction_index = transaction_index.unwrap_or_default();
|
||||
|
||||
let target_block = block_number
|
||||
.unwrap_or(reth_rpc_types::BlockId::Number(reth_rpc_types::BlockNumberOrTag::Latest));
|
||||
let target_block = block_number.unwrap_or(BlockId::Number(BlockNumberOrTag::Latest));
|
||||
let ((cfg, block_env, _), block) = futures::try_join!(
|
||||
self.inner.eth_api.evm_env_at(target_block),
|
||||
self.inner.eth_api.block_by_id(target_block),
|
||||
self.inner.eth_api.block_by_id_with_senders(target_block),
|
||||
)?;
|
||||
|
||||
let opts = opts.unwrap_or_default();
|
||||
@ -389,11 +418,10 @@ where
|
||||
if replay_block_txs {
|
||||
// only need to replay the transactions in the block if not all transactions are
|
||||
// to be replayed
|
||||
let transactions = block.body.into_iter().take(num_txs);
|
||||
let transactions = block.into_transactions_ecrecovered().take(num_txs);
|
||||
|
||||
// Execute all transactions until index
|
||||
for tx in transactions {
|
||||
let tx = tx.into_ecrecovered().ok_or(BlockError::InvalidSignature)?;
|
||||
let tx = tx_env_with_recovered(&tx);
|
||||
let env = Env { cfg: cfg.clone(), block: block_env.clone(), tx };
|
||||
let (res, _) = transact(&mut db, env)?;
|
||||
@ -632,7 +660,7 @@ where
|
||||
#[async_trait]
|
||||
impl<Provider, Eth> DebugApiServer for DebugApi<Provider, Eth>
|
||||
where
|
||||
Provider: BlockReaderIdExt + HeaderProvider + 'static,
|
||||
Provider: BlockReaderIdExt + HeaderProvider + ChainSpecProvider + 'static,
|
||||
Eth: EthApiSpec + 'static,
|
||||
{
|
||||
/// Handler for `debug_getRawHeader`
|
||||
@ -658,6 +686,136 @@ where
|
||||
Ok(res.into())
|
||||
}
|
||||
|
||||
/// Handler for `debug_getRawBlock`
|
||||
async fn raw_block(&self, block_id: BlockId) -> RpcResult<Bytes> {
|
||||
let block = self.inner.provider.block_by_id(block_id).to_rpc_result()?;
|
||||
|
||||
let mut res = Vec::new();
|
||||
if let Some(mut block) = block {
|
||||
// In RPC withdrawals are always present
|
||||
if block.withdrawals.is_none() {
|
||||
block.withdrawals = Some(vec![]);
|
||||
}
|
||||
block.encode(&mut res);
|
||||
}
|
||||
|
||||
Ok(res.into())
|
||||
}
|
||||
|
||||
/// Handler for `debug_getRawTransaction`
|
||||
/// Returns the bytes of the transaction for the given hash.
|
||||
async fn raw_transaction(&self, hash: B256) -> RpcResult<Bytes> {
|
||||
let tx = self.inner.eth_api.transaction_by_hash(hash).await?;
|
||||
Ok(tx
|
||||
.map(TransactionSource::into_recovered)
|
||||
.map(|tx| tx.envelope_encoded())
|
||||
.unwrap_or_default())
|
||||
}
|
||||
|
||||
/// Handler for `debug_getRawTransactions`
|
||||
/// Returns the bytes of the transaction for the given hash.
|
||||
async fn raw_transactions(&self, block_id: BlockId) -> RpcResult<Vec<Bytes>> {
|
||||
let block = self
|
||||
.inner
|
||||
.provider
|
||||
.block_with_senders_by_id(block_id, TransactionVariant::NoHash)
|
||||
.to_rpc_result()?
|
||||
.unwrap_or_default();
|
||||
Ok(block.into_transactions_ecrecovered().map(|tx| tx.envelope_encoded()).collect())
|
||||
}
|
||||
|
||||
/// Handler for `debug_getRawReceipts`
|
||||
async fn raw_receipts(&self, block_id: BlockId) -> RpcResult<Vec<Bytes>> {
|
||||
let receipts =
|
||||
self.inner.provider.receipts_by_block_id(block_id).to_rpc_result()?.unwrap_or_default();
|
||||
let mut all_receipts = Vec::with_capacity(receipts.len());
|
||||
|
||||
for receipt in receipts {
|
||||
let mut buf = Vec::new();
|
||||
let receipt = receipt.with_bloom();
|
||||
receipt.encode(&mut buf);
|
||||
all_receipts.push(buf.into());
|
||||
}
|
||||
|
||||
Ok(all_receipts)
|
||||
}
|
||||
|
||||
/// Handler for `debug_getBadBlocks`
|
||||
async fn bad_blocks(&self) -> RpcResult<Vec<RichBlock>> {
|
||||
Err(internal_rpc_err("unimplemented"))
|
||||
}
|
||||
|
||||
/// Handler for `debug_traceChain`
|
||||
async fn debug_trace_chain(
|
||||
&self,
|
||||
_start_exclusive: BlockNumberOrTag,
|
||||
_end_inclusive: BlockNumberOrTag,
|
||||
) -> RpcResult<Vec<BlockTraceResult>> {
|
||||
Err(internal_rpc_err("unimplemented"))
|
||||
}
|
||||
|
||||
/// Handler for `debug_traceBlock`
|
||||
async fn debug_trace_block(
|
||||
&self,
|
||||
rlp_block: Bytes,
|
||||
opts: Option<GethDebugTracingOptions>,
|
||||
) -> RpcResult<Vec<TraceResult>> {
|
||||
let _permit = self.acquire_trace_permit().await;
|
||||
Ok(DebugApi::debug_trace_raw_block(self, rlp_block, opts.unwrap_or_default()).await?)
|
||||
}
|
||||
|
||||
/// Handler for `debug_traceBlockByHash`
|
||||
async fn debug_trace_block_by_hash(
|
||||
&self,
|
||||
block: B256,
|
||||
opts: Option<GethDebugTracingOptions>,
|
||||
) -> RpcResult<Vec<TraceResult>> {
|
||||
let _permit = self.acquire_trace_permit().await;
|
||||
Ok(DebugApi::debug_trace_block(self, block.into(), opts.unwrap_or_default()).await?)
|
||||
}
|
||||
|
||||
/// Handler for `debug_traceBlockByNumber`
|
||||
async fn debug_trace_block_by_number(
|
||||
&self,
|
||||
block: BlockNumberOrTag,
|
||||
opts: Option<GethDebugTracingOptions>,
|
||||
) -> RpcResult<Vec<TraceResult>> {
|
||||
let _permit = self.acquire_trace_permit().await;
|
||||
Ok(DebugApi::debug_trace_block(self, block.into(), opts.unwrap_or_default()).await?)
|
||||
}
|
||||
|
||||
/// Handler for `debug_traceTransaction`
|
||||
async fn debug_trace_transaction(
|
||||
&self,
|
||||
tx_hash: B256,
|
||||
opts: Option<GethDebugTracingOptions>,
|
||||
) -> RpcResult<GethTrace> {
|
||||
let _permit = self.acquire_trace_permit().await;
|
||||
Ok(DebugApi::debug_trace_transaction(self, tx_hash, opts.unwrap_or_default()).await?)
|
||||
}
|
||||
|
||||
/// Handler for `debug_traceCall`
|
||||
async fn debug_trace_call(
|
||||
&self,
|
||||
request: CallRequest,
|
||||
block_number: Option<BlockId>,
|
||||
opts: Option<GethDebugTracingCallOptions>,
|
||||
) -> RpcResult<GethTrace> {
|
||||
let _permit = self.acquire_trace_permit().await;
|
||||
Ok(DebugApi::debug_trace_call(self, request, block_number, opts.unwrap_or_default())
|
||||
.await?)
|
||||
}
|
||||
|
||||
async fn debug_trace_call_many(
|
||||
&self,
|
||||
bundles: Vec<Bundle>,
|
||||
state_context: Option<StateContext>,
|
||||
opts: Option<GethDebugTracingCallOptions>,
|
||||
) -> RpcResult<Vec<Vec<GethTrace>>> {
|
||||
let _permit = self.acquire_trace_permit().await;
|
||||
Ok(DebugApi::debug_trace_call_many(self, bundles, state_context, opts).await?)
|
||||
}
|
||||
|
||||
async fn debug_backtrace_at(&self, _location: &str) -> RpcResult<()> {
|
||||
Ok(())
|
||||
}
|
||||
@ -868,136 +1026,6 @@ where
|
||||
async fn debug_write_mutex_profile(&self, _file: String) -> RpcResult<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Handler for `debug_getRawBlock`
|
||||
async fn raw_block(&self, block_id: BlockId) -> RpcResult<Bytes> {
|
||||
let block = self.inner.provider.block_by_id(block_id).to_rpc_result()?;
|
||||
|
||||
let mut res = Vec::new();
|
||||
if let Some(mut block) = block {
|
||||
// In RPC withdrawals are always present
|
||||
if block.withdrawals.is_none() {
|
||||
block.withdrawals = Some(vec![]);
|
||||
}
|
||||
block.encode(&mut res);
|
||||
}
|
||||
|
||||
Ok(res.into())
|
||||
}
|
||||
|
||||
/// Handler for `debug_getRawTransaction`
|
||||
/// Returns the bytes of the transaction for the given hash.
|
||||
async fn raw_transaction(&self, hash: B256) -> RpcResult<Bytes> {
|
||||
let tx = self.inner.eth_api.transaction_by_hash(hash).await?;
|
||||
Ok(tx
|
||||
.map(TransactionSource::into_recovered)
|
||||
.map(|tx| tx.envelope_encoded())
|
||||
.unwrap_or_default())
|
||||
}
|
||||
|
||||
/// Handler for `debug_getRawTransactions`
|
||||
/// Returns the bytes of the transaction for the given hash.
|
||||
async fn raw_transactions(&self, block_id: BlockId) -> RpcResult<Vec<Bytes>> {
|
||||
let block = self
|
||||
.inner
|
||||
.provider
|
||||
.block_with_senders_by_id(block_id, TransactionVariant::NoHash)
|
||||
.to_rpc_result()?
|
||||
.unwrap_or_default();
|
||||
Ok(block.into_transactions_ecrecovered().map(|tx| tx.envelope_encoded()).collect())
|
||||
}
|
||||
|
||||
/// Handler for `debug_getRawReceipts`
|
||||
async fn raw_receipts(&self, block_id: BlockId) -> RpcResult<Vec<Bytes>> {
|
||||
let receipts =
|
||||
self.inner.provider.receipts_by_block_id(block_id).to_rpc_result()?.unwrap_or_default();
|
||||
let mut all_receipts = Vec::with_capacity(receipts.len());
|
||||
|
||||
for receipt in receipts {
|
||||
let mut buf = Vec::new();
|
||||
let receipt = receipt.with_bloom();
|
||||
receipt.encode(&mut buf);
|
||||
all_receipts.push(buf.into());
|
||||
}
|
||||
|
||||
Ok(all_receipts)
|
||||
}
|
||||
|
||||
/// Handler for `debug_getBadBlocks`
|
||||
async fn bad_blocks(&self) -> RpcResult<Vec<RichBlock>> {
|
||||
Err(internal_rpc_err("unimplemented"))
|
||||
}
|
||||
|
||||
/// Handler for `debug_traceChain`
|
||||
async fn debug_trace_chain(
|
||||
&self,
|
||||
_start_exclusive: BlockNumberOrTag,
|
||||
_end_inclusive: BlockNumberOrTag,
|
||||
) -> RpcResult<Vec<BlockTraceResult>> {
|
||||
Err(internal_rpc_err("unimplemented"))
|
||||
}
|
||||
|
||||
/// Handler for `debug_traceBlock`
|
||||
async fn debug_trace_block(
|
||||
&self,
|
||||
rlp_block: Bytes,
|
||||
opts: Option<GethDebugTracingOptions>,
|
||||
) -> RpcResult<Vec<TraceResult>> {
|
||||
let _permit = self.acquire_trace_permit().await;
|
||||
Ok(DebugApi::debug_trace_raw_block(self, rlp_block, opts.unwrap_or_default()).await?)
|
||||
}
|
||||
|
||||
/// Handler for `debug_traceBlockByHash`
|
||||
async fn debug_trace_block_by_hash(
|
||||
&self,
|
||||
block: B256,
|
||||
opts: Option<GethDebugTracingOptions>,
|
||||
) -> RpcResult<Vec<TraceResult>> {
|
||||
let _permit = self.acquire_trace_permit().await;
|
||||
Ok(DebugApi::debug_trace_block(self, block.into(), opts.unwrap_or_default()).await?)
|
||||
}
|
||||
|
||||
/// Handler for `debug_traceBlockByNumber`
|
||||
async fn debug_trace_block_by_number(
|
||||
&self,
|
||||
block: BlockNumberOrTag,
|
||||
opts: Option<GethDebugTracingOptions>,
|
||||
) -> RpcResult<Vec<TraceResult>> {
|
||||
let _permit = self.acquire_trace_permit().await;
|
||||
Ok(DebugApi::debug_trace_block(self, block.into(), opts.unwrap_or_default()).await?)
|
||||
}
|
||||
|
||||
/// Handler for `debug_traceTransaction`
|
||||
async fn debug_trace_transaction(
|
||||
&self,
|
||||
tx_hash: B256,
|
||||
opts: Option<GethDebugTracingOptions>,
|
||||
) -> RpcResult<GethTrace> {
|
||||
let _permit = self.acquire_trace_permit().await;
|
||||
Ok(DebugApi::debug_trace_transaction(self, tx_hash, opts.unwrap_or_default()).await?)
|
||||
}
|
||||
|
||||
/// Handler for `debug_traceCall`
|
||||
async fn debug_trace_call(
|
||||
&self,
|
||||
request: CallRequest,
|
||||
block_number: Option<BlockId>,
|
||||
opts: Option<GethDebugTracingCallOptions>,
|
||||
) -> RpcResult<GethTrace> {
|
||||
let _permit = self.acquire_trace_permit().await;
|
||||
Ok(DebugApi::debug_trace_call(self, request, block_number, opts.unwrap_or_default())
|
||||
.await?)
|
||||
}
|
||||
|
||||
async fn debug_trace_call_many(
|
||||
&self,
|
||||
bundles: Vec<Bundle>,
|
||||
state_context: Option<StateContext>,
|
||||
opts: Option<GethDebugTracingCallOptions>,
|
||||
) -> RpcResult<Vec<Vec<GethTrace>>> {
|
||||
let _permit = self.acquire_trace_permit().await;
|
||||
Ok(DebugApi::debug_trace_call_many(self, bundles, state_context, opts).await?)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Provider, Eth> std::fmt::Debug for DebugApi<Provider, Eth> {
|
||||
|
||||
@ -18,7 +18,7 @@ use reth_primitives::{
|
||||
revm::env::{fill_block_env_with_coinbase, tx_env_with_recovered},
|
||||
revm_primitives::{db::DatabaseCommit, Env, ExecutionResult, ResultAndState, SpecId, State},
|
||||
Address, BlockId, BlockNumberOrTag, Bytes, FromRecoveredPooledTransaction, Header,
|
||||
IntoRecoveredTransaction, Receipt, SealedBlock,
|
||||
IntoRecoveredTransaction, Receipt, SealedBlock, SealedBlockWithSenders,
|
||||
TransactionKind::{Call, Create},
|
||||
TransactionMeta, TransactionSigned, TransactionSignedEcRecovered, B256, U128, U256, U64,
|
||||
};
|
||||
@ -100,6 +100,14 @@ pub trait EthTransactions: Send + Sync {
|
||||
/// Returns `None` if block does not exist.
|
||||
async fn block_by_id(&self, id: BlockId) -> EthResult<Option<SealedBlock>>;
|
||||
|
||||
/// Get the entire block for the given id.
|
||||
///
|
||||
/// Returns `None` if block does not exist.
|
||||
async fn block_by_id_with_senders(
|
||||
&self,
|
||||
id: BlockId,
|
||||
) -> EthResult<Option<SealedBlockWithSenders>>;
|
||||
|
||||
/// Get all transactions in the block with the given hash.
|
||||
///
|
||||
/// Returns `None` if block does not exist.
|
||||
@ -365,6 +373,13 @@ where
|
||||
self.block(id).await
|
||||
}
|
||||
|
||||
async fn block_by_id_with_senders(
|
||||
&self,
|
||||
id: BlockId,
|
||||
) -> EthResult<Option<SealedBlockWithSenders>> {
|
||||
self.block_with_senders(id).await
|
||||
}
|
||||
|
||||
async fn transactions_by_block_id(
|
||||
&self,
|
||||
block: BlockId,
|
||||
@ -379,8 +394,11 @@ where
|
||||
match this.provider().transaction_by_hash_with_meta(hash)? {
|
||||
None => Ok(None),
|
||||
Some((tx, meta)) => {
|
||||
// Note: we assume this transaction is valid, because it's mined (or part of
|
||||
// pending block) and already. We don't need to
|
||||
// check for pre EIP-2 because this transaction could be pre-EIP-2.
|
||||
let transaction = tx
|
||||
.into_ecrecovered()
|
||||
.into_ecrecovered_unchecked()
|
||||
.ok_or(EthApiError::InvalidTransactionSignature)?;
|
||||
|
||||
let tx = TransactionSource::Block {
|
||||
|
||||
Reference in New Issue
Block a user