Skip to content

Commit 5506f83

Browse files
Merge eth_simulate_v1 to master (#8153)
Co-authored-by: Lukasz Rozmej <[email protected]>
1 parent fa9abe8 commit 5506f83

File tree

6 files changed

+49
-68
lines changed

6 files changed

+49
-68
lines changed

src/Nethermind/Nethermind.Facade/Simulate/SimulateBridgeHelper.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -282,13 +282,13 @@ private BlockHeader GetCallHeader(BlockStateCall<TransactionWithSourceDetails> b
282282
UInt256.Zero,
283283
parent.Number + 1,
284284
parent.GasLimit,
285-
parent.Timestamp + 1,
285+
parent.Timestamp + blocksConfig.SecondsPerSlot,
286286
[])
287287
{
288288
MixHash = parent.MixHash,
289289
IsPostMerge = parent.Difficulty == 0,
290290
};
291-
result.Timestamp = parent.Timestamp + 1;
291+
result.Timestamp = parent.Timestamp + blocksConfig.SecondsPerSlot;
292292
result.BaseFeePerGas = block.BlockOverrides is { BaseFeePerGas: not null }
293293
? block.BlockOverrides.BaseFeePerGas.Value
294294
: !payloadValidation

src/Nethermind/Nethermind.JsonRpc.Test/Modules/Eth/EthSimulateTestsPrecompilesWithRedirection.cs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
using System.Linq;
77
using System.Threading.Tasks;
88
using Nethermind.Blockchain.Find;
9-
using Nethermind.Config;
109
using Nethermind.Core;
1110
using Nethermind.Core.Crypto;
1211
using Nethermind.Core.Extensions;
@@ -61,7 +60,7 @@ public async Task Test_eth_simulate_create()
6160
};
6261

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

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

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

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

181180
Debug.Assert(contractAddress is not null, nameof(contractAddress) + " is not null");
182181
Assert.That(chain.State.AccountExists(contractAddress), Is.True);

src/Nethermind/Nethermind.JsonRpc.Test/Modules/Eth/Simulate/EthSimulateTestsBlocksAndTransactions.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ public async Task Test_eth_simulate_serialisation()
7575
chain.BlockTree.UpdateHeadBlock(chain.BlockFinder.Head!.Hash!);
7676

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

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

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

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

src/Nethermind/Nethermind.JsonRpc/ErrorCodes.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,5 +111,10 @@ public static class ErrorCodes
111111
/// Invalid RPC simulate call transaction
112112
/// </summary>
113113
public const int InvalidTransaction = -38014;
114+
115+
/// <summary>
116+
/// Too many blocks for simulation
117+
/// </summary>
118+
public const int ClientLimitExceededError = -38026;
114119
}
115120
}

src/Nethermind/Nethermind.JsonRpc/Modules/Eth/EthRpcModule.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -343,7 +343,7 @@ public ResultWrapper<string> eth_call(TransactionForRpc transactionCall, BlockPa
343343
.ExecuteTx(transactionCall, blockParameter, stateOverride);
344344

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

349349
public ResultWrapper<UInt256?> eth_estimateGas(TransactionForRpc transactionCall, BlockParameter? blockParameter, Dictionary<Address, AccountOverride>? stateOverride = null) =>

src/Nethermind/Nethermind.JsonRpc/Modules/Eth/SimulateTxExecutor.cs

Lines changed: 36 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
using Nethermind.Blockchain.Find;
99
using Nethermind.Config;
1010
using Nethermind.Core;
11+
using Nethermind.Core.Collections;
1112
using Nethermind.Evm;
1213
using Nethermind.Facade;
1314
using Nethermind.Facade.Eth.RpcTransaction;
@@ -124,87 +125,63 @@ public override ResultWrapper<IReadOnlyList<SimulateBlockResult>> Execute(
124125

125126
if (call.BlockStateCalls is not null)
126127
{
127-
long lastBlockNumber = -1;
128-
ulong lastBlockTime = 0;
128+
long lastBlockNumber = header.Number;
129+
ulong lastBlockTime = header.Timestamp;
130+
131+
using ArrayPoolList<BlockStateCall<TransactionForRpc>> completeBlockStateCalls = new(call.BlockStateCalls.Count);
129132

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

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

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

143-
long given = (long)givenNumber;
144-
if (given > lastBlockNumber)
145-
{
146-
lastBlockNumber = given;
147-
}
148-
else
149-
{
146+
// if the no. of filler blocks are greater than maximum simulate blocks cap
147+
if (givenNumber - (ulong)lastBlockNumber > (ulong)_blocksLimit)
150148
return ResultWrapper<IReadOnlyList<SimulateBlockResult>>.Fail(
151-
$"Block number out of order {givenNumber}!", ErrorCodes.InvalidInputBlocksOutOfOrder);
149+
$"too many blocks",
150+
ErrorCodes.ClientLimitExceededError);
151+
152+
for (ulong fillBlockNumber = (ulong)lastBlockNumber + 1; fillBlockNumber < givenNumber; fillBlockNumber++)
153+
{
154+
ulong fillBlockTime = lastBlockTime + secondsPerSlot ?? new BlocksConfig().SecondsPerSlot;
155+
completeBlockStateCalls.Add(new BlockStateCall<TransactionForRpc>
156+
{
157+
BlockOverrides = new BlockOverride { Number = fillBlockNumber, Time = fillBlockTime },
158+
StateOverrides = null,
159+
Calls = []
160+
});
161+
lastBlockTime = fillBlockTime;
152162
}
153163

154-
blockToSimulate.BlockOverrides ??= new BlockOverride();
155164
blockToSimulate.BlockOverrides.Number = givenNumber;
156165

157-
ulong givenTime = blockToSimulate.BlockOverrides.Time ??
158-
(lastBlockTime == 0
159-
? header.Timestamp + secondsPerSlot.Value
160-
: lastBlockTime + secondsPerSlot.Value);
161-
162-
if (givenTime < header.Timestamp)
163-
return ResultWrapper<IReadOnlyList<SimulateBlockResult>>.Fail(
164-
$"Block timestamp out of order {givenTime} is < than given base timestamp of {header.Timestamp}!", ErrorCodes.BlockTimestampNotIncreased);
165-
166-
if (givenTime > lastBlockTime)
166+
if (blockToSimulate.BlockOverrides.Time is not null)
167167
{
168-
lastBlockTime = givenTime;
168+
if (blockToSimulate.BlockOverrides.Time <= lastBlockTime)
169+
{
170+
return ResultWrapper<IReadOnlyList<SimulateBlockResult>>.Fail(
171+
$"Block timestamp out of order {blockToSimulate.BlockOverrides.Time} is <= than given base timestamp of {lastBlockTime}!", ErrorCodes.BlockTimestampNotIncreased);
172+
}
173+
lastBlockTime = (ulong)blockToSimulate.BlockOverrides.Time;
169174
}
170175
else
171176
{
172-
return ResultWrapper<IReadOnlyList<SimulateBlockResult>>.Fail(
173-
$"Block timestamp out of order {givenTime}!", ErrorCodes.BlockTimestampNotIncreased);
177+
blockToSimulate.BlockOverrides.Time = lastBlockTime + secondsPerSlot;
178+
lastBlockTime = (ulong)blockToSimulate.BlockOverrides.Time;
174179
}
180+
lastBlockNumber = (long)givenNumber;
175181

176-
blockToSimulate.BlockOverrides.Time = givenTime;
182+
completeBlockStateCalls.Add(blockToSimulate);
177183
}
178-
179-
long minBlockNumber = Math.Min(
180-
call.BlockStateCalls.Min(b => (long)(b.BlockOverrides?.Number ?? ulong.MaxValue)),
181-
header.Number + 1);
182-
183-
long maxBlockNumber = Math.Max(
184-
call.BlockStateCalls.Max(b => (long)(b.BlockOverrides?.Number ?? ulong.MinValue)),
185-
minBlockNumber);
186-
187-
HashSet<long> existingBlockNumbers =
188-
[
189-
.. call.BlockStateCalls.Select(b => (long)(b.BlockOverrides?.Number ?? ulong.MinValue))
190-
];
191-
192-
List<BlockStateCall<TransactionForRpc>> completeBlockStateCalls = call.BlockStateCalls;
193-
194-
for (long blockNumber = minBlockNumber; blockNumber <= maxBlockNumber; blockNumber++)
195-
{
196-
if (!existingBlockNumbers.Contains(blockNumber))
197-
{
198-
completeBlockStateCalls.Add(new BlockStateCall<TransactionForRpc>
199-
{
200-
BlockOverrides = new BlockOverride { Number = (ulong)blockNumber },
201-
StateOverrides = null,
202-
Calls = []
203-
});
204-
}
205-
}
206-
207-
call.BlockStateCalls.Sort((b1, b2) => b1.BlockOverrides!.Number!.Value.CompareTo(b2.BlockOverrides!.Number!.Value));
184+
call.BlockStateCalls = [.. completeBlockStateCalls];
208185
}
209186

210187
using CancellationTokenSource timeout = _rpcConfig.BuildTimeoutCancellationToken();

0 commit comments

Comments
 (0)