-
Notifications
You must be signed in to change notification settings - Fork 42
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
feat(node): support legacy transactions #1235
Changes from all commits
169dabd
fe1a1ae
2a15a83
04edd61
fb842b9
ddbc2e0
b41e31a
d6a006d
6eb1f36
9f0f3b4
0647dcb
68a8f9b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -20,6 +20,7 @@ use fendermint_vm_actor_interface::evm; | |
use fendermint_vm_message::chain::ChainMessage; | ||
use fendermint_vm_message::query::FvmQueryHeight; | ||
use fendermint_vm_message::signed::SignedMessage; | ||
use fil_actors_evm_shared::uints; | ||
use futures::FutureExt; | ||
use fvm_ipld_encoding::RawBytes; | ||
use fvm_shared::address::Address; | ||
|
@@ -36,17 +37,15 @@ use tendermint_rpc::{ | |
Client, | ||
}; | ||
|
||
use fil_actors_evm_shared::uints; | ||
|
||
use crate::conv::from_eth::{self, to_fvm_message}; | ||
use crate::conv::from_eth::{self, derive_origin_kind, to_fvm_message}; | ||
use crate::conv::from_tm::{self, msg_hash, to_chain_message, to_cumulative, to_eth_block_zero}; | ||
use crate::error::{error_with_revert, OutOfSequence}; | ||
use crate::filters::{matches_topics, FilterId, FilterKind, FilterRecords}; | ||
use crate::{ | ||
conv::{ | ||
from_eth::to_fvm_address, | ||
from_fvm::to_eth_tokens, | ||
from_tm::{to_eth_receipt, to_eth_transaction}, | ||
from_tm::{to_eth_receipt, to_eth_transaction_response}, | ||
}, | ||
error, JsonRpcData, JsonRpcResult, | ||
}; | ||
|
@@ -427,7 +426,8 @@ where | |
C: Client + Sync + Send, | ||
{ | ||
// Check in the pending cache first. | ||
if let Some(tx) = data.tx_cache.get(&tx_hash) { | ||
if let Some((tx, sig)) = data.tx_cache.get(&tx_hash) { | ||
let tx = from_eth::to_eth_transaction_response(&tx, sig)?; | ||
Ok(Some(tx)) | ||
} else if let Some(res) = data.tx_by_hash(tx_hash).await? { | ||
let msg = to_chain_message(&res.tx)?; | ||
|
@@ -439,8 +439,11 @@ where | |
.state_params(FvmQueryHeight::Height(header.header.height.value())) | ||
.await?; | ||
let chain_id = ChainID::from(sp.value.chain_id); | ||
|
||
let hash = msg_hash(&res.tx_result.events, &res.tx); | ||
let mut tx = to_eth_transaction(msg, chain_id, hash)?; | ||
let mut tx = to_eth_transaction_response(msg, chain_id)?; | ||
debug_assert_eq!(hash, tx.hash); | ||
|
||
tx.transaction_index = Some(et::U64::from(res.index)); | ||
tx.block_hash = Some(et::H256::from_slice(header.header.hash().as_bytes())); | ||
tx.block_number = Some(et::U64::from(res.height.value())); | ||
|
@@ -612,6 +615,14 @@ pub async fn get_uncle_by_block_number_and_index<C>( | |
Ok(None) | ||
} | ||
|
||
fn normalize_signature(sig: &mut et::Signature) -> JsonRpcResult<()> { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why not use Signature::as_signature(), which returns a normalized recoverable signature? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Looks like it's not exposed... There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, a lot of methods are not exposed. Could be helpful util tools. |
||
sig.v = sig | ||
.recovery_id() | ||
.context("cannot normalize eth signature")? | ||
.to_byte() as u64; | ||
Ok(()) | ||
} | ||
|
||
/// Creates new message call transaction or a contract creation for signed transactions. | ||
pub async fn send_raw_transaction<C>( | ||
data: JsonRpcData<C>, | ||
|
@@ -621,23 +632,23 @@ where | |
C: Client + Sync + Send, | ||
{ | ||
let rlp = rlp::Rlp::new(tx.as_ref()); | ||
let (tx, sig): (TypedTransaction, et::Signature) = TypedTransaction::decode_signed(&rlp) | ||
let (tx, mut sig): (TypedTransaction, et::Signature) = TypedTransaction::decode_signed(&rlp) | ||
.context("failed to decode RLP as signed TypedTransaction")?; | ||
|
||
// for legacy eip155 transactions, the chain id is encoded in it. The `v` most likely will not | ||
// be normalized, normalize to ensure consistent txn hash calculation. | ||
normalize_signature(&mut sig)?; | ||
|
||
let sighash = tx.sighash(); | ||
let msghash = et::TxHash::from(ethers_core::utils::keccak256(rlp.as_raw())); | ||
let msghash = tx.hash(&sig); | ||
tracing::debug!(?sighash, eth_hash = ?msghash, ?tx, "received raw transaction"); | ||
|
||
if let Some(tx) = tx.as_eip1559_ref() { | ||
let tx = from_eth::to_eth_transaction(tx.clone(), sig, msghash); | ||
data.tx_cache.insert(msghash, tx); | ||
} | ||
Comment on lines
-631
to
-634
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Am I reading right that prior to this PR, if a transaction failed the checks (broadcast_tx_sync or others), it would remain forever in the cache? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It won't remain forever as it's using LRU cache. It will just stay for some time, depending on the configuration. |
||
|
||
let msg = to_fvm_message(tx, false)?; | ||
let msg = to_fvm_message(tx.clone())?; | ||
let sender = msg.from; | ||
let nonce = msg.sequence; | ||
|
||
let msg = SignedMessage { | ||
origin_kind: derive_origin_kind(&tx)?, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would it make sense to implement |
||
message: msg, | ||
signature: Signature::new_secp256k1(sig.to_vec()), | ||
}; | ||
|
@@ -648,6 +659,8 @@ where | |
// but not the execution results - those will have to be polled with get_transaction_receipt. | ||
let res: tx_sync::Response = data.tm().broadcast_tx_sync(bz).await?; | ||
if res.code.is_ok() { | ||
data.tx_cache.insert(msghash, (tx, sig)); | ||
|
||
// The following hash would be okay for ethers-rs,and we could use it to look up the TX with Tendermint, | ||
// but ethers.js would reject it because it doesn't match what Ethereum would use. | ||
// Ok(et::TxHash::from_slice(res.hash.as_bytes())) | ||
|
@@ -671,6 +684,8 @@ where | |
tracing::debug!(eth_hash = ?msghash, expected = oos.expected, got = oos.got, is_admissible, "out-of-sequence transaction received"); | ||
|
||
if is_admissible { | ||
data.tx_cache.insert(msghash, (tx, sig)); | ||
|
||
data.tx_buffer.insert(sender, nonce, msg); | ||
return Ok(msghash); | ||
} | ||
|
@@ -688,7 +703,7 @@ pub async fn call<C>( | |
where | ||
C: Client + Sync + Send, | ||
{ | ||
let msg = to_fvm_message(tx.into(), true)?; | ||
let msg = to_fvm_message(tx.into())?; | ||
let is_create = msg.to == EAM_ACTOR_ADDR; | ||
let height = data.query_height(block_id).await?; | ||
let response = data.client.call(msg, height).await?; | ||
|
@@ -737,7 +752,7 @@ where | |
EstimateGasParams::Two((tx, block_id)) => (tx, block_id), | ||
}; | ||
|
||
let msg = to_fvm_message(tx.into(), true).context("failed to convert to FVM message")?; | ||
let msg = to_fvm_message(tx.into()).context("failed to convert to FVM message")?; | ||
|
||
let height = data | ||
.query_height(block_id) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure we want to crash the process here? Wouldn't it be best to return an internal error?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
they should be the same,
debug_assert_eq
should be fine for release build though, more like a sanity check for tests.