Skip to content
Merged
Show file tree
Hide file tree
Changes from 69 commits
Commits
Show all changes
158 commits
Select commit Hold shift + click to select a range
ce5f612
Initial commit
svlachakis Mar 11, 2026
722f9ba
namespace
svlachakis Mar 11, 2026
0761875
more work
svlachakis Mar 11, 2026
683e51f
E2StoreReader verification & Validator correctness audit & Historical…
svlachakis Mar 12, 2026
14a9f87
AccumulatorCalculator.GetProof & EraWriter proof entries
svlachakis Mar 12, 2026
b74b468
Post-merge beacon roots & EraReader Validator integration
svlachakis Mar 12, 2026
1db4d61
Post-merge beacon roots & EraReader Validator integration
svlachakis Mar 12, 2026
5f65d65
EraExporter.DoExport — epoch boundary
svlachakis Mar 12, 2026
7968c1e
EraWriter.Add fixes & tests
svlachakis Mar 12, 2026
b881bb0
more fixes & tests
svlachakis Mar 12, 2026
553c31f
Tests & Wiring
svlachakis Mar 12, 2026
01090c2
Improvements
svlachakis Mar 12, 2026
1e25ca5
fixes
svlachakis Mar 12, 2026
14dce4c
Era1 & EraE file format cross tests
svlachakis Mar 12, 2026
b50b22c
Merge remote-tracking branch 'origin/master' into feature/erae
svlachakis Mar 12, 2026
01a9c02
slnx fix
svlachakis Mar 12, 2026
193a0dd
fixes
svlachakis Mar 12, 2026
992d955
step module update
svlachakis Mar 12, 2026
9d1b4f7
Merge remote-tracking branch 'origin/master' into feature/erae
svlachakis Mar 12, 2026
37294f4
format
svlachakis Mar 12, 2026
0049c82
revert evm format changes
svlachakis Mar 12, 2026
5be4c07
revert packages.lock.json
svlachakis Mar 12, 2026
2273508
fix header
svlachakis Mar 12, 2026
6970ac0
add erae to cspell.json
svlachakis Mar 12, 2026
52369c6
packages.lock.json
svlachakis Mar 12, 2026
8979223
Config properties (RemoteBaseUrl, RemoteDownloadDirectory, RemoteChe…
svlachakis Mar 12, 2026
bae6429
IRemoteEraClient + HttpRemoteEraClient
svlachakis Mar 12, 2026
2e486c7
RemoteEraStoreDecorator
svlachakis Mar 12, 2026
c1ba449
Wire in EraStoreFactory and EraEModule & Unit tests
svlachakis Mar 12, 2026
651fdd6
Integration tests with data.ethpandaops.io
svlachakis Mar 12, 2026
cc877ac
Fix 1 — receipt decoder:
svlachakis Mar 12, 2026
862a0f8
ethpandaops to cspell.json
svlachakis Mar 13, 2026
776f8fa
Merge branch 'master' into feature/erae
svlachakis Mar 13, 2026
12cecb8
Merge remote-tracking branch 'origin/master' into feature/erae
svlachakis Mar 13, 2026
57e6c64
- Validator:
svlachakis Mar 13, 2026
a161557
Refactoring and cleanup
svlachakis Mar 13, 2026
e0ccf54
fix build
svlachakis Mar 13, 2026
96c62d5
Refactor EraE infrastructure and remove duplicated logic
svlachakis Mar 13, 2026
758eba5
Merge branch 'master' into feature/erae
svlachakis Mar 13, 2026
4e70c88
Performance, async safety, and cleanup improvements
svlachakis Mar 13, 2026
39d4e94
Improve epoch verification concurrency and standardize AdminEraServic…
svlachakis Mar 14, 2026
60f5148
remove hot-path allocations in proof verification and epoch writing
svlachakis Mar 14, 2026
ba33d01
style - record BlockHeaderProof, remove XML docs, trim redundant comm…
svlachakis Mar 14, 2026
f69b42f
format
svlachakis Mar 14, 2026
fc78693
HasEpoch, EraJobRunner fire-and-forget helper, cleanup
svlachakis Mar 14, 2026
9a99c58
named tuples, SectionName, shared TestEraFile, for-loop workers, cleanup
svlachakis Mar 14, 2026
370983e
cspell.json
svlachakis Mar 15, 2026
b3d0c3f
comments
svlachakis Mar 15, 2026
a816bc9
cleanup
svlachakis Mar 15, 2026
0e26177
concurrency fixes & comments
svlachakis Mar 15, 2026
e50ef6a
Merge branch 'master' into feature/erae
svlachakis Mar 15, 2026
21324e9
postmerge blocks tests
svlachakis Mar 16, 2026
ae6f8b5
Merge branch 'master' into feature/erae
svlachakis Mar 16, 2026
ca19796
eraimport autocreate directory
svlachakis Mar 16, 2026
927ed3c
Merge remote-tracking branch 'origin/master' into feature/erae
svlachakis Mar 16, 2026
d24c60a
throw error when prunning & erae import is set simultaneously
svlachakis Mar 16, 2026
e083bc8
fixes
svlachakis Mar 16, 2026
abed488
fixes
svlachakis Mar 16, 2026
e92f393
throw specific exception when eraE fails with block bodies not found …
svlachakis Mar 17, 2026
c0989c8
Merge branch 'master' into feature/erae
svlachakis Mar 17, 2026
40b8e5c
Merge branch 'master' into feature/erae
svlachakis Mar 17, 2026
489798b
Merge branch 'master' into feature/erae
svlachakis Mar 17, 2026
ad07dda
Merge branch 'master' into feature/erae
svlachakis Mar 17, 2026
28a7159
BlockTreeInsertHeaderOptions fix
svlachakis Mar 18, 2026
dd297ba
Merge remote-tracking branch 'origin/feature/erae' into feature/erae
svlachakis Mar 18, 2026
b8c3374
else if branch - when a block already exists but it's pre-merge with …
svlachakis Mar 18, 2026
0bc941c
less strict condition
svlachakis Mar 18, 2026
c1ce4e3
postmerge blocks fix
svlachakis Mar 18, 2026
ab1953b
Merge branch 'master' into feature/erae
svlachakis Mar 18, 2026
193a661
fix filename for postmerge
svlachakis Mar 18, 2026
54be5f8
fix filename
svlachakis Mar 18, 2026
cfbe793
snappy change - proofs comment out - possible revert
svlachakis Mar 18, 2026
80dc370
revert custom SnappyFrameWritter
svlachakis Mar 19, 2026
de8cea5
Merge branch 'master' into feature/erae
svlachakis Mar 22, 2026
418467d
Merge branch 'master' into feature/erae
svlachakis Mar 27, 2026
47b3aa4
damian review comments
svlachakis Mar 27, 2026
1559f37
ssz fix
svlachakis Mar 27, 2026
cef6866
Marc PR review
svlachakis Mar 27, 2026
a08df5d
claude review
svlachakis Mar 27, 2026
24fbe67
Merge branch 'master' into feature/erae
svlachakis Mar 27, 2026
8f89d80
remove proofs logic
svlachakis Mar 27, 2026
18fb8cd
Merge branch 'master' into feature/erae
svlachakis Mar 27, 2026
65ed675
Merge branch 'master' into feature/erae
svlachakis Mar 27, 2026
31e47e5
Merge branch 'master' into feature/erae
svlachakis Mar 27, 2026
23acc49
Merge branch 'master' into feature/erae
svlachakis Mar 30, 2026
f7a24c7
Merge branch 'master' into feature/erae
svlachakis Mar 30, 2026
6b06613
fixes
svlachakis Mar 30, 2026
628b59a
add EraE module to CODEOWNERS
svlachakis Mar 30, 2026
14835f5
fixes
svlachakis Mar 30, 2026
df70629
maxsize defensive check
svlachakis Mar 30, 2026
1b421af
fix remote url
svlachakis Mar 30, 2026
9544749
Merge branch 'master' into feature/erae
svlachakis Mar 30, 2026
a314978
Merge branch 'master' into feature/erae
svlachakis Mar 31, 2026
863cddb
era exporter improvements
svlachakis Mar 31, 2026
1c5ce72
memory tweaks
svlachakis Mar 31, 2026
a4b376b
stream epoch data to disk in Add() instead of buffering to eliminate …
svlachakis Mar 31, 2026
ffc8742
build fix
svlachakis Mar 31, 2026
a6ea3ec
cache
svlachakis Mar 31, 2026
8eca363
reduce memory footprint, remove temp file approach
svlachakis Mar 31, 2026
37fe096
receipts fix
svlachakis Mar 31, 2026
d84c80d
receipts fix
svlachakis Mar 31, 2026
38e1618
revert era reader
svlachakis Apr 1, 2026
0516ac8
Merge branch 'master' into feature/erae
svlachakis Apr 1, 2026
dd15cc4
fix importer
svlachakis Apr 1, 2026
5edfde8
fix test format
svlachakis Apr 1, 2026
077e0c5
different approach for memory efficiency
svlachakis Apr 1, 2026
40d19d6
prefetch implementation
svlachakis Apr 1, 2026
6b34142
revert prefetch
svlachakis Apr 1, 2026
5510099
export receipts guard
svlachakis Apr 2, 2026
25c5100
slimreceiptdecoder changes, erawriter fixes
svlachakis Apr 2, 2026
40b3fca
Merge branch 'master' into feature/erae
svlachakis Apr 2, 2026
222e7ea
fix build
svlachakis Apr 2, 2026
791b25c
remove orphaned block validation
svlachakis Apr 2, 2026
7decc86
geth compatibility
svlachakis Apr 5, 2026
6a062b5
unused import
svlachakis Apr 5, 2026
39c3a7d
delete nm format
svlachakis Apr 5, 2026
5c4f54e
Merge branch 'master' into feature/erae
svlachakis Apr 6, 2026
3368e41
add checksums.txt
svlachakis Apr 7, 2026
bcb4c10
minor renames
svlachakis Apr 7, 2026
11795d5
Merge branch 'master' into feature/erae
svlachakis Apr 8, 2026
0bed96e
claude review
svlachakis Apr 8, 2026
f7d1a58
Merge remote-tracking branch 'origin/feature/erae' into feature/erae
svlachakis Apr 8, 2026
93090f6
fix build
svlachakis Apr 8, 2026
b643035
tests refactoring
svlachakis Apr 8, 2026
e4ca580
Merge branch 'master' into feature/erae
svlachakis Apr 8, 2026
b3d1229
claude review
svlachakis Apr 8, 2026
d035f69
fix 2026 on spdx
svlachakis Apr 8, 2026
aed3bda
logger wiring
svlachakis Apr 8, 2026
7fd1690
Merge remote-tracking branch 'origin/master' into feature/erae
svlachakis Apr 13, 2026
a833987
merge conflicts
svlachakis Apr 13, 2026
c192380
Merge branch 'master' into feature/erae
svlachakis Apr 13, 2026
b9f8f7e
build fix
svlachakis Apr 13, 2026
ac1d1d1
Merge remote-tracking branch 'origin/feature/erae' into feature/erae
svlachakis Apr 13, 2026
f73d71e
build fixes
svlachakis Apr 13, 2026
dbb6b11
more build fixes
svlachakis Apr 13, 2026
b208a4f
fix races
svlachakis Apr 13, 2026
94a5d4e
Merge branch 'master' into feature/erae
svlachakis Apr 13, 2026
271919c
remove cache
svlachakis Apr 13, 2026
b73c1dc
use Polly
svlachakis Apr 13, 2026
883e789
Merge remote-tracking branch 'origin/master' into feature/erae
svlachakis Apr 14, 2026
11ff346
fix build
svlachakis Apr 14, 2026
3e16707
fix build
svlachakis Apr 14, 2026
8e3fb94
fix build
svlachakis Apr 14, 2026
3e97eb6
Merge remote-tracking branch 'origin/master' into feature/erae
svlachakis Apr 15, 2026
3393efa
Merge remote-tracking branch 'origin/master' into feature/erae
svlachakis Apr 15, 2026
7591f0f
fix conflicts
svlachakis Apr 15, 2026
7eb5ff7
Merge branch 'master' into feature/erae
svlachakis Apr 15, 2026
11cd694
ssz changes
svlachakis Apr 15, 2026
19689d9
fix build
svlachakis Apr 15, 2026
83ad666
unused import
svlachakis Apr 15, 2026
3674eba
ssz fixes
svlachakis Apr 15, 2026
ad940b9
claude review
svlachakis Apr 15, 2026
bc2a55d
Merge branch 'master' into feature/erae
svlachakis Apr 15, 2026
684a914
EraE refactor: simplify interfaces, deduplicate tests (#11165)
LukaszRozmej Apr 16, 2026
a7f0751
Fix BeaconApiRetry maxAttempts semantics, use Span SequenceEqual
LukaszRozmej Apr 16, 2026
0d803dd
Seal concrete classes, make HistoricalSummary readonly struct
LukaszRozmej Apr 16, 2026
c6cf536
Fix dispose leak, field-to-property, thread CancellationToken
LukaszRozmej Apr 16, 2026
70eb16c
refactor
LukaszRozmej Apr 16, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions src/Nethermind/Nethermind.Core/BlockHeaderProof.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited
Comment thread
svlachakis marked this conversation as resolved.
Outdated
// SPDX-License-Identifier: LGPL-3.0-only
using Nethermind.Core.Crypto;

namespace Nethermind.Core;

public enum BlockHeaderProofType : byte
{
BlockProofHistoricalHashesAccumulator = 0,
BlockProofHistoricalRoots = 1,
BlockProofHistoricalSummaries = 2
}

public record BlockHeaderProof
{
public BlockHeaderProofType? ProofType { get; init; }
public ValueHash256[]? HashesAccumulator { get; init; }
public ValueHash256[]? BeaconBlockProof { get; init; }
public ValueHash256[]? ExecutionBlockProof { get; init; }
public ValueHash256? BeaconBlockRoot { get; init; }
public long? Slot { get; init; }
}
62 changes: 59 additions & 3 deletions src/Nethermind/Nethermind.Era1/AccumulatorCalculator.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
// SPDX-FileCopyrightText: 2023 Demerzel Solutions Limited
// SPDX-License-Identifier: LGPL-3.0-only

using System.Buffers.Binary;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using Nethermind.Core.Collections;
using Nethermind.Core.Crypto;
using Nethermind.Int256;
Expand All @@ -10,16 +12,21 @@
namespace Nethermind.Era1;

// https://github.com/ethereum/portal-network-specs/blob/master/history/history-network.md#algorithms
internal class AccumulatorCalculator : IDisposable
public class AccumulatorCalculator : IDisposable
{
private const int TreeDepth = 13; // log2(8192)
private const int ProofLength = 15; // 1 (HeaderRecord field) + 13 (tree) + 1 (length mixin)

private readonly ArrayPoolList<ReadOnlyMemory<byte>> _roots = new(EraWriter.MaxEra1Size);
private readonly ArrayPoolList<UInt256> _totalDifficulties = new(EraWriter.MaxEra1Size);

public void Add(Hash256 headerHash, UInt256 td)
{
Merkleizer merkleizer = new(Merkle.NextPowerOfTwoExponent(2));
merkleizer.Feed(headerHash.Bytes);
merkleizer.Feed(td);
_roots.Add(merkleizer.CalculateRoot().ToLittleEndian());
_totalDifficulties.Add(td);
}

public ValueHash256 ComputeRoot()
Expand All @@ -30,7 +37,56 @@ public ValueHash256 ComputeRoot()
return new ValueHash256(MemoryMarshal.Cast<UInt256, byte>(MemoryMarshal.CreateSpan(ref root, 1)));
}

internal void Clear() => _roots.Clear();
public ValueHash256[] GetProof(int blockIndex)
Comment thread
svlachakis marked this conversation as resolved.
{
if (blockIndex < 0 || blockIndex >= _roots.Count)
throw new ArgumentOutOfRangeException(nameof(blockIndex), $"Block index {blockIndex} is out of range [0, {_roots.Count - 1}].");

int count = _roots.Count;
ValueHash256[] proof = new ValueHash256[ProofLength];

// Level 0: sibling of block_hash = total_difficulty as 32-byte LE SSZ chunk
proof[0] = new ValueHash256(_totalDifficulties[blockIndex].ToLittleEndian());

// Build the flat binary tree over MaxEra1Size HeaderRecord roots.
byte[] flatTree = new byte[2 * EraWriter.MaxEra1Size * 32];
Comment thread
svlachakis marked this conversation as resolved.
Outdated
for (int i = 0; i < count; i++)
_roots[i].Span.CopyTo(flatTree.AsSpan((EraWriter.MaxEra1Size + i) * 32, 32));

Span<byte> combined = stackalloc byte[64];
for (int i = EraWriter.MaxEra1Size - 1; i >= 1; i--)
{
ReadOnlySpan<byte> left = flatTree.AsSpan(2 * i * 32, 32);
ReadOnlySpan<byte> right = flatTree.AsSpan((2 * i + 1) * 32, 32);
left.CopyTo(combined);
right.CopyTo(combined[32..]);
SHA256.TryHashData(combined, flatTree.AsSpan(i * 32, 32), out _);
}

int current = EraWriter.MaxEra1Size + blockIndex;
for (int i = 0; i < TreeDepth; i++)
{
int sibling = current ^ 1;
proof[1 + i] = new ValueHash256(flatTree.AsSpan(sibling * 32, 32));
current >>= 1;
}

Span<byte> lenBytes = stackalloc byte[32];
BinaryPrimitives.WriteUInt64LittleEndian(lenBytes, (ulong)count);
proof[14] = new ValueHash256(lenBytes);

public void Dispose() => _roots.Dispose();
return proof;
}

internal void Clear()
{
_roots.Clear();
_totalDifficulties.Clear();
}

public void Dispose()
{
_roots.Dispose();
_totalDifficulties.Dispose();
}
}
10 changes: 6 additions & 4 deletions src/Nethermind/Nethermind.Era1/E2StoreReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -167,16 +167,18 @@ public long BlockCount
}
}

public ValueHash256 CalculateChecksum()
public ValueHash256 CalculateChecksum() => ComputeChecksum(_file, _fileLength);

public static ValueHash256 ComputeChecksum(SafeFileHandle file, long fileLength)
{
using IncrementalHash sha = IncrementalHash.CreateHash(HashAlgorithmName.SHA256);
const int bufferSize = 81920;
byte[] buffer = new byte[bufferSize];
long offset = 0;
while (offset < _fileLength)
while (offset < fileLength)
{
int toRead = (int)Math.Min(bufferSize, _fileLength - offset);
int read = RandomAccess.Read(_file, buffer.AsSpan(0, toRead), offset);
int toRead = (int)Math.Min(bufferSize, fileLength - offset);
int read = RandomAccess.Read(file, buffer.AsSpan(0, toRead), offset);
if (read == 0) break;
sha.AppendData(buffer, 0, read);
offset += read;
Expand Down
93 changes: 93 additions & 0 deletions src/Nethermind/Nethermind.EraE.Test/Admin/AdminEraServiceTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited
// SPDX-License-Identifier: LGPL-3.0-only

using Nethermind.Config;
using Nethermind.Core;
using Nethermind.EraE.Admin;
using Nethermind.EraE.Export;
using Nethermind.EraE.Import;
using Nethermind.JsonRpc;
using Nethermind.Logging;
using NSubstitute;
using NUnit.Framework;

namespace Nethermind.EraE.Test.Admin;

public class AdminEraServiceTests
{
[Test]
public void ImportHistory_WhenCalled_DelegatesToImporter()
{
IEraImporter importer = Substitute.For<IEraImporter>();
AdminEraService sut = new(
importer,
Substitute.For<IEraExporter>(),
Substitute.For<IProcessExitSource>(),
LimboLogs.Instance);

sut.ImportHistory("somewhere", 99, 999, null);

importer.Received().Import("somewhere", 99, 999, null, Arg.Any<CancellationToken>());
}

[Test]
public void ImportHistory_WhenImportAlreadyRunning_ReturnsFailure()
{
IEraImporter importer = Substitute.For<IEraImporter>();
TaskCompletionSource tcs = new();
importer.Import("somewhere", 99, 999, null, Arg.Any<CancellationToken>()).Returns(tcs.Task);

AdminEraService sut = new(
importer,
Substitute.For<IEraExporter>(),
Substitute.For<IProcessExitSource>(),
LimboLogs.Instance);

sut.ImportHistory("somewhere", 99, 999, null);

ResultWrapper<string> result = sut.ImportHistory("somewhere", 99, 999, null);
Assert.That(result.Result.ResultType, Is.EqualTo(ResultType.Failure));

tcs.TrySetResult();

Assert.That(() => sut.ImportHistory("somewhere", 99, 999, null), Throws.Nothing);
}

[Test]
public void ExportHistory_WhenCalled_DelegatesToExporter()
{
IEraExporter exporter = Substitute.For<IEraExporter>();
AdminEraService sut = new(
Substitute.For<IEraImporter>(),
exporter,
Substitute.For<IProcessExitSource>(),
LimboLogs.Instance);

sut.ExportHistory("somewhere", 99, 999);

exporter.Received().Export("somewhere", 99, 999, Arg.Any<CancellationToken>());
}

[Test]
public void ExportHistory_WhenExportAlreadyRunning_ReturnsFailure()
{
IEraExporter exporter = Substitute.For<IEraExporter>();
TaskCompletionSource tcs = new();
exporter.Export("somewhere", 99, 999, Arg.Any<CancellationToken>()).Returns(tcs.Task);

AdminEraService sut = new(
Substitute.For<IEraImporter>(),
exporter,
Substitute.For<IProcessExitSource>(),
LimboLogs.Instance);

sut.ExportHistory("somewhere", 99, 999);

ResultWrapper<string> result = sut.ExportHistory("somewhere", 99, 999);
Assert.That(result.Result.ResultType, Is.EqualTo(ResultType.Failure));

tcs.TrySetResult();

Assert.That(() => sut.ExportHistory("somewhere", 99, 999), Throws.Nothing);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited
// SPDX-License-Identifier: LGPL-3.0-only

using Nethermind.Core;
using Nethermind.Core.Crypto;
using Nethermind.Core.Specs;
using Nethermind.Core.Test.Builders;
using AccumulatorCalculator = Nethermind.Era1.AccumulatorCalculator;
using Nethermind.EraE.Proofs;
using Nethermind.Int256;
using NSubstitute;
using NUnit.Framework;

namespace Nethermind.EraE.Test.Archive;

public class AccumulatorCalculatorTests
{
[Test]
public void Add_WhenCalled_DoesNotThrow()
{
using AccumulatorCalculator sut = new();
Assert.That(() => sut.Add(Keccak.Zero, 0), Throws.Nothing);
}

[Test]
public void ComputeRoot_WithKnownValues_ReturnsExpectedResult()
{
using AccumulatorCalculator sut = new();
sut.Add(Keccak.Zero, 1);
sut.Add(Keccak.MaxValue, 2);

byte[] result = sut.ComputeRoot().ToByteArray();

Assert.That(result, Is.EquivalentTo(new byte[]
{
0x3E, 0xD6, 0x26, 0x52, 0xDF, 0xB7, 0xE1, 0x07,
0x2D, 0x0F, 0x04, 0x0F, 0xEB, 0x6D, 0x00, 0x2A,
0x9F, 0x7C, 0xE3, 0x7C, 0xF8, 0xDD, 0xB1, 0x65,
0x49, 0xA7, 0xAC, 0x5C, 0xF8, 0xE3, 0xB7, 0x91
}));
}

[Test]
public void ComputeRoot_WithSameInputInTwoInstances_ReturnsSameResult()
{
using AccumulatorCalculator sut1 = new();
using AccumulatorCalculator sut2 = new();

sut1.Add(Keccak.Zero, 100);
sut2.Add(Keccak.Zero, 100);

Assert.That(sut1.ComputeRoot(), Is.EqualTo(sut2.ComputeRoot()));
}

[Test]
public void ComputeRoot_WithDifferentInputs_ReturnsDifferentResults()
{
using AccumulatorCalculator sut1 = new();
using AccumulatorCalculator sut2 = new();

sut1.Add(Keccak.Zero, 1);
sut2.Add(Keccak.MaxValue, 1);

Assert.That(sut1.ComputeRoot(), Is.Not.EqualTo(sut2.ComputeRoot()));
}

[Test]
public void GetProof_WithNegativeIndex_ThrowsArgumentOutOfRangeException()
{
using AccumulatorCalculator sut = new();
sut.Add(Keccak.Zero, 1);

Assert.That(() => sut.GetProof(-1), Throws.TypeOf<ArgumentOutOfRangeException>());
}

[Test]
public void GetProof_WithIndexAtCount_ThrowsArgumentOutOfRangeException()
{
using AccumulatorCalculator sut = new();
sut.Add(Keccak.Zero, 1);

Assert.That(() => sut.GetProof(1), Throws.TypeOf<ArgumentOutOfRangeException>());
}

[Test]
public void GetProof_WhenCalled_Returns15Elements()
{
using AccumulatorCalculator sut = new();
sut.Add(Keccak.Zero, 42);

Assert.That(sut.GetProof(0), Has.Length.EqualTo(15));
}

[TestCase(0)]
[TestCase(1)]
[TestCase(7)]
public void GetProof_WhenCalled_ProofZeroIsTotalDifficultyLE(int blockIndex)
{
using AccumulatorCalculator sut = new();
for (int i = 0; i <= blockIndex; i++)
sut.Add(Keccak.Zero, (ulong)(i + 1));

byte[] expected = new byte[32];
expected[0] = (byte)(blockIndex + 1);
Assert.That(sut.GetProof(blockIndex)[0].ToByteArray(), Is.EqualTo(expected));
}

[TestCase(0, 1)]
[TestCase(0, 3)]
[TestCase(2, 5)]
public Task GetProof_WhenValidatedByValidator_Succeeds(int blockIndex, int totalBlocks)
{
Block[] blocks = Enumerable.Range(0, totalBlocks)
.Select(i => Build.A.Block
.WithNumber(i)
.WithTotalDifficulty((UInt256)(i * 100 + 1))
.TestObject)
.ToArray();

using AccumulatorCalculator sut = new();
for (int i = 0; i < totalBlocks; i++)
sut.Add(blocks[i].Hash!, blocks[i].TotalDifficulty!.Value);

ValueHash256 root = sut.ComputeRoot();
ValueHash256[] proof = sut.GetProof(blockIndex);

Validator validator = BuildValidator(root);
BlockHeaderProof headerProof = new() { ProofType = BlockHeaderProofType.BlockProofHistoricalHashesAccumulator, HashesAccumulator = proof };

Assert.That(async () => await validator.VerifyContent(blocks[blockIndex], headerProof), Throws.Nothing);
return Task.CompletedTask;
}

[Test]
public void GetProof_WithDifferentIndices_ReturnDifferentProofs()
{
using AccumulatorCalculator sut = new();
sut.Add(Keccak.Zero, 1);
sut.Add(Keccak.MaxValue, 2);

ValueHash256[] proof0 = sut.GetProof(0);
ValueHash256[] proof1 = sut.GetProof(1);

Assert.That(proof0[0], Is.Not.EqualTo(proof1[0]));
Assert.That(proof0[1], Is.Not.EqualTo(proof1[1]));
}

private static Validator BuildValidator(ValueHash256 trustedRoot)
{
ISpecProvider specProvider = Substitute.For<ISpecProvider>();
specProvider.BeaconChainGenesisTimestamp.Returns((ulong?)1606824023UL);
return new Validator(specProvider, new List<ValueHash256> { trustedRoot }, null, null);
}
}
Loading
Loading