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

Failure to decode EIP-2718 (type2) = 'malformed transaction' #548

Closed
CedarMist opened this issue Mar 29, 2024 · 3 comments · Fixed by #549
Closed

Failure to decode EIP-2718 (type2) = 'malformed transaction' #548

CedarMist opened this issue Mar 29, 2024 · 3 comments · Fixed by #549
Assignees
Labels
bug Something isn't working

Comments

@CedarMist
Copy link
Member

CedarMist commented Mar 29, 2024

RPC Request:

{'method': 'eth_sendRawTransaction', 'params': ['0x02f8ca825afe028080830186a094334d218c9afc3e4b0b7758e081eb9e49814b3f1780b86405be9a12000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000076e6f6e616d653300000000000000000000000000000000000000000000000000c080a068bfcec2d9cb07c48c97ab52bbd619949fe5bb55990ba4b9893ed47d5802b138a0725239d9c751378a976be2f2d7f49df2d8cd45e133d9ea94a127b15a3d3ac99e'], 'id': 48, 'jsonrpc': '2.0'}

RPC Response:

{'jsonrpc': '2.0', 'id': 48, 'error': {'code': -32000, 'message': 'malformed transaction'}}

The responsible code in web3 gateway is:

func (api *publicAPI) SendRawTransaction(ctx context.Context, data hexutil.Bytes) (common.Hash, error) {
logger := api.Logger.With("method", "eth_sendRawTransaction")
logger.Debug("request", "tx", data)
// Decode the Ethereum transaction.
ethTx := &ethtypes.Transaction{}
if err := rlp.DecodeBytes(data, ethTx); err != nil {
logger.Debug("failed to decode raw transaction data", "err", err)
return common.Hash{}, ErrMalformedTransaction
}

The error shown in debug web3 gateway debug logs is:

{"caller":"api.go:419","level":"debug","method":"eth_sendRawTransaction","module":"eth_rpc","msg":"request","ts":"2024-03-29T12:58:41.791977407Z","tx":"0x02f8ca825afe028080830186a094334d218c9afc3e4b0b7758e081eb9e49814b3f1780b86405be9a12000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000076e6f6e616d653300000000000000000000000000000000000000000000000000c080a068bfcec2d9cb07c48c97ab52bbd619949fe5bb55990ba4b9893ed47d5802b138a0725239d9c751378a976be2f2d7f49df2d8cd45e133d9ea94a127b15a3d3ac99e"}
{"caller":"api.go:424","err":"typed transaction too short","level":"debug","method":"eth_sendRawTransaction","module":"eth_rpc","msg":"failed to decode raw transaction data","ts":"2024-03-29T12:58:41.79205784Z"}

This error (errShortTypedTx) looks like it's thrown from either:

We should be using UnmarshalBinary instead of rlp.DecodeBytes, as UnmarshalBinary will handle EIP-7218 typed transactions correctly.

Decoding test in modules/evm/src/raw_tx.rs successfully decodes

    #[test]
    fn test_decode_eip2718_type2() {
        /*
        rlp([
            chain_id,       = 0x5afe
            nonce,          = 0x02
            max_priority_fee_per_gas,
            max_fee_per_gas,
            gas_limit,      = 0x0186a0
            destination,    = 0x334d218c9afc3e4b0b7758e081eb9e49814b3f17
            amount,         =
            data,           = 0x05be9a12000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000076e6f6e616d653300000000000000000000000000000000000000000000000000
            access_list,    = []
            signature_y_parity,
            signature_r,    = 0x68bfcec2d9cb07c48c97ab52bbd619949fe5bb55990ba4b9893ed47d5802b138
            signature_s     = 0x725239d9c751378a976be2f2d7f49df2d8cd45e133d9ea94a127b15a3d3ac99e
        ])
        */
        decode_expect_call(
            "02f8ca825afe028080830186a094334d218c9afc3e4b0b7758e081eb9e49814b3f1780b86405be9a12000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000076e6f6e616d653300000000000000000000000000000000000000000000000000c080a068bfcec2d9cb07c48c97ab52bbd619949fe5bb55990ba4b9893ed47d5802b138a0725239d9c751378a976be2f2d7f49df2d8cd45e133d9ea94a127b15a3d3ac99e",
            Some(23294),
            "334d218c9afc3e4b0b7758e081eb9e49814b3f17",
            0,
            "05be9a12000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000076e6f6e616d653300000000000000000000000000000000000000000000000000",
            0x0186a0,
            0,
            "af8245553e1e3469cc35ecb10e5d47341a308a5d",
            2
        )
    }
@CedarMist CedarMist added the bug Something isn't working label Mar 29, 2024
CedarMist added a commit that referenced this issue Mar 29, 2024
@CedarMist CedarMist self-assigned this Mar 29, 2024
@CedarMist CedarMist changed the title Failure to decode EIP-7218 (type2) = 'malformed transaction' Failure to decode EIP-2718 (type2) = 'malformed transaction' Mar 29, 2024
@CedarMist
Copy link
Member Author

go-ethereum

The setFeeDefaults function (setFeeDefaults fills in default fee values for unspecified tx fields.) in go-ethereum depends on two RPC calls:

eth_maxPriorityFeePerGas (via the SuggestGasTipCap function) which suggests the tip to allow timely tx execution.

We could make oasis-web3-gateway return the same as eth_gasPrice for eth_maxPriorityFeePerGas, but some clients may see this as doubling the effective gas price in the UI.

web3.py

Has two methods, gas_price and max_priority_fee:

The GasPriceStrategyMiddleware notes:

Uses a gas price strategy if one is set. This is only supported for
legacy transactions. It is recommended to send dynamic fee transactions
(EIP-1559) whenever possible.

The problem from #501 was that max_priority_fee would be checked first unless gasPrice exists in the TX, if that didn't exist it would fail-down to fee_history_priority_fee which would call eth_feeHistory to estimate the priority fee.

It doesn't do any smart detection of whether or not the chain is EIP-155 or EIP-1559, so defaults to EIP-1559 transactions which will fail because eth_feeHistory isn't present - and will likely fail with the issue in this ticket.

ethers

Collects fee data:

Network is considered pre EIP-1559 if maxFeePerGas is null. The maxFeePerGas field will only be set if the latest block has the baseFeePerGas field. Otherwise it will use an EIP-155 transaction with the gasPrice defaulting to what's returned by eth_gasPrice.

No workaround possible, Ethers works fine with the current behavior as the Sapphire is correctly detected as a pre-EIP-1559 network.

Can't modify returned blocks in web3 gateway to add a baseFeePerGas field as this will make block hashes non-reproducible (serializing block will result in wrong block hash)

wagmi

Investigating

metamask

Investigating

@kostko
Copy link
Member

kostko commented Mar 29, 2024

Can't modify returned blocks in web3 gateway to add a baseFeePerGas field as this will make block hashes non-reproducible (serializing block will result in wrong block hash)

AFAIK we use our block hashes, so this isn't the case anyway as we don't actually have Ethereum blocks, just Ethereum transactions.

@CedarMist
Copy link
Member Author

CedarMist commented Mar 29, 2024

If we were to specify baseFeePerGas in the blocks and provide eth_maxPriorityFeePerGas with both set to whatever eth_gasPrice returns that would effectively double the estimated gas price for EIP-1559 transactions.

That seems like a hacky workaround that would cause clients to think Sapphire is EIP-1559 compatible and start sending type2 transactions - which we want to avoid without full support for EIP-1559.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants