Skip to content

Commit

Permalink
Fix KeccaksIterator not working when some keccak zero prefix is not i… (
Browse files Browse the repository at this point in the history
#5780)

* Fix KeccaksIterator not working when some keccak zero prefix is not included.

* Fix whitespace

* Fix reset

* More tests

* One more scenario
  • Loading branch information
asdacap authored and kamilchodola committed Jun 6, 2023
1 parent 9b69d7b commit 6fa1393
Show file tree
Hide file tree
Showing 3 changed files with 121 additions and 3 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
// SPDX-FileCopyrightText: 2023 Demerzel Solutions Limited
// SPDX-License-Identifier: LGPL-3.0-only

using System;
using System.Collections.Generic;
using FluentAssertions;
using Nethermind.Blockchain.Receipts;
using Nethermind.Core.Crypto;
using Nethermind.Core.Extensions;
using Nethermind.Core.Test.Builders;
using Nethermind.Serialization.Rlp;
using NUnit.Framework;

namespace Nethermind.Blockchain.Test.Receipts;

public class KeccaksIteratorTests
{
[TestCaseSource(nameof(TestKeccaks))]
public void TestKeccakIteratorDecodeCorrectly(Keccak[] keccak)
{
Keccak[] keccaks = new[] { TestItem.KeccakA, Keccak.Zero };
Keccak[] decoded = EncodeDecode(keccaks);
decoded.Should().BeEquivalentTo(keccaks);
}

[TestCaseSource(nameof(TestKeccaks))]
public void TestKeccakIteratorDecodedCorrectlyWithReset(Keccak[] keccak)
{
Keccak[] keccaks = new[] { TestItem.KeccakA, Keccak.Zero };
Keccak[] decoded = EncodeDecodeReDecoded(keccaks);
decoded.Should().BeEquivalentTo(keccaks);
}

public static IEnumerable<Keccak[]> TestKeccaks()
{
yield return Array.Empty<Keccak>();
yield return new[] { TestItem.KeccakA };
yield return new[] { Keccak.Zero };
yield return new[] { TestItem.KeccakA, Keccak.Zero };
yield return new[] { Keccak.Zero, TestItem.KeccakA };
yield return new[] { TestItem.KeccakA, TestItem.KeccakB, TestItem.KeccakC, Keccak.Zero, };
yield return new[] { TestItem.KeccakA, new Keccak("0xffffffffffffffffffffffffffffffff00000000000000000000000000000000") };
yield return new[] { TestItem.KeccakA, new Keccak("0x00000000000000000000000000000000ffffffffffffffffffffffffffffffff") };
yield return new[] { TestItem.KeccakA, new Keccak("0xffffffffffffffffffffffffffffffff00000000000000000000000000000000"), TestItem.KeccakB };
yield return new[] { TestItem.KeccakA, new Keccak("0x00000000000000000000000000000000ffffffffffffffffffffffffffffffff"), TestItem.KeccakB };
yield return new[] { new Keccak("0xffffffffffffffffffffffffffffffff00000000000000000000000000000000"), TestItem.KeccakB };
yield return new[] { new Keccak("0x00000000000000000000000000000000ffffffffffffffffffffffffffffffff"), TestItem.KeccakB };
}

private Keccak[] EncodeDecode(Keccak[] input)
{
int totalLength = 0;
foreach (Keccak keccak in input)
{
totalLength += Rlp.LengthOf(keccak.Bytes.WithoutLeadingZerosOrEmpty());
}
int sequenceLength = Rlp.LengthOfSequence(totalLength);

RlpStream rlpStream = new RlpStream(sequenceLength);
rlpStream.StartSequence(totalLength);
foreach (Keccak keccak in input)
{
rlpStream.Encode(keccak.Bytes.WithoutLeadingZerosOrEmpty());
}

Span<byte> buffer = stackalloc byte[32];
KeccaksIterator iterator = new(rlpStream.Data, buffer);

List<Keccak> decoded = new();
while (iterator.TryGetNext(out KeccakStructRef kec))
{
decoded.Add(kec.ToKeccak());
}

return decoded.ToArray();
}

private Keccak[] EncodeDecodeReDecoded(Keccak[] input)
{
int totalLength = 0;
foreach (Keccak keccak in input)
{
totalLength += Rlp.LengthOf(keccak.Bytes.WithoutLeadingZerosOrEmpty());
}
int sequenceLength = Rlp.LengthOfSequence(totalLength);

RlpStream rlpStream = new RlpStream(sequenceLength);
rlpStream.StartSequence(totalLength);
foreach (Keccak keccak in input)
{
rlpStream.Encode(keccak.Bytes.WithoutLeadingZerosOrEmpty());
}

Span<byte> buffer = stackalloc byte[32];
KeccaksIterator iterator = new(rlpStream.Data, buffer);

List<Keccak> decoded = new();
while (iterator.TryGetNext(out KeccakStructRef kec))
{
decoded.Add(kec.ToKeccak());
}

decoded.Clear();
iterator.Reset();

while (iterator.TryGetNext(out KeccakStructRef kec))
{
decoded.Add(kec.ToKeccak());
}

return decoded.ToArray();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ namespace Nethermind.Blockchain.Receipts
public ref struct KeccaksIterator
{
private readonly int _length;
private readonly int _startPosition;
private Rlp.ValueDecoderContext _decoderContext;
private readonly Span<byte> _buffer;
public long Index { get; private set; }
Expand All @@ -19,13 +20,14 @@ public KeccaksIterator(Span<byte> data, Span<byte> buffer)
if (buffer.Length != 32) throw new ArgumentException("Buffer must be 32 bytes long");
_decoderContext = new Rlp.ValueDecoderContext(data);
_length = _decoderContext.ReadSequenceLength();
_startPosition = _decoderContext.Position;
_buffer = buffer;
Index = -1;
}

public bool TryGetNext(out KeccakStructRef current)
{
if (_decoderContext.Position < _length)
if (_decoderContext.Position < _length + _startPosition)
{
_decoderContext.DecodeZeroPrefixedKeccakStructRef(out current, _buffer);
Index++;
Expand All @@ -40,8 +42,7 @@ public bool TryGetNext(out KeccakStructRef current)

public void Reset()
{
_decoderContext.Position = 0;
_decoderContext.ReadSequenceLength();
_decoderContext.Position = _startPosition;
}
}
}
4 changes: 4 additions & 0 deletions src/Nethermind/Nethermind.Serialization.Rlp/Rlp.cs
Original file line number Diff line number Diff line change
Expand Up @@ -889,6 +889,10 @@ public void DecodeZeroPrefixedKeccakStructRef(out KeccakStructRef keccak, Span<b
else
{
ReadOnlySpan<byte> theSpan = DecodeByteArraySpan();
if (theSpan.Length < 32)
{
buffer[..(32 - theSpan.Length)].Clear();
}
theSpan.CopyTo(buffer[(32 - theSpan.Length)..]);
keccak = new KeccakStructRef(buffer);
}
Expand Down

0 comments on commit 6fa1393

Please sign in to comment.