Skip to content

Unify types with Geth#11937

Open
Dyslex7c wants to merge 33 commits into
NethermindEth:masterfrom
Dyslex7c:unify-geth-types
Open

Unify types with Geth#11937
Dyslex7c wants to merge 33 commits into
NethermindEth:masterfrom
Dyslex7c:unify-geth-types

Conversation

@Dyslex7c

@Dyslex7c Dyslex7c commented Jun 8, 2026

Copy link
Copy Markdown
Contributor

Closes #9685

Changes

  • Unified block number, gas-related, and other numeric fields with Geth by replacing signed integer types with ulong.
  • Removed unnecessary casts and adjusted logic to work with the updated types.

Types of changes

What types of changes does your code introduce?

  • Bugfix (a non-breaking change that fixes an issue)
  • New feature (a non-breaking change that adds functionality)
  • Breaking change (a change that causes existing functionality not to work as expected)
  • Optimization
  • Refactoring
  • Documentation update
  • Build-related changes
  • Other: Description

Testing

Requires testing

  • Yes
  • No

If yes, did you write tests?

  • Yes
  • No

@LukaszRozmej

Copy link
Copy Markdown
Member

I disagree with cutting. Yes it is cumbersome but IMO it is all-or-nothing refactor.

@Dyslex7c

Copy link
Copy Markdown
Contributor Author

I disagree with cutting. Yes it is cumbersome but IMO it is all-or-nothing refactor.

just to clarify, I meant to say cutting it up into smaller PRs is cumbersome, not this PR itself. I'm also in favour of keeping this single PR

Dyslex7c added 5 commits June 11, 2026 17:53
# Conflicts:
#	src/Nethermind/Nethermind.Synchronization/FastSync/StateSyncPivot.cs
#	src/Nethermind/Nethermind.Synchronization/FastSync/StateSyncRunner.cs
# Conflicts:
#	src/Nethermind/Nethermind.Consensus/Stateless/WitnessGeneratingHeaderFinder.cs
#	src/Nethermind/Nethermind.JsonRpc.Test/Modules/DebugRpcModuleTests.ExecutionWitness.cs
@Dyslex7c Dyslex7c marked this pull request as ready for review June 11, 2026 15:53
@Dyslex7c Dyslex7c requested a review from LukaszRozmej June 11, 2026 15:53
Dyslex7c added 2 commits June 15, 2026 16:53
# Conflicts:
#	src/Nethermind/Nethermind.Db/FlatDbConfig.cs
#	src/Nethermind/Nethermind.JsonRpc.Test/Modules/DebugRpcModuleTests.TraceCallMany.cs
#	src/Nethermind/Nethermind.Merge.Plugin.Test/SszRest/SszMiddlewareTests.cs
#	src/Nethermind/Nethermind.Merge.Plugin/IEngineRpcModule.Amsterdam.cs
#	src/Nethermind/Nethermind.Merge.Plugin/SszRest/Handlers/GetPayloadBodiesByRangeSszHandler.cs
#	src/Nethermind/Nethermind.TxPool/ITxPool.cs
Comment thread src/Nethermind/Nethermind.BalRecorder.Test/EraFlatStoreTests.cs Outdated
Comment thread src/Nethermind/Nethermind.BalRecorder/SlotStore.cs Outdated
Comment thread src/Nethermind/Nethermind.Benchmark/Rlp/RlpDecodeBlockBenchmark.cs Outdated
Comment thread src/Nethermind/Nethermind.Benchmark/Rlp/RlpEncodeBlockBenchmark.cs Outdated
Comment thread src/Nethermind/Nethermind.Benchmark/Rlp/RlpEncodeHeaderBenchmark.cs Outdated
.WithType(TxType.SetCode)
.WithTo(signer.Address)
.WithGasLimit(GasCostOf.Transaction + GasCostOf.NewAccount * count)
.WithGasLimit(GasCostOf.Transaction + GasCostOf.NewAccount * (ulong)count)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

count should be ulong

Comment thread src/Nethermind/Nethermind.Blockchain.Test/TxPoolSourceTests.cs Outdated
Comment thread src/Nethermind/Nethermind.Blockchain/Blocks/BlockhashStore.cs Outdated
Comment thread src/Nethermind/Nethermind.Blockchain/Blocks/BlockStore.cs Outdated
@LukaszRozmej

Copy link
Copy Markdown
Member

Deep review

Scope. 1,265 files, +10,160/-9,028 — signed→unsigned migration of block-number/gas counters. I ran five parallel passes (BlockTree, EVM/gas, sync/migration, JSON-RPC/RLP/SSZ, broad pattern sweep) and verified the highest-impact claims against the PR branch. CI is fully green but does not exercise the failure modes below — the pattern is that the original signed code silently no-op'd on out-of-range inputs, the tests were written against that, and post-migration the same inputs wrap rather than no-op.

I agree with keeping this as one PR — the type-graph is too interconnected to bisect cleanly.


HIGH — please fix before merge

1. StateSyncPivot.Diff operator-precedence bug

src/Nethermind/Nethermind.Synchronization/FastSync/StateSyncPivot.cs:19-20

// BestSuggestedHeader.Number and _bestHeader.Number are ulong. Diff is long (can be negative during reorgs).
public ulong Diff => blockTree.BestSuggestedHeader?.Number ?? 0UL - (_bestHeader?.Number ?? 0UL);

?? binds looser than -, so this parses as BestSuggestedHeader?.Number ?? (0UL - _bestHeader.Number). When BestSuggestedHeader is non-null (the normal case) Diff returns the absolute best-suggested number, not a difference. The comment also still says "Diff is long (can be negative)" but the type is now ulong.

public ulong Diff
{
    get
    {
        ulong best = blockTree.BestSuggestedHeader?.Number ?? 0UL;
        ulong have = _bestHeader?.Number ?? 0UL;
        return best > have ? best - have : 0UL;
    }
}

2. VmState.Refund -= sClearRefunds — unguarded ulong subtract on the consensus path

src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Storage.cs:553

vmState.Refund -= sClearRefunds;
if (vm.TxTracer.IsTracingRefunds)
    vm.TxTracer.ReportRefund(-(long)sClearRefunds); ///?????

The ///????? is the author flagging it. With long Refund the EIP-2200/3529 net-metering accumulator could dip transiently negative (e.g. a 0→X→0 pattern where the "undo a clear" branch fires before the compensating clear). With ulong, the subtract wraps to ~2^64; the post-tx cap Math.Min(spentGas/quotient, refund) then picks spentGas/quotient — the tx receives the maximum possible refund instead of the correct (smaller) one. Consensus relevant. Saturating subtract.

3. BlockTree.AcceptVisitor — visitor range underflow

src/Nethermind/Nethermind.Blockchain/BlockTree.AcceptVisitor.cs:23-25

ulong levelNumber = visitor.StartLevelInclusive;
ulong blocksToVisit = visitor.EndLevelExclusive - visitor.StartLevelInclusive;
for (ulong i = 0; i < blocksToVisit; i++)

A visitor with End <= Start previously yielded a no-op (signed negative). Now wraps to ~2^64 and scans until cancellation. if (visitor.EndLevelExclusive <= visitor.StartLevelInclusive) return; at entry.

4. BlockTreeSuggestPacer.WaitForQueue — pacer underflow

src/Nethermind/Nethermind.Blockchain/BlockTreeSuggestPacer.cs:58-61

ulong currentHeadNumber = _blockTree.Head?.Number ?? 0;
if (currentBlockNumber - currentHeadNumber > _stopBatchSize && _dbBatchProcessed is null)
{
    _blockNumberReachedToUnlock = currentBlockNumber - _stopBatchSize + _resumeBatchSize;
}

The era1 fix commit (b61f2ef) patched only the import side. The caller-side currentBlockNumber - currentHeadNumber here is still unguarded: when head transiently overtakes the in-flight suggestion (reorg, parallel import advance, post-FCU head update), the subtract wraps and the pacer pauses indefinitely. Cheap fix: currentBlockNumber > currentHeadNumber && currentBlockNumber - currentHeadNumber > _stopBatchSize.


MEDIUM

5. Engine getPayloadBodiesByRangestart + count - 1 overflow

src/Nethermind/Nethermind.Merge.Plugin/Handlers/GetPayloadBodiesByRangeV1Handler.cs:42
ulong end = Math.Min(start + count - 1, headNumber); — spec-valid count is capped at 1024 but the handler does not enforce that before this arithmetic. Validate count first.

6. SSZ CheckedLong is redundant and now a spec deviation

src/Nethermind/Nethermind.Merge.Plugin/SszRest/SszNumericChecks.cs:18 still throws when value > long.MaxValue. It's called from SszExecutionPayload.cs:83/89/95 for BlockNumber/GasLimit/GasUsed setters whose backing fields are now ulong. SSZ spec says uint64; the narrowing rationale is gone. Practically unreachable on mainnet but worth removing.

7. ReceiptMigration tx-index expiry — correct only by wraparound coincidence

src/Nethermind/Nethermind.Init/Steps/Migrations/ReceiptMigration.cs:299

bool txIndexExpired = _receiptConfig.TxLookupLimit != 0 && (long?)(_blockTree.Head?.Number - block.Number) > _receiptConfig.TxLookupLimit;

When block.Number > Head.Number (post-reorg, mid-migration), the ulong subtract wraps and the (long?) cast yields a large negative — preserving the original "false" answer only because the wrap lands in [2^63, 2^64). Make the intent explicit: Head?.Number is ulong h && h > block.Number && h - block.Number > (ulong)TxLookupLimit.

8. SaturatingSub should be shared

src/Nethermind/Nethermind.Evm/GasPolicy/EthereumGasPolicy.cs:205 defines it private. The same a > b ? a - b : 0 ternary is open-coded in TransactionProcessor.cs lines 610, 1347, 1374, 1404, 1428, 1432, 1567, 1612, 1639, 1670 (and others across the diff). Already raised on the original review; flagging it again because finding #2 above is exactly how the next refactor will reintroduce the bug. A Roslyn analyzer that forbids a - b on ulong outside an explicit helper would be the durable fix.

9. CreateAvailableFromIntrinsic raw subtractions rely on upstream validation

EthereumGasPolicy.cs:514,520gasLimit - intrinsicGas.Value - intrinsicGas.StateReservoir and Eip7825Constants.DefaultTxGasLimitCap - intrinsicGas.Value. Both safe by the current call graph (ValidateIntrinsicGas / Eip8037.ExceedsCap), but if a future caller (e.g. eth_call with skipped gas validation) bypasses those checks the underflow is silent. Debug.Assert minimum.

10. EraStore.GetEpochNumberblockNumber - FirstBlock ulong wrap

src/Nethermind/Nethermind.Era1/EraStore.cs:121-125 — previously blockNumber < FirstBlock gave a negative epoch offset that hit GuardMissingEpoch cleanly. Now wraps to a near-MaxValue quotient that may collide with a real epoch key. Explicit if (blockNumber < FirstBlock) throw ….

11. BlockTree.Initializer.BinarySearchBlockNumber Down branch

right = index - 1 wraps to ulong.MaxValue when index == 0. Current call sites use left = 1UL so the failure is unreachable today, but it's a footgun for any future Down caller. Clamp or early-exit.

12. JSON-RPC: negative number → BlockParameter now throws

src/Nethermind/Nethermind.Blockchain/Find/BlockParameter.cs:48
JsonTokenType.Number when !EthereumJsonSerializer.StrictHexFormat => new BlockParameter(reader.GetUInt64()). GetUInt64() throws FormatException on a negative JSON number that GetInt64() would have accepted (and downstream FindHeader(-1) returned null for). Older tooling sending {"blockNumber": -1} now gets a hard error rather than a null result. Worth a changelog line and/or mapping to -32602 rather than 500.

13. RLP strictness change for Tx/Account/Receipt

Nethermind.Serialization.Rlp/TxDecoders/BaseTxDecoder.cs:91 Nonce = DecodeULong() (was DecodeUInt256()); GasLimit = DecodeULong() (was DecodePositiveLong()). Also AccountDecoder.cs:149,234 and ReceiptStorageDecoder.cs:55. EIP-2681 caps nonces at 2^64-1 so this is spec-correct, but existing on-disk receipts written with the (long) encoding may have a different leading-zero pattern. Worth confirming receipt-store round-trip on a real DB before merge.


NIT

  • StateSyncPivot.TrySetNewBestHeader:42-43targetBlockNumber = Math.Max(targetBlockNumber, 0) on a ulong is a no-op; the new comment "target is long for Math.Max and FindHeader" is wrong (it's ulong).
  • PowForwardHeaderProvider.cs:136Math.Max(0UL, Math.Min(...)) dead outer guard; stale comment "Remember, _currentNumber is -1 than what we want."
  • NewPayloadHandler.GetGasChange (Nethermind.Merge.Plugin/Handlers/NewPayloadHandler.cs:99) — emoji bug, ulong subtract is always non-negative, so the up-arrow shows on gas-limit decreases too.
  • EthashDifficultyCalculator.TimeBomb cap at 256 — settled above; the cap matches Geth's effective behavior for any realistic block number.
  • BarrierSyncFeed.cs:38 — read uses (ulong)BarrierWhenStartedMetadataDbKey, writes pass the int unchanged. Same key bytes by accident (small positive ints encode identically); pick one for clarity.
  • Math.Max(0UL, x) no-op pattern in BlockDownloaderTests.cs:112, 361, 362 and similar test files — please scrub in the same pass.
  • EvmPooledMemory.cs:354-356 — Yellow-Paper memory-cost expression (ulong)(N*N>>9) - (ulong)(A*A>>9) is safe by entry assertion; one-line comment to prevent future "simplification."
  • memoryCost == 0L in EthereumGasPolicy.cs:2870L widens against ulong fine; the sibling overload at line 298 uses 0. Pick one.
  • Type drift from earlier inline commentsaccountState.Value.Nonce, test GasLimit/Nonce, AuRaStep, PosdaoTransition, FinalizingBlock, hashSeeds, JsonToEthereumTest direct ulong parsing — don't all appear addressed yet.

What the test suite is missing

CI green here is misleading. Findings #1, #3, #4, #10, #11 all share a shape: original signed code silently no-op'd on out-of-range inputs (negative diff → loop doesn't run), tests were written against that no-op, post-migration the same inputs wrap. A property-based test fuzzing Start > End and Head > Block ranges against the visitor / pacer / migration would surface them.

Recommendation

Block on the 4 HIGH findings (#1#4); #2 is consensus-relevant. The 9 MEDIUM findings are mostly 1-line fixes worth bundling while the diff is hot. Hoist SaturatingSub (#8) before this lands so it becomes the canonical pattern, and add an analyzer rule against open-coded a > b ? a - b : 0 on ulong if the toolchain supports it — that's the one durable defense.

// has the marker atomically with the swap — a crash in between cannot leave
// the new DB live without the floor.
_stateBoundary.OldestStateBlock = stateToCopy;
_stateBoundary.OldestStateBlock = (long)stateToCopy;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

_stateBoundary.OldestStateBlock should be ulong

// Delete old tx index
if (_receiptConfig.TxLookupLimit > 0 && newMain.Number > _receiptConfig.TxLookupLimit.Value)
// safe: TxLookupLimit is checked to be > 0, so it is safe to cast to ulong
if (_receiptConfig.TxLookupLimit > 0 && newMain.Number > (ulong)_receiptConfig.TxLookupLimit.Value)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

_receiptConfig.TxLookupLimit should be ulong?

Task<LevelVisitOutcome> IBlockTreeVisitor.VisitLevelEnd(ChainLevelInfo chainLevelInfo, ulong levelNumber,
CancellationToken cancellationToken)
{
int expectedVisitedBlocksCount = _currentLevel?.BlockInfos.Length ?? 0;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Cast once

Suggested change
int expectedVisitedBlocksCount = _currentLevel?.BlockInfos.Length ?? 0;
ulong expectedVisitedBlocksCount = (ulong)_currentLevel?.BlockInfos.Length ?? 0ul;

private ulong _minBlock = ulong.MaxValue;
private Task _pruningTask = Task.CompletedTask;

public Hash256? GetHash(BlockHeader headBlock, int depth) =>

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
public Hash256? GetHash(BlockHeader headBlock, int depth) =>
public Hash256? GetHash(BlockHeader headBlock, ulong depth) =>

: _flatCache.TryGet(headBlock.ParentHash!, out Hash256[] array) ? array[depth - 2]
: Load(headBlock, depth, out _)?.Hash;

private CacheNode? Load(BlockHeader blockHeader, int depth, out Hash256[]? hashes, CancellationToken cancellationToken = default)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
private CacheNode? Load(BlockHeader blockHeader, int depth, out Hash256[]? hashes, CancellationToken cancellationToken = default)
private CacheNode? Load(BlockHeader blockHeader, ulong depth, out Hash256[]? hashes, CancellationToken cancellationToken = default)

public Hash256? FindHash(long number) => GetBlockHashOnMainOrBestDifficultyHash(number);
public Hash256? FindHash(ulong number) => GetBlockHashOnMainOrBestDifficultyHash(number);

public IOwnedReadOnlyList<BlockHeader> FindHeaders(Hash256? blockHash, int numberOfBlocks, int skip, bool reverse)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Maybe - not sure if better?

Suggested change
public IOwnedReadOnlyList<BlockHeader> FindHeaders(Hash256? blockHash, int numberOfBlocks, int skip, bool reverse)
public IOwnedReadOnlyList<BlockHeader> FindHeaders(Hash256? blockHash, ulong numberOfBlocks, int skip, bool reverse)

result[responseIndex] = current;
responseIndex++;
long nextNumber = startHeader.Number + directionMultiplier * (responseIndex * skip + responseIndex);
long nextNumber = (long)startHeader.Number + directionMultiplier * (responseIndex * skip + responseIndex);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
long nextNumber = (long)startHeader.Number + directionMultiplier * (responseIndex * skip + responseIndex);
ulong nextNumber = startHeader.Number + directionMultiplier * (responseIndex * skip + responseIndex);

Comment on lines +845 to +848
// Guard against underflow: currentNumber is ulong, so only subtract if > 0
BestKnownNumber = currentNumber > 0
? Math.Min(BestKnownNumber, currentNumber - 1)
: 0;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Maybe we should have this guards centralized in some helper methods?

using BatchWrite batch = _chainLevelInfoRepository.StartBatch();

long repairedAbove = ClearStaleMarkersAbove(start.Number, batch);
long repairedAbove = ClearStaleMarkersAbove(start.Number, batch); // start.Number is ulong (BlockHeader)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
long repairedAbove = ClearStaleMarkersAbove(start.Number, batch); // start.Number is ulong (BlockHeader)
long repairedAbove = ClearStaleMarkersAbove(start.Number, batch);

.AssertHeadBlockIs(keys[i], 1);
}

for (int i = 1; i <= 10; i++)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
for (ulong i = 1; i <= 10; i++)

{
bool hasHead = blockTree.Head is not null;
long level = hasHead ? blockTree.Head!.Number + 1 : 0;
long level = hasHead ? (long)blockTree.Head!.Number + 1 : 0;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
long level = hasHead ? (long)blockTree.Head!.Number + 1 : 0;
ulong level = hasHead ? blockTree.Head!.Number + 1 : 0;

// as everything before pivot should be finalized
long blocksAfter = _blockTree.BestKnownNumber - level + 1;
if (blocksAfter >= minSealersForFinalization)
long blocksAfter = (long)_blockTree.BestKnownNumber - (long)level + 1;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
long blocksAfter = (long)_blockTree.BestKnownNumber - (long)level + 1;
ulong blocksAfter = _blockTree.BestKnownNumber + 1 - level;

{
long mod = parentStep - currentStep + emptyStepsCount;
if (mod > 0)
if (parentStep + emptyStepsCount >= currentStep)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Save parentStep + emptyStepsCount to variable?

Comment on lines +135 to +136
StepDurationMilliseconds = (long)stepDuration * millisecondsInSecond;
TransitionTimestampMilliseconds = (long)transitionTimestamp * millisecondsInSecond;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

change StepDurationMilliseconds and TransitionTimestampMilliseconds to ulong?

// more or less at the constant component
// prevents attack on all Nethermind nodes at once
_sealValidationInterval = SealValidationIntervalConstantComponent - 8 + _cryptoRandom.NextInt(16);
_sealValidationInterval = (uint)(SealValidationIntervalConstantComponent - 8 + _cryptoRandom.NextInt(16));

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

SealValidationIntervalConstantComponent - uint?

Comment on lines +89 to +91
&& (orphaned || ValidateBlockNumber(header, parent, ref error))
&& (orphaned || ValidateTimestamp(header, parent, ref error))
&& (orphaned || ValidateGasLimitRange(header, parent, spec, ref error))

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Won't order break any tests?

Comment on lines +213 to +216
protected virtual bool ValidateFieldLimit(BlockHeader blockHeader, ref string? error) =>
// Number, GasLimit and GasUsed are ulong — they can never be negative.
// This method is kept for subclass extensibility; no checks are needed at this level.
true;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Is this overriden anywhere (maybe Arbitrum plugin?) If not remove the whole method.


protected virtual bool ValidateExtraData(BlockHeader header, IReleaseSpec spec, bool isUncle, ref string? error)
{
bool extraDataValid = header.ExtraData.Length <= spec.MaximumExtraDataSize
&& (isUncle
|| _daoBlockNumber is null
|| header.Number < _daoBlockNumber
|| header.Number >= _daoBlockNumber + 10
// Safe cast: DaoBlockNumber is a known small constant (~1.9M), always fits ulong

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
// Safe cast: DaoBlockNumber is a known small constant (~1.9M), always fits ulong

@@ -105,7 +105,7 @@ private static bool IsKin(BlockHeader header, BlockHeader uncle, int relationshi
{
int maxDepth = Math.Min(Math.Min(ancestorsCount, relationshipLevel), (int)header.Number);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Cast early

Suggested change
int maxDepth = Math.Min(Math.Min(ancestorsCount, relationshipLevel), (int)header.Number);
ulong maxDepth = (ulong)Math.Min(Math.Min(ancestorsCount, relationshipLevel), (int)header.Number);

Comment on lines +16 to +17
// Safe cast: ElasticityMultiplier is always a small positive constant (e.g. 2)
adjustedGasLimit *= (ulong)releaseSpec.ElasticityMultiplier;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Make ElasticityMultiplier ulong

return currentMasternodes[currentLeaderIndex];
}

private static bool IsMasternode(EpochSwitchInfo epochInfo, Address node) =>
epochInfo.Masternodes.AsSpan().IndexOf(node) != -1;

// TODO: consider using a another sync indicator
private bool IsSynced() => !_blockTree.IsSyncing().isSyncing && _blockTree.Head is not null;
private bool IsSynced()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Logic change

Comment thread src/Nethermind/Nethermind.Xdc/VotesManager.cs Outdated
Comment thread src/Nethermind/Nethermind.Xdc/VotesManager.cs Outdated
Comment thread src/Nethermind/Nethermind.Xdc/VotesManager.cs Outdated
Comment thread src/Nethermind/Nethermind.Xdc/TimeoutCertificateManager.cs Outdated
Comment thread src/Nethermind/Nethermind.Facade/Find/LogIndexBuilder.cs Outdated
public Hash256 ParentHash { get; set; }
public Hash256 ReceiptsRoot { get; set; }
public Hash256 Sha3Uncles { get; set; }
public byte[]? Signature { get; set; }
public long Size { get; set; }
public Hash256 StateRoot { get; set; }
[JsonConverter(typeof(NullableRawLongConverter))]
public long? Step { get; set; }
[JsonConverter(typeof(NullableRawULongConverter))]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Is it custom converter just for such fields or it's a default one that does not need special mention per property?

Comment thread src/Nethermind/Nethermind.Facade.Test/Eth/EthSyncingInfoTests.cs Outdated
@@ -1373,11 +1377,6 @@ protected virtual CallResult RunByteCode<TTracingInst, TCancelable>(
Revert:
// Return a CallResult indicating a revert.
return new CallResult((byte[])ReturnData, null, shouldRevert: true, exceptionType);

OutOfGas:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Weird thing to remove, why?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

it was likely unreachable code since ulong < 0 is always false

long cost = (newActiveWords - activeWords) * GasCostOf.Memory +
((newActiveWords * newActiveWords) >> 9) -
((activeWords * activeWords) >> 9);
ulong cost = (ulong)(newActiveWords - activeWords) * GasCostOf.Memory +

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Before the change long allowed it to be negative, all here looks a bit dangerous

@flcl42 flcl42 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Focused pass on signed-to-unsigned behavior changes and the EIP-8037 gas-accounting path. I left comments only where the changed line creates a concrete consensus/correctness regression or silently reinterprets negative ranges.

Comment thread src/Nethermind/Nethermind.Evm/GasPolicy/Eip8037BlockGasInclusionCheck.cs Outdated
Comment thread src/Nethermind/Nethermind.Xdc/SubnetPenaltyHandler.cs Outdated
Comment thread src/Nethermind/Nethermind.Xdc/SubnetPenaltyHandler.cs Outdated
Comment thread src/Nethermind/Nethermind.Xdc/EpochSwitchManager.cs Outdated
Comment thread src/Nethermind/Nethermind.Consensus/Stateless/WitnessGeneratingHeaderFinder.cs Outdated
Comment thread src/Nethermind/Nethermind.Init/Steps/Migrations/ReceiptFixMigration.cs Outdated
Comment thread src/Nethermind/Nethermind.Era1/JsonRpc/EraAdminRpcModule.cs Outdated
Comment thread src/Nethermind/Nethermind.EraE/Admin/AdminEraService.cs
Comment thread src/Nethermind/Nethermind.EraE/E2Store/E2StoreReader.cs Outdated
Comment thread src/Nethermind/Nethermind.Serialization.Json/ULongConverter.cs
Dyslex7c and others added 10 commits June 17, 2026 02:38
Conflict resolution highlights:
- BlockTree.cs: keep master's TryUpdateMainChain refactor, apply ulong types
- ISnapshotRepository / PersistenceManager / SnapshotRepository: take master's
  head-ancestor / committed-head tracking, switch new APIs to ulong, keep
  PR's PreGenesis-aware fallback in DetermineSnapshotToPersist and FlushToPersistence
- BlockTreeTestDouble: switch master's new test double to ulong end-to-end
- TestBlockTree / BlockTreeCallSpy: collapse to BlockTreeTestDouble base
- MemoryHintMan: switch master's 1.GiB/32.MB int literals to 1UL.GiB / 32UL.MB
- Other test files: adopt master's structural changes, keep PR ulong types

Build-fixes triggered by the merge (not pre-existing in either branch):
- DebugModuleTests: GetBundleTraces ulong?, MigrateReceipts ulong, BlockParameter(0UL)
- ClearAllColumnsBatchingTests: StateId.PreGenesis sentinel instead of (-1)
- TestingRpcModuleBlockchainTests: WithNonce ulong overload

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…and dead checks

HIGH (consensus / correctness):
- EvmInstructions.Storage SSTORE refund: saturating subtract to prevent the ulong
  wrap that would silently grant the maximum post-cap refund
- StateSyncPivot.Diff: precedence bug returned the absolute number instead of the
  difference; reroute through SaturatingSub
- BlockTree.AcceptVisitor: bail on empty/inverted range instead of looping ~2^64
- BlockTreeSuggestPacer.WaitForQueue: guard the subtract so a head overtake
  doesn't pause the pacer indefinitely
- BinarySearchBlockNumber: bail at index==0 in the Down branch
- WitnessGeneratingHeaderFinder: count-driven loop; old i-- looped forever when
  _lowestRequestedHeader == 0

MEDIUM:
- SaturatingSub promoted to a shared UInt64Extensions; TransactionProcessor and
  EthereumGasPolicy now use it
- SszNumericChecks deleted; ulong fields no longer narrow to long
- ReceiptMigration.tx-index expiry: explicit-guard intent, no more (long?) wrap trick
- EraStore.GetEpochNumber: throw on blockNumber < FirstBlock
- ReceiptFixMigration: SaturatingSub Head?.Number - 2
- EraAdminRpcModule / EraE.AdminEraService: reject negative start/end before
  the (ulong) cast turns -1 into MaxValue
- E2StoreReader: reject negative starting_number from on-disk int64
- BlockParameter JSON Read: TryGetUInt64 path instead of throwing FormatException

Xdc (flcl42):
- SubnetPenaltyHandler.minBlockNumber / startRange: guard underflows before clamp
- EpochSwitchManager.estBlockNum: SaturatingSub before Math.Max
- Eip8037BlockGasInclusionCheck.CalculateBlockRegularGas: saturate the multi-step
  subtraction so a bookkeeping bug doesn't silently blow the block gas limit

Cleanups:
- NewPayloadHandler emoji: explicit if-tree (the < 0 branch was dead on ulong)
- DebugRpcModule.debug_getBlockRlp: remove dead blockNumber >= 0 check
- TraceStorePruner: early-return on block.Number <= _blockToKeep
- XdcRpcModule: dead BlockNumber < 0 checks → BlockNumber is null
- StateSyncPivot.TrySetNewBestHeader: drop the no-op Math.Max(x, 0)
- PowForwardHeaderProvider: drop the no-op outer Math.Max and the stale comment
- BlockDownloaderTests: drop the no-op Math.Max(0UL, …) noise
- SurgeGasPriceOracle.GetAverageGasUsagePerBlock: dead currentBlockNumber >= 0 and
  the unguarded i-- at i==0
- BlockhashStore.GetBlockHashFromState: drop dead requiredBlockNumber < 0
- ISurgeConfig.FeeHistoryBlockCount, L2GasUsageWindowSize, MaxGasLimitRatio:
  int → ulong (eliminates the casts at call sites)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Type changes that eliminate casts at multiple sites:
- HeadersSyncBatch.RequestSize: ulong → int (14 (int) casts dropped in callers;
  one (ulong) cast added in EndNumber). Sync request sizes are inherently small.
- EraPathUtils.Filename (Era1 + EraE): long epoch → ulong epoch (4 cast sites)
- IOpcodeTracingConfig.{StartBlock,EndBlock,RecentBlocks}: long? → ulong?,
  cascading through TraceConfiguration / FromConfig / BlockRangeValidator and
  the recorder's currentChainTip (~12 cast sites)
- ProcessingStats: _chunkBlobs and BlockData.BlobCount long → ulong
- MinBlockInCachePruneStrategyTests / MaxBlockInCachePruneStrategyTests:
  const long → const ulong for PruneBoundary and {Min,Max}BlockFromPersisted

Per-site cleanups:
- BlockHeaderTests: hex literals (0x2fefbaUL) instead of
  (ulong)Bytes.FromHexString(...).ToUnsignedBigInteger()
- Int64Extensions.ToLongFromBigEndianByteArrayWithoutLeadingZeros: cast
  internally once so call sites drop their (long) cast
- ProcessedTransactionsDbCleaner: switch to ToULongFromBigEndian... directly
- SyncConfig / RocksDbConfigFactoryTests / SyncPeerProtocolHandlerBase:
  N.MB / N.MiB ulong-extension forms drop the outer (ulong) cast
- BlockAccessListsSyncFeedTests: ToULongFromBigEndian... directly

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…nt cleanups

- Nethermind.Stateless.Executor.csproj: drop link to deleted SszRest/SszNumericChecks.cs
- Nethermind.OpcodeTracing.Plugin/OpcodeTracingConfig.cs: restore space lost in
  long?→ulong? replace_all
- Nethermind.Taiko.Test/SurgeGasPriceOracleTests.cs: drop unused System using
  left over after Math.Max(0UL, …) removal

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The PR's Validate signature dropped the intrinsicRegular/intrinsicState
subtractions, making the worst-case dimension calculation strictly
match `min(TX_MAX_GAS_LIMIT, tx.gas)` and `tx.gas`. Master subtracts the
opposite-dimension intrinsic before clamping, yielding a smaller worst-
case that lets txs through when one dimension is tight but the tx's
intrinsic mass lives in the other dimension. The Amsterdam test
`test_block_2d_gas_valid_when_cumulative_exceeds_limit` fails under the
PR's stricter check; master is green.

Port master's behavior to the PR's ulong signature using SaturatingSub
for the txGas - intrinsic{State,Regular} subtractions. Also revert the
CalculateBlockRegularGas + Storage SSTORE Refund SaturatingSub changes
back to raw subtracts so they match the PR's tested behavior.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ormula

The PR-added unit / integration tests asserted the "raw tx.gas" inclusion
predicate (worst-case state = tx.gas, no intrinsicRegular subtraction).
That contradicts the Pyspec fixture
`tests/amsterdam/eip8037_state_creation_gas_cost_increase/.../test_block_2d_gas_valid_when_cumulative_exceeds_limit`,
which passes on master because master's Validate subtracts the opposite-
dimension intrinsic before clamping. Drop the two PR-added cases that
required the raw-tx.gas predicate; rewrite the parameterized Boundary_state
case to assert the worst-case-after-intrinsic-subtract semantics.

- Eip8037BlockGasInclusionCheckTests: drop Creation_tx_regular_check_without_subtraction_rejects;
  update Boundary_state deltas so the rejection lands when (tx.gas - intrinsicRegular) > stateAvailable
- Eip8037BlockGasIntegrationTests: drop Eip8037_creation_tx_regular_check_without_subtraction_rejects

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
HIGH:
- BeaconPivot.GetLowestBlockToFinalize: guard `Head.Number - MaxDepth + 1` —
  wrapped to a near-ulong.MaxValue safe number when head < 64.
- Optimism BatchV1.DecodeTxs: drop (ulong) cast on UInt256 tx values; the
  Transaction.Value / DecodedMaxFeePerGas fields are UInt256 and the cast
  silently truncated any tx > ~18.4 ETH.
- EthereumGasPolicy.CreateAvailableFromIntrinsic: Debug.Assert on the two
  unguarded subtracts (gasLimit - intrinsicRegular - intrinsicState and
  TX_MAX_GAS_LIMIT - intrinsicRegular). Callers validate today; this catches
  future eth_call paths that skip validation.
- EvmInstructions.Storage SSTORE refund: Debug.Assert + SaturatingSub on
  `Refund -= sClearRefunds`. Per EIP-2200/3529 the matching `+= sClearRefunds`
  ran earlier so the invariant holds; the saturating subtract guards against
  an out-of-order wrap silently granting the maximum post-cap refund.
- ReceiptMigration.MigrationPointerTracker.ReportCompleted: guard
  `_nextToConfirm--` at 0 so a fully-migrated chain doesn't wrap the
  tracker into ulong.MaxValue. Reports `MigratedBlockNumber = 0` when
  genesis is reached.

MEDIUM:
- VmState.CommitToParent: `checked` the parentState.Refund += Refund. Gas-
  bounded so unreachable in practice, but the unsigned add no longer caught
  a buggy negative-child propagation that master's signed long would have.
- Eip8037BlockGasInclusionCheck.Validate: early-return RegularDimensionExceeded
  / StateDimensionExceeded when cumulative > limit. The prior SaturatingSub
  silently floored to 0 and the worst-case check could falsely succeed.
- CompactionSchedule.NextFullCompactionAfter: clamp `from + distance` at
  ulong.MaxValue. Unreachable at any realistic chain height; explicit.
- AuRaBlockFinalizationManager.LoadInitialLastFinalizedBlockLevel and
  GetFinalizationLevel: drop the long round-trip and use ulong arithmetic
  with explicit zero/underflow guards. Avoids the 2^63-1 wrap that was only
  theoretical but exposed by the signed→unsigned migration.

NIT:
- BlockTree.GetBlockHashOnMainOrBestDifficultyHash: drop dead `blockNumber < 0`
  check on ulong parameter.
- ReceiptsSyncFeed: drop stale "_barrier is long" comment (now ulong).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
# Conflicts:
#	src/Nethermind/Nethermind.AuRa.Test/Validators/MultiValidatorTests.cs
#	src/Nethermind/Nethermind.Blockchain/IBlockFinalizationManager.cs
#	src/Nethermind/Nethermind.Blockchain/ManualFinalizationManager.cs
#	src/Nethermind/Nethermind.Core.Test/Builders/BlockTreeTestDouble.cs
#	src/Nethermind/Nethermind.Merge.AuRa/AuRaMergeFinalizationManager.cs
#	src/Nethermind/Nethermind.Merge.Plugin.Test/MergeFinalizationManagerTests.cs
#	src/Nethermind/Nethermind.Merge.Plugin/MergeFinalizationManager.cs
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Unify types with Geth

5 participants