From 6fa13930fe11ed63b087a9c77a24c47fff853df8 Mon Sep 17 00:00:00 2001 From: Amirul Ashraf Date: Tue, 6 Jun 2023 19:00:48 +0800 Subject: [PATCH] =?UTF-8?q?Fix=20KeccaksIterator=20not=20working=20when=20?= =?UTF-8?q?some=20keccak=20zero=20prefix=20is=20not=20i=E2=80=A6=20(#5780)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fix KeccaksIterator not working when some keccak zero prefix is not included. * Fix whitespace * Fix reset * More tests * One more scenario --- .../Receipts/KeccaksIteratorTests.cs | 113 ++++++++++++++++++ .../Receipts/KeccaksIterator.cs | 7 +- .../Nethermind.Serialization.Rlp/Rlp.cs | 4 + 3 files changed, 121 insertions(+), 3 deletions(-) create mode 100644 src/Nethermind/Nethermind.Blockchain.Test/Receipts/KeccaksIteratorTests.cs diff --git a/src/Nethermind/Nethermind.Blockchain.Test/Receipts/KeccaksIteratorTests.cs b/src/Nethermind/Nethermind.Blockchain.Test/Receipts/KeccaksIteratorTests.cs new file mode 100644 index 00000000000..9bcc7026de6 --- /dev/null +++ b/src/Nethermind/Nethermind.Blockchain.Test/Receipts/KeccaksIteratorTests.cs @@ -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 TestKeccaks() + { + yield return Array.Empty(); + 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 buffer = stackalloc byte[32]; + KeccaksIterator iterator = new(rlpStream.Data, buffer); + + List 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 buffer = stackalloc byte[32]; + KeccaksIterator iterator = new(rlpStream.Data, buffer); + + List 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(); + } +} diff --git a/src/Nethermind/Nethermind.Blockchain/Receipts/KeccaksIterator.cs b/src/Nethermind/Nethermind.Blockchain/Receipts/KeccaksIterator.cs index e8088b667ad..04efc201d84 100644 --- a/src/Nethermind/Nethermind.Blockchain/Receipts/KeccaksIterator.cs +++ b/src/Nethermind/Nethermind.Blockchain/Receipts/KeccaksIterator.cs @@ -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 _buffer; public long Index { get; private set; } @@ -19,13 +20,14 @@ public KeccaksIterator(Span data, Span 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++; @@ -40,8 +42,7 @@ public bool TryGetNext(out KeccakStructRef current) public void Reset() { - _decoderContext.Position = 0; - _decoderContext.ReadSequenceLength(); + _decoderContext.Position = _startPosition; } } } diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/Rlp.cs b/src/Nethermind/Nethermind.Serialization.Rlp/Rlp.cs index fbfdbed5a69..6b799f9b31b 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/Rlp.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/Rlp.cs @@ -889,6 +889,10 @@ public void DecodeZeroPrefixedKeccakStructRef(out KeccakStructRef keccak, Span theSpan = DecodeByteArraySpan(); + if (theSpan.Length < 32) + { + buffer[..(32 - theSpan.Length)].Clear(); + } theSpan.CopyTo(buffer[(32 - theSpan.Length)..]); keccak = new KeccakStructRef(buffer); }