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

'Invalid Params' Error on Nethermind for Type-3 Transactions #7987

Open
MingxiYe opened this issue Dec 31, 2024 · 2 comments
Open

'Invalid Params' Error on Nethermind for Type-3 Transactions #7987

MingxiYe opened this issue Dec 31, 2024 · 2 comments

Comments

@MingxiYe
Copy link

Description

After the Cancun upgrade, Ethereum introduced Type-3 transactions, which can store “blob” data with significantly reduced gas costs. While eth_estimateGas should return the correct gas usage for a Type-3 transaction, it currently returns {'code': -32602, 'message': 'Invalid params'}.

Steps to Reproduce

To reproduce this issue, follow these steps:

  1. Run a testnet or use an existing mainnet node provided by Nethermind.
  2. Execute the script in the appendix with the appropriate environment configuration (i.e., TESTNET_RPC_URL=<your-nethermind-node-rpc-url> python3 estimate.py).
  3. Observe that the script returns {'code': -32602, 'message': 'Invalid params'}.

Actual behavior

The error returned is {'code': -32602, 'message': 'Invalid params'}. After reviewing the following code:

if (method.ExpectedParameters.Length > 0)
{
(parameters, hasMissing) = DeserializeParameters(method.ExpectedParameters, providedParametersLength, providedParameters, missingParamsCount);
if (parameters is null)
{
if (_logger.IsWarn) _logger.Warn($"Incorrect JSON RPC parameters when calling {methodName} with params [{string.Join(", ", providedParameters)}]");
return GetErrorResponse(methodName, ErrorCodes.InvalidParams, "Invalid params", null, request.Id);
}
}

I suspect that the RPC module does not yet support Type-3 transactions, as suggested by this snippet.

Expected behavior

Switching the TESTNET_RPC_URL to a endpoint of geth, reth, besu, or erigon allows the script to successfully return the estimated gas.

Desktop (please complete the following information):
Please provide the following information regarding your setup:

  • Operating System: Linux
  • Version: v1.30.1
  • Installation Method: Using Kurtosis to run testnet
  • Consensus Client: Lighthouse

Appendix

The python script (i.e., estimate.py used for poc):

#!/usr/bin/env python3

import os
import hashlib
import json
import traceback
from web3 import Web3, HTTPProvider
from eth_account import Account

def generate_demo_blob(blob_index: int, size_kb: int = 128) -> bytes:
    """
    Generates a demo blob of specified size filled with deterministic data for consistency.
    
    Args:
        blob_index (int): Index of the blob for pattern variation.
        size_kb (int): Size of the blob in kilobytes (default is 128 KB).
        
    Returns:
        bytes: Generated blob data.
    """
    # Each blob is size_kb KB
    pattern = (f"Blob{blob_index}Data").encode()
    blob_size = size_kb * 1024  # Convert KB to bytes
    blob = (pattern * (blob_size // len(pattern) + 1))[:blob_size]
    return blob

def compute_blob_hash(blob, version=1):
    sha256_hash = hashlib.sha256(blob).digest()
    versioned_hash = bytes([0x01]) + sha256_hash[1:]
    return '0x' + versioned_hash.hex()

def to_hex(value):
    """
    Converts an integer value to a hex string with '0x' prefix.
    
    Args:
        value (int): The integer value to convert.
        
    Returns:
        str: Hexadecimal string representation with '0x' prefix.
    """
    return hex(value)

def main():
    # Initialize Web3
    rpc_url = os.getenv("TESTNET_RPC_URL")
    if not rpc_url:
        raise EnvironmentError("TESTNET_RPC_URL environment variable not set.")
    w3 = Web3(HTTPProvider(rpc_url))
    assert w3.is_connected(), "Web3 provider is not connected."

    # Load account from private key
    private_key = os.getenv("PRIVATE_KEY", "0xbf3beef3bd999ba9f2451e06936f0423cd62b815c9233dd3bc90f7e02a1e8673")
    account = Account.from_key(private_key)

    # Get the nonce for the account
    nonce = w3.eth.get_transaction_count(account.address)

    # Number of blobs to include (1 blob of 128 KB)
    num_blobs = 1
    blob_size_kb = 128  # 128 KB per blob

    # Generate demo blobs and their hashes
    blobs = []
    blob_versioned_hashes = []
    for i in range(num_blobs):
        blob = generate_demo_blob(i, size_kb=blob_size_kb)
        blobs.append('0x' + blob.hex())  # Blob as hex string with '0x' prefix
        blob_hash = compute_blob_hash(blob)
        blob_versioned_hashes.append(blob_hash)

    # Validate blob sizes
    for idx, blob in enumerate(blobs):
        blob_bytes = bytes.fromhex(blob[2:])  # Remove '0x' prefix
        expected_size = blob_size_kb * 1024
        actual_size = len(blob_bytes)
        assert actual_size == expected_size, f"Blob {idx + 1} size is incorrect: {actual_size} bytes, expected {expected_size} bytes."

    # Validate blob hashes sizes
    for idx, hash_str in enumerate(blob_versioned_hashes):
        hash_hex = hash_str[2:]  # Remove '0x' prefix
        assert len(hash_hex) == 64, f"Blob {idx + 1} hash length is incorrect: {len(hash_hex)} characters, expected 64 characters."

    # Construct the Type-3 transaction with hexadecimal fields
    tx = {
        "type": 3,  # Type-3 Transaction
        "chainId": to_hex(w3.eth.chain_id),
        "nonce": to_hex(nonce),
        "from": account.address,  # Optional when signing
        "to": "0x0000000000000000000000000000000000000000",
        "value": to_hex(0),
        "maxPriorityFeePerGas": to_hex(w3.to_wei("2", "gwei")),
        "maxFeePerGas": to_hex(w3.to_wei("30", "gwei")),
        "maxFeePerBlobGas": to_hex(w3.to_wei("100", "gwei")),
        "gas": to_hex(1500000),
        "data": "0x",
        "blobVersionedHashes": blob_versioned_hashes,  # List containing one 32-byte hash
        "blobs": blobs,  # List containing one 128 KB blob as hex string
    }

    # Estimate Gas
    try:
        estimated_gas = w3.eth.estimate_gas(tx)
        print(f"Estimated Gas: {estimated_gas}")
    except Exception as e:
        print("Error estimating gas:", e)
        traceback.print_exc()

if __name__ == "__main__":
    main()
@kamilchodola
Copy link
Contributor

@flcl42 @marcindsobczak Any chance you can look at this?

@kamilchodola
Copy link
Contributor

Not sure if I reproduced it or found something else but calling estimateGas like this:

curl -X POST \
  -H "Content-Type: application/json" \
  --data '{
    "jsonrpc": "2.0",
    "method": "eth_estimateGas",
    "params": [{
      "from": "0xc1b634853cb333d3ad8663715b08f41a3aec47cc",
      "value": "0x0",
      "data": "0x3e5aa08200000000000000000000000000000000000000000000000000000000000bf52400000000000000000000000000000000000000000000000000000000001bcb43000000000000000000000000e64a54e2533fd126c2e452c5fab544d80e2e4eb50000000000000000000000000000000000000000000000000000000010097d3f0000000000000000000000000000000000000000000000000000000010097e8d",
      "type": "0x3",
      "maxFeePerGas": "0x246dfab0ea",
      "maxPriorityFeePerGas": "0x7d2b7500"
    }],
    "id": 1
  }' \
  http://localhost:8545

I got such errors:

{"jsonrpc":"2.0","error":{"code":-32603,"message":"Internal error","data":"System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation.\n ---> System.InvalidOperationException: Nullable object must have a value.\n at Nethermind.Serialization.Rlp.TxDecoders.BlobTxDecoder1.EncodeTypedWrapped(Transaction transaction, RlpStream stream, RlpBehaviors rlpBehaviors, Boolean forSigning, Int32 contentLength) in /src/Nethermind/Nethermind.Serialization.Rlp/TxDecoders/BlobTxDecoder.cs:line 100\n at Nethermind.Serialization.Rlp.TxDecoders.BaseAccessListTxDecoder1.Encode(Transaction transaction, RlpStream stream, RlpBehaviors rlpBehaviors, Boolean forSigning, Boolean isEip155Enabled, UInt64 chainId) in /src/Nethermind/Nethermind.Serialization.Rlp/TxDecoders/AccessListTxDecoder.cs:line 25\n at Nethermind.Crypto.TransactionExtensions.CalculateHash(Transaction transaction) in /src/Nethermind/Nethermind.Crypto/TransactionExtensions.cs:line 17\n at Nethermind.Facade.BlockchainBridge.CallAndRestore(BlockHeader blockHeader, Transaction transaction, Boolean treatBlockHeaderAsParentBlock, ITxTracer tracer, IOverridableTxProcessingScope scope) in /src/Nethermind/Nethermind.Facade/BlockchainBridge.cs:line 300\n at Nethermind.Facade.BlockchainBridge.TryCallAndRestore(IOverridableTxProcessingScope scope, BlockHeader blockHeader, Transaction transaction, Boolean treatBlockHeaderAsParentBlock, ITxTracer tracer) in /src/Nethermind/Nethermind.Facade/BlockchainBridge.cs:line 243\n at Nethermind.Facade.BlockchainBridge.EstimateGas(BlockHeader header, Transaction tx, Int32 errorMargin, Dictionary2 stateOverride, CancellationToken cancellationToken) in /src/Nethermind/Nethermind.Facade/BlockchainBridge.cs:line 199\n at Nethermind.JsonRpc.Modules.Eth.EthRpcModule.EstimateGasTxExecutor.ExecuteTx(BlockHeader header, Transaction tx, Dictionary2 stateOverride, CancellationToken token) in /src/Nethermind/Nethermind.JsonRpc/Modules/Eth/EthRpcModule.TransactionExecutor.cs:line 84\n at Nethermind.JsonRpc.Modules.Eth.EthRpcModule.TxExecutor1.Execute(BlockHeader header, Transaction tx, Dictionary2 stateOverride, CancellationToken token) in /src/Nethermind/Nethermind.JsonRpc/Modules/Eth/EthRpcModule.TransactionExecutor.cs:line 45\n at Nethermind.JsonRpc.Modules.Eth.ExecutorBase3.Execute(TRequest call, BlockParameter blockParameter, Dictionary2 stateOverride) in /src/Nethermind/Nethermind.JsonRpc/Modules/Eth/ExecutorBase.cs:line 41\n at Nethermind.JsonRpc.Modules.Eth.EthRpcModule.TxExecutor1.Execute(TransactionForRpc transactionCall, BlockParameter blockParameter, Dictionary2 stateOverride) in /src/Nethermind/Nethermind.JsonRpc/Modules/Eth/EthRpcModule.TransactionExecutor.cs:line 55\n at Nethermind.JsonRpc.Modules.Eth.EthRpcModule.TxExecutor1.ExecuteTx(TransactionForRpc transactionCall, BlockParameter blockParameter, Dictionary2 stateOverride) in /src/Nethermind/Nethermind.JsonRpc/Modules/Eth/EthRpcModule.TransactionExecutor.cs:line 59\n at Nethermind.JsonRpc.Modules.Eth.EthRpcModule.eth_estimateGas(TransactionForRpc transactionCall, BlockParameter blockParameter, Dictionary2 stateOverride) in /src/Nethermind/Nethermind.JsonRpc/Modules/Eth/EthRpcModule.cs:line 348\n at InvokeStub_IEthRpcModule.eth_estimateGas(Object, Span1)\n at System.Reflection.MethodBaseInvoker.InvokeWithFewArgs(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)\n --- End of inner exception stack trace ---\n at System.Reflection.MethodBaseInvoker.InvokeWithFewArgs(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)\n at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)\n at Nethermind.JsonRpc.JsonRpcService.ExecuteAsync(JsonRpcRequest request, String methodName, ResolvedMethodInfo method, JsonRpcContext context) in /src/Nethermind/Nethermind.JsonRpc/JsonRpcService.cs:line 175"},"id":1}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants