Skip to content

Commit

Permalink
Merge eth_simulate_v1 to master (#8153)
Browse files Browse the repository at this point in the history
Co-authored-by: Lukasz Rozmej <[email protected]>
  • Loading branch information
rjnrohit and LukaszRozmej authored Feb 3, 2025
1 parent fa9abe8 commit 5506f83
Show file tree
Hide file tree
Showing 6 changed files with 49 additions and 68 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -282,13 +282,13 @@ private BlockHeader GetCallHeader(BlockStateCall<TransactionWithSourceDetails> b
UInt256.Zero,
parent.Number + 1,
parent.GasLimit,
parent.Timestamp + 1,
parent.Timestamp + blocksConfig.SecondsPerSlot,
[])
{
MixHash = parent.MixHash,
IsPostMerge = parent.Difficulty == 0,
};
result.Timestamp = parent.Timestamp + 1;
result.Timestamp = parent.Timestamp + blocksConfig.SecondsPerSlot;
result.BaseFeePerGas = block.BlockOverrides is { BaseFeePerGas: not null }
? block.BlockOverrides.BaseFeePerGas.Value
: !payloadValidation
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
using System.Linq;
using System.Threading.Tasks;
using Nethermind.Blockchain.Find;
using Nethermind.Config;
using Nethermind.Core;
using Nethermind.Core.Crypto;
using Nethermind.Core.Extensions;
Expand Down Expand Up @@ -61,7 +60,7 @@ public async Task Test_eth_simulate_create()
};

//will mock our GetCachedCodeInfo function - it shall be called 3 times if redirect is working, 2 times if not
SimulateTxExecutor executor = new(chain.Bridge, chain.BlockFinder, new JsonRpcConfig(), new BlocksConfig().SecondsPerSlot);
SimulateTxExecutor executor = new(chain.Bridge, chain.BlockFinder, new JsonRpcConfig());

ResultWrapper<IReadOnlyList<SimulateBlockResult>> result = executor.Execute(payload, BlockParameter.Latest);

Expand Down Expand Up @@ -176,7 +175,7 @@ function ecrecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) public returns(
};

//will mock our GetCachedCodeInfo function - it shall be called 3 times if redirect is working, 2 times if not
SimulateTxExecutor executor = new(chain.Bridge, chain.BlockFinder, new JsonRpcConfig(), new BlocksConfig().SecondsPerSlot);
SimulateTxExecutor executor = new(chain.Bridge, chain.BlockFinder, new JsonRpcConfig());

Debug.Assert(contractAddress is not null, nameof(contractAddress) + " is not null");
Assert.That(chain.State.AccountExists(contractAddress), Is.True);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ public async Task Test_eth_simulate_serialisation()
chain.BlockTree.UpdateHeadBlock(chain.BlockFinder.Head!.Hash!);

//will mock our GetCachedCodeInfo function - it shall be called 3 times if redirect is working, 2 times if not
SimulateTxExecutor executor = new(chain.Bridge, chain.BlockFinder, new JsonRpcConfig(), new BlocksConfig().SecondsPerSlot);
SimulateTxExecutor executor = new(chain.Bridge, chain.BlockFinder, new JsonRpcConfig());
ResultWrapper<IReadOnlyList<SimulateBlockResult>> result = executor.Execute(payload, BlockParameter.Latest);
IReadOnlyList<SimulateBlockResult> data = result.Data;
Assert.That(data.Count, Is.EqualTo(7));
Expand Down Expand Up @@ -149,7 +149,7 @@ public async Task Test_eth_simulate_eth_moved()
chain.BlockTree.UpdateHeadBlock(chain.BlockFinder.Head!.Hash!);

//will mock our GetCachedCodeInfo function - it shall be called 3 times if redirect is working, 2 times if not
SimulateTxExecutor executor = new(chain.Bridge, chain.BlockFinder, new JsonRpcConfig(), new BlocksConfig().SecondsPerSlot);
SimulateTxExecutor executor = new(chain.Bridge, chain.BlockFinder, new JsonRpcConfig());
ResultWrapper<IReadOnlyList<SimulateBlockResult>> result =
executor.Execute(payload, BlockParameter.Latest);
IReadOnlyList<SimulateBlockResult> data = result.Data;
Expand Down Expand Up @@ -233,7 +233,7 @@ public async Task Test_eth_simulate_transactions_forced_fail()
chain.BlockTree.UpdateHeadBlock(chain.BlockFinder.Head!.Hash!);

//will mock our GetCachedCodeInfo function - it shall be called 3 times if redirect is working, 2 times if not
SimulateTxExecutor executor = new(chain.Bridge, chain.BlockFinder, new JsonRpcConfig(), new BlocksConfig().SecondsPerSlot);
SimulateTxExecutor executor = new(chain.Bridge, chain.BlockFinder, new JsonRpcConfig());

ResultWrapper<IReadOnlyList<SimulateBlockResult>> result =
executor.Execute(payload, BlockParameter.Latest);
Expand Down
5 changes: 5 additions & 0 deletions src/Nethermind/Nethermind.JsonRpc/ErrorCodes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -111,5 +111,10 @@ public static class ErrorCodes
/// Invalid RPC simulate call transaction
/// </summary>
public const int InvalidTransaction = -38014;

/// <summary>
/// Too many blocks for simulation
/// </summary>
public const int ClientLimitExceededError = -38026;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -343,7 +343,7 @@ public ResultWrapper<string> eth_call(TransactionForRpc transactionCall, BlockPa
.ExecuteTx(transactionCall, blockParameter, stateOverride);

public ResultWrapper<IReadOnlyList<SimulateBlockResult>> eth_simulateV1(SimulatePayload<TransactionForRpc> payload, BlockParameter? blockParameter = null) =>
new SimulateTxExecutor(_blockchainBridge, _blockFinder, _rpcConfig, _secondsPerSlot)
new SimulateTxExecutor(_blockchainBridge, _blockFinder, _rpcConfig)
.Execute(payload, blockParameter);

public ResultWrapper<UInt256?> eth_estimateGas(TransactionForRpc transactionCall, BlockParameter? blockParameter, Dictionary<Address, AccountOverride>? stateOverride = null) =>
Expand Down
95 changes: 36 additions & 59 deletions src/Nethermind/Nethermind.JsonRpc/Modules/Eth/SimulateTxExecutor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using Nethermind.Blockchain.Find;
using Nethermind.Config;
using Nethermind.Core;
using Nethermind.Core.Collections;
using Nethermind.Evm;
using Nethermind.Facade;
using Nethermind.Facade.Eth.RpcTransaction;
Expand Down Expand Up @@ -124,87 +125,63 @@ public override ResultWrapper<IReadOnlyList<SimulateBlockResult>> Execute(

if (call.BlockStateCalls is not null)
{
long lastBlockNumber = -1;
ulong lastBlockTime = 0;
long lastBlockNumber = header.Number;
ulong lastBlockTime = header.Timestamp;

using ArrayPoolList<BlockStateCall<TransactionForRpc>> completeBlockStateCalls = new(call.BlockStateCalls.Count);

foreach (BlockStateCall<TransactionForRpc>? blockToSimulate in call.BlockStateCalls)
{
ulong givenNumber = blockToSimulate.BlockOverrides?.Number ??
(lastBlockNumber == -1 ? (ulong)header.Number + 1 : (ulong)lastBlockNumber + 1);
blockToSimulate.BlockOverrides ??= new BlockOverride();
ulong givenNumber = blockToSimulate.BlockOverrides.Number ?? (ulong)lastBlockNumber + 1;

if (givenNumber > long.MaxValue)
return ResultWrapper<IReadOnlyList<SimulateBlockResult>>.Fail(
$"Block number too big {givenNumber}!", ErrorCodes.InvalidParams);

if (givenNumber < (ulong)header.Number)
if (givenNumber <= (ulong)lastBlockNumber)
return ResultWrapper<IReadOnlyList<SimulateBlockResult>>.Fail(
$"Block number out of order {givenNumber} is < than given base number of {header.Number}!", ErrorCodes.InvalidInputBlocksOutOfOrder);
$"Block number out of order {givenNumber} is <= than previous block number of {header.Number}!", ErrorCodes.InvalidInputBlocksOutOfOrder);

long given = (long)givenNumber;
if (given > lastBlockNumber)
{
lastBlockNumber = given;
}
else
{
// if the no. of filler blocks are greater than maximum simulate blocks cap
if (givenNumber - (ulong)lastBlockNumber > (ulong)_blocksLimit)
return ResultWrapper<IReadOnlyList<SimulateBlockResult>>.Fail(
$"Block number out of order {givenNumber}!", ErrorCodes.InvalidInputBlocksOutOfOrder);
$"too many blocks",
ErrorCodes.ClientLimitExceededError);

for (ulong fillBlockNumber = (ulong)lastBlockNumber + 1; fillBlockNumber < givenNumber; fillBlockNumber++)
{
ulong fillBlockTime = lastBlockTime + secondsPerSlot ?? new BlocksConfig().SecondsPerSlot;
completeBlockStateCalls.Add(new BlockStateCall<TransactionForRpc>
{
BlockOverrides = new BlockOverride { Number = fillBlockNumber, Time = fillBlockTime },
StateOverrides = null,
Calls = []
});
lastBlockTime = fillBlockTime;
}

blockToSimulate.BlockOverrides ??= new BlockOverride();
blockToSimulate.BlockOverrides.Number = givenNumber;

ulong givenTime = blockToSimulate.BlockOverrides.Time ??
(lastBlockTime == 0
? header.Timestamp + secondsPerSlot.Value
: lastBlockTime + secondsPerSlot.Value);

if (givenTime < header.Timestamp)
return ResultWrapper<IReadOnlyList<SimulateBlockResult>>.Fail(
$"Block timestamp out of order {givenTime} is < than given base timestamp of {header.Timestamp}!", ErrorCodes.BlockTimestampNotIncreased);

if (givenTime > lastBlockTime)
if (blockToSimulate.BlockOverrides.Time is not null)
{
lastBlockTime = givenTime;
if (blockToSimulate.BlockOverrides.Time <= lastBlockTime)
{
return ResultWrapper<IReadOnlyList<SimulateBlockResult>>.Fail(
$"Block timestamp out of order {blockToSimulate.BlockOverrides.Time} is <= than given base timestamp of {lastBlockTime}!", ErrorCodes.BlockTimestampNotIncreased);
}
lastBlockTime = (ulong)blockToSimulate.BlockOverrides.Time;
}
else
{
return ResultWrapper<IReadOnlyList<SimulateBlockResult>>.Fail(
$"Block timestamp out of order {givenTime}!", ErrorCodes.BlockTimestampNotIncreased);
blockToSimulate.BlockOverrides.Time = lastBlockTime + secondsPerSlot;
lastBlockTime = (ulong)blockToSimulate.BlockOverrides.Time;
}
lastBlockNumber = (long)givenNumber;

blockToSimulate.BlockOverrides.Time = givenTime;
completeBlockStateCalls.Add(blockToSimulate);
}

long minBlockNumber = Math.Min(
call.BlockStateCalls.Min(b => (long)(b.BlockOverrides?.Number ?? ulong.MaxValue)),
header.Number + 1);

long maxBlockNumber = Math.Max(
call.BlockStateCalls.Max(b => (long)(b.BlockOverrides?.Number ?? ulong.MinValue)),
minBlockNumber);

HashSet<long> existingBlockNumbers =
[
.. call.BlockStateCalls.Select(b => (long)(b.BlockOverrides?.Number ?? ulong.MinValue))
];

List<BlockStateCall<TransactionForRpc>> completeBlockStateCalls = call.BlockStateCalls;

for (long blockNumber = minBlockNumber; blockNumber <= maxBlockNumber; blockNumber++)
{
if (!existingBlockNumbers.Contains(blockNumber))
{
completeBlockStateCalls.Add(new BlockStateCall<TransactionForRpc>
{
BlockOverrides = new BlockOverride { Number = (ulong)blockNumber },
StateOverrides = null,
Calls = []
});
}
}

call.BlockStateCalls.Sort((b1, b2) => b1.BlockOverrides!.Number!.Value.CompareTo(b2.BlockOverrides!.Number!.Value));
call.BlockStateCalls = [.. completeBlockStateCalls];
}

using CancellationTokenSource timeout = _rpcConfig.BuildTimeoutCancellationToken();
Expand Down

0 comments on commit 5506f83

Please sign in to comment.