From 5caf93e7a4355b162ec7d63953727be87b85c471 Mon Sep 17 00:00:00 2001 From: Rohit Ranjan Date: Mon, 30 Sep 2024 15:31:52 +0530 Subject: [PATCH 1/5] add changes for flat encoding --- .../Requests/ConsensusRequestsProcessor.cs | 2 +- .../Validators/BlockValidator.cs | 2 +- .../Builders/BlockBuilder.cs | 2 +- .../ConsensusRequests/ConsensusRequest.cs | 45 +++++++++++++++++++ .../ConsensusRequests/ConsolidationRequest.cs | 22 +++++++++ .../ConsensusRequests/Deposit.cs | 27 +++++++++++ .../ConsensusRequests/WithdrawalRequest.cs | 24 +++++++++- .../Data/ExecutionPayloadV4.cs | 2 +- .../BlockDecoder.cs | 17 +++---- .../Nethermind.State/Proofs/RequestsTrie.cs | 15 +++++++ 10 files changed, 141 insertions(+), 17 deletions(-) diff --git a/src/Nethermind/Nethermind.Consensus/Requests/ConsensusRequestsProcessor.cs b/src/Nethermind/Nethermind.Consensus/Requests/ConsensusRequestsProcessor.cs index cefc5943b0c..379dff65d72 100644 --- a/src/Nethermind/Nethermind.Consensus/Requests/ConsensusRequestsProcessor.cs +++ b/src/Nethermind/Nethermind.Consensus/Requests/ConsensusRequestsProcessor.cs @@ -32,7 +32,7 @@ public void ProcessRequests(Block block, IWorldState state, TxReceipt[] receipts requestsList.AddRange(_consolidationRequestsProcessor.ReadConsolidationRequests(block, state, spec)); ConsensusRequest[] requests = requestsList.ToArray(); - Hash256 root = new RequestsTrie(requests).RootHash; + Hash256 root = requests.CalculateRootHash(); block.Body.Requests = requests; block.Header.RequestsRoot = root; } diff --git a/src/Nethermind/Nethermind.Consensus/Validators/BlockValidator.cs b/src/Nethermind/Nethermind.Consensus/Validators/BlockValidator.cs index 1bfbdf072b7..3554e88eb95 100644 --- a/src/Nethermind/Nethermind.Consensus/Validators/BlockValidator.cs +++ b/src/Nethermind/Nethermind.Consensus/Validators/BlockValidator.cs @@ -466,7 +466,7 @@ private static bool ValidateRequestsHashMatches(BlockHeader header, BlockBody bo return header.RequestsRoot is null; } - return (requestsRoot = new RequestsTrie(body.Requests).RootHash) == header.RequestsRoot; + return (requestsRoot = body.Requests.CalculateRootHash()) == header.RequestsRoot; } private static string Invalid(Block block) => diff --git a/src/Nethermind/Nethermind.Core.Test/Builders/BlockBuilder.cs b/src/Nethermind/Nethermind.Core.Test/Builders/BlockBuilder.cs index 4b5e591949b..7c0a3219482 100644 --- a/src/Nethermind/Nethermind.Core.Test/Builders/BlockBuilder.cs +++ b/src/Nethermind/Nethermind.Core.Test/Builders/BlockBuilder.cs @@ -293,7 +293,7 @@ public BlockBuilder WithConsensusRequests(params ConsensusRequest[]? requests) TestObjectInternal.Header.RequestsRoot = requests is null ? null - : new RequestsTrie(requests).RootHash; + : requests.CalculateRootHash(); return this; } diff --git a/src/Nethermind/Nethermind.Core/ConsensusRequests/ConsensusRequest.cs b/src/Nethermind/Nethermind.Core/ConsensusRequests/ConsensusRequest.cs index e9e5f6d38e1..88103c8aa58 100644 --- a/src/Nethermind/Nethermind.Core/ConsensusRequests/ConsensusRequest.cs +++ b/src/Nethermind/Nethermind.Core/ConsensusRequests/ConsensusRequest.cs @@ -5,6 +5,7 @@ using System; using System.Text.Json.Serialization; + namespace Nethermind.Core.ConsensusRequests; public enum ConsensusRequestsType : byte @@ -18,6 +19,21 @@ public abstract class ConsensusRequest { [JsonIgnore] public ConsensusRequestsType Type { get; protected set; } + + /// + /// Encodes the request into a byte array + /// reference: https://eips.ethereum.org/EIPS/eip-7685 + /// + /// request = request_type ++ request_data + public abstract byte[] Encode(); + + /// + /// Decodes the request from a byte array + /// reference: https://eips.ethereum.org/EIPS/eip-7685 + /// + /// request = request_type ++ request_data + /// request + public abstract ConsensusRequest Decode(byte[] data); } public static class ConsensusRequestExtensions @@ -75,4 +91,33 @@ public static (Deposit[]? deposits, WithdrawalRequest[]? withdrawalRequests, Con return (deposits, withdrawalRequests, consolidationRequests); } + + public static byte[][] Encode(this ConsensusRequest[]? requests) + { + if (requests is null) return Array.Empty(); + byte[][] requestsEncoded = new byte[requests.Length][]; + for (int i = 0; i < requests.Length; i++) + { + requestsEncoded[i] = requests[i].Encode(); + } + return requestsEncoded; + } + + public static ConsensusRequest Decode(byte[] data) + { + if (data.Length < 2) + { + throw new ArgumentException("Invalid data length"); + } + + ConsensusRequestsType type = (ConsensusRequestsType)data[0]; + return type switch + { + ConsensusRequestsType.Deposit => new Deposit().Decode(data), + ConsensusRequestsType.WithdrawalRequest => new WithdrawalRequest().Decode(data), + ConsensusRequestsType.ConsolidationRequest => new ConsolidationRequest().Decode(data), + _ => throw new ArgumentException("Invalid request type") + }; + } + } diff --git a/src/Nethermind/Nethermind.Core/ConsensusRequests/ConsolidationRequest.cs b/src/Nethermind/Nethermind.Core/ConsensusRequests/ConsolidationRequest.cs index abf79b924a1..ef72c94f6b5 100644 --- a/src/Nethermind/Nethermind.Core/ConsensusRequests/ConsolidationRequest.cs +++ b/src/Nethermind/Nethermind.Core/ConsensusRequests/ConsolidationRequest.cs @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only using System; +using System.Linq; using Nethermind.Core.Extensions; namespace Nethermind.Core.ConsensusRequests; @@ -27,5 +28,26 @@ public string ToString(string indentation) => @$"{indentation}{nameof(Consolidat {nameof(TargetPubkey)}: {TargetPubkey?.Span.ToHexString()}, }}"; + public override byte[] Encode() + { + byte[] sourceAddress = SourceAddress?.Bytes ?? Array.Empty(); + byte[] sourcePubkey = SourcePubkey?.ToArray() ?? Array.Empty(); + byte[] targetPubkey = TargetPubkey?.ToArray() ?? Array.Empty(); + byte[] type = new byte[] { (byte)Type }; + return type.Concat(sourceAddress).Concat(sourcePubkey).Concat(targetPubkey).ToArray(); + } + public override ConsensusRequest Decode(byte[] data) + { + if (data.Length < 2) + { + throw new ArgumentException("Invalid data length"); + } + + Type = (ConsensusRequestsType)data[0]; + SourceAddress = new Address(data.Slice(1, Address.Size)); + SourcePubkey = data.AsMemory().Slice(1 + Address.Size); + TargetPubkey = data.AsMemory().Slice(1 + Address.Size + SourcePubkey.Value.Length); + return this; + } } diff --git a/src/Nethermind/Nethermind.Core/ConsensusRequests/Deposit.cs b/src/Nethermind/Nethermind.Core/ConsensusRequests/Deposit.cs index 50b46fc27c2..1ccab3af95c 100644 --- a/src/Nethermind/Nethermind.Core/ConsensusRequests/Deposit.cs +++ b/src/Nethermind/Nethermind.Core/ConsensusRequests/Deposit.cs @@ -2,6 +2,7 @@ // SPDX-License-Identifier: LGPL-3.0-only using System; +using System.Linq; using Nethermind.Core.Extensions; namespace Nethermind.Core.ConsensusRequests; @@ -32,5 +33,31 @@ public string ToString(string indentation) => @$"{indentation}{nameof(Deposit)} {nameof(Signature)}: {Signature?.ToHexString()}, {nameof(Pubkey)}: {Pubkey?.Span.ToHexString()}}}"; + public override byte[] Encode() + { + byte[] pubkey = Pubkey?.ToArray() ?? Array.Empty(); + byte[] withdrawalCredentials = WithdrawalCredentials ?? Array.Empty(); + byte[] amount = BitConverter.GetBytes(Amount); + byte[] signature = Signature ?? Array.Empty(); + byte[] index = Index.HasValue ? BitConverter.GetBytes(Index.Value) : Array.Empty(); + byte[] type = new byte[] { (byte)Type }; + return type.Concat(pubkey).Concat(withdrawalCredentials).Concat(amount).Concat(signature).Concat(index).ToArray(); + } + + public override ConsensusRequest Decode(byte[] data) + { + if (data.Length < 2) + { + throw new ArgumentException("Invalid data length"); + } + + Type = (ConsensusRequestsType)data[0]; + Pubkey = data.AsMemory()[1..]; + WithdrawalCredentials = data.Slice(1 + Pubkey.Value.Length, 32); + Amount = BitConverter.ToUInt64(data, 1 + Pubkey.Value.Length + 32); + Signature = data.Slice(1 + Pubkey.Value.Length + 32 + sizeof(ulong)); + Index = data.Length > 1 + Pubkey.Value.Length + 32 + sizeof(ulong) + Signature!.Length ? BitConverter.ToUInt64(data, 1 + Pubkey.Value.Length + 32 + sizeof(ulong) + Signature!.Length) : null; + return this; + } } diff --git a/src/Nethermind/Nethermind.Core/ConsensusRequests/WithdrawalRequest.cs b/src/Nethermind/Nethermind.Core/ConsensusRequests/WithdrawalRequest.cs index f9d79f127c7..33ecd64a066 100644 --- a/src/Nethermind/Nethermind.Core/ConsensusRequests/WithdrawalRequest.cs +++ b/src/Nethermind/Nethermind.Core/ConsensusRequests/WithdrawalRequest.cs @@ -3,7 +3,7 @@ using System; using Nethermind.Core.Extensions; -using System.Text; +using System.Linq; namespace Nethermind.Core.ConsensusRequests; @@ -30,4 +30,26 @@ public string ToString(string indentation) => @$"{indentation}{nameof(Withdrawal {nameof(Amount)}: {Amount}}}"; + public override byte[] Encode() + { + byte[] sourceAddress = SourceAddress?.Bytes ?? Array.Empty(); + byte[] validatorPubkey = ValidatorPubkey?.ToArray() ?? Array.Empty(); + byte[] amount = BitConverter.GetBytes(Amount); + byte[] type = new byte[] { (byte)Type }; + return type.Concat(sourceAddress).Concat(validatorPubkey).Concat(amount).ToArray(); + } + + public override ConsensusRequest Decode(byte[] data) + { + if (data.Length < 2) + { + throw new ArgumentException("Invalid data length"); + } + + Type = (ConsensusRequestsType)data[0]; + SourceAddress = new Address(data.Slice(1, Address.Size)); + ValidatorPubkey = data.AsMemory().Slice(1 + Address.Size); + Amount = BitConverter.ToUInt64(data, 1 + Address.Size + ValidatorPubkey.Value.Length); + return this; + } } diff --git a/src/Nethermind/Nethermind.Merge.Plugin/Data/ExecutionPayloadV4.cs b/src/Nethermind/Nethermind.Merge.Plugin/Data/ExecutionPayloadV4.cs index e0d65da1215..373bc6946a9 100644 --- a/src/Nethermind/Nethermind.Merge.Plugin/Data/ExecutionPayloadV4.cs +++ b/src/Nethermind/Nethermind.Merge.Plugin/Data/ExecutionPayloadV4.cs @@ -62,7 +62,7 @@ public override bool TryGetBlock([NotNullWhen(true)] out Block? block, UInt256? } block.Body.Requests = requests; - block.Header.RequestsRoot = new RequestsTrie(requests).RootHash; + block.Header.RequestsRoot = requests.CalculateRootHash(); } else { diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/BlockDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/BlockDecoder.cs index 0530f141fd8..fa3fa04bd84 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/BlockDecoder.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/BlockDecoder.cs @@ -116,16 +116,14 @@ public class BlockDecoder : IRlpValueDecoder, IRlpStreamDecoder if (lengthWasRead) { - int requestsLength = rlpStream.ReadSequenceLength(); - int requestsCheck = rlpStream.Position + requestsLength; + // read an array of byte arrays + byte[][] requestsEncoded = rlpStream.DecodeByteArrays(); requests = new(); - - while (rlpStream.Position < requestsCheck) + for (int i = 0; i < requestsEncoded.Length; i++) { - requests.Add(Rlp.Decode(rlpStream)); + requests.Add(ConsensusRequestExtensions.Decode(requestsEncoded[i])); } - rlpStream.Check(requestsCheck); } } @@ -359,12 +357,7 @@ public void Encode(RlpStream stream, Block? item, RlpBehaviors rlpBehaviors = Rl if (requestsLength.HasValue) { - stream.StartSequence(requestsLength.Value); - - for (int i = 0; i < item.Requests.Length; i++) - { - stream.Encode(item.Requests[i]); - } + stream.Encode(item.Requests.Encode()); } } diff --git a/src/Nethermind/Nethermind.State/Proofs/RequestsTrie.cs b/src/Nethermind/Nethermind.State/Proofs/RequestsTrie.cs index d69e1d0c8d7..91156e0b3b2 100644 --- a/src/Nethermind/Nethermind.State/Proofs/RequestsTrie.cs +++ b/src/Nethermind/Nethermind.State/Proofs/RequestsTrie.cs @@ -33,3 +33,18 @@ public static Hash256 CalculateRoot(ConsensusRequest[] requests) return rootHash; } } + + +public static class ConsensusRequestExtensions +{ + public static Hash256 CalculateRootHash(this ConsensusRequest[]? requests) + { + Rlp[] encodedRequests = new Rlp[requests!.Length]; + for (int i = 0; i < encodedRequests.Length; i++) + { + encodedRequests[i] = Rlp.Encode(requests![i].Encode()); + } + + return Keccak.Compute(Rlp.Encode(encodedRequests).Bytes); + } +} From 737b9509fa24771249654a3ebce01c5c5df37087 Mon Sep 17 00:00:00 2001 From: Rohit Ranjan Date: Mon, 30 Sep 2024 17:02:10 +0530 Subject: [PATCH 2/5] remove redundant allocations --- .../ConsensusRequests/ConsolidationRequest.cs | 8 ++++---- .../Nethermind.Core/ConsensusRequests/Deposit.cs | 12 ++++++------ .../ConsensusRequests/WithdrawalRequest.cs | 8 ++++---- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/Nethermind/Nethermind.Core/ConsensusRequests/ConsolidationRequest.cs b/src/Nethermind/Nethermind.Core/ConsensusRequests/ConsolidationRequest.cs index ef72c94f6b5..75def5d8307 100644 --- a/src/Nethermind/Nethermind.Core/ConsensusRequests/ConsolidationRequest.cs +++ b/src/Nethermind/Nethermind.Core/ConsensusRequests/ConsolidationRequest.cs @@ -30,11 +30,11 @@ public string ToString(string indentation) => @$"{indentation}{nameof(Consolidat public override byte[] Encode() { - byte[] sourceAddress = SourceAddress?.Bytes ?? Array.Empty(); - byte[] sourcePubkey = SourcePubkey?.ToArray() ?? Array.Empty(); - byte[] targetPubkey = TargetPubkey?.ToArray() ?? Array.Empty(); byte[] type = new byte[] { (byte)Type }; - return type.Concat(sourceAddress).Concat(sourcePubkey).Concat(targetPubkey).ToArray(); + return type + .Concat(SourceAddress?.Bytes ?? Array.Empty()) + .Concat(SourcePubkey?.ToArray() ?? Array.Empty()) + .Concat(TargetPubkey?.ToArray() ?? Array.Empty()).ToArray(); } public override ConsensusRequest Decode(byte[] data) diff --git a/src/Nethermind/Nethermind.Core/ConsensusRequests/Deposit.cs b/src/Nethermind/Nethermind.Core/ConsensusRequests/Deposit.cs index 1ccab3af95c..e150892ec66 100644 --- a/src/Nethermind/Nethermind.Core/ConsensusRequests/Deposit.cs +++ b/src/Nethermind/Nethermind.Core/ConsensusRequests/Deposit.cs @@ -35,13 +35,13 @@ public string ToString(string indentation) => @$"{indentation}{nameof(Deposit)} public override byte[] Encode() { - byte[] pubkey = Pubkey?.ToArray() ?? Array.Empty(); - byte[] withdrawalCredentials = WithdrawalCredentials ?? Array.Empty(); - byte[] amount = BitConverter.GetBytes(Amount); - byte[] signature = Signature ?? Array.Empty(); - byte[] index = Index.HasValue ? BitConverter.GetBytes(Index.Value) : Array.Empty(); byte[] type = new byte[] { (byte)Type }; - return type.Concat(pubkey).Concat(withdrawalCredentials).Concat(amount).Concat(signature).Concat(index).ToArray(); + return type + .Concat(Pubkey?.ToArray() ?? Array.Empty()) + .Concat(WithdrawalCredentials ?? Array.Empty()) + .Concat(BitConverter.GetBytes(Amount)) + .Concat(Signature ?? Array.Empty()) + .Concat(Index.HasValue ? BitConverter.GetBytes(Index.Value) : Array.Empty()).ToArray(); } public override ConsensusRequest Decode(byte[] data) diff --git a/src/Nethermind/Nethermind.Core/ConsensusRequests/WithdrawalRequest.cs b/src/Nethermind/Nethermind.Core/ConsensusRequests/WithdrawalRequest.cs index 33ecd64a066..3560db94faf 100644 --- a/src/Nethermind/Nethermind.Core/ConsensusRequests/WithdrawalRequest.cs +++ b/src/Nethermind/Nethermind.Core/ConsensusRequests/WithdrawalRequest.cs @@ -32,11 +32,11 @@ public string ToString(string indentation) => @$"{indentation}{nameof(Withdrawal public override byte[] Encode() { - byte[] sourceAddress = SourceAddress?.Bytes ?? Array.Empty(); - byte[] validatorPubkey = ValidatorPubkey?.ToArray() ?? Array.Empty(); - byte[] amount = BitConverter.GetBytes(Amount); byte[] type = new byte[] { (byte)Type }; - return type.Concat(sourceAddress).Concat(validatorPubkey).Concat(amount).ToArray(); + return type + .Concat(SourceAddress?.Bytes ?? Array.Empty()) + .Concat(ValidatorPubkey?.ToArray() ?? Array.Empty()) + .Concat(BitConverter.GetBytes(Amount)).ToArray(); } public override ConsensusRequest Decode(byte[] data) From 3c4699b2d17cda47b4e95e42c0c7464bbaa2a692 Mon Sep 17 00:00:00 2001 From: Rohit Ranjan Date: Tue, 1 Oct 2024 22:44:08 +0530 Subject: [PATCH 3/5] address PR comments --- .../Nethermind.Serialization.Rlp/BlockDecoder.cs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/BlockDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/BlockDecoder.cs index fa3fa04bd84..05a12d77fd6 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/BlockDecoder.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/BlockDecoder.cs @@ -116,14 +116,17 @@ public class BlockDecoder : IRlpValueDecoder, IRlpStreamDecoder if (lengthWasRead) { - // read an array of byte arrays - byte[][] requestsEncoded = rlpStream.DecodeByteArrays(); + int requestsLength = rlpStream.ReadSequenceLength(); + int requestsCheck = rlpStream.Position + requestsLength; requests = new(); - for (int i = 0; i < requestsEncoded.Length; i++) + + while (rlpStream.Position < requestsCheck) { - requests.Add(ConsensusRequestExtensions.Decode(requestsEncoded[i])); + requests.Add(ConsensusRequestExtensions.Decode(rlpStream.DecodeByteArray())); } + rlpStream.Check(requestsCheck); + } } From 4decc8c123d0f46be453a71ee0c2b610b569474a Mon Sep 17 00:00:00 2001 From: Rohit Ranjan Date: Wed, 2 Oct 2024 14:35:11 +0530 Subject: [PATCH 4/5] block decoder fix --- src/Nethermind/Nethermind.Serialization.Rlp/BlockDecoder.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/BlockDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/BlockDecoder.cs index 05a12d77fd6..aa5a9542e6e 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/BlockDecoder.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/BlockDecoder.cs @@ -304,7 +304,7 @@ public int GetLength(Block? item, RlpBehaviors rlpBehaviors) while (decoderContext.Position < requestsCheck) { - requests.Add(Rlp.Decode(ref decoderContext)); + requests.Add(ConsensusRequestExtensions.Decode(decoderContext.DecodeByteArray())); } decoderContext.Check(requestsCheck); From 7000edbc9726cd88c9fd529b7cfd092e7a41ac6c Mon Sep 17 00:00:00 2001 From: Rohit Ranjan Date: Wed, 2 Oct 2024 16:06:16 +0530 Subject: [PATCH 5/5] fix deposit decoding --- src/Nethermind/Nethermind.Core/ConsensusRequests/Deposit.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Nethermind/Nethermind.Core/ConsensusRequests/Deposit.cs b/src/Nethermind/Nethermind.Core/ConsensusRequests/Deposit.cs index e150892ec66..d501e90e3b7 100644 --- a/src/Nethermind/Nethermind.Core/ConsensusRequests/Deposit.cs +++ b/src/Nethermind/Nethermind.Core/ConsensusRequests/Deposit.cs @@ -52,7 +52,7 @@ public override ConsensusRequest Decode(byte[] data) } Type = (ConsensusRequestsType)data[0]; - Pubkey = data.AsMemory()[1..]; + Pubkey = data.AsMemory()[1..33]; WithdrawalCredentials = data.Slice(1 + Pubkey.Value.Length, 32); Amount = BitConverter.ToUInt64(data, 1 + Pubkey.Value.Length + 32); Signature = data.Slice(1 + Pubkey.Value.Length + 32 + sizeof(ulong));