Skip to content

Commit 769e6d3

Browse files
authored
Merge branch 'master' into evm-stream
2 parents eac39a2 + 22be4a4 commit 769e6d3

38 files changed

Lines changed: 270 additions & 158 deletions

File tree

src/Nethermind/Nethermind.Core.Test/Modules/FixedIpResolver.cs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ namespace Nethermind.Core.Test.Modules;
1111

1212
public class FixedIpResolver(INetworkConfig networkConfig) : IIPResolver
1313
{
14-
public IPAddress LocalIp => IPAddress.Parse(networkConfig.LocalIp!);
15-
public IPAddress ExternalIp => IPAddress.Parse(networkConfig.ExternalIp!);
16-
public Task Initialize(CancellationToken cancellationToken = default) => Task.CompletedTask;
14+
public ValueTask<IIPResolver.NethermindIp> Resolve(CancellationToken cancellationToken = default) =>
15+
new(new IIPResolver.NethermindIp(
16+
networkConfig.LocalIp is null ? IPAddress.Loopback : IPAddress.Parse(networkConfig.LocalIp),
17+
networkConfig.ExternalIp is null ? IPAddress.None : IPAddress.Parse(networkConfig.ExternalIp)));
1718
}

src/Nethermind/Nethermind.Core/Crypto/KeccakCache.cs

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -40,39 +40,37 @@ public static unsafe class KeccakCache
4040
private const int InputLengthOfAddress = Address.Size;
4141
private const int CacheLineSizeBytes = 64;
4242

43-
#if ZK_EVM
44-
// zkEVM: avoid NativeMemory.AlignedAlloc (can fault in some environments). Use managed pinned storage instead.
45-
private static readonly byte[] ManagedBuffer;
46-
private static readonly GCHandle ManagedHandle;
47-
#endif
43+
#if !ZK_EVM
4844
private static readonly Entry* Memory;
4945

5046
static KeccakCache()
5147
{
5248
const nuint size = Count * Entry.Size;
5349

54-
#if ZK_EVM
55-
ManagedBuffer = GC.AllocateArray<byte>((int)size, pinned: true);
56-
ManagedHandle = GCHandle.Alloc(ManagedBuffer, GCHandleType.Pinned);
57-
Memory = (Entry*)ManagedHandle.AddrOfPinnedObject();
58-
#else
5950
// Aligned, so that no torn reads if fields of Entry are properly aligned.
6051
Memory = (Entry*)NativeMemory.AlignedAlloc(size, BitOperations.RoundUpToPowerOf2(Entry.Size));
6152
NativeMemory.Clear(Memory, size);
6253
GC.AddMemoryPressure((long)size);
63-
#endif
6454
}
55+
#endif
6556

6657
[SkipLocalsInit]
6758
public static ValueHash256 Compute(ReadOnlySpan<byte> input)
6859
{
60+
#if ZK_EVM
61+
return input.IsEmpty ? ValueKeccak.OfAnEmptyString : ValueKeccak.Compute(input);
62+
#else
6963
ComputeTo(input, out ValueHash256 keccak256);
7064
return keccak256;
65+
#endif
7166
}
7267

7368
[SkipLocalsInit]
7469
public static void ComputeTo(ReadOnlySpan<byte> input, out ValueHash256 keccak256)
7570
{
71+
#if ZK_EVM
72+
keccak256 = input.IsEmpty ? ValueKeccak.OfAnEmptyString : ValueKeccak.Compute(input);
73+
#else
7674
// Special cases jump forward as unpredicted
7775
if (input.Length is 0 or > Entry.MaxPayloadLength)
7876
{
@@ -207,6 +205,7 @@ public static void ComputeTo(ReadOnlySpan<byte> input, out ValueHash256 keccak25
207205

208206
Uncommon:
209207
keccak256 = input.Length == 0 ? ValueKeccak.OfAnEmptyString : ValueKeccak.Compute(input);
208+
#endif
210209
}
211210

212211
/// <summary>

src/Nethermind/Nethermind.Evm.Precompiles/zkevm/ModExpPrecompile.cs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,14 @@ public partial Result<byte[]> Run(ReadOnlyMemory<byte> inputData, IReleaseSpec r
3030
uint expStart = expOffset > uint.MaxValue ? uint.MaxValue : (uint)expOffset;
3131
uint modulusStart = modulusOffset > uint.MaxValue ? uint.MaxValue : (uint)modulusOffset;
3232

33-
ReadOnlySpan<byte> @base = inputSpan.SliceWithZeroPaddingEmptyOnError(96U, baseLength);
34-
ReadOnlySpan<byte> exp = inputSpan.SliceWithZeroPaddingEmptyOnError(expStart, expLength);
3533
ReadOnlySpan<byte> modulus = inputSpan.SliceWithZeroPaddingEmptyOnError(modulusStart, modulusLength);
3634
byte[] result = new byte[modulusLength];
3735

36+
if (modulus.IsEmpty || modulus.IndexOfAnyExcept((byte)0) < 0)
37+
return result;
38+
39+
ReadOnlySpan<byte> @base = inputSpan.SliceWithZeroPaddingEmptyOnError(96U, baseLength);
40+
ReadOnlySpan<byte> exp = inputSpan.SliceWithZeroPaddingEmptyOnError(expStart, expLength);
3841
Accelerators.ModExp(@base, exp, modulus, result);
3942

4043
return result;
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// SPDX-FileCopyrightText: 2026 Demerzel Solutions Limited
2+
// SPDX-License-Identifier: LGPL-3.0-only
3+
4+
using Nethermind.Specs;
5+
using NUnit.Framework;
6+
7+
namespace Nethermind.Evm.Test;
8+
9+
public class StackUnderflowRegressionTests : VirtualMachineTestsBase
10+
{
11+
protected override long BlockNumber => MainnetSpecProvider.ParisBlockNumber;
12+
protected override ulong Timestamp => MainnetSpecProvider.CancunBlockTimestamp;
13+
14+
// Each case leaves the stack exactly one item short of what the opcode's converted pop needs:
15+
// the preceding pops succeed, then the value/topic/salt pop underflows.
16+
private static readonly object[] UnderflowCases =
17+
[
18+
new object[] { Instruction.BYTE, Prepare.EvmCode.PushData(0).Op(Instruction.BYTE).Done },
19+
new object[] { Instruction.SSTORE, Prepare.EvmCode.PushData(0).Op(Instruction.SSTORE).Done },
20+
new object[] { Instruction.TSTORE, Prepare.EvmCode.PushData(0).Op(Instruction.TSTORE).Done },
21+
new object[] { Instruction.LOG1, Prepare.EvmCode.PushData(0).PushData(0).Op(Instruction.LOG1).Done },
22+
new object[] { Instruction.CREATE2, Prepare.EvmCode.PushData(0).PushData(0).PushData(0).Op(Instruction.CREATE2).Done },
23+
// Index 0 is in-range, so SIGNEXTEND skips the out-of-range short-circuit and peeks the missing value.
24+
new object[] { Instruction.SIGNEXTEND, Prepare.EvmCode.PushData(0).Op(Instruction.SIGNEXTEND).Done },
25+
];
26+
27+
[TestCaseSource(nameof(UnderflowCases))]
28+
public void Signals_stack_underflow_when_final_operand_missing(Instruction opcode, byte[] code)
29+
{
30+
TestAllTracerWithOutput result = Execute(code);
31+
Assert.That(result.Error, Is.EqualTo(EvmExceptionType.StackUnderflow.ToString()), opcode.ToString());
32+
}
33+
}

src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Create.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,8 @@ public static EvmExceptionType InstructionCreate<TGasPolicy, TOpCreate, TTracing
101101
// For CREATE2, an extra salt value is required. Use type check to differentiate.
102102
if (typeof(TOpCreate) == typeof(OpCreate2))
103103
{
104-
salt = stack.PopWord256();
104+
if (!stack.PopWord256(out salt))
105+
goto StackUnderflow;
105106
}
106107

107108
// EIP-3860: Limit the maximum size of the initialization code.

src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Math1Param.cs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,8 @@ public static EvmExceptionType InstructionByte<TGasPolicy, TTracingInst>(Virtual
130130
// Pop the byte position and the 256-bit word.
131131
if (!stack.PopUInt256(out UInt256 a))
132132
goto StackUnderflow;
133-
Span<byte> bytes = stack.PopWord256();
133+
if (!stack.PopWord256(out Span<byte> bytes))
134+
goto StackUnderflow;
134135

135136
// If the position is out-of-range, push zero. Using direct limb access avoids the
136137
// full 256-bit vector compare + defensive `in` copy the JIT emits for `a >= BigInt32`,
@@ -174,7 +175,11 @@ public static EvmExceptionType InstructionSignExtend<TGasPolicy>(VirtualMachine<
174175
int position = 31 - (int)a;
175176

176177
// Peek at the 256-bit word without removing it.
177-
Span<byte> bytes = stack.PeekWord256();
178+
ref byte bytesRef = ref stack.PeekBytesByRef();
179+
if (IsNullRef(ref bytesRef))
180+
goto StackUnderflow;
181+
182+
Span<byte> bytes = MemoryMarshal.CreateSpan(ref bytesRef, EvmStack.WordSize);
178183
sbyte sign = (sbyte)bytes[position];
179184

180185
// Extend the sign by replacing higher-order bytes.

src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Stack.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1103,7 +1103,8 @@ public static EvmExceptionType InstructionLog<TGasPolicy, TOpCount>(VirtualMachi
11031103
Hash256[] topics = new Hash256[topicsCount];
11041104
for (int i = 0; i < topics.Length; i++)
11051105
{
1106-
topics[i] = new Hash256(stack.PopWord256());
1106+
if (!stack.PopWord256(out Span<byte> topic)) goto StackUnderflow;
1107+
topics[i] = new Hash256(topic);
11071108
}
11081109

11091110
// Create a new log entry with the executing account, log data, and topics.

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

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ public static EvmExceptionType InstructionTStore<TGasPolicy>(VirtualMachine<TGas
104104
StorageCell storageCell = new(vmState.Env.ExecutingAccount, in result);
105105

106106
// Pop the 32-byte value from the stack.
107-
Span<byte> bytes = stack.PopWord256();
107+
if (!stack.PopWord256(out Span<byte> bytes)) goto StackUnderflow;
108108

109109
// Store either the actual value (if non-zero) or a predefined zero constant.
110110
vm.WorldState.SetTransientState(in storageCell, !bytes.IsZero() ? bytes.ToArray() : BytesZero32);
@@ -370,7 +370,8 @@ internal static EvmExceptionType InstructionSStoreUnmetered<TGasPolicy, TTracing
370370

371371
// Pop the key and then the new value for storage; signal underflow if unavailable.
372372
if (!stack.PopUInt256(out UInt256 result)) goto StackUnderflow;
373-
ReadOnlySpan<byte> bytes = stack.PopWord256();
373+
if (!stack.PopWord256(out Span<byte> bytesSpan)) goto StackUnderflow;
374+
ReadOnlySpan<byte> bytes = bytesSpan;
374375

375376
// Determine if the new value is effectively zero and normalize non-zero values by stripping leading zeros.
376377
bool newIsZero = bytes.IsZero();
@@ -484,7 +485,8 @@ internal static EvmExceptionType InstructionSStoreMetered<TGasPolicy, TTracingIn
484485

485486
// Pop the key and then the new value for storage; signal underflow if unavailable.
486487
if (!stack.PopUInt256(out UInt256 result)) goto StackUnderflow;
487-
ReadOnlySpan<byte> bytes = stack.PopWord256();
488+
if (!stack.PopWord256(out Span<byte> bytesSpan)) goto StackUnderflow;
489+
ReadOnlySpan<byte> bytes = bytesSpan;
488490

489491
// Determine if the new value is effectively zero and normalize non-zero values by stripping leading zeros.
490492
bool newIsZero = bytes.IsZero();

src/Nethermind/Nethermind.Evm/VirtualMachine.zkevm.cs

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,25 @@ public unsafe partial class VirtualMachine<TGasPolicy> where TGasPolicy : struct
1111
{
1212
private delegate*<VirtualMachine<TGasPolicy>, ref EvmStack, ref TGasPolicy, ref int, EvmExceptionType>[] _opcodeMethods;
1313

14-
// For tracing-enabled execution, generate (if necessary) and cache the traced opcode set.
15-
private partial void PrepareOpcodes<TTracingInst>(IReleaseSpec spec) where TTracingInst : struct, IFlag =>
16-
_opcodeMethods = (delegate*<VirtualMachine<TGasPolicy>, ref EvmStack, ref TGasPolicy, ref int, EvmExceptionType>[])(spec.EvmInstructionsTraced ??= GenerateOpCodes<TTracingInst>(spec));
14+
// Select and lazily build the opcode dispatch table for the active tracing mode, caching each
15+
// mode separately on the spec. Mirrors the std build minus its periodic PGO-driven cache refresh,
16+
// which is moot for the AOT-compiled guest.
17+
private partial void PrepareOpcodes<TTracingInst>(IReleaseSpec spec) where TTracingInst : struct, IFlag
18+
{
19+
if (!TTracingInst.IsActive)
20+
{
21+
_opcodeMethods =
22+
(delegate*<VirtualMachine<TGasPolicy>, ref EvmStack, ref TGasPolicy, ref int, EvmExceptionType>[])
23+
(spec.EvmInstructionsNoTrace ??= GenerateOpCodes<TTracingInst>(spec));
24+
}
25+
else
26+
{
27+
_opcodeMethods =
28+
(delegate*<VirtualMachine<TGasPolicy>, ref EvmStack, ref TGasPolicy, ref int, EvmExceptionType>[])
29+
(spec.EvmInstructionsTraced ??= GenerateOpCodes<TTracingInst>(spec));
30+
}
31+
}
1732

1833
protected delegate*<VirtualMachine<TGasPolicy>, ref EvmStack, ref TGasPolicy, ref int, EvmExceptionType>[] GenerateOpCodes<TTracingInst>(IReleaseSpec spec) where TTracingInst : struct, IFlag =>
19-
EvmInstructions.GenerateOpCodes<TGasPolicy, OffFlag>(spec);
34+
EvmInstructions.GenerateOpCodes<TGasPolicy, TTracingInst>(spec);
2035
}

src/Nethermind/Nethermind.HealthChecks.Test/HealthChecksWebhookInfoTests.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33

44
using System.Net;
55
using System;
6+
using System.Threading;
7+
using System.Threading.Tasks;
68
using NSubstitute;
79
using NUnit.Framework;
810
using Nethermind.Monitoring.Config;
@@ -19,7 +21,8 @@ public void HealthChecksWebhookInfo_returns_expected_results()
1921

2022
IIPResolver ipResolver = Substitute.For<IIPResolver>();
2123
byte[] ip = { 1, 2, 3, 4 };
22-
ipResolver.ExternalIp.Returns(new IPAddress(ip));
24+
ipResolver.Resolve(Arg.Any<CancellationToken>())
25+
.Returns(new ValueTask<IIPResolver.NethermindIp>(new IIPResolver.NethermindIp(IPAddress.Loopback, new IPAddress(ip))));
2326

2427
IMetricsConfig metricsConfig = new MetricsConfig() { NodeName = "nodeName" };
2528

0 commit comments

Comments
 (0)