Skip to content
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

Merged
merged 12 commits into from
Dec 20, 2024
Merged

feat(node): support legacy transactions #1235

merged 12 commits into from
Dec 20, 2024

Conversation

cryptoAtwill
Copy link
Contributor

@cryptoAtwill cryptoAtwill commented Dec 16, 2024

Closes #927.

The current main does not allow send_raw_transaction for legacy transactions. This PR removes this assumption. To achieve it, the following changes are made:

  • Adding OriginKind enum to explicitly specify the original transaction type. The type information is lost when converting to Fvm messages.
  • Support gas params from legacy transactions as specified in https://github.com/filecoin-project/FIPs/blob/master/FIPS/fip-0091.md
  • Any ethereum transaction sent from fendermint client is enforced to be eip1559.
  • TransactionCache is updated to use (TypeTransaction, Signature) instead of the response dto Transaction.

@cryptoAtwill cryptoAtwill requested a review from a team as a code owner December 16, 2024 12:14
Comment on lines 88 to 92
_ => error::error_with_revert(
ExitCode::USR_ILLEGAL_ARGUMENT,
"txn type not supported",
None::<Vec<u8>>,
),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are we duplicating this logic here? send_raw_transaction is already producing this error?

}
data.tx_cache.insert(
msghash,
from_eth::to_eth_transaction_response(&tx, sig, msghash)?,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you mean with transaction response here? This module is an API, so I interpret as if this is returning the response the API sends to the caller, which isn't the case? (unless there's an error)

We are duplicating the transaction type validation in this method and when we calculate the origin kind above. Can we refactor this to make it DRY?

BTW -- how can we have a module called from_eth that then has to_eth methods? Wouldn't they cancel each other out? 😆

Comment on lines 27 to 31
pub fn to_eth_transaction_response(
tx: &TypedTransaction,
sig: et::Signature,
hash: et::TxHash,
) -> et::Transaction {
et::Transaction {
hash,
nonce: tx.nonce.unwrap_or_default(),
block_hash: None,
block_number: None,
transaction_index: None,
from: tx.from.unwrap_or_default(),
to: tx.to.and_then(|to| to.as_address().cloned()),
value: tx.value.unwrap_or_default(),
gas: tx.gas.unwrap_or_default(),
max_fee_per_gas: tx.max_fee_per_gas,
max_priority_fee_per_gas: tx.max_priority_fee_per_gas,
// Strictly speaking a "Type 2" transaction should not need to set this, but we do because Blockscout
// has a database constraint that if a transaction is included in a block this can't be null.
gas_price: Some(
tx.max_fee_per_gas.unwrap_or_default()
+ tx.max_priority_fee_per_gas.unwrap_or_default(),
) -> Result<et::Transaction, JsonRpcError> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I really dislike that the cache uses et::Transaction -- this type is intended as a return type from the JSON-RPC API, not to store canonical signed transactions.

tx.max_fee_per_gas.unwrap_or_default()
+ tx.max_priority_fee_per_gas.unwrap_or_default(),
) -> Result<et::Transaction, JsonRpcError> {
match &tx {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This all looks very repetitive. Can't you populate the common values from TransactionEssentials via essentials()? And then add the specifics?

Isn't there a method to normalize typed transactions into the Transaction response type in ethers?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't there a method to normalize typed transactions into the Transaction response type in ethers?
From what I found, not really. My guess is the Transaction response contains transaction index, which does not make sense to do the conversion using From.

@cryptoAtwill
Copy link
Contributor Author

@raulk Because the types and from_eth::* and to_eth::* are used in multiple places, I just did a extension to make it work. I think it's a good chance to do a more in depth refactoring. Will update accordingly.

@cryptoAtwill cryptoAtwill changed the base branch from legacy-txn to main December 18, 2024 13:52
@cryptoAtwill cryptoAtwill requested a review from raulk December 18, 2024 13:57
@raulk raulk changed the title feat(node): support legacy in tx cache feat(node): support legacy transactions Dec 18, 2024
@raulk raulk linked an issue Dec 18, 2024 that may be closed by this pull request
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);
Copy link
Contributor

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?

Copy link
Contributor Author

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.

fendermint/eth/api/src/apis/eth.rs Outdated Show resolved Hide resolved
@@ -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<()> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not use Signature::as_signature(), which returns a normalized recoverable signature?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like it's not exposed...

Copy link
Contributor Author

Choose a reason for hiding this comment

The 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.

let sender = msg.from;
let nonce = msg.sequence;

let msg = SignedMessage {
origin_kind: derive_origin_kind(&tx)?,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it make sense to implement TryFrom<TypedTransaction> for OriginKind instead? Then you can do tx.try_into()? here?

Comment on lines -631 to -634
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);
}
Copy link
Contributor

Choose a reason for hiding this comment

The 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?

Copy link
Contributor Author

Choose a reason for hiding this comment

The 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.

/// Cache submitted transactions by their Ethereum hash, because the CometBFT
/// API would not be able to find them until they are delivered to the application
/// and indexed by their domain hash, which some tools interpret as the transaction
/// being dropped from the mempool.
pub type TransactionCache = Cache<et::TxHash, et::Transaction>;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Honestly, now that I look at the final solution, I think this was better the way it was before. We ended up leaking all of ethers' type complexity into Tendermint. In fact, I don't think TypedTransaction should've leaked anywhere at all. I'll work on rolling this back, removing to_eth_typed_transaction, to_eth_legacy_request, to_eth_eip1559_request, etc.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel the conv::* should not be in message crate. It should be shifted to eth crate. That might be better.

Copy link
Contributor

@raulk raulk left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's go ahead and merge this to unblock users who are waiting for this feature to land. We can clean this up in main later.

@raulk raulk merged commit 2894821 into main Dec 20, 2024
15 checks passed
@raulk raulk deleted the legacy-txn-cache branch December 20, 2024 11:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Archived in project
Development

Successfully merging this pull request may close these issues.

Support Eth legacy transactions
2 participants