Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Exclude empty proofs; 0x0 for 0 instead of 0x #7766

Merged
merged 5 commits into from
Nov 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited
// SPDX-License-Identifier: LGPL-3.0-only

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
using Nethermind.Serialization.Json;
using NUnit.Framework;

namespace Nethermind.Core.Test.Json;

[TestFixture]
public class NullableByteReadOnlyMemoryConverterTests : ConverterTestBase<ReadOnlyMemory<byte>?>
{
static readonly NullableByteReadOnlyMemoryConverter ConverterWithLeadingZeros = new(false);
static readonly NullableByteReadOnlyMemoryConverter ConverterWithoutLeadingZeros = new(true);
static readonly JsonSerializerOptions options = new() { Converters = { ConverterWithoutLeadingZeros } };

[TestCaseSource(nameof(NullableBytesTestCaseSource))]
public void Test_roundtrip_with_leading_zeros(ReadOnlyMemory<byte>? value, string? _)
=> TestConverter(value, (src, expected) => (src is null && expected is null) || src is not null && expected is not null && src.Value.Span.SequenceEqual(expected.Value.Span), ConverterWithLeadingZeros);


[TestCaseSource(nameof(NullableBytesTestCaseSource))]
public void Test_serialization_without_leading_zeros(ReadOnlyMemory<byte>? value, string? expectedSerialization)
{
string result = JsonSerializer.Serialize(value, options);
Assert.That(result, Is.EqualTo(expectedSerialization));
}

public static IEnumerable<TestCaseData> NullableBytesTestCaseSource
{
get
{
yield return new TestCaseData(null, "null") { TestName = "Null maps into null" };
yield return new TestCaseData(ReadOnlyMemory<byte>.Empty, "\"0x\"") { TestName = "Empty array maps into 0x" };
yield return new TestCaseData(new ReadOnlyMemory<byte>([0]), "\"0x0\"") { TestName = "0 maps into 0x0" };
yield return new TestCaseData(new ReadOnlyMemory<byte>([0, 0, 0, 0]), "\"0x0\"") { TestName = "Any zeros map into 0x0" };
yield return new TestCaseData(new ReadOnlyMemory<byte>([1]), "\"0x1\"") { TestName = "Number maps into a hex number" };
yield return new TestCaseData(new ReadOnlyMemory<byte>([0, 1]), "\"0x1\"") { TestName = "Number maps into a hex number" };
yield return new TestCaseData(new ReadOnlyMemory<byte>([0, 0, 0, 1]), "\"0x1\"") { TestName = "Number maps into a hex number" };
}
}
}
6 changes: 3 additions & 3 deletions src/Nethermind/Nethermind.Core/BloomConverter.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited
// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited
// SPDX-License-Identifier: LGPL-3.0-only

using System;
using Nethermind.Core;
using System.Text.Json.Serialization;
using System;
using System.Text.Json;
using System.Text.Json.Serialization;

namespace Nethermind.Serialization.Json;

Expand Down
8 changes: 6 additions & 2 deletions src/Nethermind/Nethermind.Core/Extensions/Bytes.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited
// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited
// SPDX-License-Identifier: LGPL-3.0-only

using System;
Expand Down Expand Up @@ -31,6 +31,10 @@ public static unsafe partial class Bytes
public static readonly ReadOnlyMemory<byte> ZeroByte = new byte[] { 0 };
public static readonly ReadOnlyMemory<byte> OneByte = new byte[] { 1 };

public const string ZeroHexValue = "0x0";
public const string ZeroValue = "0";
public const string EmptyHexValue = "0x";

private class BytesEqualityComparer : EqualityComparer<byte[]>
{
public override bool Equals(byte[]? x, byte[]? y)
Expand Down Expand Up @@ -654,7 +658,7 @@ private static string ByteArrayToHexViaLookup32(byte[] bytes, bool withZeroX, bo
int length = bytes.Length * 2 + (withZeroX ? 2 : 0) - leadingZerosFirstCheck;
if (skipLeadingZeros && length == (withZeroX ? 2 : 0))
{
return withZeroX ? "0x0" : "0";
return withZeroX ? ZeroHexValue : ZeroValue;
}

State stateToPass = new(bytes, leadingZerosFirstCheck, withZeroX);
Expand Down
9 changes: 4 additions & 5 deletions src/Nethermind/Nethermind.Core/Extensions/Int64Extensions.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited
// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited
// SPDX-License-Identifier: LGPL-3.0-only

using System;
using System.Buffers.Binary;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

using Nethermind.Int256;

namespace Nethermind.Core.Extensions;
Expand Down Expand Up @@ -46,7 +45,7 @@ public static string ToHexString(this long value, bool skipLeadingZeros)
{
if (value == 0L)
{
return "0x0";
return Bytes.ZeroHexValue;
}

Span<byte> bytes = stackalloc byte[8];
Expand All @@ -59,7 +58,7 @@ public static string ToHexString(this ulong value, bool skipLeadingZeros)
{
if (value == 0UL)
{
return "0x0";
return Bytes.ZeroHexValue;
}

Span<byte> bytes = stackalloc byte[8];
Expand All @@ -74,7 +73,7 @@ public static string ToHexString(this in UInt256 value, bool skipLeadingZeros)
{
if (value == default)
{
return "0x0";
return Bytes.ZeroHexValue;
}

if (value == UInt256.One)
Expand Down
4 changes: 2 additions & 2 deletions src/Nethermind/Nethermind.Core/Extensions/SpanExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited
// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited
// SPDX-License-Identifier: LGPL-3.0-only

using System;
Expand Down Expand Up @@ -68,7 +68,7 @@ private static unsafe string ToHexViaLookup(ReadOnlySpan<byte> bytes, bool withZ

if (skipLeadingZeros && length == (withZeroX ? 2 : 0))
{
return withZeroX ? "0x0" : "0";
return withZeroX ? Bytes.ZeroHexValue : Bytes.ZeroValue;
}

fixed (byte* input = &Unsafe.Add(ref MemoryMarshal.GetReference(bytes), leadingZeros / 2))
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited
// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited
// SPDX-License-Identifier: LGPL-3.0-only

using System;
using System.Runtime.CompilerServices;
using System.Text.Json;
using System.Text.Json.Serialization;

using Nethermind.Core;
using Nethermind.Core.Extensions;

Expand All @@ -18,7 +17,7 @@ public class AddressConverter : JsonConverter<Address>
Type typeToConvert,
JsonSerializerOptions options)
{
byte[]? bytes = ByteArrayConverter.Convert(ref reader);
var bytes = ByteArrayConverter.Convert(ref reader);
return bytes is null ? null : new Address(bytes);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited
// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited
// SPDX-License-Identifier: LGPL-3.0-only

using Nethermind.Core.Extensions;
using System;
using System.Buffers;
using System.Diagnostics;
Expand All @@ -9,7 +10,6 @@
using System.Runtime.InteropServices;
using System.Text.Json;
using System.Text.Json.Serialization;
using Nethermind.Core.Extensions;

namespace Nethermind.Serialization.Json;

Expand All @@ -29,39 +29,31 @@ public class ByteArrayConverter : JsonConverter<byte[]>
{
JsonTokenType tokenType = reader.TokenType;
if (tokenType == JsonTokenType.None || tokenType == JsonTokenType.Null)
{
return null;
}
else if (tokenType != JsonTokenType.String)
{
ThrowInvalidOperationException();
}

int length = reader.ValueSpan.Length;
var length = reader.ValueSpan.Length;
byte[]? bytes = null;
if (length == 0)
{
length = checked((int)reader.ValueSequence.Length);
if (length == 0)
{
return null;
}

bytes = ArrayPool<byte>.Shared.Rent(length);
reader.ValueSequence.CopyTo(bytes);
}

ReadOnlySpan<byte> hex = bytes is null ? reader.ValueSpan : bytes.AsSpan(0, length);
if (length >= 2 && Unsafe.As<byte, ushort>(ref MemoryMarshal.GetReference(hex)) == _hexPrefix)
{
hex = hex[2..];
}

byte[] returnVal = Bytes.FromUtf8HexString(hex);
var returnVal = Bytes.FromUtf8HexString(hex);
if (bytes is not null)
{
ArrayPool<byte>.Shared.Return(bytes);
}

return returnVal;
}
Expand Down Expand Up @@ -102,17 +94,23 @@ public static void Convert(
const int maxStackLength = 128;
const int stackLength = 256;

int leadingNibbleZeros = skipLeadingZeros ? bytes.CountLeadingNibbleZeros() : 0;
int length = bytes.Length * 2 - leadingNibbleZeros + 2 + (addQuotations ? 2 : 0);
var leadingNibbleZeros = skipLeadingZeros ? bytes.CountLeadingNibbleZeros() : 0;
var nibblesCount = bytes.Length * 2;

if (skipLeadingZeros && nibblesCount is not 0 && leadingNibbleZeros == nibblesCount)
{
writer.WriteStringValue(Bytes.ZeroHexValue);
return;
}

var length = nibblesCount - leadingNibbleZeros + 2 + (addQuotations ? 2 : 0);

byte[]? array = null;
if (length > maxStackLength)
{
array = ArrayPool<byte>.Shared.Rent(length);
}

Span<byte> hex = (array ?? stackalloc byte[stackLength])[..length];
int start = 0;
var start = 0;
Index end = ^0;
if (addQuotations)
{
Expand All @@ -131,8 +129,6 @@ public static void Convert(
writeAction(writer, hex);

if (array is not null)
{
ArrayPool<byte>.Shared.Return(array);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

namespace Nethermind.Serialization.Json;

public class NullableByteReadOnlyMemoryConverter : JsonConverter<ReadOnlyMemory<byte>?>
public class NullableByteReadOnlyMemoryConverter(bool skipLeadingZeros = false) : JsonConverter<ReadOnlyMemory<byte>?>
{
public override ReadOnlyMemory<byte>? Read(
ref Utf8JsonReader reader,
Expand All @@ -19,5 +19,5 @@ public override void Write(
Utf8JsonWriter writer,
ReadOnlyMemory<byte>? bytes,
JsonSerializerOptions options) =>
ByteArrayConverter.Convert(writer, bytes is null ? [] : bytes.Value.Span, skipLeadingZeros: false);
ByteArrayConverter.Convert(writer, bytes is null ? [] : bytes.Value.Span, skipLeadingZeros: skipLeadingZeros);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited
// SPDX-License-Identifier: LGPL-3.0-only

namespace Nethermind.Serialization.Json;

public class ProofStorageValueConverter() : NullableByteReadOnlyMemoryConverter(true);
5 changes: 3 additions & 2 deletions src/Nethermind/Nethermind.Serialization.Json/LongConverter.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited
// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited
// SPDX-License-Identifier: LGPL-3.0-only

using System;
using System.Globalization;

namespace Nethermind.Serialization.Json
{
using Nethermind.Core.Extensions;
using System.Buffers;
using System.Buffers.Binary;
using System.Buffers.Text;
Expand All @@ -22,7 +23,7 @@ public static long FromString(string s)
throw new JsonException("null cannot be assigned to long");
}

if (s == "0x0")
if (s == Bytes.ZeroHexValue)
{
return 0L;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited
// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited
// SPDX-License-Identifier: LGPL-3.0-only

using System;
Expand Down Expand Up @@ -462,8 +462,8 @@ public void Storage_proofs_when_values_are_missing_setup()
Assert.That(proof.StorageProofs?[3].Value?.Span.ToHexString(true) ?? "0x", Is.EqualTo("0x00"));
Assert.That(proof.StorageProofs?[4].Value?.Span.ToHexString(true) ?? "0x", Is.EqualTo("0xab9a000000000000000000000000000000000000000000000000000000000000000000000000000000"));

proof.StorageProofs?[1].Proof.Should().HaveCount(3);
proof.StorageProofs?[3].Proof.Should().HaveCount(2);
proof.StorageProofs?[1].Proof.Should().HaveCount(2);
proof.StorageProofs?[3].Proof.Should().HaveCount(1);
}

[Test]
Expand Down
26 changes: 2 additions & 24 deletions src/Nethermind/Nethermind.State/Proofs/AccountProofCollector.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited
// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited
// SPDX-License-Identifier: LGPL-3.0-only

using System;
Expand Down Expand Up @@ -176,11 +176,7 @@ public void VisitBranch(TrieNode node, TrieVisitContext trieVisitContext)
{
Nibble childIndex = _fullStoragePaths[storageIndex][_pathTraversalIndex];
Hash256 childHash = node.GetChildHash((byte)childIndex);
if (childHash is null)
{
AddEmpty(node, trieVisitContext);
}
else
if (childHash is not null)
{
ref StorageNodeInfo? value = ref CollectionsMarshal.GetValueRefOrAddDefault(_storageNodeInfos, childHash, out bool exists);
if (!exists)
Expand Down Expand Up @@ -254,24 +250,6 @@ private void AddProofItem(TrieNode node, TrieVisitContext trieVisitContext)
}
}

private void AddEmpty(TrieNode node, TrieVisitContext trieVisitContext)
{
if (trieVisitContext.IsStorage)
{
if (_storageNodeInfos.TryGetValue(node.Keccak, out StorageNodeInfo value))
{
foreach (int storageIndex in value.StorageIndices)
{
_storageProofItems[storageIndex].Add([]);
}
}
}
else
{
_accountProofItems.Add([]);
}
}

public void VisitLeaf(TrieNode node, TrieVisitContext trieVisitContext, ReadOnlySpan<byte> value)
{
AddProofItem(node, trieVisitContext);
Expand Down
25 changes: 14 additions & 11 deletions src/Nethermind/Nethermind.State/Proofs/StorageProof.cs
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited
// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited
// SPDX-License-Identifier: LGPL-3.0-only

using Nethermind.Serialization.Json;
using System;
using System.Text.Json.Serialization;

namespace Nethermind.State.Proofs
namespace Nethermind.State.Proofs;

/// <summary>
/// EIP-1186 style storage proof
/// </summary>
public class StorageProof
{
/// <summary>
/// EIP-1186 style storage proof
/// </summary>
public class StorageProof
{
public byte[]? Key { get; set; }
public byte[][]? Proof { get; set; }
public ReadOnlyMemory<byte>? Value { get; set; }
}
public byte[]? Key { get; set; }
public byte[][]? Proof { get; set; }

[JsonConverter(typeof(ProofStorageValueConverter))]
public ReadOnlyMemory<byte>? Value { get; set; }
}