diff --git a/src/Nethermind/Nethermind.Core/Collections/ArrayPoolSpan.cs b/src/Nethermind/Nethermind.Core/Collections/ArrayPoolSpan.cs new file mode 100644 index 00000000000..79c836a61ee --- /dev/null +++ b/src/Nethermind/Nethermind.Core/Collections/ArrayPoolSpan.cs @@ -0,0 +1,38 @@ + +using System.Buffers; +using System; +using System.Diagnostics.CodeAnalysis; + +namespace Nethermind.Core.Collections; + +public readonly struct ArrayPoolSpan(ArrayPool arrayPool, int length) : IDisposable +{ + private readonly T[] _array = arrayPool.Rent(length); + private readonly int _length = length; + public ArrayPoolSpan(int length) : this(ArrayPool.Shared, length) { } + + public readonly int Length => _length; + public readonly ref T this[int index] + { + get + { + if (index > _length) + { + ThrowArgumentOutOfRangeException(); + } + + return ref _array[index]; + + [DoesNotReturn] + static void ThrowArgumentOutOfRangeException() + { + throw new ArgumentOutOfRangeException(nameof(index)); + } + } + } + + public static implicit operator Span(ArrayPoolSpan arrayPoolSpan) => arrayPoolSpan._array.AsSpan(0, arrayPoolSpan._length); + public static implicit operator ReadOnlySpan(ArrayPoolSpan arrayPoolSpan) => arrayPoolSpan._array.AsSpan(0, arrayPoolSpan._length); + + public readonly void Dispose() => arrayPool.Return(_array); +} diff --git a/src/Nethermind/Nethermind.Merkleization/Merkle.BitArray.cs b/src/Nethermind/Nethermind.Merkleization/Merkle.BitArray.cs index 1c7257852ca..0dd25596531 100644 --- a/src/Nethermind/Nethermind.Merkleization/Merkle.BitArray.cs +++ b/src/Nethermind/Nethermind.Merkleization/Merkle.BitArray.cs @@ -8,17 +8,17 @@ namespace Nethermind.Merkleization; public static partial class Merkle { - public static void IzeBitvector(out UInt256 root, BitArray value) + public static void Ize(out UInt256 root, BitArray value) { Merkleizer merkleizer = new Merkleizer(0); - merkleizer.FeedBitvector(value); + merkleizer.Feed(value); merkleizer.CalculateRoot(out root); } - public static void IzeBitlist(out UInt256 root, BitArray value, ulong maximumBitlistLength) + public static void Ize(out UInt256 root, BitArray value, ulong limit) { Merkleizer merkleizer = new Merkleizer(0); - merkleizer.FeedBitlist(value, maximumBitlistLength); + merkleizer.Feed(value, limit); merkleizer.CalculateRoot(out root); } diff --git a/src/Nethermind/Nethermind.Merkleization/Merkle.Containers.cs b/src/Nethermind/Nethermind.Merkleization/Merkle.Containers.cs deleted file mode 100644 index a16b088d6e7..00000000000 --- a/src/Nethermind/Nethermind.Merkleization/Merkle.Containers.cs +++ /dev/null @@ -1,427 +0,0 @@ -// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited -// SPDX-License-Identifier: LGPL-3.0-only - -using Nethermind.Int256; - -namespace Nethermind.Merkleization -{ - // public partial class Merkle - // { - // public static int DepositContractTreeDepth { get; private set; } - // private static int JustificationBitsLength; - // internal static ulong MaximumDepositContracts { get; private set; } - // - // internal static uint MaxValidatorsPerCommittee { get; private set; } - // - // internal static uint SlotsPerEpoch { get; private set; } - // internal static int SlotsPerEth1VotingPeriod { get; private set; } - // public static int SlotsPerHistoricalRoot { get; private set; } - // - // public static int EpochsPerHistoricalVector { get; private set; } - // public static int EpochsPerSlashingsVector { get; private set; } - // internal static ulong HistoricalRootsLimit { get; private set; } - // internal static ulong ValidatorRegistryLimit { get; private set; } - // - // internal static uint MaxProposerSlashings { get; private set; } - // internal static uint MaxAttesterSlashings { get; private set; } - // internal static uint MaxAttestations { get; private set; } - // internal static uint MaxDeposits { get; private set; } - // internal static uint MaxVoluntaryExits { get; private set; } - // - // public static void Init(int depositContractTreeDepth, - // int justificationBitsLength, - // ulong maximumValidatorsPerCommittee, - // ulong slotsPerEpoch, - // ulong slotsPerEth1VotingPeriod, - // ulong slotsPerHistoricalRoot, - // ulong epochsPerHistoricalVector, - // ulong epochsPerSlashingsVector, - // ulong historicalRootsLimit, - // ulong validatorRegistryLimit, - // ulong maximumProposerSlashings, - // ulong maximumAttesterSlashings, - // ulong maximumAttestations, - // ulong maximumDeposits, - // ulong maximumVoluntaryExits - // ) - // { - // DepositContractTreeDepth = depositContractTreeDepth; - // JustificationBitsLength = justificationBitsLength; - // MaxValidatorsPerCommittee = (uint)maximumValidatorsPerCommittee; - // SlotsPerEpoch = (uint)slotsPerEpoch; - // SlotsPerEth1VotingPeriod = (int)slotsPerEth1VotingPeriod; - // SlotsPerHistoricalRoot = (int)slotsPerHistoricalRoot; - // EpochsPerHistoricalVector = (int)epochsPerHistoricalVector; - // EpochsPerSlashingsVector = (int)epochsPerSlashingsVector; - // HistoricalRootsLimit = historicalRootsLimit; - // ValidatorRegistryLimit = validatorRegistryLimit; - // MaxProposerSlashings = (uint)maximumProposerSlashings; - // MaxAttesterSlashings = (uint)maximumAttesterSlashings; - // MaxAttestations = (uint)maximumAttestations; - // MaxDeposits = (uint)maximumDeposits; - // MaxVoluntaryExits = (uint)maximumVoluntaryExits; - // - // MaximumDepositContracts = (ulong) 1 << depositContractTreeDepth; - // } - // } - - public static partial class Merkle - { - - - //public static void Ize(out UInt256 root, BlsPublicKey container) - //{ - // Ize(out root, container.Bytes); - //} - - //public static void Ize(out UInt256 root, BlsSignature container) - //{ - // Ize(out root, container.Bytes); - //} - - //public static void Ize(out UInt256 root, Gwei container) - //{ - // Ize(out root, container.Amount); - //} - - //public static void Ize(out UInt256 root, Slot container) - //{ - // Ize(out root, container.Number); - //} - - //public static void Ize(out UInt256 root, Epoch container) - //{ - // Ize(out root, container.Number); - //} - - //public static void Ize(out UInt256 root, ValidatorIndex container) - //{ - // Ize(out root, container.Number); - //} - - //public static void Ize(out UInt256 root, CommitteeIndex container) - //{ - // Ize(out root, container.Number); - //} - - //public static void Ize(out UInt256 root, Eth1Data? container) - //{ - // if (container is null) - // { - // root = RootOfNull; - // return; - // } - - // Merkleizer merkleizer = new Merkleizer(2); - // merkleizer.Feed(container.DepositRoot); - // merkleizer.Feed(container.DepositCount); - // merkleizer.Feed(container.BlockHash); - // merkleizer.CalculateRoot(out root); - //} - - //public static void Ize(out UInt256 root, DepositMessage container) - //{ - // Merkleizer merkleizer = new Merkleizer(2); - // merkleizer.Feed(container.PublicKey); - // merkleizer.Feed(container.WithdrawalCredentials); - // merkleizer.Feed(container.Amount); - // merkleizer.CalculateRoot(out root); - //} - - //public static void Ize(out UInt256 root, DepositData container) - //{ - // Merkleizer merkleizer = new Merkleizer(2); - // merkleizer.Feed(container.PublicKey); - // merkleizer.Feed(container.WithdrawalCredentials); - // merkleizer.Feed(container.Amount); - // merkleizer.Feed(container.Signature); - // merkleizer.CalculateRoot(out root); - //} - - //public static void Ize(out UInt256 root, Ref container) - //{ - // if (container.Root is null) - // { - // Ize(out root, container.Item); - // container.Root = new Root(root); - // } - // else - // { - // container.Root.AsInt(out root); - // } - //} - - //public static void Ize(out UInt256 root, List> value) - //{ - // Merkleizer merkleizer = new Merkleizer(0); - // merkleizer.Feed(value, Ssz.Ssz.MaximumDepositContracts); - // merkleizer.CalculateRoot(out root); - //} - - //public static void Ize(out UInt256 root, List value) - //{ - // Merkleizer merkleizer = new Merkleizer(0); - // merkleizer.Feed(value, Ssz.Ssz.MaximumDepositContracts); - // merkleizer.CalculateRoot(out root); - //} - - //public static void Ize(out UInt256 root, AttestationData? container) - //{ - // if (container is null) - // { - // root = RootOfNull; - // return; - // } - - // Merkleizer merkleizer = new Merkleizer(3); - // merkleizer.Feed(container.Slot); - // merkleizer.Feed(container.Index); - // merkleizer.Feed(container.BeaconBlockRoot); - // merkleizer.Feed(container.Source); - // merkleizer.Feed(container.Target); - // merkleizer.CalculateRoot(out root); - //} - - //public static void Ize(out UInt256 root, BeaconBlockBody? container) - //{ - // if (container is null) - // { - // root = RootOfNull; - // return; - // } - - // Merkleizer merkleizer = new Merkleizer(3); - // merkleizer.Feed(container.RandaoReveal); - // merkleizer.Feed(container.Eth1Data); - // merkleizer.Feed(container.Graffiti); - // merkleizer.Feed(container.ProposerSlashings, Ssz.Ssz.MaxProposerSlashings); - // merkleizer.Feed(container.AttesterSlashings, Ssz.Ssz.MaxAttesterSlashings); - // merkleizer.Feed(container.Attestations, Ssz.Ssz.MaxAttestations); - // merkleizer.Feed(container.Deposits, Ssz.Ssz.MaxDeposits); - // merkleizer.Feed(container.VoluntaryExits, Ssz.Ssz.MaxVoluntaryExits); - // merkleizer.CalculateRoot(out root); - //} - - //public static void Ize(out UInt256 root, BeaconState? container) - //{ - // if (container is null) - // { - // root = RootOfNull; - // return; - // } - - // Merkleizer merkleizer = new Merkleizer(5); - // merkleizer.Feed(container.GenesisTime); - // merkleizer.Feed(container.Slot); - // merkleizer.Feed(container.Fork); - // merkleizer.Feed(container.LatestBlockHeader); - // merkleizer.Feed(container.BlockRoots); - // merkleizer.Feed(container.StateRoots); - // merkleizer.Feed(container.HistoricalRoots.ToArray(), Ssz.Ssz.HistoricalRootsLimit); - // merkleizer.Feed(container.Eth1Data); - // merkleizer.Feed(container.Eth1DataVotes.ToArray(), (uint)Ssz.Ssz.SlotsPerEth1VotingPeriod); - // merkleizer.Feed(container.Eth1DepositIndex); - // merkleizer.Feed(container.Validators, Ssz.Ssz.ValidatorRegistryLimit); - // merkleizer.Feed(container.Balances.ToArray().ToArray()); - // merkleizer.Feed(container.PreviousEpochAttestations, Ssz.Ssz.MaxAttestations * Ssz.Ssz.SlotsPerEpoch); - // merkleizer.Feed(container.CurrentEpochAttestations, Ssz.Ssz.MaxAttestations * Ssz.Ssz.SlotsPerEpoch); - // merkleizer.FeedBitvector(container.JustificationBits); - // merkleizer.Feed(container.PreviousJustifiedCheckpoint); - // merkleizer.Feed(container.CurrentJustifiedCheckpoint); - // merkleizer.Feed(container.FinalizedCheckpoint); - // merkleizer.CalculateRoot(out root); - //} - - //public static void Ize(out UInt256 root, BeaconBlock container) - //{ - // Merkleizer merkleizer = new Merkleizer(2); - // merkleizer.Feed(container.Slot); - // merkleizer.Feed(container.ParentRoot); - // merkleizer.Feed(container.StateRoot); - // merkleizer.Feed(container.Body); - // merkleizer.CalculateRoot(out root); - //} - - //public static void Ize(out UInt256 root, Attestation? container) - //{ - // if (container is null) - // { - // root = RootOfNull; - // return; - // } - - // Merkleizer merkleizer = new Merkleizer(2); - // merkleizer.FeedBitlist(container.AggregationBits, Ssz.Ssz.MaxValidatorsPerCommittee); - // merkleizer.Feed(container.Data); - // merkleizer.Feed(container.Signature); - // merkleizer.CalculateRoot(out root); - //} - - //public static void Ize(out UInt256 root, IndexedAttestation? container) - //{ - // if (container is null) - // { - // root = RootOfNull; - // return; - // } - - // Merkleizer merkleizer = new Merkleizer(2); - // merkleizer.Feed(container.AttestingIndices.ToArray(), Ssz.Ssz.MaxValidatorsPerCommittee); - // merkleizer.Feed(container.Data); - // merkleizer.Feed(container.Signature); - // merkleizer.CalculateRoot(out root); - //} - - //public static void Ize(out UInt256 root, PendingAttestation? container) - //{ - // if (container is null) - // { - // root = RootOfNull; - // return; - // } - - // Merkleizer merkleizer = new Merkleizer(2); - // merkleizer.FeedBitlist(container.AggregationBits, Ssz.Ssz.MaxValidatorsPerCommittee); - // merkleizer.Feed(container.Data); - // merkleizer.Feed(container.InclusionDelay); - // merkleizer.Feed(container.ProposerIndex); - // merkleizer.CalculateRoot(out root); - //} - - //public static void Ize(out UInt256 root, AttesterSlashing? container) - //{ - // if (container is null) - // { - // root = RootOfNull; - // return; - // } - - // Merkleizer merkleizer = new Merkleizer(1); - // merkleizer.Feed(container.Attestation1); - // merkleizer.Feed(container.Attestation2); - // merkleizer.CalculateRoot(out root); - //} - - //public static void Ize(out UInt256 root, Deposit? container) - //{ - // if (container is null) - // { - // root = RootOfNull; - // return; - // } - - // Merkleizer merkleizer = new Merkleizer(1); - // merkleizer.Feed(container.Proof); - // merkleizer.Feed(container.Data); - // merkleizer.CalculateRoot(out root); - //} - - private static readonly UInt256 RootOfNull; - - //public static void Ize(out UInt256 root, ProposerSlashing container) - //{ - // Merkleizer merkleizer = new Merkleizer(2); - // merkleizer.Feed(container.ProposerIndex); - // merkleizer.Feed(container.SignedHeader1); - // merkleizer.Feed(container.SignedHeader2); - // merkleizer.CalculateRoot(out root); - //} - - //public static void Ize(out UInt256 root, Fork? container) - //{ - // if (container is null) - // { - // root = RootOfNull; - // return; - // } - - // Merkleizer merkleizer = new Merkleizer(2); - // merkleizer.Feed(container.Value.PreviousVersion); - // merkleizer.Feed(container.Value.CurrentVersion); - // merkleizer.Feed(container.Value.Epoch); - // merkleizer.CalculateRoot(out root); - //} - - //public static void Ize(out UInt256 root, Checkpoint? container) - //{ - // if (container is null) - // { - // root = RootOfNull; - // return; - // } - - // Merkleizer merkleizer = new Merkleizer(1); - // merkleizer.Feed(container.Value.Epoch); - // merkleizer.Feed(container.Value.Root); - // merkleizer.CalculateRoot(out root); - //} - - //public static void Ize(out UInt256 root, HistoricalBatch? container) - //{ - // if (container is null) - // { - // root = RootOfNull; - // return; - // } - - // Merkleizer merkleizer = new Merkleizer(1); - // merkleizer.Feed(container.BlockRoots); - // merkleizer.Feed(container.StateRoots); - // merkleizer.CalculateRoot(out root); - //} - - //public static void Ize(out UInt256 root, SignedVoluntaryExit container) - //{ - // Merkleizer merkleizer = new Merkleizer(1); - // merkleizer.Feed(container.Message); - // merkleizer.Feed(container.Signature); - // merkleizer.CalculateRoot(out root); - //} - - //public static void Ize(out UInt256 root, VoluntaryExit container) - //{ - // Merkleizer merkleizer = new Merkleizer(1); - // merkleizer.Feed(container.Epoch); - // merkleizer.Feed(container.ValidatorIndex); - // merkleizer.CalculateRoot(out root); - //} - - //public static void Ize(out UInt256 root, Validator? container) - //{ - // if (container is null) - // { - // root = RootOfNull; - // return; - // } - - // Merkleizer merkleizer = new Merkleizer(3); - // merkleizer.Feed(container.PublicKey); - // merkleizer.Feed(container.WithdrawalCredentials); - // merkleizer.Feed(container.EffectiveBalance); - // merkleizer.Feed(container.IsSlashed); - // merkleizer.Feed(container.ActivationEligibilityEpoch); - // merkleizer.Feed(container.ActivationEpoch); - // merkleizer.Feed(container.ExitEpoch); - // merkleizer.Feed(container.WithdrawableEpoch); - // merkleizer.CalculateRoot(out root); - //} - - //public static void Ize(out UInt256 root, BeaconBlockHeader container) - //{ - // Merkleizer merkleizer = new Merkleizer(2); - // merkleizer.Feed(container.Slot); - // merkleizer.Feed(container.ParentRoot); - // merkleizer.Feed(container.StateRoot); - // merkleizer.Feed(container.BodyRoot); - // merkleizer.CalculateRoot(out root); - //} - - //public static void Ize(out UInt256 root, SignedBeaconBlockHeader container) - //{ - // Merkleizer merkleizer = new Merkleizer(1); - // merkleizer.Feed(container.Message); - // merkleizer.Feed(container.Signature); - // merkleizer.CalculateRoot(out root); - //} - } -} diff --git a/src/Nethermind/Nethermind.Merkleization/Merkle.cs b/src/Nethermind/Nethermind.Merkleization/Merkle.cs index 23e161686fc..b35d618f986 100644 --- a/src/Nethermind/Nethermind.Merkleization/Merkle.cs +++ b/src/Nethermind/Nethermind.Merkleization/Merkle.cs @@ -28,6 +28,8 @@ private static void BuildZeroHashes() } } + private static readonly UInt256 RootOfNull; + static Merkle() { BuildZeroHashes(); @@ -88,48 +90,54 @@ public static void MixIn(ref UInt256 root, int value) root = HashConcatenation(root, lengthPart, 0); } - public static void Ize(out UInt256 root, bool value) + public static void Merkleize(out UInt256 root, bool value) { root = value ? UInt256.One : UInt256.Zero; } - public static void Ize(out UInt256 root, byte value) + public static void Merkleize(out UInt256 root, byte value) { root = new UInt256(value); } - public static void Ize(out UInt256 root, ushort value) + public static void Merkleize(out UInt256 root, ushort value) { root = new UInt256(value); } - public static void Ize(out UInt256 root, int value) + public static void Merkleize(out UInt256 root, int value) { var v = value < 0 ? ulong.MaxValue : 0L; root = new UInt256((ulong)value, v, v, v); } - public static void Ize(out UInt256 root, uint value) + public static void Merkleize(out UInt256 root, uint value) { root = new UInt256(value); } - public static void Ize(out UInt256 root, ulong value) + public static void Merkleize(out UInt256 root, long value) + { + var v = value < 0 ? ulong.MaxValue : 0L; + root = new UInt256((ulong)value, v, v, v); + } + + public static void Merkleize(out UInt256 root, ulong value) { root = new UInt256(value); } - public static void Ize(out UInt256 root, UInt128 value) + public static void Merkleize(out UInt256 root, UInt128 value) { root = new UInt256((ulong)(value & ulong.MaxValue), (ulong)(value >> 64)); } - public static void Ize(out UInt256 root, UInt256 value) + public static void Merkleize(out UInt256 root, UInt256 value) { root = value; } - public static void Ize(out UInt256 root, Bytes32 value) + public static void Merkleize(out UInt256 root, Bytes32 value) { ReadOnlySpan readOnlyBytes = value.AsSpan(); unsafe @@ -143,7 +151,7 @@ public static void Ize(out UInt256 root, Bytes32 value) } } - public static void Ize(out UInt256 root, Root value) + public static void Merkleize(out UInt256 root, Root value) { ReadOnlySpan readOnlyBytes = value.AsSpan(); unsafe @@ -157,7 +165,7 @@ public static void Ize(out UInt256 root, Root value) } } - public static void Ize(out UInt256 root, ReadOnlySpan value) + public static void Merkleize(out UInt256 root, ReadOnlySpan value) { const int typeSize = 1; int partialChunkLength = value.Length % (32 / typeSize); @@ -166,15 +174,15 @@ public static void Ize(out UInt256 root, ReadOnlySpan value) ReadOnlySpan fullChunks = value[..^partialChunkLength]; Span lastChunk = stackalloc bool[32 / typeSize]; value[^partialChunkLength..].CopyTo(lastChunk); - Ize(out root, MemoryMarshal.Cast(fullChunks), MemoryMarshal.Cast(lastChunk)); + Merkleize(out root, MemoryMarshal.Cast(fullChunks), MemoryMarshal.Cast(lastChunk)); } else { - Ize(out root, MemoryMarshal.Cast(value)); + Merkleize(out root, MemoryMarshal.Cast(value)); } } - public static void Ize(out UInt256 root, ReadOnlySpan value) + public static void Merkleize(out UInt256 root, ReadOnlySpan value) { const int typeSize = 1; int partialChunkLength = value.Length % (32 / typeSize); @@ -183,15 +191,15 @@ public static void Ize(out UInt256 root, ReadOnlySpan value) ReadOnlySpan fullChunks = value[..^partialChunkLength]; Span lastChunk = stackalloc byte[32 / typeSize]; value[^partialChunkLength..].CopyTo(lastChunk); - Ize(out root, MemoryMarshal.Cast(fullChunks), MemoryMarshal.Cast(lastChunk)); + Merkleize(out root, MemoryMarshal.Cast(fullChunks), MemoryMarshal.Cast(lastChunk)); } else { - Ize(out root, MemoryMarshal.Cast(value)); + Merkleize(out root, MemoryMarshal.Cast(value)); } } - public static void Ize(out UInt256 root, ReadOnlySpan value, ulong chunkCount) + public static void Merkleize(out UInt256 root, ReadOnlySpan value, ulong chunkCount) { const int typeSize = 1; int partialChunkLength = value.Length % (32 / typeSize); @@ -200,15 +208,15 @@ public static void Ize(out UInt256 root, ReadOnlySpan value, ulong chunkCo ReadOnlySpan fullChunks = value[..^partialChunkLength]; Span lastChunk = stackalloc byte[32 / typeSize]; value[^partialChunkLength..].CopyTo(lastChunk); - Ize(out root, MemoryMarshal.Cast(fullChunks), MemoryMarshal.Cast(lastChunk), chunkCount); + Merkleize(out root, MemoryMarshal.Cast(fullChunks), MemoryMarshal.Cast(lastChunk), chunkCount); } else { - Ize(out root, MemoryMarshal.Cast(value), chunkCount); + Merkleize(out root, MemoryMarshal.Cast(value), chunkCount); } } - public static void IzeBits(out UInt256 root, Span value, uint limit) + public static void MerkleizeBits(out UInt256 root, Span value, uint limit) { // reset lowest bit perf int lastBitPosition = ResetLastBit(ref value[^1]); @@ -225,11 +233,11 @@ public static void IzeBits(out UInt256 root, Span value, uint limit) Span fullChunks = value[..^partialChunkLength]; Span lastChunk = stackalloc byte[32 / typeSize]; value[^partialChunkLength..].CopyTo(lastChunk); - Ize(out root, MemoryMarshal.Cast(fullChunks), MemoryMarshal.Cast(lastChunk), limit); + Merkleize(out root, MemoryMarshal.Cast(fullChunks), MemoryMarshal.Cast(lastChunk), limit); } else { - Ize(out root, MemoryMarshal.Cast(value), default, limit); + Merkleize(out root, MemoryMarshal.Cast(value), default, limit); } MixIn(ref root, length); @@ -288,7 +296,7 @@ private static int ResetLastBit(ref byte lastByte) return 8; } - public static void Ize(out UInt256 root, ReadOnlySpan value) + public static void Merkleize(out UInt256 root, ReadOnlySpan value) { const int typeSize = 2; int partialChunkLength = value.Length % (32 / typeSize); @@ -297,15 +305,15 @@ public static void Ize(out UInt256 root, ReadOnlySpan value) ReadOnlySpan fullChunks = value[..^partialChunkLength]; Span lastChunk = stackalloc ushort[32 / typeSize]; value[^partialChunkLength..].CopyTo(lastChunk); - Ize(out root, MemoryMarshal.Cast(fullChunks), MemoryMarshal.Cast(lastChunk)); + Merkleize(out root, MemoryMarshal.Cast(fullChunks), MemoryMarshal.Cast(lastChunk)); } else { - Ize(out root, MemoryMarshal.Cast(value)); + Merkleize(out root, MemoryMarshal.Cast(value)); } } - public static void Ize(out UInt256 root, ReadOnlySpan value) + public static void Merkleize(out UInt256 root, ReadOnlySpan value) { const int typeSize = 4; int partialChunkLength = value.Length % (32 / typeSize); @@ -314,15 +322,15 @@ public static void Ize(out UInt256 root, ReadOnlySpan value) ReadOnlySpan fullChunks = value[..^partialChunkLength]; Span lastChunk = stackalloc uint[32 / typeSize]; value[^partialChunkLength..].CopyTo(lastChunk); - Ize(out root, MemoryMarshal.Cast(fullChunks), MemoryMarshal.Cast(lastChunk)); + Merkleize(out root, MemoryMarshal.Cast(fullChunks), MemoryMarshal.Cast(lastChunk)); } else { - Ize(out root, MemoryMarshal.Cast(value)); + Merkleize(out root, MemoryMarshal.Cast(value)); } } - public static void Ize(out UInt256 root, ReadOnlySpan value, ulong maxLength = 0U) + public static void Merkleize(out UInt256 root, ReadOnlySpan value, ulong maxLength = 0U) { const int typeSize = sizeof(ulong); ulong limit = (maxLength * typeSize + 31) / 32; @@ -332,11 +340,11 @@ public static void Ize(out UInt256 root, ReadOnlySpan value, ulong maxLen ReadOnlySpan fullChunks = value[..^partialChunkLength]; Span lastChunk = stackalloc ulong[32 / typeSize]; value[^partialChunkLength..].CopyTo(lastChunk); - Ize(out root, MemoryMarshal.Cast(fullChunks), MemoryMarshal.Cast(lastChunk), limit); + Merkleize(out root, MemoryMarshal.Cast(fullChunks), MemoryMarshal.Cast(lastChunk), limit); } else { - Ize(out root, MemoryMarshal.Cast(value), limit); + Merkleize(out root, MemoryMarshal.Cast(value), limit); } } @@ -349,15 +357,15 @@ public static void Ize(out UInt256 root, ReadOnlySpan value) ReadOnlySpan fullChunks = value[..^partialChunkLength]; Span lastChunk = stackalloc UInt128[32 / typeSize]; value[^partialChunkLength..].CopyTo(lastChunk); - Ize(out root, MemoryMarshal.Cast(fullChunks), MemoryMarshal.Cast(lastChunk)); + Merkleize(out root, MemoryMarshal.Cast(fullChunks), MemoryMarshal.Cast(lastChunk)); } else { - Ize(out root, MemoryMarshal.Cast(value)); + Merkleize(out root, MemoryMarshal.Cast(value)); } } - public static void Ize(out UInt256 root, ReadOnlySpan value, ReadOnlySpan lastChunk, ulong limit = 0) + public static void Merkleize(out UInt256 root, ReadOnlySpan value, ReadOnlySpan lastChunk, ulong limit = 0) { if (limit == 0 && (value.Length + lastChunk.Length == 1)) { @@ -381,47 +389,7 @@ public static void Ize(out UInt256 root, ReadOnlySpan value, ReadOnlySp merkleizer.CalculateRoot(out root); } - //public static void Ize(out UInt256 root, List> value, ulong limit) - //{ - // int length = value.Count; - // if (limit == 0 && length == 1) - // { - // Merkle.Ize(out root, value[0]); - // return; - // } - - // int depth = NextPowerOfTwoExponent(limit == 0UL ? (ulong)length : limit); - // Merkleizer merkleizer = new Merkleizer(depth); - // for (int i = 0; i < length; i++) - // { - // Merkle.Ize(out UInt256 subroot, value[i]); - // merkleizer.Feed(subroot); - // } - - // merkleizer.CalculateRoot(out root); - //} - - //public static void Ize(out UInt256 root, List value, ulong limit) - //{ - // int length = value.Count; - // if (limit == 0 && length == 1) - // { - // Merkle.Ize(out root, value[0]); - // return; - // } - - // int depth = NextPowerOfTwoExponent(limit == 0UL ? (ulong)length : limit); - // Merkleizer merkleizer = new Merkleizer(depth); - // for (int i = 0; i < length; i++) - // { - // Merkle.Ize(out UInt256 subroot, value[i]); - // merkleizer.Feed(subroot); - // } - - // merkleizer.CalculateRoot(out root); - //} - - public static void Ize(out UInt256 root, ReadOnlySpan value, ulong limit = 0UL) + public static void Merkleize(out UInt256 root, ReadOnlySpan value, ulong limit = 0UL) { if (limit == 0 && value.Length == 1) { diff --git a/src/Nethermind/Nethermind.Merkleization/Merkleizer.cs b/src/Nethermind/Nethermind.Merkleization/Merkleizer.cs index edf48702878..2b102b25d00 100644 --- a/src/Nethermind/Nethermind.Merkleization/Merkleizer.cs +++ b/src/Nethermind/Nethermind.Merkleization/Merkleizer.cs @@ -4,6 +4,7 @@ using System; using System.Collections; using System.Collections.Generic; +using System.Linq; using System.Runtime.InteropServices; using Nethermind.Core; using Nethermind.Core.Collections; @@ -58,26 +59,153 @@ public void Feed(UInt256 chunk) FeedAtLevel(chunk, 0); } - public void Feed(Span bytes) + public void Feed(long value) { - FeedAtLevel(MemoryMarshal.Cast(bytes)[0], 0); + Merkle.Merkleize(out _chunks[^1], value); + Feed(_chunks[^1]); + } + + public void Feed(Span data, int? limit = null) + { + if (data.Length is 0) + { + Merkle.Merkleize(out _chunks[^1], UInt256.Zero); + } + else + { + Merkle.Merkleize(out _chunks[^1], data); + } + + if (limit is not null) Merkle.MixIn(ref _chunks[^1], limit.Value); + } + + public void Feed(Span data, int? limit = null) + { + if (data.Length is 0) + { + Merkle.Merkleize(out _chunks[^1], UInt256.Zero); + } + else + { + Merkle.Merkleize(out _chunks[^1], MemoryMarshal.Cast(data)); + } + + if (limit is not null) Merkle.MixIn(ref _chunks[^1], limit.Value); + } + + public void Feed(Span data, int? limit = null) + { + if (data.Length is 0) + { + Merkle.Merkleize(out _chunks[^1], UInt256.Zero); + } + else + { + Merkle.Merkleize(out _chunks[^1], MemoryMarshal.Cast(data)); + } + + if (limit is not null) Merkle.MixIn(ref _chunks[^1], limit.Value); + } + + public void Feed(Span data, int? limit = null) + { + if (data.Length is 0) + { + Merkle.Merkleize(out _chunks[^1], UInt256.Zero); + } + else + { + Merkle.Merkleize(out _chunks[^1], MemoryMarshal.Cast(data)); + } + + if (limit is not null) Merkle.MixIn(ref _chunks[^1], limit.Value); + } + + public void Feed(Span data, int? limit = null) + { + if (data.Length is 0) + { + Merkle.Merkleize(out _chunks[^1], UInt256.Zero); + } + else + { + Merkle.Merkleize(out _chunks[^1], MemoryMarshal.Cast(data)); + } + + if (limit is not null) Merkle.MixIn(ref _chunks[^1], limit.Value); + } + + public void Feed(Span data, int? limit = null) + { + if (data.Length is 0) + { + Merkle.Merkleize(out _chunks[^1], UInt256.Zero); + } + else + { + Merkle.Merkleize(out _chunks[^1], MemoryMarshal.Cast(data)); + } + + if (limit is not null) Merkle.MixIn(ref _chunks[^1], limit.Value); + } + + public void Feed(Span data, int? limit = null) + { + if (data.Length is 0) + { + Merkle.Merkleize(out _chunks[^1], UInt256.Zero); + } + else + { + Merkle.Merkleize(out _chunks[^1], MemoryMarshal.Cast(data)); + } + + if (limit is not null) Merkle.MixIn(ref _chunks[^1], limit.Value); + } + + public void Feed(Span data, int? limit = null) + { + if (data.Length is 0) + { + Merkle.Merkleize(out _chunks[^1], UInt256.Zero); + } + else + { + Merkle.Merkleize(out _chunks[^1], MemoryMarshal.Cast(data)); + } + + if (limit is not null) Merkle.MixIn(ref _chunks[^1], limit.Value); } public void Feed(bool value) { - Merkle.Ize(out _chunks[^1], value); + Merkle.Merkleize(out _chunks[^1], value); Feed(_chunks[^1]); } public void Feed(uint value) { - Merkle.Ize(out _chunks[^1], value); + Merkle.Merkleize(out _chunks[^1], value); + Feed(_chunks[^1]); + } + public void Feed(int value) + { + Merkle.Merkleize(out _chunks[^1], value); + Feed(_chunks[^1]); + } + public void Feed(int? value) + { + if (value is null) + { + return; + } + Merkle.Merkleize(out _chunks[^1], value.Value); Feed(_chunks[^1]); } public void Feed(ulong value) { - Merkle.Ize(out _chunks[^1], value); + Merkle.Merkleize(out _chunks[^1], value); Feed(_chunks[^1]); } @@ -88,7 +216,7 @@ public void Feed(byte[]? value) return; } - Merkle.Ize(out _chunks[^1], value); + Merkle.Merkleize(out _chunks[^1], value); Feed(_chunks[^1]); } @@ -99,31 +227,53 @@ public void FeedBits(byte[]? value, uint limit) return; } - Merkle.IzeBits(out _chunks[^1], value, limit); + Merkle.MerkleizeBits(out _chunks[^1], value, limit); Feed(_chunks[^1]); } - public void FeedBitvector(BitArray bitArray) + public void Feed(BitArray? vector) { + if (vector is null) return; // bitfield_bytes - byte[] bytes = new byte[(bitArray.Length + 7) / 8]; - bitArray.CopyTo(bytes, 0); + byte[] bytes = new byte[(vector.Length + 7) / 8]; + vector.CopyTo(bytes, 0); - Merkle.Ize(out _chunks[^1], bytes); + Merkle.Merkleize(out _chunks[^1], bytes); Feed(_chunks[^1]); } - public void FeedBitlist(BitArray bitArray, ulong maximumBitlistLength) + public void Feed(BitArray? list, ulong maximumBitlistLength) { + if (list is null) return; + // chunk count ulong chunkCount = (maximumBitlistLength + 255) / 256; // bitfield_bytes - byte[] bytes = new byte[(bitArray.Length + 7) / 8]; - bitArray.CopyTo(bytes, 0); + byte[] bytes = new byte[(list.Length + 7) / 8]; + list.CopyTo(bytes, 0); + + Merkle.Merkleize(out _chunks[^1], bytes, chunkCount); + Merkle.MixIn(ref _chunks[^1], list.Length); + Feed(_chunks[^1]); + } + + public void Feed(IReadOnlyList value, ulong maxLength) + { + if (value is null) + { + return; + } - Merkle.Ize(out _chunks[^1], bytes, chunkCount); - Merkle.MixIn(ref _chunks[^1], bitArray.Length); + using ArrayPoolSpan subRoots = new(value.Count); + + for (int i = 0; i < value.Count; i++) + { + Merkle.Merkleize(out subRoots[i], value[i]); + } + + Merkle.Merkleize(out _chunks[^1], subRoots, maxLength); + Merkle.MixIn(ref _chunks[^1], value.Count); Feed(_chunks[^1]); } @@ -134,405 +284,20 @@ public void Feed(IEnumerable>? value, ulong maxLength) return; } - using ArrayPoolList subRoots = new ArrayPoolList((int)maxLength); + using ArrayPoolSpan subRoots = new(value.Count()); + int i = 0; + foreach (ReadOnlyMemory memory in value) { - Merkle.Ize(out UInt256 root, memory.Span); - subRoots.Add(root); + Merkle.Merkleize(out UInt256 root, memory.Span); + subRoots[i++] = root; } - Merkle.Ize(out _chunks[^1], subRoots.AsSpan(), maxLength); - Merkle.MixIn(ref _chunks[^1], subRoots.Count); + Merkle.Merkleize(out _chunks[^1], subRoots, maxLength); + Merkle.MixIn(ref _chunks[^1], subRoots.Length); Feed(_chunks[^1]); } - //public void Feed(BlsPublicKey? value) - //{ - // if (value is null) - // { - // return; - // } - - // Merkle.Ize(out _chunks[^1], value.Bytes); - // Feed(_chunks[^1]); - //} - - //public void Feed(BlsSignature? value) - //{ - // if (value is null) - // { - // return; - // } - - // Merkle.Ize(out _chunks[^1], value.Bytes); - // Feed(_chunks[^1]); - //} - - //public void Feed(ValidatorIndex value) - //{ - // Merkle.Ize(out _chunks[^1], value); - // Feed(_chunks[^1]); - //} - - //public void Feed(IReadOnlyList value, ulong maxLength) - //{ - // if (value is null) - // { - // return; - // } - - // UInt256[] subRoots = new UInt256[value.Count]; - // for (int i = 0; i < value.Count; i++) - // { - // Merkle.Ize(out subRoots[i], value[i]); - // } - - // Merkle.Ize(out _chunks[^1], subRoots, maxLength); - // Merkle.MixIn(ref _chunks[^1], value.Count); - // Feed(_chunks[^1]); - //} - - //public void Feed(IReadOnlyList value, ulong maxLength) - //{ - // if (value is null) - // { - // return; - // } - - // UInt256[] subRoots = new UInt256[value.Count]; - // for (int i = 0; i < value.Count; i++) - // { - // Merkle.Ize(out subRoots[i], value[i]); - // } - - // Merkle.Ize(out _chunks[^1], subRoots, maxLength); - // Merkle.MixIn(ref _chunks[^1], value.Count); - // Feed(_chunks[^1]); - //} - - //public void Feed(IReadOnlyList value, ulong maxLength) - //{ - // if (value is null) - // { - // return; - // } - - // UInt256[] subRoots = new UInt256[value.Count]; - // for (int i = 0; i < value.Count; i++) - // { - // Merkle.Ize(out subRoots[i], value[i]); - // } - - // Merkle.Ize(out _chunks[^1], subRoots, maxLength); - // Merkle.MixIn(ref _chunks[^1], value.Count); - // Feed(_chunks[^1]); - //} - - //public void Feed(IReadOnlyList value, ulong maxLength) - //{ - // if (value is null) - // { - // return; - // } - - // UInt256[] subRoots = new UInt256[value.Count]; - // for (int i = 0; i < value.Count; i++) - // { - // Merkle.Ize(out subRoots[i], value[i]); - // } - - // Merkle.Ize(out _chunks[^1], subRoots, maxLength); - // Merkle.MixIn(ref _chunks[^1], value.Count); - // Feed(_chunks[^1]); - //} - - //public void Feed(IReadOnlyList value, ulong maxLength) - //{ - // if (value is null) - // { - // return; - // } - - // UInt256[] subRoots = new UInt256[value.Count]; - // for (int i = 0; i < value.Count; i++) - // { - // Merkle.Ize(out subRoots[i], value[i]); - // } - - // Merkle.Ize(out _chunks[^1], subRoots, maxLength); - // Merkle.MixIn(ref _chunks[^1], value.Count); - // Feed(_chunks[^1]); - //} - - //public void Feed(IReadOnlyList value, ulong maxLength) - //{ - // if (value is null) - // { - // return; - // } - - // UInt256[] subRoots = new UInt256[value.Count]; - // for (int i = 0; i < value.Count; i++) - // { - // Merkle.Ize(out subRoots[i], value[i]); - // } - - // Merkle.Ize(out _chunks[^1], subRoots, maxLength); - // Merkle.MixIn(ref _chunks[^1], value.Count); - // Feed(_chunks[^1]); - //} - - //public void Feed(Eth1Data[]? value, uint maxLength) - //{ - // if (value is null) - // { - // return; - // } - - // UInt256[] subRoots = new UInt256[value.Length]; - // for (int i = 0; i < value.Length; i++) - // { - // Merkle.Ize(out subRoots[i], value[i]); - // } - - // Merkle.Ize(out _chunks[^1], subRoots, maxLength); - // Merkle.MixIn(ref _chunks[^1], value.Length); - // Feed(_chunks[^1]); - //} - - //public void Feed(IReadOnlyList value, uint maxLength) - //{ - // if (value is null) - // { - // return; - // } - - // UInt256[] subRoots = new UInt256[value.Count]; - // for (int i = 0; i < value.Count; i++) - // { - // Merkle.Ize(out subRoots[i], value[i]); - // } - - // Merkle.Ize(out _chunks[^1], subRoots, maxLength); - // Merkle.MixIn(ref _chunks[^1], value.Count); - // Feed(_chunks[^1]); - //} - - //public void Feed(ValidatorIndex[]? value, uint maxLength) - //{ - // if (value is null) - // { - // return; - // } - - // Merkle.Ize(out _chunks[^1], MemoryMarshal.Cast(value.AsSpan()), maxLength); - // Merkle.MixIn(ref _chunks[^1], value.Length); - // Feed(_chunks[^1]); - //} - - //public void Feed(Gwei[]? value, ulong maxLength) - //{ - // if (value is null) - // { - // return; - // } - - // Merkle.Ize(out _chunks[^1], MemoryMarshal.Cast(value.AsSpan()), maxLength); - // Merkle.MixIn(ref _chunks[^1], value.Length); - // Feed(_chunks[^1]); - //} - - //public void Feed(Gwei[]? value) - //{ - // if (value is null) - // { - // return; - // } - - // Merkle.Ize(out _chunks[^1], MemoryMarshal.Cast(value.AsSpan())); - // Feed(_chunks[^1]); - //} - - //public void Feed(CommitteeIndex value) - //{ - // Merkle.Ize(out _chunks[^1], value.Number); - // Feed(_chunks[^1]); - //} - - //public void Feed(Epoch value) - //{ - // Merkle.Ize(out _chunks[^1], value); - // Feed(_chunks[^1]); - //} - - //public void Feed(Fork? value) - //{ - // if (value is null) - // { - // return; - // } - - // Merkle.Ize(out _chunks[^1], value); - // Feed(_chunks[^1]); - //} - - //public void Feed(Eth1Data? value) - //{ - // if (value is null) - // { - // return; - // } - - // Merkle.Ize(out _chunks[^1], value); - // Feed(_chunks[^1]); - //} - - //public void Feed(Checkpoint? value) - //{ - // if (value is null) - // { - // return; - // } - - // Merkle.Ize(out _chunks[^1], value); - // Feed(_chunks[^1]); - //} - - //public void Feed(BeaconBlockHeader value) - //{ - // Merkle.Ize(out _chunks[^1], value); - // Feed(_chunks[^1]); - //} - - //public void Feed(SignedBeaconBlockHeader value) - //{ - // Merkle.Ize(out _chunks[^1], value); - // Feed(_chunks[^1]); - //} - - //public void Feed(BeaconBlockBody? value) - //{ - // if (value is null) - // { - // return; - // } - - // Merkle.Ize(out _chunks[^1], value); - // Feed(_chunks[^1]); - //} - - //public void Feed(VoluntaryExit value) - //{ - // Merkle.Ize(out _chunks[^1], value); - // Feed(_chunks[^1]); - //} - - //public void Feed(AttestationData? value) - //{ - // if (value is null) - // { - // return; - // } - - // Merkle.Ize(out _chunks[^1], value); - // Feed(_chunks[^1]); - //} - - //public void Feed(IndexedAttestation? value) - //{ - // if (value is null) - // { - // return; - // } - - // Merkle.Ize(out _chunks[^1], value); - // Feed(_chunks[^1]); - //} - - //public void Feed(DepositData? value) - //{ - // if (value is null) - // { - // return; - // } - - // Merkle.Ize(out _chunks[^1], value); - // Feed(_chunks[^1]); - //} - - //public void Feed(Ref value) - //{ - // if (value.Root is null) - // { - // if (value.Item is null) - // { - // return; - // } - - // Merkle.Ize(out _chunks[^1], value); - // value.Root = new Root(_chunks[^1]); - // Feed(_chunks[^1]); - // } - // else - // { - // Feed(value.Root); - // } - //} - - //public static UInt256 GetSubroot(DepositData depositData) - //{ - // Merkle.Ize(out UInt256 subRoot, depositData); - // return subRoot; - //} - - // public void Feed(List value, ulong maxLength) - // { - // Merkle.Ize(out _chunks[^1], value, maxLength); - // Merkle.MixIn(ref _chunks[^1], value.Count); - // Feed(_chunks[^1]); - // } - - //public void Feed(List> value, ulong maxLength) - //{ - // Merkle.Ize(out _chunks[^1], value, maxLength); - // Merkle.MixIn(ref _chunks[^1], value.Count); - // Feed(_chunks[^1]); - //} - - //public void Feed(List value, ulong maxLength) - //{ - // UInt256[] subRoots = new UInt256[value.Count]; - // for (int i = 0; i < value.Count; i++) - // { - // Merkle.Ize(out subRoots[i], value[i]); - // } - - // Merkle.Ize(out _chunks[^1], subRoots, maxLength); - // Merkle.MixIn(ref _chunks[^1], value.Count); - // Feed(_chunks[^1]); - //} - - - //public void Feed(ForkVersion value) - //{ - // Span padded = stackalloc byte[32]; - // value.AsSpan().CopyTo(padded); - // Merkle.Ize(out _chunks[^1], padded); - // Feed(_chunks[^1]); - //} - - //public void Feed(Gwei value) - //{ - // Merkle.Ize(out _chunks[^1], value.Amount); - // Feed(_chunks[^1]); - //} - - //public void Feed(Slot value) - //{ - // Merkle.Ize(out _chunks[^1], value.Number); - // Feed(_chunks[^1]); - //} - public void Feed(Bytes32 value) { // TODO: Is this going to have correct endianness? (the ulongs inside UInt256 are the correct order, @@ -550,51 +315,66 @@ public void Feed(IReadOnlyList value) // TODO: If the above MemoryMarshal.Cast of a single Bytes32, we could use that here // (rather than the CreateFromLittleEndian() that wants an (unnecessarily) writeable Span.) // Better yet, just MemoryMarshal.Cast the entire span and pass directly to Merkle.Ize ? - UInt256[] input = new UInt256[value.Count]; + using ArrayPoolSpan input = new(value.Count); for (int i = 0; i < value.Count; i++) { - Merkle.Ize(out input[i], value[i]); + Merkle.Merkleize(out input[i], value[i]); } - Merkle.Ize(out _chunks[^1], input); + Merkle.Merkleize(out _chunks[^1], input); Feed(_chunks[^1]); } public void Feed(IReadOnlyList value, ulong maxLength) + { + using ArrayPoolSpan subRoots = new(value.Count); + + for (int i = 0; i < value.Count; i++) + { + Merkle.Merkleize(out subRoots[i], value[i]); + } + + Merkle.Merkleize(out _chunks[^1], subRoots, maxLength); + Merkle.MixIn(ref _chunks[^1], value.Count); + Feed(_chunks[^1]); + } + + public void Feed(IReadOnlyList value, ulong maxLength) { // TODO: If UInt256 is the correct memory layout - UInt256[] subRoots = new UInt256[value.Count]; + using ArrayPoolSpan subRoots = new(value.Count); + for (int i = 0; i < value.Count; i++) { - Merkle.Ize(out subRoots[i], value[i]); + Merkle.Merkleize(out subRoots[i], value[i]); } - Merkle.Ize(out _chunks[^1], subRoots, maxLength); + Merkle.Merkleize(out _chunks[^1], subRoots, maxLength); Merkle.MixIn(ref _chunks[^1], value.Count); Feed(_chunks[^1]); } public void Feed(IReadOnlyList value) { - UInt256[] input = new UInt256[value.Count]; + using ArrayPoolSpan input = new(value.Count); for (int i = 0; i < value.Count; i++) { - Merkle.Ize(out input[i], value[i]); + Merkle.Merkleize(out input[i], value[i]); } - Merkle.Ize(out _chunks[^1], input); + Merkle.Merkleize(out _chunks[^1], input); Feed(_chunks[^1]); } public void Feed(IReadOnlyList value, ulong maxLength) { - UInt256[] subRoots = new UInt256[value.Count]; + using ArrayPoolSpan subRoots = new(value.Count); for (int i = 0; i < value.Count; i++) { - Merkle.Ize(out subRoots[i], value[i]); + Merkle.Merkleize(out subRoots[i], value[i]); } - Merkle.Ize(out _chunks[^1], subRoots, maxLength); + Merkle.Merkleize(out _chunks[^1], subRoots, maxLength); Merkle.MixIn(ref _chunks[^1], value.Count); Feed(_chunks[^1]); } diff --git a/src/Nethermind/Nethermind.Serialization.Ssz.Test/BitArrayTests.cs b/src/Nethermind/Nethermind.Serialization.Ssz.Test/BitArrayTests.cs index 3e850d02bff..21a8e9ab70e 100644 --- a/src/Nethermind/Nethermind.Serialization.Ssz.Test/BitArrayTests.cs +++ b/src/Nethermind/Nethermind.Serialization.Ssz.Test/BitArrayTests.cs @@ -53,7 +53,7 @@ public void Can_merkleize_bitarray_bitvector(bool[] value, string expectedByteSt // Act var hashTreeRoot = new byte[32]; - Merkle.IzeBitvector(out UInt256 root, input); + Merkle.Ize(out UInt256 root, input); root.ToLittleEndian(hashTreeRoot); // Assert @@ -97,7 +97,7 @@ public void Can_merkleize_bitarray_bitlist(bool[] value, ulong maximumBitlistLen // Act var hashTreeRoot = new byte[32]; - Merkle.IzeBitlist(out UInt256 root, input, maximumBitlistLength); + Merkle.Ize(out UInt256 root, input, maximumBitlistLength); root.ToLittleEndian(hashTreeRoot); // Assert diff --git a/src/Nethermind/Nethermind.Serialization.Ssz.Test/HashTreeRootTests.cs b/src/Nethermind/Nethermind.Serialization.Ssz.Test/HashTreeRootTests.cs deleted file mode 100644 index dfa6ff5867b..00000000000 --- a/src/Nethermind/Nethermind.Serialization.Ssz.Test/HashTreeRootTests.cs +++ /dev/null @@ -1,585 +0,0 @@ -// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited -// SPDX-License-Identifier: LGPL-3.0-only - -using System; -using System.Linq; -using System.Runtime.InteropServices; -using Nethermind.Core; -//using Nethermind.Core2.Containers; -//using Nethermind.Core2.Crypto; -//using Nethermind.Core2.Types; -using Nethermind.Int256; -using Nethermind.Merkleization; -using NUnit.Framework; -using Shouldly; - -namespace Nethermind.Serialization.Ssz.Test -{ - [TestFixture] - public class HashTreeRootTests - { - [SetUp] - public void Setup() - { - Ssz.Init( - 32, - 4, - 2048, - 32, - 1024, - 8192, - 65536, - 8192, - 16_777_216, - 1_099_511_627_776, - 16, - 1, - 128, - 16, - 16 - ); - } - - //[Test] - //public void Can_merkleize_epoch_0() - //{ - // // arrange - // Epoch epoch = Epoch.Zero; - - // // act - // Merkleizer merklezier = new Merkleizer(0); - // merklezier.Feed(epoch); - // UInt256 root = merklezier.CalculateRoot(); - // Span bytes = MemoryMarshal.Cast(MemoryMarshal.CreateSpan(ref root, 1)); - - // // assert - // byte[] expected = HashUtility.Chunk(new byte[] { 0x0 }).ToArray(); - // bytes.ToArray().ShouldBe(expected); - //} - - //[Test] - //public void Can_merkleize_epoch_1() - //{ - // // arrange - // Epoch epoch = Epoch.One; - - // // act - // Merkleizer merklezier = new Merkleizer(0); - // merklezier.Feed(epoch); - // UInt256 root = merklezier.CalculateRoot(); - // Span bytes = MemoryMarshal.Cast(MemoryMarshal.CreateSpan(ref root, 1)); - - // // assert - // byte[] expected = HashUtility.Chunk(new byte[] { 0x1 }).ToArray(); - // bytes.ToArray().ShouldBe(expected); - //} - - [Test] - public void Can_merkleize_bytes32() - { - // arrange - Bytes32 bytes32 = new Bytes32(Enumerable.Repeat((byte)0x34, 32).ToArray()); - - // act - Merkle.Ize(out UInt256 root, bytes32); - Span bytes = MemoryMarshal.Cast(MemoryMarshal.CreateSpan(ref root, 1)); - - // assert - byte[] expected = Enumerable.Repeat((byte)0x34, 32).ToArray(); - bytes.ToArray().ShouldBe(expected); - } - - //[Test] - //public void Can_merkleize_checkpoint() - //{ - // // arrange - // Checkpoint checkpoint = new Checkpoint( - // new Epoch(3), - // new Root(Enumerable.Repeat((byte)0x34, 32).ToArray()) - // ); - - // // act - // Merkle.Ize(out UInt256 root, checkpoint); - // Span bytes = MemoryMarshal.Cast(MemoryMarshal.CreateSpan(ref root, 1)); - - // // assert - // byte[] expected = HashUtility.Hash( - // HashUtility.Chunk(new byte[] { 0x03 }), - // Enumerable.Repeat((byte)0x34, 32).ToArray() - // ).ToArray(); - // bytes.ToArray().ShouldBe(expected); - //} - - //[Test] - //public void Can_merkleize_attestion_data() - //{ - // // arrange - // AttestationData attestationData = new AttestationData( - // Slot.One, - // new CommitteeIndex(2), - // new Root(Enumerable.Repeat((byte)0x12, 32).ToArray()), - // new Checkpoint( - // new Epoch(3), - // new Root(Enumerable.Repeat((byte)0x34, 32).ToArray()) - // ), - // new Checkpoint( - // new Epoch(4), - // new Root(Enumerable.Repeat((byte)0x56, 32).ToArray()) - // ) - // ); - - // // act - // Merkleizer merklezier = new Merkleizer(0); - // merklezier.Feed(attestationData); - // UInt256 root = merklezier.CalculateRoot(); - // Span bytes = MemoryMarshal.Cast(MemoryMarshal.CreateSpan(ref root, 1)); - - // // assert - // byte[] expected = HashUtility.Hash( - // HashUtility.Hash( - // HashUtility.Hash( - // HashUtility.Chunk(new byte[] { 0x01 }), // slot - // HashUtility.Chunk(new byte[] { 0x02 }) // committee - // ), - // HashUtility.Hash( - // Enumerable.Repeat((byte)0x12, 32).ToArray(), // beacon block root - // HashUtility.Hash( // source - // HashUtility.Chunk(new byte[] { 0x03 }), - // Enumerable.Repeat((byte)0x34, 32).ToArray() - // ) - // ) - // ), - // HashUtility.Merge( - // HashUtility.Hash( // target - // HashUtility.Chunk(new byte[] { 0x04 }), - // Enumerable.Repeat((byte)0x56, 32).ToArray() - // ), - // HashUtility.ZeroHashes(0, 2) - // ) - // ).ToArray(); - // bytes.ToArray().ShouldBe(expected); - //} - - //[Test] - //public void Can_merkleize_deposit_data() - //{ - // // arrange - // DepositData depositData = new DepositData( - // new BlsPublicKey(Enumerable.Repeat((byte)0x12, BlsPublicKey.Length).ToArray()), - // new Bytes32(Enumerable.Repeat((byte)0x34, Bytes32.Length).ToArray()), - // new Gwei(5), - // new BlsSignature(Enumerable.Repeat((byte)0x67, BlsSignature.Length).ToArray()) - // ); - - // // act - // Merkle.Ize(out UInt256 root, depositData); - // Span bytes = MemoryMarshal.Cast(MemoryMarshal.CreateSpan(ref root, 1)); - - // // assert - // byte[] expected = HashUtility.Hash( - // HashUtility.Hash( - // HashUtility.Hash( // public key - // Enumerable.Repeat((byte)0x12, 32).ToArray(), - // HashUtility.Chunk(Enumerable.Repeat((byte)0x12, 16).ToArray()) - // ), - // Enumerable.Repeat((byte)0x34, Bytes32.Length).ToArray() // withdrawal credentials - // ), - // HashUtility.Hash( - // HashUtility.Chunk(new byte[] { 0x05 }), // amount - // HashUtility.Hash( // signature - // HashUtility.Hash( - // Enumerable.Repeat((byte)0x67, 32).ToArray(), - // Enumerable.Repeat((byte)0x67, 32).ToArray() - // ), - // HashUtility.Hash( - // Enumerable.Repeat((byte)0x67, 32).ToArray(), - // Enumerable.Repeat((byte)0x00, 32).ToArray() - // ) - // ) - // ) - // ); - - // TestContext.Out.WriteLine("root: {0:x}", root); - // TestContext.Out.WriteLine("bytes: {0}", bytes.ToHexString(true)); - // TestContext.Out.WriteLine("expected: {0}", expected.ToHexString(true)); - - // bytes.ToArray().ShouldBe(expected); - //} - - //[Test] - //public void Can_merkleize_deposit_data_list() - //{ - // // arrange - // DepositData depositData1 = new DepositData( - // new BlsPublicKey(Enumerable.Repeat((byte)0x12, BlsPublicKey.Length).ToArray()), - // new Bytes32(Enumerable.Repeat((byte)0x34, Bytes32.Length).ToArray()), - // new Gwei(5), - // new BlsSignature(Enumerable.Repeat((byte)0x67, BlsSignature.Length).ToArray()) - // ); - // DepositData depositData2 = new DepositData( - // new BlsPublicKey(Enumerable.Repeat((byte)0x9a, BlsPublicKey.Length).ToArray()), - // new Bytes32(Enumerable.Repeat((byte)0xbc, Bytes32.Length).ToArray()), - // new Gwei(0xd), - // new BlsSignature(Enumerable.Repeat((byte)0xef, BlsSignature.Length).ToArray()) - // ); - // List depositDataList = new List { depositData1, depositData2 }; - - // // act - // Merkle.Ize(out UInt256 root, depositDataList); - // Span bytes = MemoryMarshal.Cast(MemoryMarshal.CreateSpan(ref root, 1)); - - // Merkle.Ize(out UInt256 root0, depositDataList[0]); - // TestContext.Out.WriteLine("root0: {0:x}", root0); - // Merkle.Ize(out UInt256 root1, depositDataList[1]); - // TestContext.Out.WriteLine("root1: {0:x}", root1); - - // // assert - // byte[] hash1 = HashUtility.Hash( - // HashUtility.Hash( - // HashUtility.Hash( // public key - // Enumerable.Repeat((byte)0x12, 32).ToArray(), - // HashUtility.Chunk(Enumerable.Repeat((byte)0x12, 16).ToArray()) - // ), - // Enumerable.Repeat((byte)0x34, Bytes32.Length).ToArray() // withdrawal credentials - // ), - // HashUtility.Hash( - // HashUtility.Chunk(new byte[] { 0x05 }), // amount - // HashUtility.Hash( // signature - // HashUtility.Hash( - // Enumerable.Repeat((byte)0x67, 32).ToArray(), - // Enumerable.Repeat((byte)0x67, 32).ToArray() - // ), - // HashUtility.Hash( - // Enumerable.Repeat((byte)0x67, 32).ToArray(), - // Enumerable.Repeat((byte)0x00, 32).ToArray() - // ) - // ) - // ) - // ); - // byte[] hash2 = HashUtility.Hash( - // HashUtility.Hash( - // HashUtility.Hash( // public key - // Enumerable.Repeat((byte)0x9a, 32).ToArray(), - // HashUtility.Chunk(Enumerable.Repeat((byte)0x9a, 16).ToArray()) - // ), - // Enumerable.Repeat((byte)0xbc, Bytes32.Length).ToArray() // withdrawal credentials - // ), - // HashUtility.Hash( - // HashUtility.Chunk(new byte[] { 0x0d }), // amount - // HashUtility.Hash( // signature - // HashUtility.Hash( - // Enumerable.Repeat((byte)0xef, 32).ToArray(), - // Enumerable.Repeat((byte)0xef, 32).ToArray() - // ), - // HashUtility.Hash( - // Enumerable.Repeat((byte)0xef, 32).ToArray(), - // Enumerable.Repeat((byte)0x00, 32).ToArray() - // ) - // ) - // ) - // ); - - // TestContext.Out.WriteLine("Hash1: {0}", Bytes.ToHexString(hash1, true)); - // TestContext.Out.WriteLine("Hash2: {0}", Bytes.ToHexString(hash2, true)); - - // byte[] hashList = HashUtility.Merge( // list, depth 32 - // HashUtility.Hash( - // hash1, - // hash2 - // ), - // HashUtility.ZeroHashes(1, 32) - // ).ToArray(); - // TestContext.Out.WriteLine("Hash list: {0}", Bytes.ToHexString(hashList, true)); - - // byte[] expected = HashUtility.Hash( - // hashList, - // HashUtility.Chunk(new byte[] { 0x02 }) // mix in length - // ); - // TestContext.Out.WriteLine("Hash expected: {0}", Bytes.ToHexString(expected, true)); - - // bytes.ToArray().ShouldBe(expected); - //} - - // TODO: Add tests for deposit, and deposit list (for beacon block body) - - // [Test] - // public void Can_merkleize_deposit() - // { - // // arrange - // Deposit deposit = new Deposit( - // Enumerable.Repeat(new Hash32(), 33), - // new DepositData( - // new BlsPublicKey(Enumerable.Repeat((byte) 0x12, BlsPublicKey.Length).ToArray()), - // new Hash32(Enumerable.Repeat((byte) 0x34, Hash32.Length).ToArray()), - // new Gwei(5), - // new BlsSignature(Enumerable.Repeat((byte) 0x67, BlsSignature.Length).ToArray()) - // ) - // ); - // - // // act - // Merkle.Ize(out UInt256 root, deposit); - // Span bytes = MemoryMarshal.Cast(MemoryMarshal.CreateSpan(ref root, 1)); - // - // // assert - // byte[] expected = HashUtility.Hash( - // HashUtility.Hash( - // HashUtility.Hash( // public key - // Enumerable.Repeat((byte) 0x12, 32).ToArray(), - // HashUtility.Chunk(Enumerable.Repeat((byte) 0x12, 16).ToArray()) - // ), - // Enumerable.Repeat((byte) 0x34, Hash32.Length).ToArray() // withdrawal credentials - // ), - // HashUtility.Hash( - // HashUtility.Chunk(new byte[] {0x05}), // amount - // HashUtility.Hash( // signature - // HashUtility.Hash( - // Enumerable.Repeat((byte) 0x67, 32).ToArray(), - // Enumerable.Repeat((byte) 0x67, 32).ToArray() - // ), - // HashUtility.Hash( - // Enumerable.Repeat((byte) 0x67, 32).ToArray(), - // Enumerable.Repeat((byte) 0x00, 32).ToArray() - // ) - // ) - // ) - // ); - // - // TestContext.Out.WriteLine("root: {0:x}", root); - // TestContext.Out.WriteLine("bytes: {0}", bytes.ToHexString(true)); - // TestContext.Out.WriteLine("expected: {0}", expected.ToHexString(true)); - // - // bytes.ToArray().ShouldBe(expected); - // } - - //[Test] - //public void Can_merkleize_eth1data() - //{ - // // arrange - // Eth1Data eth1Data = new Eth1Data( - // new Root(Enumerable.Repeat((byte)0x34, Root.Length).ToArray()), - // 5, - // new Bytes32(Enumerable.Repeat((byte)0x67, Bytes32.Length).ToArray()) - // ); - - // // act - // Merkle.Ize(out UInt256 root, eth1Data); - // Span bytes = MemoryMarshal.Cast(MemoryMarshal.CreateSpan(ref root, 1)); - - // // assert - // byte[] expected = HashUtility.Hash( - // HashUtility.Hash( - // Enumerable.Repeat((byte)0x34, 32).ToArray(), - // HashUtility.Chunk(new byte[] { 0x05 }) - // ), - // HashUtility.Hash( - // Enumerable.Repeat((byte)0x67, 32).ToArray(), - // Enumerable.Repeat((byte)0x00, 32).ToArray() - // ) - // ).ToArray(); - // bytes.ToArray().ShouldBe(expected); - //} - - //[Test] - //public void Can_merkleize_empty_beacon_block_body() - //{ - // // arrange - // List proposerSlashings = new List(); - // List attesterSlashings = new List(); - // List attestations = new List(); - // List deposits = new List(); - // List voluntaryExits = new List(); - - // BeaconBlockBody beaconBlockBody = new BeaconBlockBody( - // new BlsSignature(Enumerable.Repeat((byte)0x12, BlsSignature.Length).ToArray()), - // new Eth1Data( - // new Root(Enumerable.Repeat((byte)0x34, Root.Length).ToArray()), - // 5, - // new Bytes32(Enumerable.Repeat((byte)0x67, Bytes32.Length).ToArray()) - // ), - // new Bytes32(Enumerable.Repeat((byte)0x89, Bytes32.Length).ToArray()), - // proposerSlashings, - // attesterSlashings, - // attestations, - // deposits, - // voluntaryExits - // ); - - // // act - // Merkle.Ize(out UInt256 root, beaconBlockBody); - // Span bytes = MemoryMarshal.Cast(MemoryMarshal.CreateSpan(ref root, 1)); - - // // assert - // byte[] proposerSlashingsHash = HashUtility.Hash( - // HashUtility.ZeroHashes(4, 5)[0], - // HashUtility.Chunk(new byte[] { 0x00 }) - // ); - // byte[] attesterSlashingsHash = HashUtility.Hash( - // HashUtility.ZeroHashes(0, 1)[0], - // HashUtility.Chunk(new byte[] { 0x00 }) - // ); - // byte[] attestationsHash = HashUtility.Hash( - // HashUtility.ZeroHashes(7, 8)[0], - // HashUtility.Chunk(new byte[] { 0x00 }) - // ); - // byte[] depositsHash = HashUtility.Hash( - // HashUtility.ZeroHashes(4, 5)[0], - // HashUtility.Chunk(new byte[] { 0x00 }) - // ); - // byte[] voluntaryExitsHash = HashUtility.Hash( - // HashUtility.ZeroHashes(4, 5)[0], - // HashUtility.Chunk(new byte[] { 0x00 }) - // ); - - // byte[] expected = HashUtility.Hash( - // HashUtility.Hash( - // HashUtility.Hash( - // HashUtility.Hash( // randao - // HashUtility.Hash( - // Enumerable.Repeat((byte)0x12, 32).ToArray(), - // Enumerable.Repeat((byte)0x12, 32).ToArray() - // ), - // HashUtility.Hash( - // Enumerable.Repeat((byte)0x12, 32).ToArray(), - // Enumerable.Repeat((byte)0x00, 32).ToArray() - // ) - // ), - // HashUtility.Hash( // eth1data - // HashUtility.Hash( - // Enumerable.Repeat((byte)0x34, 32).ToArray(), - // HashUtility.Chunk(new byte[] { 0x05 }) - // ), - // HashUtility.Hash( - // Enumerable.Repeat((byte)0x67, 32).ToArray(), - // Enumerable.Repeat((byte)0x00, 32).ToArray() - // ) - // ) - // ), - // HashUtility.Hash( - // Enumerable.Repeat((byte)0x89, Bytes32.Length).ToArray(), // graffiti - // proposerSlashingsHash // proposer slashings - // ) - // ), - // HashUtility.Hash( - // HashUtility.Hash( - // attesterSlashingsHash, // attester slashings - // attestationsHash // attestations - // ), - // HashUtility.Hash( - // depositsHash, // deposits - // voluntaryExitsHash // voluntary exits - // ) - // ) - // ); - - // bytes.ToArray().ShouldBe(expected); - //} - - // TODO: Finish test for beacon block body with data, e.g. deposit, to get it working - - // [Test] - // public void Can_merkleize_beacon_block_body_with_deposit() - // { - // // arrange - // List proposerSlashings = new List(); - // List attesterSlashings = new List(); - // List attestations = new List(); - // List deposits = new List() - // { - // new Deposit( - // Enumerable.Repeat(new Hash32(), 33), - // new DepositData( - // new BlsPublicKey(Enumerable.Repeat((byte) 0x12, BlsPublicKey.Length).ToArray()), - // new Hash32(Enumerable.Repeat((byte) 0x34, Hash32.Length).ToArray()), - // new Gwei(5), - // new BlsSignature(Enumerable.Repeat((byte) 0x67, BlsSignature.Length).ToArray()) - // ) - // ) - // }; - // List voluntaryExits = new List(); - // - // BeaconBlockBody beaconBlockBody = new BeaconBlockBody( - // new BlsSignature(Enumerable.Repeat((byte) 0x12, BlsSignature.Length).ToArray()), - // new Eth1Data( - // new Hash32(Enumerable.Repeat((byte) 0x34, Hash32.Length).ToArray()), - // 5, - // new Hash32(Enumerable.Repeat((byte) 0x67, Hash32.Length).ToArray()) - // ), - // new Bytes32(Enumerable.Repeat((byte) 0x89, Bytes32.Length).ToArray()), - // proposerSlashings, - // attesterSlashings, - // attestations, - // deposits, - // voluntaryExits - // ); - // - // // act - // Merkle.Ize(out UInt256 root, beaconBlockBody); - // Span bytes = MemoryMarshal.Cast(MemoryMarshal.CreateSpan(ref root, 1)); - // - // // assert - // byte[] proposerSlashingsHash = HashUtility.Hash( - // HashUtility.ZeroHashes(4, 5)[0], - // HashUtility.Chunk(new byte[] { 0x00 }) - // ); - // byte[] attesterSlashingsHash = HashUtility.Hash( - // HashUtility.ZeroHashes(0, 1)[0], - // HashUtility.Chunk(new byte[] { 0x00 }) - // ); - // byte[] attestationsHash = HashUtility.Hash( - // HashUtility.ZeroHashes(7, 8)[0], - // HashUtility.Chunk(new byte[] { 0x00 }) - // ); - // byte[] depositsHash = HashUtility.Hash( - // HashUtility.ZeroHashes(4, 5)[0], - // HashUtility.Chunk(new byte[] { 0x00 }) - // ); - // byte[] voluntaryExitsHash = HashUtility.Hash( - // HashUtility.ZeroHashes(4, 5)[0], - // HashUtility.Chunk(new byte[] { 0x00 }) - // ); - // - // byte[] expected = HashUtility.Hash( - // HashUtility.Hash( - // HashUtility.Hash( - // HashUtility.Hash( // randao - // HashUtility.Hash( - // Enumerable.Repeat((byte) 0x12, 32).ToArray(), - // Enumerable.Repeat((byte) 0x12, 32).ToArray() - // ), - // HashUtility.Hash( - // Enumerable.Repeat((byte) 0x12, 32).ToArray(), - // Enumerable.Repeat((byte) 0x00, 32).ToArray() - // ) - // ), - // HashUtility.Hash( // eth1data - // HashUtility.Hash( - // Enumerable.Repeat((byte) 0x34, 32).ToArray(), - // HashUtility.Chunk(new byte[] {0x05}) - // ), - // HashUtility.Hash( - // Enumerable.Repeat((byte) 0x67, 32).ToArray(), - // Enumerable.Repeat((byte) 0x00, 32).ToArray() - // ) - // ) - // ), - // HashUtility.Hash( - // Enumerable.Repeat((byte) 0x89, Bytes32.Length).ToArray(), // graffiti - // proposerSlashingsHash // proposer slashings - // ) - // ), - // HashUtility.Hash( - // HashUtility.Hash( - // attesterSlashingsHash, // attester slashings - // attestationsHash // attestations - // ), - // HashUtility.Hash( - // depositsHash, // deposits - // voluntaryExitsHash // voluntary exits - // ) - // ) - // ); - // - // bytes.ToArray().ShouldBe(expected); - // } - } -} diff --git a/src/Nethermind/Nethermind.Serialization.Ssz.Test/MerkleTests.cs b/src/Nethermind/Nethermind.Serialization.Ssz.Test/MerkleTests.cs index b54165083e5..f63bb3f0fe1 100644 --- a/src/Nethermind/Nethermind.Serialization.Ssz.Test/MerkleTests.cs +++ b/src/Nethermind/Nethermind.Serialization.Ssz.Test/MerkleTests.cs @@ -67,42 +67,42 @@ public void Zero_hashes_0_is_correct() [Test] public void Can_merkleize_bool() { - Merkle.Ize(out UInt256 root, true); + Merkle.Merkleize(out UInt256 root, true); Assert.That(root.ToHexString(true), Is.EqualTo("0x0100000000000000000000000000000000000000000000000000000000000000")); } [Test] public void Can_merkleize_byte() { - Merkle.Ize(out UInt256 root, (byte)34); + Merkle.Merkleize(out UInt256 root, (byte)34); Assert.That(root.ToHexString(true), Is.EqualTo("0x2200000000000000000000000000000000000000000000000000000000000000")); } [Test] public void Can_merkleize_ushort() { - Merkle.Ize(out UInt256 root, (ushort)(34 + byte.MaxValue)); + Merkle.Merkleize(out UInt256 root, (ushort)(34 + byte.MaxValue)); Assert.That(root.ToHexString(true), Is.EqualTo("0x2101000000000000000000000000000000000000000000000000000000000000")); } [Test] public void Can_merkleize_uint() { - Merkle.Ize(out UInt256 root, (uint)34 + byte.MaxValue + ushort.MaxValue); + Merkle.Merkleize(out UInt256 root, (uint)34 + byte.MaxValue + ushort.MaxValue); Assert.That(root.ToHexString(true), Is.EqualTo("0x2001010000000000000000000000000000000000000000000000000000000000")); } [Test] public void Can_merkleize_int() { - Merkle.Ize(out UInt256 root, 34 + byte.MaxValue + ushort.MaxValue); + Merkle.Merkleize(out UInt256 root, 34 + byte.MaxValue + ushort.MaxValue); Assert.That(root.ToHexString(true), Is.EqualTo("0x2001010000000000000000000000000000000000000000000000000000000000")); } [Test] public void Can_merkleize_ulong() { - Merkle.Ize(out UInt256 root, (ulong)34 + byte.MaxValue + ushort.MaxValue + uint.MaxValue); + Merkle.Merkleize(out UInt256 root, (ulong)34 + byte.MaxValue + ushort.MaxValue + uint.MaxValue); Assert.That(root.ToHexString(true), Is.EqualTo("0x1f01010001000000000000000000000000000000000000000000000000000000")); } @@ -116,7 +116,7 @@ public void Can_merkleize_uint128() input += uint.MaxValue; input += ulong.MaxValue; - Merkle.Ize(out UInt256 root, input); + Merkle.Merkleize(out UInt256 root, input); Assert.That(root.ToHexString(true), Is.EqualTo("0x1e01010001000000010000000000000000000000000000000000000000000000")); } @@ -130,35 +130,35 @@ public void Can_merkleize_uint256() input += uint.MaxValue; input += ulong.MaxValue; - Merkle.Ize(out UInt256 root, input); + Merkle.Merkleize(out UInt256 root, input); Assert.That(root.ToHexString(true), Is.EqualTo("0x1e01010001000000010000000000000000000000000000000000000000000000")); } [Test] public void Can_merkleize_bool_vector() { - Merkle.Ize(out UInt256 root, new[] { true, false }); + Merkle.Merkleize(out UInt256 root, new[] { true, false }); Assert.That(root.ToHexString(true), Is.EqualTo("0x0100000000000000000000000000000000000000000000000000000000000000")); } [Test] public void Can_merkleize_ushort_vector() { - Merkle.Ize(out UInt256 root, new[] { (ushort)1, (ushort)3 }); + Merkle.Merkleize(out UInt256 root, new[] { (ushort)1, (ushort)3 }); Assert.That(root.ToHexString(true), Is.EqualTo("0x0100030000000000000000000000000000000000000000000000000000000000")); } [Test] public void Can_merkleize_uint_vector() { - Merkle.Ize(out UInt256 root, new[] { 1U, 3U }); + Merkle.Merkleize(out UInt256 root, new[] { 1U, 3U }); Assert.That(root.ToHexString(true), Is.EqualTo("0x0100000003000000000000000000000000000000000000000000000000000000")); } [Test] public void Can_merkleize_ulong_vector() { - Merkle.Ize(out UInt256 root, new[] { 1UL, 3UL }); + Merkle.Merkleize(out UInt256 root, new[] { 1UL, 3UL }); Assert.That(root.ToHexString(true), Is.EqualTo("0x0100000000000000030000000000000000000000000000000000000000000000")); } @@ -172,14 +172,14 @@ public void Can_merkleize_uint128_vector() [Test] public void Can_merkleize_uint256_vector() { - Merkle.Ize(out UInt256 root, new UInt256[] { 1 }); + Merkle.Merkleize(out UInt256 root, new UInt256[] { 1 }); Assert.That(root.ToHexString(true), Is.EqualTo("0x0100000000000000000000000000000000000000000000000000000000000000")); } [Test] public void Can_merkleize_uint256_vector_longer() { - Merkle.Ize(out UInt256 root, new UInt256[] { 1, 2, 3, 4 }); + Merkle.Merkleize(out UInt256 root, new UInt256[] { 1, 2, 3, 4 }); Assert.That(root.ToHexString(true), Is.EqualTo("0xbfe3c665d2e561f13b30606c580cb703b2041287e212ade110f0bfd8563e21bb")); } @@ -193,35 +193,35 @@ public void Can_merkleize_uint128_vector_full() [Test] public void Can_merkleize_bitlist() { - Merkle.IzeBits(out UInt256 root, new byte[] { 123 }, 0); + Merkle.MerkleizeBits(out UInt256 root, new byte[] { 123 }, 0); Assert.That(root.ToHexString(true), Is.EqualTo("0xe5e12694be373406e317c583b5fd9e7a642913dc20a5c4947edb202dafbbc0ee")); } [Test] public void Can_merkleize_bitlist_with_limit() { - Merkle.IzeBits(out UInt256 root, new byte[] { 17 }, 2); + Merkle.MerkleizeBits(out UInt256 root, new byte[] { 17 }, 2); Assert.That(root.ToHexString(true), Is.EqualTo("0x60d461bd1cec1a858ba48a27799c9686c15ad1625743bafa70674afc530f981a")); } [Test] public void Can_merkleize_bitlist_high_limit_and_null() { - Merkle.IzeBits(out UInt256 root, new byte[] { 0 }, 8); + Merkle.MerkleizeBits(out UInt256 root, new byte[] { 0 }, 8); Assert.That(root.ToHexString(true), Is.EqualTo("0x881690bb860e3a4f7681f51f1eccc59dac2718eeb0c0585cd698ad0650938b33")); } [Test] public void Can_merkleize_bitlist_high_limit_and_small() { - Merkle.IzeBits(out UInt256 root, new byte[] { 3 }, 8); + Merkle.MerkleizeBits(out UInt256 root, new byte[] { 3 }, 8); Assert.That(root.ToHexString(true), Is.EqualTo("0x9e1ff035a32c3d3085074e676356984c077f70bed47814956a9ef8852dcb8161")); } [Test] public void Can_merkleize_bitvector() { - Merkle.Ize(out UInt256 root, new byte[] { 123 }); + Merkle.Merkleize(out UInt256 root, new byte[] { 123 }); Assert.That(root.ToHexString(true), Is.EqualTo("0x7b00000000000000000000000000000000000000000000000000000000000000")); } diff --git a/src/Nethermind/Nethermind.Serialization.Ssz.Test/SszContainersTests.cs b/src/Nethermind/Nethermind.Serialization.Ssz.Test/SszContainersTests.cs deleted file mode 100644 index 8e7821c667f..00000000000 --- a/src/Nethermind/Nethermind.Serialization.Ssz.Test/SszContainersTests.cs +++ /dev/null @@ -1,628 +0,0 @@ -//// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited -//// SPDX-License-Identifier: LGPL-3.0-only - -//using System; -//using System.Collections; -//using System.Linq; -//using System.Text.Json; -//using Nethermind.Core.Crypto; -//using Nethermind.Core.Extensions; -//using Nethermind.Core2; -//using Nethermind.Core2.Containers; -//using Nethermind.Core2.Crypto; -//using Nethermind.Core2.Json; -//using Nethermind.Core2.Types; -//using Nethermind.Dirichlet.Numerics; -//using Nethermind.Merkleization; -//using NUnit.Framework; -//using Bytes = Nethermind.Core.Extensions.Bytes; -//using Shouldly; - -//namespace Nethermind.Serialization.Ssz.Test -//{ -// [TestFixture] -// public class SszContainersTests -// { -// public static BlsPublicKey TestKey1 = new BlsPublicKey( -// "0x000102030405060708090a0b0c0d0e0f" + -// "101112131415161718191a1b1c1d1e1f" + -// "202122232425262728292a2b2c2d2e2f"); - -// public static BlsSignature TestSig1 = new BlsSignature(new byte[BlsSignature.Length]); - -// [SetUp] -// public void Setup() -// { -// Ssz.Init( -// 32, -// 4, -// 2048, -// 32, -// 1024, -// 8192, -// 65536, -// 8192, -// 16_777_216, -// 1_099_511_627_776, -// 16, -// 1, -// 128, -// 16, -// 16 -// ); -// } - -// //[Test] -// public void Fork_there_and_back() -// { -// Fork container = new Fork(new ForkVersion(new byte[] { 0x01, 0x00, 0x00, 0x00 }), new ForkVersion(new byte[] { 0x02, 0x00, 0x00, 0x00 }), new Epoch(3)); -// Span encoded = new byte[Ssz.ForkLength]; -// Ssz.Encode(encoded, container); -// Fork decoded = Ssz.DecodeFork(encoded); -// Assert.AreEqual(container, decoded); - -// Merkle.Ize(out UInt256 root, container); -// } - -// [Test] -// public void Checkpoint_there_and_back() -// { -// Checkpoint container = new Checkpoint(new Epoch(1), Sha256.RootOfAnEmptyString); -// Span encoded = new byte[Ssz.CheckpointLength]; -// Ssz.Encode(encoded, container); -// Checkpoint decoded = Ssz.DecodeCheckpoint(encoded); -// Assert.AreEqual(container, decoded); - -// Merkle.Ize(out UInt256 root, container); -// } - -// [Test] -// public void Validator_there_and_back() -// { -// Validator container = new Validator( -// TestKey1, -// Sha256.Bytes32OfAnEmptyString, -// Gwei.One, -// true, -// new Epoch(4), -// new Epoch(5), -// new Epoch(6), -// new Epoch(7) -// ); - -// Span encoded = new byte[Ssz.ValidatorLength]; -// Ssz.Encode(encoded, container); -// Validator decoded = Ssz.DecodeValidator(encoded); -// decoded.ShouldBe(container); -// //Assert.AreEqual(container, decoded); -// Assert.AreEqual(7, decoded.WithdrawableEpoch.Number); - -// Merkle.Ize(out UInt256 root, container); -// } - -// [Test] -// public void Attestation_data_there_and_back() -// { -// AttestationData container = new AttestationData( -// new Slot(1), -// new CommitteeIndex(2), -// Sha256.RootOfAnEmptyString, -// new Checkpoint(new Epoch(1), Sha256.RootOfAnEmptyString), -// new Checkpoint(new Epoch(2), Sha256.RootOfAnEmptyString)); - -// Span encoded = new byte[Ssz.AttestationDataLength]; -// Ssz.Encode(encoded, container); -// AttestationData decoded = Ssz.DecodeAttestationData(encoded); -// Assert.AreEqual(container, decoded); - -// Span encodedAgain = new byte[Ssz.AttestationDataLength]; -// Ssz.Encode(encodedAgain, decoded); -// Assert.True(Bytes.AreEqual(encodedAgain, encoded)); - -// Merkle.Ize(out UInt256 root, container); -// } - -// [Test] -// public void Indexed_attestation_there_and_back() -// { -// AttestationData data = new AttestationData( -// new Slot(1), -// new CommitteeIndex(2), -// Sha256.RootOfAnEmptyString, -// new Checkpoint(new Epoch(1), Sha256.RootOfAnEmptyString), -// new Checkpoint(new Epoch(2), Sha256.RootOfAnEmptyString)); - -// IndexedAttestation container = new IndexedAttestation( -// new ValidatorIndex[3], -// data, -// TestSig1); - -// Span encoded = new byte[Ssz.IndexedAttestationLength(container)]; -// Ssz.Encode(encoded, container); -// IndexedAttestation decoded = Ssz.DecodeIndexedAttestation(encoded); - -// decoded.ShouldBe(container); - -// Merkle.Ize(out UInt256 root, container); -// } - -// [Test] -// public void Pending_attestation_there_and_back() -// { -// AttestationData data = new AttestationData( -// new Slot(1), -// new CommitteeIndex(2), -// Sha256.RootOfAnEmptyString, -// new Checkpoint(new Epoch(1), Sha256.RootOfAnEmptyString), -// new Checkpoint(new Epoch(2), Sha256.RootOfAnEmptyString)); - -// PendingAttestation container = new PendingAttestation( -// new BitArray(new byte[3]), -// data, -// new Slot(7), -// new ValidatorIndex(13)); - -// Span encoded = new byte[Ssz.PendingAttestationLength(container)]; -// Ssz.Encode(encoded, container); -// PendingAttestation? decoded = Ssz.DecodePendingAttestation(encoded); - -// decoded.ShouldBe(container); - -// Merkle.Ize(out UInt256 root, container); -// } - -// [Test] -// public void Eth1_data_there_and_back() -// { -// Eth1Data container = new Eth1Data( -// Sha256.RootOfAnEmptyString, -// 1, -// Sha256.Bytes32OfAnEmptyString); -// Span encoded = new byte[Ssz.Eth1DataLength]; -// Ssz.Encode(encoded, container); -// Eth1Data decoded = Ssz.DecodeEth1Data(encoded); -// Assert.AreEqual(container, decoded); - -// Merkle.Ize(out UInt256 root, container); -// } - -// [Test] -// public void Historical_batch_there_and_back() -// { -// Root[] blockRoots = Enumerable.Repeat(Root.Zero, Ssz.SlotsPerHistoricalRoot).ToArray(); -// Root[] stateRoots = Enumerable.Repeat(Root.Zero, Ssz.SlotsPerHistoricalRoot).ToArray(); -// blockRoots[3] = Sha256.RootOfAnEmptyString; -// stateRoots[7] = Sha256.RootOfAnEmptyString; -// HistoricalBatch container = new HistoricalBatch(blockRoots, stateRoots); -// Span encoded = new byte[Ssz.HistoricalBatchLength()]; -// Ssz.Encode(encoded, container); -// HistoricalBatch? decoded = Ssz.DecodeHistoricalBatch(encoded); -// Assert.AreEqual(container, decoded); - -// Merkle.Ize(out UInt256 root, container); -// } - -// [Test] -// public void Deposit_data_there_and_back() -// { -// DepositData container = new DepositData( -// TestKey1, -// Sha256.Bytes32OfAnEmptyString, -// Gwei.One, -// TestSig1); -// Span encoded = new byte[Ssz.DepositDataLength]; -// Ssz.Encode(encoded, container); -// DepositData decoded = Ssz.DecodeDepositData(encoded); -// Assert.AreEqual(container, decoded); - -// Merkle.Ize(out UInt256 root, container); -// } - -// [Test] -// public void Beacon_block_header_there_and_back() -// { -// BeaconBlockHeader container = new BeaconBlockHeader( -// new Slot(1), -// Sha256.RootOfAnEmptyString, -// Sha256.RootOfAnEmptyString, -// Sha256.RootOfAnEmptyString); -// Span encoded = new byte[Ssz.BeaconBlockHeaderLength]; -// Ssz.Encode(encoded, container); -// BeaconBlockHeader decoded = Ssz.DecodeBeaconBlockHeader(encoded); -// Assert.AreEqual(container, decoded); - -// Merkle.Ize(out UInt256 root, container); -// } - -// [Test] -// public void Proposer_slashing_there_and_back() -// { -// BeaconBlockHeader header1 = new BeaconBlockHeader( -// new Slot(1), -// Sha256.RootOfAnEmptyString, -// Sha256.RootOfAnEmptyString, -// Sha256.RootOfAnEmptyString); - -// BeaconBlockHeader header2 = new BeaconBlockHeader( -// new Slot(2), -// Sha256.RootOfAnEmptyString, -// Sha256.RootOfAnEmptyString, -// Sha256.RootOfAnEmptyString); - -// ProposerSlashing container = new ProposerSlashing( -// new ValidatorIndex(1), -// new SignedBeaconBlockHeader(header1, TestSig1), -// new SignedBeaconBlockHeader(header2, TestSig1)); - -// Span encoded = new byte[Ssz.ProposerSlashingLength]; -// Ssz.Encode(encoded, container); -// ProposerSlashing? decoded = Ssz.DecodeProposerSlashing(encoded); -// Assert.AreEqual(container, decoded); - -// Merkle.Ize(out UInt256 root, container); -// } - -// [Test] -// public void Attester_slashing_there_and_back() -// { -// AttestationData data = new AttestationData( -// new Slot(1), -// new CommitteeIndex(2), -// Sha256.RootOfAnEmptyString, -// new Checkpoint(new Epoch(1), Sha256.RootOfAnEmptyString), -// new Checkpoint(new Epoch(2), Sha256.RootOfAnEmptyString)); - -// IndexedAttestation indexedAttestation1 = new IndexedAttestation( -// new ValidatorIndex[3], -// data, -// TestSig1); - -// IndexedAttestation indexedAttestation2 = new IndexedAttestation( -// new ValidatorIndex[5], -// data, -// TestSig1); - -// AttesterSlashing container = new AttesterSlashing(indexedAttestation1, indexedAttestation2); - -// Span encoded = new byte[Ssz.AttesterSlashingLength(container)]; -// Ssz.Encode(encoded, container); -// AttesterSlashing? decoded = Ssz.DecodeAttesterSlashing(encoded); -// Assert.AreEqual(container, decoded); - -// Merkle.Ize(out UInt256 root, container); -// } - -// [Test] -// public void Attestation_there_and_back() -// { -// AttestationData data = new AttestationData( -// new Slot(1), -// new CommitteeIndex(2), -// Sha256.RootOfAnEmptyString, -// new Checkpoint(new Epoch(1), Sha256.RootOfAnEmptyString), -// new Checkpoint(new Epoch(2), Sha256.RootOfAnEmptyString)); - -// Attestation container = new Attestation( -// new BitArray(new byte[] { 1, 2, 3 }), -// data, -// TestSig1); - -// Span encoded = new byte[Ssz.AttestationLength(container)]; -// Ssz.Encode(encoded, container); -// Attestation decoded = Ssz.DecodeAttestation(encoded); -// Assert.AreEqual(container, decoded); - -// Merkle.Ize(out UInt256 root, container); -// } - -// [Test] -// public void Deposit_there_and_back() -// { -// DepositData data = new DepositData( -// TestKey1, -// Sha256.Bytes32OfAnEmptyString, -// Gwei.One, -// TestSig1); - -// Bytes32[] proof = Enumerable.Repeat(Bytes32.Zero, Ssz.DepositContractTreeDepth + 1).ToArray(); -// proof[7] = Sha256.Bytes32OfAnEmptyString; -// Deposit container = new Deposit(proof, new Ref(data)); - -// Span encoded = new byte[Ssz.DepositLength()]; -// Ssz.Encode(encoded, container); -// Deposit? decoded = Ssz.DecodeDeposit(encoded); -// Assert.AreEqual(container, decoded); - -// Merkle.Ize(out UInt256 root, container); -// } - -// [Test] -// public void Voluntary_exit_there_and_back() -// { -// VoluntaryExit container = new VoluntaryExit( -// new Epoch(1), -// new ValidatorIndex(2)); - -// Span encoded = new byte[Ssz.VoluntaryExitLength]; -// Ssz.Encode(encoded, container); -// VoluntaryExit? decoded = Ssz.DecodeVoluntaryExit(encoded); -// Assert.AreEqual(container, decoded); - -// Merkle.Ize(out UInt256 root, container); -// } - -// [Test] -// public void Beacon_block_body_there_and_back() -// { -// Eth1Data eth1Data = new Eth1Data( -// Sha256.RootOfAnEmptyString, -// 1, -// Sha256.Bytes32OfAnEmptyString); - -// Deposit zeroDeposit = new Deposit(Enumerable.Repeat(Bytes32.Zero, Ssz.DepositContractTreeDepth + 1), new Ref(DepositData.Zero)); -// BeaconBlockBody container = new BeaconBlockBody( -// TestSig1, -// eth1Data, -// new Bytes32(new byte[32]), -// Enumerable.Repeat(ProposerSlashing.Zero, 2).ToArray(), -// Enumerable.Repeat(AttesterSlashing.Zero, 3).ToArray(), -// Enumerable.Repeat(Attestation.Zero, 4).ToArray(), -// Enumerable.Repeat(zeroDeposit, 5).ToArray(), -// Enumerable.Repeat(SignedVoluntaryExit.Zero, 6).ToArray() -// ); - -// Span encoded = new byte[Ssz.BeaconBlockBodyLength(container)]; -// Ssz.Encode(encoded, container); -// BeaconBlockBody decoded = Ssz.DecodeBeaconBlockBody(encoded); - -// AssertBeaconBlockBodyEqual(container, decoded); - -// Merkle.Ize(out UInt256 root, container); -// } - -// [Test] -// public void Beacon_block_body_more_detailed() -// { -// AttestationData data = new AttestationData( -// new Slot(1), -// new CommitteeIndex(4), -// Sha256.RootOfAnEmptyString, -// new Checkpoint(new Epoch(2), Sha256.RootOfAnEmptyString), -// new Checkpoint(new Epoch(3), Sha256.RootOfAnEmptyString)); - -// Attestation attestation = new Attestation( -// new BitArray(new byte[5]), -// data, -// TestSig1); - -// Ref depositData = new Ref(new DepositData( -// TestKey1, -// Sha256.Bytes32OfAnEmptyString, -// new Gwei(7), -// TestSig1)); - -// Deposit deposit = new Deposit(Enumerable.Repeat(Bytes32.Zero, Ssz.DepositContractTreeDepth + 1), depositData); - -// IndexedAttestation indexedAttestation1 = new IndexedAttestation( -// new ValidatorIndex[8], -// data, -// TestSig1); - -// IndexedAttestation indexedAttestation2 = new IndexedAttestation( -// new ValidatorIndex[8], -// data, -// TestSig1); - -// AttesterSlashing slashing = new AttesterSlashing(indexedAttestation1, indexedAttestation2); - -// Eth1Data eth1Data = new Eth1Data( -// Sha256.RootOfAnEmptyString, -// 9, -// Sha256.Bytes32OfAnEmptyString); - -// Attestation[] attestations = Enumerable.Repeat(Attestation.Zero, 3).ToArray(); -// attestations[1] = attestation; - -// Deposit zeroDeposit = new Deposit(Enumerable.Repeat(Bytes32.Zero, Ssz.DepositContractTreeDepth + 1), new Ref(DepositData.Zero)); -// Deposit[] deposits = Enumerable.Repeat(zeroDeposit, 3).ToArray(); -// deposits[2] = deposit; - -// Bytes32 graffiti = new Bytes32(new byte[32]); - -// AttesterSlashing[] attesterSlashings = Enumerable.Repeat(AttesterSlashing.Zero, 3).ToArray(); -// attesterSlashings[0] = slashing; - -// ProposerSlashing[] proposerSlashings = Enumerable.Repeat(ProposerSlashing.Zero, 10).ToArray(); - -// SignedVoluntaryExit[] signedVoluntaryExits = Enumerable.Repeat(SignedVoluntaryExit.Zero, 11).ToArray(); - -// BeaconBlockBody body = new BeaconBlockBody( -// TestSig1, -// eth1Data, -// graffiti, -// proposerSlashings, -// attesterSlashings, -// attestations, -// deposits, -// signedVoluntaryExits -// ); - -// byte[] encoded = new byte[Ssz.BeaconBlockBodyLength(body)]; -// Ssz.Encode(encoded, body); -// } - -// [Test] -// public void Beacon_block_there_and_back() -// { -// Eth1Data eth1Data = new Eth1Data( -// Sha256.RootOfAnEmptyString, -// 1, -// Sha256.Bytes32OfAnEmptyString); - -// Deposit zeroDeposit = new Deposit(Enumerable.Repeat(Bytes32.Zero, Ssz.DepositContractTreeDepth + 1), new Ref(DepositData.Zero)); -// BeaconBlockBody beaconBlockBody = new BeaconBlockBody( -// TestSig1, -// eth1Data, -// new Bytes32(new byte[32]), -// Enumerable.Repeat(ProposerSlashing.Zero, 2).ToArray(), -// Enumerable.Repeat(AttesterSlashing.Zero, 3).ToArray(), -// Enumerable.Repeat(Attestation.Zero, 4).ToArray(), -// Enumerable.Repeat(zeroDeposit, 5).ToArray(), -// Enumerable.Repeat(SignedVoluntaryExit.Zero, 6).ToArray() -// ); - -// BeaconBlock container = new BeaconBlock( -// new Slot(1), -// Sha256.RootOfAnEmptyString, -// Sha256.RootOfAnEmptyString, -// beaconBlockBody); - -// Span encoded = new byte[Ssz.BeaconBlockLength(container)]; -// Ssz.Encode(encoded, container); -// BeaconBlock decoded = Ssz.DecodeBeaconBlock(encoded); - -// AssertBeaconBlockEqual(container, decoded); - -// Span encodedAgain = new byte[Ssz.BeaconBlockLength(container)]; -// Ssz.Encode(encodedAgain, decoded); - -// Assert.True(Bytes.AreEqual(encodedAgain, encoded)); - -// Merkle.Ize(out UInt256 root, container); -// } - -// [Test] -// public void Beacon_state_there_and_back() -// { -// Eth1Data eth1Data = new Eth1Data( -// Sha256.RootOfAnEmptyString, -// 1, -// Sha256.Bytes32OfAnEmptyString); - -// BeaconBlockHeader beaconBlockHeader = new BeaconBlockHeader( -// new Slot(14), -// Sha256.RootOfAnEmptyString, -// Sha256.RootOfAnEmptyString, -// Sha256.RootOfAnEmptyString); - -// Deposit zeroDeposit = new Deposit(Enumerable.Repeat(Bytes32.Zero, Ssz.DepositContractTreeDepth + 1), new Ref(DepositData.Zero)); -// BeaconBlockBody beaconBlockBody = new BeaconBlockBody( -// TestSig1, -// eth1Data, -// new Bytes32(new byte[32]), -// Enumerable.Repeat(ProposerSlashing.Zero, 2).ToArray(), -// Enumerable.Repeat(AttesterSlashing.Zero, 3).ToArray(), -// Enumerable.Repeat(Attestation.Zero, 4).ToArray(), -// Enumerable.Repeat(zeroDeposit, 5).ToArray(), -// Enumerable.Repeat(SignedVoluntaryExit.Zero, 6).ToArray() -// ); - -// BeaconBlock beaconBlock = new BeaconBlock( -// new Slot(1), -// Sha256.RootOfAnEmptyString, -// Sha256.RootOfAnEmptyString, -// beaconBlockBody); - -// BeaconState container = new BeaconState( -// 123, -// new Slot(1), -// new Fork(new ForkVersion(new byte[] { 0x05, 0x00, 0x00, 0x00 }), -// new ForkVersion(new byte[] { 0x07, 0x00, 0x00, 0x00 }), new Epoch(3)), -// beaconBlockHeader, -// Enumerable.Repeat(Root.Zero, Ssz.SlotsPerHistoricalRoot).ToArray(), -// Enumerable.Repeat(Root.Zero, Ssz.SlotsPerHistoricalRoot).ToArray(), -// Enumerable.Repeat(Root.Zero, 13).ToArray(), -// eth1Data, -// Enumerable.Repeat(Eth1Data.Zero, 2).ToArray(), -// 1234, -// Enumerable.Repeat(Validator.Zero, 7).ToArray(), -// new Gwei[3], -// Enumerable.Repeat(Bytes32.Zero, Ssz.EpochsPerHistoricalVector).ToArray(), -// new Gwei[Ssz.EpochsPerSlashingsVector], -// Enumerable.Repeat(PendingAttestation.Zero, 1).ToArray(), -// Enumerable.Repeat(PendingAttestation.Zero, 11).ToArray(), -// new BitArray(new byte[] { 0x09 }), -// new Checkpoint(new Epoch(3), Sha256.RootOfAnEmptyString), -// new Checkpoint(new Epoch(5), Sha256.RootOfAnEmptyString), -// new Checkpoint(new Epoch(7), Sha256.RootOfAnEmptyString) -// ); - -// JsonSerializerOptions options = new JsonSerializerOptions { WriteIndented = true }; -// options.ConfigureNethermindCore2(); -// TestContext.Out.WriteLine("Original state: {0}", JsonSerializer.Serialize(container, options)); - -// int encodedLength = Ssz.BeaconStateLength(container); -// TestContext.Out.WriteLine("Encoded length: {0}", encodedLength); -// Span encoded = new byte[encodedLength]; -// Ssz.Encode(encoded, container); -// BeaconState decoded = Ssz.DecodeBeaconState(encoded); - -// TestContext.Out.WriteLine("Decoded state: {0}", JsonSerializer.Serialize(decoded, options)); - -// AssertBeaconStateEqual(container, decoded); - -// Span encodedAgain = new byte[Ssz.BeaconStateLength(decoded)]; -// Ssz.Encode(encodedAgain, decoded); - -// byte[] encodedArray = encoded.ToArray(); -// byte[] encodedAgainArray = encodedAgain.ToArray(); - -// encodedAgainArray.Length.ShouldBe(encodedArray.Length); -// //encodedAgainArray.ShouldBe(encodedArray); -// //Assert.True(Bytes.AreEqual(encodedAgain, encoded)); - -// Merkle.Ize(out UInt256 root, container); -// } - -// private void AssertBeaconBlockBodyEqual(BeaconBlockBody expected, BeaconBlockBody actual) -// { -// actual.RandaoReveal.ShouldBe(expected.RandaoReveal); -// actual.Eth1Data.ShouldBe(expected.Eth1Data); -// actual.Graffiti.ShouldBe(expected.Graffiti); -// actual.ProposerSlashings.Count.ShouldBe(expected.ProposerSlashings.Count); -// actual.AttesterSlashings.Count.ShouldBe(expected.AttesterSlashings.Count); -// actual.Attestations.Count.ShouldBe(expected.Attestations.Count); -// actual.Deposits.Count.ShouldBe(expected.Deposits.Count); -// actual.VoluntaryExits.Count.ShouldBe(expected.VoluntaryExits.Count); - -// actual.AttesterSlashings.ShouldBe(expected.AttesterSlashings); -// actual.ProposerSlashings.ShouldBe(expected.ProposerSlashings); -// actual.Attestations.ShouldBe(expected.Attestations); -// actual.Deposits.ShouldBe(expected.Deposits); -// actual.VoluntaryExits.ShouldBe(expected.VoluntaryExits); -// } - -// private void AssertBeaconBlockEqual(BeaconBlock expected, BeaconBlock actual) -// { -// actual.Slot.ShouldBe(expected.Slot); -// actual.ParentRoot.ShouldBe(expected.ParentRoot); -// actual.StateRoot.ShouldBe(expected.StateRoot); - -// AssertBeaconBlockBodyEqual(expected.Body, actual.Body); -// } - -// public void AssertBeaconStateEqual(BeaconState expected, BeaconState actual) -// { -// actual.GenesisTime.ShouldBe(expected.GenesisTime); -// actual.Slot.ShouldBe(expected.Slot); -// actual.Fork.ShouldBe(expected.Fork); -// actual.LatestBlockHeader.ShouldBe(expected.LatestBlockHeader); -// actual.BlockRoots.Count.ShouldBe(expected.BlockRoots?.Count ?? 0); -// actual.StateRoots.Count.ShouldBe(expected.StateRoots?.Count ?? 0); -// actual.HistoricalRoots.Count.ShouldBe(expected.HistoricalRoots?.Count ?? 0); -// actual.Eth1Data.ShouldBe(expected.Eth1Data); -// actual.Eth1DataVotes.Count.ShouldBe(expected.Eth1DataVotes?.Count ?? 0); -// actual.Eth1DepositIndex.ShouldBe(expected.Eth1DepositIndex); -// actual.Validators.Count.ShouldBe(expected.Validators?.Count ?? 0); -// actual.Balances.Count.ShouldBe(expected.Balances?.Count ?? 0); -// actual.RandaoMixes.Count.ShouldBe(expected.RandaoMixes?.Count ?? 0); -// actual.Slashings.Count.ShouldBe(expected.Slashings?.Count ?? 0); -// actual.PreviousEpochAttestations.Count.ShouldBe(expected.PreviousEpochAttestations?.Count ?? 0); -// actual.CurrentEpochAttestations.Count.ShouldBe(expected.CurrentEpochAttestations?.Count ?? 0); -// //expected.JustificationBits.Count.ShouldBe(actual.JustificationBits.Count); -// actual.PreviousJustifiedCheckpoint.ShouldBe(expected.PreviousJustifiedCheckpoint); -// actual.CurrentJustifiedCheckpoint.ShouldBe(expected.CurrentJustifiedCheckpoint); -// actual.FinalizedCheckpoint.ShouldBe(expected.FinalizedCheckpoint); -// } -// } -//} diff --git a/src/Nethermind/Nethermind.Serialization.Ssz/Crypto/Ssz.Root.cs b/src/Nethermind/Nethermind.Serialization.Ssz/Crypto/Ssz.Root.cs index 782dec6d669..92308a77518 100644 --- a/src/Nethermind/Nethermind.Serialization.Ssz/Crypto/Ssz.Root.cs +++ b/src/Nethermind/Nethermind.Serialization.Ssz/Crypto/Ssz.Root.cs @@ -54,6 +54,8 @@ public static Root DecodeRoot(ReadOnlySpan span) return new Root(span); } + + public static Root[] DecodeRoots(ReadOnlySpan span) { if (span.Length == 0) diff --git a/src/Nethermind/Nethermind.Serialization.Ssz/MiscDependencies/Ssz.Bytes32.cs b/src/Nethermind/Nethermind.Serialization.Ssz/MiscDependencies/Ssz.Bytes32.cs index b83e902dc15..e562ce13c1d 100644 --- a/src/Nethermind/Nethermind.Serialization.Ssz/MiscDependencies/Ssz.Bytes32.cs +++ b/src/Nethermind/Nethermind.Serialization.Ssz/MiscDependencies/Ssz.Bytes32.cs @@ -12,7 +12,7 @@ public static partial class Ssz { public const int Bytes32Length = Bytes32.Length; - public static ReadOnlySpan DecodeBytes(ReadOnlySpan span) + public static byte[] DecodeBytes(ReadOnlySpan span) { return span.ToArray(); } diff --git a/src/Nethermind/Nethermind.Serialization.Ssz/Ssz.BasicTypes.cs b/src/Nethermind/Nethermind.Serialization.Ssz/Ssz.BasicTypes.cs index a18dcc8d18e..401a26470b5 100644 --- a/src/Nethermind/Nethermind.Serialization.Ssz/Ssz.BasicTypes.cs +++ b/src/Nethermind/Nethermind.Serialization.Ssz/Ssz.BasicTypes.cs @@ -16,47 +16,47 @@ namespace Nethermind.Serialization.Ssz; public static partial class Ssz { [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static void Encode(Span span, byte[] value, ref int offset) + public static void Encode(Span span, byte[] value, ref int offset) { Encode(span.Slice(offset, value.Length), value); offset += value.Length; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static void Encode(Span span, int value, ref int offset) + public static void Encode(Span span, int value, ref int offset) { BinaryPrimitives.WriteInt32LittleEndian(span.Slice(offset, sizeof(int)), value); offset += sizeof(int); } - private static void Encode(Span span, uint value, ref int offset) + public static void Encode(Span span, uint value, ref int offset) { BinaryPrimitives.WriteUInt32LittleEndian(span.Slice(offset, sizeof(uint)), value); offset += sizeof(uint); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static void Encode(Span span, ulong value, ref int offset) + public static void Encode(Span span, ulong value, ref int offset) { Encode(span.Slice(offset, sizeof(ulong)), value); offset += sizeof(ulong); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static void Encode(Span span, UInt256 value, ref int offset) + public static void Encode(Span span, UInt256 value, ref int offset) { Encode(span.Slice(offset, 32), value); offset += 32; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static void Encode(Span span, bool value, ref int offset) + public static void Encode(Span span, bool value, ref int offset) { Encode(span.Slice(offset, 1), value); offset++; } - private static bool DecodeBool(Span span, ref int offset) + public static bool DecodeBool(Span span, ref int offset) { return span[offset++] == 1; } @@ -66,7 +66,7 @@ public static void Encode(Span span, byte value) span[0] = value; } - private static void Encode(Span span, byte value, ref int offset) + public static void Encode(Span span, byte value, ref int offset) { span[offset++] = value; } @@ -95,6 +95,12 @@ public static void Encode(Span span, ulong value) BinaryPrimitives.WriteUInt64LittleEndian(span, value); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Encode(Span span, long value) + { + BinaryPrimitives.WriteInt64LittleEndian(span, value); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Encode(Span span, UInt128 value) { @@ -194,7 +200,7 @@ public static void Encode(Span span, Span value) MemoryMarshal.Cast(value).CopyTo(span); } - private static void Encode(Span span, Span value, ref int offset, ref int dynamicOffset) + public static void Encode(Span span, Span value, ref int offset, ref int dynamicOffset) { BinaryPrimitives.WriteInt32LittleEndian(span.Slice(offset, VarOffsetSize), dynamicOffset); offset += VarOffsetSize; @@ -277,7 +283,7 @@ public static ulong DecodeULong(ReadOnlySpan span) } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static ulong DecodeULong(ReadOnlySpan span, ref int offset) + public static ulong DecodeULong(ReadOnlySpan span, ref int offset) { ulong result = BinaryPrimitives.ReadUInt64LittleEndian(span.Slice(offset, sizeof(ulong))); offset += sizeof(ulong); @@ -335,7 +341,7 @@ public static UInt256[] DecodeUInts256(Span span) return result; } - private static byte[][] DecodeBytesArrays(ReadOnlySpan span, int itemsCount, int itemLength, ref int offset) + public static byte[][] DecodeBytesArrays(ReadOnlySpan span, int itemsCount, int itemLength, ref int offset) { byte[][] result = new byte[itemsCount][]; @@ -408,7 +414,7 @@ public static Span DecodeBools(Span span) } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static void ThrowTargetLength(int targetLength, int expectedLength) + public static void ThrowTargetLength(int targetLength, int expectedLength) { Type type = typeof(T); throw new InvalidDataException( @@ -416,7 +422,7 @@ private static void ThrowTargetLength(int targetLength, int expectedLength) } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static void ThrowSourceLength(int sourceLength, int expectedLength) + public static void ThrowSourceLength(int sourceLength, int expectedLength) { Type type = typeof(T); throw new InvalidDataException( @@ -424,7 +430,7 @@ private static void ThrowSourceLength(int sourceLength, int expectedLength) } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static void ThrowInvalidSourceArrayLength(int sourceLength, int expectedItemLength) + public static void ThrowInvalidSourceArrayLength(int sourceLength, int expectedItemLength) { Type type = typeof(T); throw new InvalidDataException( diff --git a/src/Nethermind/Nethermind.Serialization.Ssz/Ssz.Configuration.cs b/src/Nethermind/Nethermind.Serialization.Ssz/Ssz.Configuration.cs deleted file mode 100644 index 8f9381b4941..00000000000 --- a/src/Nethermind/Nethermind.Serialization.Ssz/Ssz.Configuration.cs +++ /dev/null @@ -1,64 +0,0 @@ -// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited -// SPDX-License-Identifier: LGPL-3.0-only - -namespace Nethermind.Serialization.Ssz; - -public partial class Ssz -{ - public static int DepositContractTreeDepth { get; private set; } - private static int JustificationBitsLength; - public static ulong MaximumDepositContracts { get; private set; } - - public static uint MaxValidatorsPerCommittee { get; private set; } - - public static uint SlotsPerEpoch { get; private set; } - public static int SlotsPerEth1VotingPeriod { get; private set; } - public static int SlotsPerHistoricalRoot { get; private set; } - - public static int EpochsPerHistoricalVector { get; private set; } - public static int EpochsPerSlashingsVector { get; private set; } - public static ulong HistoricalRootsLimit { get; private set; } - public static ulong ValidatorRegistryLimit { get; private set; } - - public static uint MaxProposerSlashings { get; private set; } - public static uint MaxAttesterSlashings { get; private set; } - public static uint MaxAttestations { get; private set; } - public static uint MaxDeposits { get; private set; } - public static uint MaxVoluntaryExits { get; private set; } - - public static void Init(int depositContractTreeDepth, - int justificationBitsLength, - ulong maximumValidatorsPerCommittee, - ulong slotsPerEpoch, - ulong slotsPerEth1VotingPeriod, - ulong slotsPerHistoricalRoot, - ulong epochsPerHistoricalVector, - ulong epochsPerSlashingsVector, - ulong historicalRootsLimit, - ulong validatorRegistryLimit, - ulong maximumProposerSlashings, - ulong maximumAttesterSlashings, - ulong maximumAttestations, - ulong maximumDeposits, - ulong maximumVoluntaryExits - ) - { - DepositContractTreeDepth = depositContractTreeDepth; - JustificationBitsLength = justificationBitsLength; - MaxValidatorsPerCommittee = (uint)maximumValidatorsPerCommittee; - SlotsPerEpoch = (uint)slotsPerEpoch; - SlotsPerEth1VotingPeriod = (int)slotsPerEth1VotingPeriod; - SlotsPerHistoricalRoot = (int)slotsPerHistoricalRoot; - EpochsPerHistoricalVector = (int)epochsPerHistoricalVector; - EpochsPerSlashingsVector = (int)epochsPerSlashingsVector; - HistoricalRootsLimit = historicalRootsLimit; - ValidatorRegistryLimit = validatorRegistryLimit; - MaxProposerSlashings = (uint)maximumProposerSlashings; - MaxAttesterSlashings = (uint)maximumAttesterSlashings; - MaxAttestations = (uint)maximumAttestations; - MaxDeposits = (uint)maximumDeposits; - MaxVoluntaryExits = (uint)maximumVoluntaryExits; - - MaximumDepositContracts = (ulong)1 << depositContractTreeDepth; - } -} diff --git a/src/Nethermind/Nethermind.Serialization.Ssz/Ssz.Containers.cs b/src/Nethermind/Nethermind.Serialization.Ssz/Ssz.Containers.cs index b9539d7fe94..3055b6038a6 100644 --- a/src/Nethermind/Nethermind.Serialization.Ssz/Ssz.Containers.cs +++ b/src/Nethermind/Nethermind.Serialization.Ssz/Ssz.Containers.cs @@ -14,4 +14,5 @@ private static void DecodeDynamicOffset(ReadOnlySpan span, ref int offset, dynamicOffset = (int)DecodeUInt(span.Slice(offset, VarOffsetSize)); offset += sizeof(uint); } + } diff --git a/src/Nethermind/Nethermind.Serialization.Ssz/Ssz.Decode.cs b/src/Nethermind/Nethermind.Serialization.Ssz/Ssz.Decode.cs new file mode 100644 index 00000000000..eeeaae4abe4 --- /dev/null +++ b/src/Nethermind/Nethermind.Serialization.Ssz/Ssz.Decode.cs @@ -0,0 +1,267 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System; +using System.Buffers.Binary; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using Nethermind.Int256; + +namespace Nethermind.Serialization.Ssz; + +/// +/// https://github.com/ethereum/eth2.0-specs/blob/dev/specs/simple-serialize.md#simpleserialize-ssz +/// +public static partial class Ssz +{ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Decode(ReadOnlySpan span, out bool result) + { + result = span[0] != 0; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Decode(ReadOnlySpan span, out byte result) + { + const int expectedLength = 1; + if (span.Length != expectedLength) + { + throw new InvalidDataException( + $"{nameof(DecodeByte)} expects input of length {expectedLength} and received {span.Length}"); + } + + result = span[0]; + } + + public static void Decode(ReadOnlySpan span, out ushort result) + { + const int expectedLength = 2; + if (span.Length != expectedLength) + { + throw new InvalidDataException( + $"{nameof(DecodeUShort)} expects input of length {expectedLength} and received {span.Length}"); + } + + result = BinaryPrimitives.ReadUInt16LittleEndian(span); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Decode(ReadOnlySpan span, out uint result) + { + const int expectedLength = 4; + if (span.Length != expectedLength) + { + throw new InvalidDataException( + $"{nameof(DecodeUInt)} expects input of length {expectedLength} and received {span.Length}"); + } + + result = BinaryPrimitives.ReadUInt32LittleEndian(span); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Decode(ReadOnlySpan span, out int result) + { + const int expectedLength = 4; + if (span.Length != expectedLength) + { + throw new InvalidDataException( + $"{nameof(DecodeUInt)} expects input of length {expectedLength} and received {span.Length}"); + } + + result = BinaryPrimitives.ReadInt32LittleEndian(span); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Decode(ReadOnlySpan span, out ulong result) + { + const int expectedLength = 8; + if (span.Length != expectedLength) + { + throw new InvalidDataException( + $"{nameof(Decode)} expects input of length {expectedLength} and received {span.Length}"); + } + + result = BinaryPrimitives.ReadUInt64LittleEndian(span); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Decode(ReadOnlySpan span, out long result) + { + const int expectedLength = 8; + if (span.Length != expectedLength) + { + throw new InvalidDataException( + $"{nameof(Decode)} expects input of length {expectedLength} and received {span.Length}"); + } + + result = BinaryPrimitives.ReadInt64LittleEndian(span); + } + + public static void Decode(ReadOnlySpan span, out UInt128 result) + { + const int expectedLength = 16; + if (span.Length != expectedLength) + { + throw new InvalidDataException( + $"{nameof(DecodeUInt128)} expects input of length {expectedLength} and received {span.Length}"); + } + + ulong s0 = BinaryPrimitives.ReadUInt64LittleEndian(span[..8]); + ulong s1 = BinaryPrimitives.ReadUInt64LittleEndian(span.Slice(8, 8)); + result = new UInt128(s0, s1); + } + + public static void Decode(ReadOnlySpan span, out UInt256 value) + { + const int expectedLength = 32; + if (span.Length != expectedLength) + { + throw new InvalidDataException( + $"{nameof(DecodeUInt256)} expects input of length {expectedLength} and received {span.Length}"); + } + + value = new UInt256(span); + } + + + + public static void Decode(ReadOnlySpan span, out ReadOnlySpan result) + { + result = span; + } + + public static void Decode(ReadOnlySpan span, out ReadOnlySpan result) + { + const int typeSize = 2; + if (span.Length % typeSize != 0) + { + throw new InvalidDataException( + $"{nameof(DecodeUShorts)} expects input in multiples of {typeSize} and received {span.Length}"); + } + + result = MemoryMarshal.Cast(span); + } + + public static void Decode(ReadOnlySpan span, out ReadOnlySpan result) + { + const int typeSize = 2; + if (span.Length % typeSize != 0) + { + throw new InvalidDataException( + $"{nameof(DecodeUShorts)} expects input in multiples of {typeSize} and received {span.Length}"); + } + + result = MemoryMarshal.Cast(span); + } + + public static void Decode(ReadOnlySpan span, out ReadOnlySpan result) + { + const int typeSize = 4; + if (span.Length % typeSize != 0) + { + throw new InvalidDataException( + $"{nameof(DecodeUInts)} expects input in multiples of {typeSize} and received {span.Length}"); + } + + result = MemoryMarshal.Cast(span); + } + + public static void Decode(ReadOnlySpan span, out ReadOnlySpan result) + { + const int typeSize = 4; + if (span.Length % typeSize != 0) + { + throw new InvalidDataException( + $"{nameof(DecodeUInts)} expects input in multiples of {typeSize} and received {span.Length}"); + } + + result = MemoryMarshal.Cast(span); + } + + public static void Decode(ReadOnlySpan span, out ReadOnlySpan result) + { + const int typeSize = 4; + if (span.Length % typeSize != 0) + { + throw new InvalidDataException( + $"{nameof(DecodeUInts)} expects input in multiples of {typeSize} and received {span.Length}"); + } + + result = MemoryMarshal.Cast(span); + } + + public static void Decode(ReadOnlySpan span, out ReadOnlySpan result) + { + const int typeSize = 4; + if (span.Length % typeSize != 0) + { + throw new InvalidDataException( + $"{nameof(DecodeUInts)} expects input in multiples of {typeSize} and received {span.Length}"); + } + + result = MemoryMarshal.Cast(span); + } + + public static void Decode(ReadOnlySpan span, out ReadOnlySpan result) + { + const int typeSize = 16; + if (span.Length % typeSize != 0) + { + throw new InvalidDataException( + $"{nameof(DecodeUInts128)} expects input in multiples of {typeSize} and received {span.Length}"); + } + + UInt128[] array = new UInt128[span.Length / typeSize]; + + for (int i = 0; i < span.Length / typeSize; i++) + { + Decode(span.Slice(i * typeSize, typeSize), out array[i]); + } + + result = array; + } + + public static void Decode(ReadOnlySpan span, out ReadOnlySpan result) + { + const int typeSize = 32; + if (span.Length % typeSize != 0) + { + throw new InvalidDataException( + $"{nameof(DecodeUInts128)} expects input in multiples of {typeSize} and received {span.Length}"); + } + + UInt256[] array = new UInt256[span.Length / typeSize]; + + for (int i = 0; i < span.Length / typeSize; i++) + { + Decode(span.Slice(i * typeSize, typeSize), out array[i]); + } + + result = array; + } + + public static void Decode(ReadOnlySpan span, int vectorLength, out BitArray vector) + { + BitArray value = new BitArray(span.ToArray()); + value.Length = vectorLength; + vector = value; + } + + public static void Decode(ReadOnlySpan span, out BitArray list) + { + BitArray value = new BitArray(span.ToArray()); + int length = value.Length - 1; + int lastByte = span[^1]; + int mask = 0x80; + while ((lastByte & mask) == 0 && mask > 0) + { + length--; + mask >>= 1; + } + value.Length = length; + list = value; + } +} diff --git a/src/Nethermind/Nethermind.Serialization.Ssz/Ssz.Encode.cs b/src/Nethermind/Nethermind.Serialization.Ssz/Ssz.Encode.cs new file mode 100644 index 00000000000..a398d17b038 --- /dev/null +++ b/src/Nethermind/Nethermind.Serialization.Ssz/Ssz.Encode.cs @@ -0,0 +1,38 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System; +using System.Collections; + +namespace Nethermind.Serialization.Ssz; + +/// +/// https://github.com/ethereum/eth2.0-specs/blob/dev/specs/simple-serialize.md#simpleserialize-ssz +/// +public static partial class Ssz +{ + public static void Encode(Span span, BitArray? vector) + { + if (vector is null) + { + return; + } + int byteLength = (vector.Length + 7) / 8; + byte[] bytes = new byte[byteLength]; + vector.CopyTo(bytes, 0); + Encode(span, bytes); + } + + public static void Encode(Span span, BitArray? list, int limit) + { + if (list is null) + { + return; + } + int byteLength = (list.Length + 8) / 8; + byte[] bytes = new byte[byteLength]; + list.CopyTo(bytes, 0); + bytes[byteLength - 1] |= (byte)(1 << (list.Length % 8)); + Encode(span, bytes); + } +} diff --git a/src/Nethermind/Nethermind.Serialization.Ssz/SszSerializableAttribute.cs b/src/Nethermind/Nethermind.Serialization.Ssz/SszSerializableAttribute.cs new file mode 100644 index 00000000000..89830705f1e --- /dev/null +++ b/src/Nethermind/Nethermind.Serialization.Ssz/SszSerializableAttribute.cs @@ -0,0 +1,21 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System; + +namespace Nethermind.Serialization.Ssz; + +[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct)] +public class SszSerializableAttribute : Attribute; + +[AttributeUsage(AttributeTargets.Property)] +public class SszListAttribute(int limit) : Attribute +{ + public int Limit { get; } = limit; +} + +[AttributeUsage(AttributeTargets.Property)] +public class SszVectorAttribute(int length) : Attribute +{ + public int Length { get; } = length; +} diff --git a/src/Nethermind/Nethermind.Serialization.SszGenerator.Test/EncodingTest.cs b/src/Nethermind/Nethermind.Serialization.SszGenerator.Test/EncodingTest.cs new file mode 100644 index 00000000000..627829e7bb1 --- /dev/null +++ b/src/Nethermind/Nethermind.Serialization.SszGenerator.Test/EncodingTest.cs @@ -0,0 +1,41 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using Nethermind.Int256; +using NUnit.Framework; +using System.Linq; + +namespace Nethermind.Serialization.SszGenerator.Test; + +public class EncodingTest +{ + [Test] + public void Test_ComplexStructure_EncodingRoundTrip() + { + ComplexStruct test = new() + { + VariableC = new VariableC { Fixed1 = 2, Fixed2 = [1, 2, 3, 4] }, + Test2 = Enumerable.Range(0, 10).Select(i => (ulong)i).ToArray(), + FixedCVec = Enumerable.Range(0, 10).Select(i => new FixedC()).ToArray(), + VariableCVec = Enumerable.Range(0, 10).Select(i => new VariableC()).ToArray(), + Test2UnionVec = Enumerable.Range(0, 10).Select(i => new UnionTest3() + { + VariableC = new VariableC { Fixed1 = 2, Fixed2 = [1, 2, 3, 4] }, + Test2 = Enumerable.Range(0, 10).Select(i => (ulong)i).ToArray(), + FixedCVec = Enumerable.Range(0, 10).Select(i => new FixedC()).ToArray(), + VariableCVec = Enumerable.Range(0, 10).Select(i => new VariableC()).ToArray(), + BitVec = new System.Collections.BitArray(10), + }).ToArray(), + BitVec = new System.Collections.BitArray(10), + }; + + var encoded = SszEncoding.Encode(test); + SszEncoding.Merkleize(test, out UInt256 root); + SszEncoding.Decode(encoded, out ComplexStruct decodedTest); + + Assert.That(decodedTest.VariableC.Fixed1, Is.EqualTo(test.VariableC.Fixed1)); + Assert.That(decodedTest.VariableC.Fixed2, Is.EqualTo(test.VariableC.Fixed2)); + SszEncoding.Merkleize(test, out UInt256 decodedRoot); + Assert.That(root, Is.EqualTo(decodedRoot)); + } +} diff --git a/src/Nethermind/Nethermind.Serialization.SszGenerator.Test/Nethermind.Serialization.SszGenerator.Test.csproj b/src/Nethermind/Nethermind.Serialization.SszGenerator.Test/Nethermind.Serialization.SszGenerator.Test.csproj new file mode 100644 index 00000000000..795e6710761 --- /dev/null +++ b/src/Nethermind/Nethermind.Serialization.SszGenerator.Test/Nethermind.Serialization.SszGenerator.Test.csproj @@ -0,0 +1,32 @@ + + + + false + enable + false + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + + + + diff --git a/src/Nethermind/Nethermind.Serialization.SszGenerator.Test/SszTypes.cs b/src/Nethermind/Nethermind.Serialization.SszGenerator.Test/SszTypes.cs new file mode 100644 index 00000000000..f1249c1ae66 --- /dev/null +++ b/src/Nethermind/Nethermind.Serialization.SszGenerator.Test/SszTypes.cs @@ -0,0 +1,157 @@ +// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using Nethermind.Merkleization; +using Nethermind.Serialization.Ssz; +using System.Collections; +using System.ComponentModel; + +namespace Nethermind.Serialization.SszGenerator.Test +{ + [SszSerializable] + public struct ComplexStruct + { + public ulong Test1 { get; set; } + + [SszVector(10)] + public ulong[] Test2 { get; set; } + + [SszList(10)] + public ulong[] Test3 { get; set; } + public FixedC FixedC { get; set; } + public VariableC VariableC { get; set; } + + + [SszVector(10)] + public FixedC[] FixedCVec { get; set; } + + [SszList(10)] + public FixedC[] FixedCList { get; set; } + + + [SszVector(10)] + public VariableC[] VariableCVec { get; set; } + + [SszList(10)] + public VariableC[] VariableCList { get; set; } + + public UnionTest3 Test2Union { get; set; } + + [SszVector(10)] + public UnionTest3[]? Test2UnionVec { get; set; } + + [SszList(10)] + public UnionTest3[]? Test2UnionList { get; set; } + + [SszVector(10)] + public BitArray? BitVec { get; set; } + + [SszList(10)] + public BitArray? BitList2 { get; set; } + } + + [SszSerializable] + public struct FixedC + { + public ulong Fixed1 { get; set; } + public ulong Fixed2 { get; set; } + } + + [SszSerializable] + public struct VariableC + { + public ulong Fixed1 { get; set; } + + [SszList(10)] + public ulong[]? Fixed2 { get; set; } + } + + //// Does not compile + //[SszSerializable] + //public struct NoProps + //{ + + //} + + [SszSerializable] + public struct UnionTest3 + { + public Test3Union Selector { get; set; } + public ulong Test1 { get; set; } + + [SszVector(10)] + public ulong[] Test2 { get; set; } + + [SszList(10)] + public ulong[] Test3 { get; set; } + public FixedC FixedC { get; set; } + public VariableC VariableC { get; set; } + + + [SszVector(10)] + public FixedC[] FixedCVec { get; set; } + + [SszList(10)] + public FixedC[] FixedCList { get; set; } + + + [SszVector(10)] + public VariableC[] VariableCVec { get; set; } + + [SszList(10)] + public VariableC[] VariableCList { get; set; } + + public Test2 Test2Union { get; set; } + + [SszVector(10)] + public Test2[]? Test2UnionVec { get; set; } + + [SszList(10)] + public Test2[]? Test2UnionList { get; set; } + + [SszVector(10)] + public BitArray? BitVec { get; set; } + + [SszList(10)] + public BitArray? BitList { get; set; } + } + + [SszSerializable] + public struct Test2 + { + public Test2() + { + int[] x = []; + Merkleizer merkleizer = new Merkleizer(Merkle.NextPowerOfTwoExponent(14)); + merkleizer.Feed(x); + } + + public Test2Union Selector { get; set; } + public long Type1 { get; set; } + public int Type2 { get; set; } + } + + public enum Test2Union : byte + { + None, + Type1, + Type2, + } + public enum Test3Union : byte + { + Test1, + Test2, + Test3, + FixedC, + VariableC, + FixedCVec, + FixedCList, + VariableCVec, + VariableCList, + Test2Union, + Test2UnionVec, + Test2UnionList, + BitVec, + BitList, + } +} diff --git a/src/Nethermind/Nethermind.Serialization.SszGenerator/AnalyzerReleases.Shipped.md b/src/Nethermind/Nethermind.Serialization.SszGenerator/AnalyzerReleases.Shipped.md new file mode 100644 index 00000000000..60b59dd99b4 --- /dev/null +++ b/src/Nethermind/Nethermind.Serialization.SszGenerator/AnalyzerReleases.Shipped.md @@ -0,0 +1,3 @@ +; Shipped analyzer releases +; https://github.com/dotnet/roslyn-analyzers/blob/main/src/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md + diff --git a/src/Nethermind/Nethermind.Serialization.SszGenerator/AnalyzerReleases.Unshipped.md b/src/Nethermind/Nethermind.Serialization.SszGenerator/AnalyzerReleases.Unshipped.md new file mode 100644 index 00000000000..af79f7e195f --- /dev/null +++ b/src/Nethermind/Nethermind.Serialization.SszGenerator/AnalyzerReleases.Unshipped.md @@ -0,0 +1,9 @@ +; Unshipped analyzer release +; https://github.com/dotnet/roslyn-analyzers/blob/main/src/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md + +### New Rules + +Rule ID | Category | Severity | Notes +--------|----------|----------|------- +SSZ001 | Design | Error | PropertyRequiredInSszTypeAnalyzer +SSZ002 | Design | Error | CollectionTypeAnalyzer diff --git a/src/Nethermind/Nethermind.Serialization.SszGenerator/Analyzers/CollectionTypeAnalyzer.cs b/src/Nethermind/Nethermind.Serialization.SszGenerator/Analyzers/CollectionTypeAnalyzer.cs new file mode 100644 index 00000000000..3892a502d3f --- /dev/null +++ b/src/Nethermind/Nethermind.Serialization.SszGenerator/Analyzers/CollectionTypeAnalyzer.cs @@ -0,0 +1,78 @@ +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; + +[DiagnosticAnalyzer(LanguageNames.CSharp)] +public class CollectionTypeAnalyzer : DiagnosticAnalyzer +{ + public const string DiagnosticId = "SSZ002"; + private static readonly LocalizableString Title = "Property with a collection type should be marked as SszList or SszVector"; + private static readonly LocalizableString MessageFormat = "Property {0} should be marked as SszList or SszVector"; + private const string Category = "Design"; + + private static readonly DiagnosticDescriptor Rule = new(DiagnosticId, Title, MessageFormat, Category, DiagnosticSeverity.Error, isEnabledByDefault: true); + + public override ImmutableArray SupportedDiagnostics { get { return [Rule]; } } + + public override void Initialize(AnalysisContext context) + { + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); + context.EnableConcurrentExecution(); + + context.RegisterSyntaxNodeAction(AnalyzeNode, SyntaxKind.ClassDeclaration, SyntaxKind.StructDeclaration); + } + + private static void AnalyzeNode(SyntaxNodeAnalysisContext context) + { + TypeDeclarationSyntax typeDeclaration = (TypeDeclarationSyntax)context.Node; + + if (!typeDeclaration.AttributeLists.SelectMany(attrList => attrList.Attributes).Any(attr => attr.ToString() == "SszSerializable" || attr.ToString() == "SszSerializableAttribute")) + { + return; + } + + foreach (var property in typeDeclaration.Members.OfType() + .Where(prop => + prop.Modifiers.Any(SyntaxKind.PublicKeyword) && + prop.AccessorList?.Accessors.Any(a => a.Kind() == SyntaxKind.GetAccessorDeclaration && !a.Modifiers.Any(m => m.IsKind(SyntaxKind.PrivateKeyword))) == true && + prop.AccessorList?.Accessors.Any(a => a.Kind() == SyntaxKind.SetAccessorDeclaration && !a.Modifiers.Any(m => m.IsKind(SyntaxKind.PrivateKeyword))) == true)) + { + CheckProperty(context, property); + } + } + + private static void CheckProperty(SyntaxNodeAnalysisContext context, PropertyDeclarationSyntax propertyDeclaration) + { + static bool IsCollectionType(ITypeSymbol typeSymbol) + { + if (typeSymbol is INamedTypeSymbol namedTypeSymbol) + { + ImmutableArray interfaces = namedTypeSymbol.AllInterfaces; + return interfaces.Any(i => i.Name == "IEnumerable"); + } + + return false; + } + + ITypeSymbol? typeSymbol = context.SemanticModel.GetTypeInfo(propertyDeclaration.Type).Type; + + if (typeSymbol is not null && (typeSymbol is IArrayTypeSymbol || IsCollectionType(typeSymbol))) + { + bool hasRequiredAttribute = propertyDeclaration.AttributeLists + .SelectMany(attrList => attrList.Attributes) + .Any(attr => + { + var name = attr.Name.ToString(); + return name == "SszList" || name == "SszVector" || name == "SszListAttribute" || name == "SszVectorAttribute" || name == "BitArray"; + }); + + if (!hasRequiredAttribute) + { + var diagnostic = Diagnostic.Create(Rule, propertyDeclaration.GetLocation(), propertyDeclaration.Identifier.Text); + context.ReportDiagnostic(diagnostic); + } + } + } +} diff --git a/src/Nethermind/Nethermind.Serialization.SszGenerator/Analyzers/PropertyRequiredInSszTypeAnalyzer.cs b/src/Nethermind/Nethermind.Serialization.SszGenerator/Analyzers/PropertyRequiredInSszTypeAnalyzer.cs new file mode 100644 index 00000000000..d99d1b13efc --- /dev/null +++ b/src/Nethermind/Nethermind.Serialization.SszGenerator/Analyzers/PropertyRequiredInSszTypeAnalyzer.cs @@ -0,0 +1,49 @@ +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; + +[DiagnosticAnalyzer(LanguageNames.CSharp)] +public class PropertyRequiredInSszTypeAnalyzer : DiagnosticAnalyzer +{ + public const string DiagnosticId = "SSZ001"; + private static readonly LocalizableString Title = "Class or struct marked with SszSerializable must have at least one public property with public getter and setter"; + private static readonly LocalizableString MessageFormat = "Type '{0}' is marked with SszSerializable, but does not have any public property with public getter and setter"; + private static readonly LocalizableString Description = "A class or struct marked with SszSerializable should have at least one public property with both a public getter and setter."; + private const string Category = "Design"; + + private static readonly DiagnosticDescriptor Rule = new DiagnosticDescriptor(DiagnosticId, Title, MessageFormat, Category, DiagnosticSeverity.Error, isEnabledByDefault: true, description: Description); + + public override ImmutableArray SupportedDiagnostics { get { return [Rule]; } } + + public override void Initialize(AnalysisContext context) + { + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); + context.EnableConcurrentExecution(); + + context.RegisterSyntaxNodeAction(AnalyzeNode, SyntaxKind.ClassDeclaration, SyntaxKind.StructDeclaration); + } + + private static void AnalyzeNode(SyntaxNodeAnalysisContext context) + { + TypeDeclarationSyntax typeDeclaration = (TypeDeclarationSyntax)context.Node; + + if (!typeDeclaration.AttributeLists.SelectMany(attrList => attrList.Attributes).Any(attr => attr.ToString() == "SszSerializable" || attr.ToString() == "SszSerializableAttribute")) + { + return; + } + + bool hasValidProperty = typeDeclaration.Members.OfType() + .Any(prop => + prop.Modifiers.Any(SyntaxKind.PublicKeyword) && + prop.AccessorList?.Accessors.Any(a => a.Kind() == SyntaxKind.GetAccessorDeclaration && !a.Modifiers.Any(m => m.IsKind(SyntaxKind.PrivateKeyword))) == true && + prop.AccessorList?.Accessors.Any(a => a.Kind() == SyntaxKind.SetAccessorDeclaration && !a.Modifiers.Any(m => m.IsKind(SyntaxKind.PrivateKeyword))) == true); + + if (!hasValidProperty) + { + Diagnostic diagnostic = Diagnostic.Create(Rule, typeDeclaration.Identifier.GetLocation(), typeDeclaration.Identifier.Text); + context.ReportDiagnostic(diagnostic); + } + } +} diff --git a/src/Nethermind/Nethermind.Serialization.SszGenerator/Analyzers/SszDiagnosticAnalyzer.cs b/src/Nethermind/Nethermind.Serialization.SszGenerator/Analyzers/SszDiagnosticAnalyzer.cs new file mode 100644 index 00000000000..83c0418577f --- /dev/null +++ b/src/Nethermind/Nethermind.Serialization.SszGenerator/Analyzers/SszDiagnosticAnalyzer.cs @@ -0,0 +1,29 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; +using System.Collections; + +public abstract class SszDiagnosticAnalyzer : DiagnosticAnalyzer +{ + protected static string[] SszSerializableAttributeNames = ["SszSerializable", "SszSerializableAttribute"]; + protected static string[] SszCollectionAttributeNames = ["SszVector", "SszList", "SszListAttribute", "SszVectorAttribute"]; + + protected static bool IsSerializableType(TypeDeclarationSyntax type) => + type.AttributeLists.SelectMany(attrList => attrList.Attributes).Any(attr => SszSerializableAttributeNames.Contains(attr.ToString())); + + protected static bool IsCollectionType(ITypeSymbol typeSymbol) => + typeSymbol is IArrayTypeSymbol || (typeSymbol is INamedTypeSymbol namedTypeSymbol + && (namedTypeSymbol.Name == nameof(BitArray) || namedTypeSymbol.AllInterfaces.Any(i => i.Name == nameof(IEnumerable)))); + + protected static bool IsPropertyMarkedWithCollectionAttribute(PropertyDeclarationSyntax property) => + property.AttributeLists.SelectMany(attrList => attrList.Attributes).Any(attr => SszCollectionAttributeNames.Contains(attr.Name.ToString())); + + public static bool IsPublicGetSetProperty(PropertyDeclarationSyntax property) => + property.Modifiers.Any(SyntaxKind.PublicKeyword) && + property.AccessorList?.Accessors.Any(a => a.Kind() == SyntaxKind.GetAccessorDeclaration && !a.Modifiers.Any(m => m.IsKind(SyntaxKind.PrivateKeyword))) == true && + property.AccessorList?.Accessors.Any(a => a.Kind() == SyntaxKind.SetAccessorDeclaration && !a.Modifiers.Any(m => m.IsKind(SyntaxKind.PrivateKeyword))) == true; +} diff --git a/src/Nethermind/Nethermind.Serialization.SszGenerator/Attributes.cs b/src/Nethermind/Nethermind.Serialization.SszGenerator/Attributes.cs new file mode 100644 index 00000000000..8adab610e01 --- /dev/null +++ b/src/Nethermind/Nethermind.Serialization.SszGenerator/Attributes.cs @@ -0,0 +1,22 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +namespace Nethermind.Serialization.Ssz; + +[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct)] +public class SszSerializableAttribute(bool isCollectionItself = false) : Attribute +{ + public bool IsCollectionItself { get; } = isCollectionItself; +} + +[AttributeUsage(AttributeTargets.Property)] +public class SszListAttribute(int limit) : Attribute +{ + public int Limit { get; } = limit; +} + +[AttributeUsage(AttributeTargets.Property)] +public class SszVectorAttribute(int length) : Attribute +{ + public int Length { get; } = length; +} diff --git a/src/Nethermind/Nethermind.Serialization.SszGenerator/Attrs.cs b/src/Nethermind/Nethermind.Serialization.SszGenerator/Attrs.cs new file mode 100644 index 00000000000..8664d57bff8 --- /dev/null +++ b/src/Nethermind/Nethermind.Serialization.SszGenerator/Attrs.cs @@ -0,0 +1,19 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +namespace Nethermind.Serialization.Ssz; + +[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct)] +public class SszSerializableAttribute : Attribute; + +[AttributeUsage(AttributeTargets.Property)] +public class SszListAttribute(int limit) : Attribute +{ + public int Limit { get; } = limit; +} + +[AttributeUsage(AttributeTargets.Property)] +public class SszVectorAttribute(int length) : Attribute +{ + public int Length { get; } = length; +} diff --git a/src/Nethermind/Nethermind.Serialization.SszGenerator/IsExternalInit.cs b/src/Nethermind/Nethermind.Serialization.SszGenerator/IsExternalInit.cs new file mode 100644 index 00000000000..21a4f119416 --- /dev/null +++ b/src/Nethermind/Nethermind.Serialization.SszGenerator/IsExternalInit.cs @@ -0,0 +1,12 @@ +namespace System.Runtime.CompilerServices; + +// .NET standard 2.0 needs these types to allow modern C# features + +internal static class IsExternalInit; + +public class RequiredMemberAttribute : Attribute; + +public class CompilerFeatureRequiredAttribute : Attribute +{ + public CompilerFeatureRequiredAttribute(string name) { } +} diff --git a/src/Nethermind/Nethermind.Serialization.SszGenerator/Kind.cs b/src/Nethermind/Nethermind.Serialization.SszGenerator/Kind.cs new file mode 100644 index 00000000000..4aa2de67057 --- /dev/null +++ b/src/Nethermind/Nethermind.Serialization.SszGenerator/Kind.cs @@ -0,0 +1,16 @@ +public enum Kind +{ + None = 0x0, + Basic = 0x1, + Container = 0x2, + + Vector = 0x4, + List = 0x8, + + BitVector = 0x10, + BitList = 0x20, + + Union = 0x40, + + Collection = Vector | List, +} diff --git a/src/Nethermind/Nethermind.Serialization.SszGenerator/Nethermind.Serialization.SszGenerator.csproj b/src/Nethermind/Nethermind.Serialization.SszGenerator/Nethermind.Serialization.SszGenerator.csproj new file mode 100644 index 00000000000..ae6ab740113 --- /dev/null +++ b/src/Nethermind/Nethermind.Serialization.SszGenerator/Nethermind.Serialization.SszGenerator.csproj @@ -0,0 +1,19 @@ + + + Library + netstandard2.0 + enable + enable + latest + true + true + Nethermind.Generated.Ssz + true + Generated + + + + + + + \ No newline at end of file diff --git a/src/Nethermind/Nethermind.Serialization.SszGenerator/Properties/launchSettings.json b/src/Nethermind/Nethermind.Serialization.SszGenerator/Properties/launchSettings.json new file mode 100644 index 00000000000..2d2d49cf6be --- /dev/null +++ b/src/Nethermind/Nethermind.Serialization.SszGenerator/Properties/launchSettings.json @@ -0,0 +1,12 @@ +{ + "profiles": { + "Generate SSZ for tests": { + "commandName": "DebugRoslynComponent", + "targetProject": "..\\Nethermind.Serialization.SszGenerator.Test\\Nethermind.Serialization.SszGenerator.Test.csproj" + }, + "Generate SSZ for Nethermind.Network.Discovery": { + "commandName": "DebugRoslynComponent", + "targetProject": "..\\Nethermind.Network.Discovery\\Nethermind.Network.Discovery.csproj" + } + } +} diff --git a/src/Nethermind/Nethermind.Serialization.SszGenerator/README.md b/src/Nethermind/Nethermind.Serialization.SszGenerator/README.md new file mode 100644 index 00000000000..2b073b15570 --- /dev/null +++ b/src/Nethermind/Nethermind.Serialization.SszGenerator/README.md @@ -0,0 +1,75 @@ +# SSZ Encoders generator + +Generates static encoding/decoding/merkleization methods for SSZ types. + +Allows to: +- Encode +- Decode +- Merkleize + +Supports vectors, lists, bitvectors, bitlists, unions, etc. + +## Usage + +- Add reference to the generator + +```xml + +``` + +- Output of the generation will appear under Project/Dependencies/Analyzers/Nethermind.Serialization.SszGenerator/SszGenerator. +- Partial static class SszEncoding will contain all the methods. + + +## Examples + +### Container with marked collections + +```csharp + +[SszSerializable] +public struct MyClass +{ + public ulong Test1 { get; set; } + + [SszVector(10)] + public ulong[] Test2 { get; set; } + + [SszList(10)] + public ulong[] Test3 { get; set; } +} + +... +MyClass instance = new(); +SszEncoding.Merkleize(instance, out UInt256 root); +byte[] encoded = SszEncoding.Encode(instance); +SszEncoding.Decode(encoded, out decodedInstance); +``` + + +### Union + +```csharp +[SszSerializable] +public struct MyUnion +{ + public MyUnionEnum Selector { get; set; } + + public ulong Test1 { get; set; } + + [SszVector(10)] + public ulong[] Test2 { get; set; } + + [SszList(10)] + public ulong[] Test3 { get; set; } +} + +public enum MyUnionEnum +{ + Test1, + Test2, + Test3 +} +``` + +More examples in Nethermind.Serialization.SszGenerator.Test project. diff --git a/src/Nethermind/Nethermind.Serialization.SszGenerator/SszGenerator.cs b/src/Nethermind/Nethermind.Serialization.SszGenerator/SszGenerator.cs new file mode 100644 index 00000000000..8f7670dabc2 --- /dev/null +++ b/src/Nethermind/Nethermind.Serialization.SszGenerator/SszGenerator.cs @@ -0,0 +1,665 @@ +[Generator] +public partial class SszGenerator : IIncrementalGenerator +{ + public void Initialize(IncrementalGeneratorInitializationContext context) + { + IncrementalValuesProvider<(SszType, List)?> classDeclarations = context.SyntaxProvider + .CreateSyntaxProvider( + predicate: (syntaxNode, _) => IsClassWithAttribute(syntaxNode), + transform: (context, _) => GetClassWithAttribute(context)) + .Where(classNode => classNode is not null); + + context.RegisterSourceOutput(classDeclarations, (spc, decl) => + { + if (decl is null) + { + return; + } + var generatedCode = GenerateClassCode(decl.Value.Item1, decl.Value.Item2); + spc.AddSource($"Serialization.SszEncoding.{decl.Value.Item1.Name}.cs", SourceText.From(generatedCode, Encoding.UTF8)); + }); + } + + private static bool IsClassWithAttribute(SyntaxNode syntaxNode) + { + return syntaxNode is TypeDeclarationSyntax classDeclaration && + classDeclaration.AttributeLists.Any(x => x.Attributes.Any()); + } + + private static (SszType, List)? GetClassWithAttribute(GeneratorSyntaxContext context) + { + TypeDeclarationSyntax classDeclaration = (TypeDeclarationSyntax)context.Node; + foreach (AttributeListSyntax attributeList in classDeclaration.AttributeLists) + { + foreach (AttributeSyntax attribute in attributeList.Attributes) + { + IMethodSymbol? methodSymbol = context.SemanticModel.GetSymbolInfo(attribute).Symbol as IMethodSymbol; + if (methodSymbol is not null && methodSymbol.ContainingType.ToString() == "Nethermind.Serialization.Ssz.SszSerializableAttribute") + { + var foundTypes = new List(SszType.BasicTypes); + return (SszType.From(context.SemanticModel, foundTypes, context.SemanticModel.GetDeclaredSymbol(classDeclaration)!), foundTypes); + } + } + } + return null; + } + + const string Whitespace = "/**/"; + static Regex OpeningWhiteSpaceRegex = new("{/(\\n\\s+)+\\n/"); + static Regex ClosingWhiteSpaceRegex = new("/(\\s+\\n)+ }/"); + public static string FixWhitespace(string data) => OpeningWhiteSpaceRegex.Replace( + ClosingWhiteSpaceRegex.Replace( + string.Join("\n", data.Split('\n').Where(x => !string.IsNullOrWhiteSpace(x)).Select(x => x.Contains(Whitespace) ? "" : x)), + " }"), + "{\n" + ); + public static string Shift(int tabCount, string data) => string.Empty.PadLeft(4 * tabCount) + data; + public static string Shift(int tabCount, IEnumerable data, string? end = null) => string.Join("\n", data.Select(d => Shift(tabCount, d))) + (end is null || !data.Any() ? "" : end); + + private static string VarName(string name) + { + if (string.IsNullOrEmpty(name)) return name; + + string lowerCased = name.Substring(0, 1).ToLower() + name.Substring(1); + + return lowerCased == "data" || lowerCased == "container" || lowerCased.Contains("offset") ? $"_{lowerCased}" : lowerCased; + } + + private static string DynamicLength(SszType container, SszProperty m) + { + if ((m.Kind & (Kind.Collection | Kind.BitList)) != Kind.None && m.Type.Kind == Kind.Basic) + { + return $"(container.{m.Name} is not null ? {(m.Kind == Kind.BitList ? $"container.{m.Name}.Length / 8 + 1" : $"{m.Type.StaticLength} * container.{m.Name}.Count()")} : 0)"; + } + + return $"GetLength(container.{m.Name})"; + } + + private static string GenerateClassCode(SszType decl, List foundTypes) + { + try + { + List variables = decl.Members.Where(m => m.IsVariable).ToList(); + int encodeOffsetIndex = 0, encodeStaticOffset = 0; + int offsetIndex = 0, offset = 0; + string result = FixWhitespace(decl.IsSszListItself ? +$@"using Nethermind.Merkleization; +using System.Collections.Generic; +using System.Linq; +{string.Join("\n", foundTypes.Select(x => x.Namespace).Distinct().OrderBy(x => x).Where(x => !string.IsNullOrEmpty(x)).Select(n => $"using {n};"))} +{Whitespace} +using SszLib = Nethermind.Serialization.Ssz.Ssz; +{Whitespace} +namespace Nethermind.Serialization; +{Whitespace} +public partial class SszEncoding +{{ + public static int GetLength({decl.Name}{(decl.IsStruct ? "" : "?")} container) + {{ + {(decl.IsStruct ? "" : @"if(container is null) + { + return 0; + }")} +{Whitespace} + return {DynamicLength(decl, variables[0])}; + }} +{Whitespace} + public static int GetLength(ICollection<{decl.Name}>? container) + {{ + if(container is null) + {{ + return 0; + }} +{Whitespace} + int length = container.Count * {SszType.PointerLength}; + foreach({decl.Name} item in container) + {{ + length += GetLength(item); + }} +{Whitespace} + return length; + }} +{Whitespace} + public static byte[] Encode({decl.Name}{(decl.IsStruct ? "" : "?")} container) + {{ + {(decl.IsStruct ? "" : @"if(container is null) + { + return []; + }")} + byte[] buf = new byte[GetLength(container)]; + Encode(buf, container); + return buf; + }} +{Whitespace} + public static void Encode(Span data, {decl.Name}{(decl.IsStruct ? "" : "?")} container) + {{ + {(decl.IsStruct ? "" : @"if(container is null) + { + return; + }")} +{Whitespace} + if (container.{variables[0].Name} is not null) {(variables[0].HandledByStd ? "SszLib.Encode" : "Encode")}(data, container.{variables[0].Name}); + }} +{Whitespace} + public static byte[] Encode(ICollection<{decl.Name}>? items) + {{ + if (items is null) + {{ + return []; + }} + byte[] buf = new byte[GetLength(items)]; + Encode(buf, items); + return buf; + }} +{Whitespace} + public static void Encode(Span data, ICollection<{decl.Name}>? items) + {{ + if(items is null) return; +{Whitespace} + int offset = items.Count * {(SszType.PointerLength)}; + int itemOffset = 0; +{Whitespace} + foreach({decl.Name} item in items) + {{ + SszLib.Encode(data.Slice(itemOffset, {(SszType.PointerLength)}), offset); + itemOffset += {(SszType.PointerLength)}; + int length = GetLength(item); + Encode(data.Slice(offset, length), item); + offset += length; + }} + }} +{Whitespace} + public static void Decode(ReadOnlySpan data, out {decl.Name} container) + {{ + container = new(); +{Whitespace} + if (data.Length > 0) {{ {(variables[0].HandledByStd ? "SszLib.Decode" : "Decode")}(data, out {(variables[0].HandledByStd ? $"ReadOnlySpan<{variables[0].Type.Name}>" : $"{variables[0].Type.Name}[]")} {VarName(variables[0].Name)}); container.{variables[0].Name} = [ ..{VarName(variables[0].Name)}]; }} + }} +{Whitespace} + public static void Decode(ReadOnlySpan data, out {decl.Name}[] container) + {{ + if(data.Length is 0) + {{ + container = []; + return; + }} +{Whitespace} + {(decl.IsVariable ? $@"SszLib.Decode(data.Slice(0, 4), out int firstOffset); + int length = firstOffset / {SszType.PointerLength}" : $"int length = data.Length / {decl.StaticLength}")}; + container = new {decl.Name}[length]; +{Whitespace} + {(decl.IsVariable ? @$"int index = 0; + int offset = firstOffset; + for(int nextOffsetIndex = {SszType.PointerLength}; index < length - 1; index++, nextOffsetIndex += {SszType.PointerLength}) + {{ + SszLib.Decode(data.Slice(nextOffsetIndex, {SszType.PointerLength}), out int nextOffset); + Decode(data.Slice(offset, nextOffset - offset), out container[index]); + offset = nextOffset; + }} +{Whitespace} + Decode(data.Slice(offset), out container[index]);" : @$"int offset = 0; + for(int index = 0; index < length; index++) + {{ + Decode(data.Slice(offset, {decl.StaticLength}), out container[index]); + offset += {decl.StaticLength}; + }}")} + }} +{Whitespace} + public static void Merkleize({decl.Name}{(decl.IsStruct ? "" : "?")} container, out UInt256 root) + {{ + {(decl.IsStruct ? "" : @"if(container is null) + { + root = 0; + return; + }")} + Merkleizer merkleizer = new Merkleizer(Merkle.NextPowerOfTwoExponent({decl.Members!.Length})); + {(variables[0].HandledByStd ? $"merkleizer.Feed(container.{variables[0].Name}, {variables[0].Limit});" : $"MerkleizeList(container.{variables[0].Name}, {variables[0].Limit}, out UInt256 {VarName(variables[0].Name)}Root); merkleizer.Feed({VarName(variables[0].Name)}Root);")} + merkleizer.CalculateRoot(out root); + }} +{Whitespace} + public static void MerkleizeVector(IList<{decl.Name}>? container, out UInt256 root) + {{ + if(container is null) + {{ + root = 0; + return; + }} +{Whitespace} + UInt256[] subRoots = new UInt256[container.Count]; + for(int i = 0; i < container.Count; i++) + {{ + Merkleize(container[i], out subRoots[i]); + }} +{Whitespace} + Merkle.Ize(out root, subRoots); + }} +{Whitespace} + public static void MerkleizeList(IList<{decl.Name}>? container, ulong limit, out UInt256 root) + {{ + if(container is null || container.Count is 0) + {{ + root = 0; + Merkle.MixIn(ref root, (int)limit); + return; + }} +{Whitespace} + MerkleizeVector(container, out root); + Merkle.MixIn(ref root, container.Count); + }} +}} +" : +decl.Kind == Kind.Container ? $@"using Nethermind.Merkleization; +using System.Collections.Generic; +using System.Linq; +{string.Join("\n", foundTypes.Select(x => x.Namespace).Distinct().OrderBy(x => x).Where(x => !string.IsNullOrEmpty(x)).Select(n => $"using {n};"))} +{Whitespace} +using SszLib = Nethermind.Serialization.Ssz.Ssz; +{Whitespace} +namespace Nethermind.Serialization; +{Whitespace} +public partial class SszEncoding +{{ + public static int GetLength({decl.Name}{(decl.IsStruct ? "" : "?")} container) + {{ + {(decl.IsStruct ? "" : @"if(container is null) + { + return 0; + }")} +{Whitespace} + return {decl.StaticLength}{(variables.Any() ? "" : ";")} +{Shift(4, variables.Select(m => $"+ {DynamicLength(decl, m)}"), ";")} + }} +{Whitespace} + public static int GetLength(ICollection<{decl.Name}>? container) + {{ + if(container is null) + {{ + return 0; + }} +{Whitespace} + {(decl.IsVariable ? @$"int length = container.Count * {SszType.PointerLength}; + foreach({decl.Name} item in container) + {{ + length += GetLength(item); + }} +{Whitespace} + return length;" : $"return container.Count * {(decl.StaticLength)};")} + }} +{Whitespace} + public static byte[] Encode({decl.Name}{(decl.IsStruct ? "" : "?")} container) + {{ + {(decl.IsStruct ? "" : @"if(container is null) + { + return []; + }")} + byte[] buf = new byte[GetLength(container)]; + Encode(buf, container); + return buf; + }} +{Whitespace} + public static void Encode(Span data, {decl.Name}{(decl.IsStruct ? "" : "?")} container) + {{ + {(decl.IsStruct ? "" : @"if(container is null) + { + return; + }")} +{Whitespace} +{Shift(2, variables.Select((_, i) => $"int offset{i + 1} = {(i == 0 ? decl.StaticLength : $"offset{i} + {DynamicLength(decl, variables[i - 1])}")};"))} +{Whitespace} +{Shift(2, decl.Members.Select(m => +{ + if (m.IsVariable) encodeOffsetIndex++; + string result = m.IsVariable ? $"SszLib.Encode(data.Slice({encodeStaticOffset}, 4), offset{encodeOffsetIndex});" + : m.HandledByStd ? $"SszLib.Encode(data.Slice({encodeStaticOffset}, {m.StaticLength}), container.{m.Name});" + : $"Encode(data.Slice({encodeStaticOffset}, {m.StaticLength}), container.{m.Name});"; + encodeStaticOffset += m.StaticLength; + return result; +}))} +{Whitespace} +{Shift(2, variables.Select((m, i) => (m.Type.IsStruct ? "" : $"if (container.{m.Name} is not null) ") + $"{(m.HandledByStd ? "SszLib.Encode" : "Encode")}(data.Slice(offset{i + 1}, {(i + 1 == variables.Count ? "data.Length" : $"offset{i + 2}")} - offset{i + 1}), container.{m.Name}{(m.Kind == Kind.BitList ? $", {m.Limit}" : "")});"))} + }} +{Whitespace} + public static byte[] Encode(ICollection<{decl.Name}>? items) + {{ + if (items is null) + {{ + return []; + }} + byte[] buf = new byte[GetLength(items)]; + Encode(buf, items); + return buf; + }} +{Whitespace} + public static void Encode(Span data, ICollection<{decl.Name}>? items) + {{ + if(items is null) return; +{Whitespace} + {(decl.IsVariable ? @$"int offset = items.Count * {(SszType.PointerLength)}; + int itemOffset = 0; +{Whitespace} + foreach({decl.Name} item in items) + {{ + SszLib.Encode(data.Slice(itemOffset, {(SszType.PointerLength)}), offset); + itemOffset += {(SszType.PointerLength)}; + int length = GetLength(item); + Encode(data.Slice(offset, length), item); + offset += length; + }}" : @$"int offset = 0; + foreach({decl.Name} item in items) + {{ + int length = GetLength(item); + Encode(data.Slice(offset, length), item); + offset += length; + }}")} + }} +{Whitespace} + public static void Decode(ReadOnlySpan data, out {decl.Name} container) + {{ + container = new(); +{Whitespace} +{Shift(2, decl.Members.Select(m => +{ + if (m.IsVariable) offsetIndex++; + string result = m.IsVariable ? $"SszLib.Decode(data.Slice({offset}, 4), out int offset{offsetIndex});" + : m.HandledByStd ? $"SszLib.Decode(data.Slice({offset}, {m.StaticLength}), out {(m.IsCollection ? $"ReadOnlySpan<{m.Type.Name}>" : m.Type.Name)} {VarName(m.Name)}); container.{m.Name} = {(m.IsCollection ? $"[ ..{VarName(m.Name)}]" : VarName(m.Name))};" + : $"Decode(data.Slice({offset}, {m.StaticLength}), out {(m.IsCollection ? $"{m.Type.Name}[]" : m.Type.Name)} {VarName(m.Name)}); container.{m.Name} = {(m.IsCollection ? $"[ ..{VarName(m.Name)}]" : VarName(m.Name))};"; + offset += m.StaticLength; + return result; +}))} +{Whitespace} +{Shift(2, variables.Select((m, i) => string.Format($"if ({(i + 1 == variables.Count ? "data.Length" : $"offset{i + 2}")} - offset{i + 1} > 0) {{{{ {{0}} }}}}", + $"{(m.HandledByStd ? "SszLib.Decode" : "Decode")}(data.Slice(offset{i + 1}, {(i + 1 == variables.Count ? "data.Length" : $"offset{i + 2}")} - offset{i + 1}), out {(m.IsCollection ? (m.HandledByStd ? $"ReadOnlySpan<{m.Type.Name}>" : $"{m.Type.Name}[]") : m.Type.Name)} {VarName(m.Name)}); container.{m.Name} = {(m.IsCollection ? $"[ ..{VarName(m.Name)}]" : VarName(m.Name))};")))} + }} +{Whitespace} + public static void Decode(ReadOnlySpan data, out {decl.Name}[] container) + {{ + if(data.Length is 0) + {{ + container = []; + return; + }} +{Whitespace} + {(decl.IsVariable ? $@"SszLib.Decode(data.Slice(0, 4), out int firstOffset); + int length = firstOffset / {SszType.PointerLength}" : $"int length = data.Length / {decl.StaticLength}")}; + container = new {decl.Name}[length]; +{Whitespace} + {(decl.IsVariable ? @$"int index = 0; + int offset = firstOffset; + for(int nextOffsetIndex = {SszType.PointerLength}; index < length - 1; index++, nextOffsetIndex += {SszType.PointerLength}) + {{ + SszLib.Decode(data.Slice(nextOffsetIndex, {SszType.PointerLength}), out int nextOffset); + Decode(data.Slice(offset, nextOffset - offset), out container[index]); + offset = nextOffset; + }} +{Whitespace} + Decode(data.Slice(offset), out container[index]);" : @$"int offset = 0; + for(int index = 0; index < length; index++) + {{ + Decode(data.Slice(offset, {decl.StaticLength}), out container[index]); + offset += {decl.StaticLength}; + }}")} + }} +{Whitespace} + public static void Merkleize({decl.Name}{(decl.IsStruct ? "" : "?")} container, out UInt256 root) + {{ + {(decl.IsStruct ? "" : @"if(container is null) + { + root = 0; + return; + }")} + Merkleizer merkleizer = new Merkleizer(Merkle.NextPowerOfTwoExponent({decl.Members!.Length})); +{Shift(2, decl.Members.Select(m => + { + if (m.IsVariable) offsetIndex++; + string result = m.HandledByStd ? $"merkleizer.Feed(container.{m.Name}{(m.Kind == Kind.List || m.Kind == Kind.BitList ? $", {m.Limit}" : "")});" + : m.Kind == Kind.List ? $"MerkleizeList(container.{m.Name}, {m.Limit}, out UInt256 {VarName(m.Name)}Root); merkleizer.Feed({VarName(m.Name)}Root);" + : m.Kind == Kind.Vector ? $"MerkleizeVector(container.{m.Name}, out UInt256 {VarName(m.Name)}Root); merkleizer.Feed({VarName(m.Name)}Root);" + : $"Merkleize(container.{m.Name}, out UInt256 {VarName(m.Name)}Root); merkleizer.Feed({VarName(m.Name)}Root);"; + offset += m.StaticLength; + return result; + }))} + merkleizer.CalculateRoot(out root); + }} +{Whitespace} + public static void MerkleizeVector(IList<{decl.Name}>? container, out UInt256 root) + {{ + if(container is null) + {{ + root = 0; + return; + }} +{Whitespace} + UInt256[] subRoots = new UInt256[container.Count]; + for(int i = 0; i < container.Count; i++) + {{ + Merkleize(container[i], out subRoots[i]); + }} +{Whitespace} + Merkle.Ize(out root, subRoots); + }} +{Whitespace} + public static void MerkleizeList(IList<{decl.Name}>? container, ulong limit, out UInt256 root) + {{ + if(container is null || container.Count is 0) + {{ + root = 0; + Merkle.MixIn(ref root, (int)limit); + return; + }} +{Whitespace} + MerkleizeVector(container, out root); + Merkle.MixIn(ref root, container.Count); + }} +}} +" : +// Unions +$@"using Nethermind.Merkleization; +using System.Collections.Generic; +using System.Linq; +{string.Join("\n", foundTypes.Select(x => x.Namespace).Distinct().OrderBy(x => x).Where(x => !string.IsNullOrEmpty(x)).Select(n => $"using {n};"))} +{Whitespace} +using SszLib = Nethermind.Serialization.Ssz.Ssz; +{Whitespace} +namespace Nethermind.Serialization; +{Whitespace} +public partial class SszEncoding +{{ + public static int GetLength({decl.Name}{(decl.IsStruct ? "" : "?")} container) + {{ + {(decl.IsStruct ? "" : @"if(container is null) + { + return 0; + }")} + return 1 + container.Selector switch {{ +{Shift(3, decl.UnionMembers.Select(m => $"{decl.Selector!.Type.Name}.{m.Name} => {(m.IsVariable ? DynamicLength(decl, m) : m.StaticLength.ToString())},"))} + _ => 0, + }}; + }} +{Whitespace} + public static int GetLength(ICollection<{decl.Name}>? container) + {{ + if(container is null) + {{ + return 0; + }} +{Whitespace} + int length = container.Count * {SszType.PointerLength}; + foreach({decl.Name} item in container) + {{ + length += GetLength(item); + }} +{Whitespace} + return length; + }} +{Whitespace} + public static byte[] Encode({decl.Name}{(decl.IsStruct ? "" : "?")} container) + {{ + {(decl.IsStruct ? "" : @"if(container is null) + { + return []; + }")} + byte[] buf = new byte[GetLength(container)]; + Encode(buf, container); + return buf; + }} +{Whitespace} + public static void Encode(Span data, {decl.Name}{(decl.IsStruct ? "" : "?")} container) + {{ + {(decl.IsStruct ? "" : @"if(container is null) + { + return; + }")} + SszLib.Encode(data.Slice(0, 1), (byte)container.Selector); + if(data.Length is 1) + {{ + return; + }} +{Whitespace} + switch(container.Selector) {{ +{Shift(3, decl.UnionMembers.Select(m => $"case {decl.Selector!.Type.Name}.{m.Name}: {(m.IsVariable ? $"{(m.HandledByStd ? "SszLib.Encode" : "Encode")}(data.Slice(1), container.{m.Name}{(m.Kind == Kind.BitList ? $", {m.Limit}" : "")});" + : m.HandledByStd ? $"SszLib.Encode(data.Slice(1), container.{m.Name});" + : $"Encode(data.Slice(1), container.{m.Name});")} break;"))} + }}; + }} +{Whitespace} + public static byte[] Encode(ICollection<{decl.Name}>? items) + {{ + if (items is null) + {{ + return []; + }} + byte[] buf = new byte[GetLength(items)]; + Encode(buf, items); + return buf; + }} +{Whitespace} + public static void Encode(Span data, ICollection<{decl.Name}>? items) + {{ + if(items is null) return; + int offset = items.Count * {(SszType.PointerLength)}; + int itemOffset = 0; + foreach({decl.Name} item in items) + {{ + SszLib.Encode(data.Slice(itemOffset, {(SszType.PointerLength)}), offset); + itemOffset += {(SszType.PointerLength)}; + int length = GetLength(item); + Encode(data.Slice(offset, length), item); + offset += length; + }} + }} +{Whitespace} + public static void Decode(ReadOnlySpan data, out {decl.Name}[] container) + {{ + if(data.Length is 0) + {{ + container = []; + return; + }} +{Whitespace} + SszLib.Decode(data.Slice(0, 4), out int firstOffset); + int length = firstOffset / {SszType.PointerLength}; + container = new {decl.Name}[length]; +{Whitespace} + int index = 0; + int offset = firstOffset; + for(int nextOffsetIndex = {SszType.PointerLength}; index < length - 1; index++, nextOffsetIndex += {SszType.PointerLength}) + {{ + SszLib.Decode(data.Slice(nextOffsetIndex, {SszType.PointerLength}), out int nextOffset); + Decode(data.Slice(offset, nextOffset - offset), out container[index]); + offset = nextOffset; + }} +{Whitespace} + Decode(data.Slice(offset), out container[index]); + }} +{Whitespace} + public static void Decode(ReadOnlySpan data, out {decl.Name} container) + {{ + container = new(); + container.Selector = ({decl.Selector!.Type.Name})data[0]; + switch(container.Selector) {{ +{Shift(3, decl.UnionMembers.Select(m => $"case {decl.Selector!.Type.Name}.{m.Name}: {(m.IsVariable ? $"{(m.HandledByStd ? "SszLib.Decode" : "Decode")}(data.Slice(1), out {(m.IsCollection ? (m.HandledByStd ? $"ReadOnlySpan<{m.Type.Name}>" : $"{m.Type.Name}[]") : m.Type.Name)} {VarName(m.Name)}); container.{m.Name} = {(m.IsCollection ? $"[ ..{VarName(m.Name)}]" : VarName(m.Name))};" + : m.HandledByStd ? $"SszLib.Decode(data.Slice(1), out {(m.IsCollection ? $"ReadOnlySpan<{m.Type.Name}>" : m.Type.Name)} {VarName(m.Name)}); container.{m.Name} = {(m.IsCollection ? $"[ ..{VarName(m.Name)}]" : VarName(m.Name))};" + : $"Decode(data.Slice(1), out {(m.IsCollection ? $"{m.Type.Name}[]" : m.Type.Name)} {VarName(m.Name)}); container.{m.Name} = {(m.IsCollection ? $"[ ..{VarName(m.Name)}]" : VarName(m.Name))};")} break;"))} + }}; + }} +{Whitespace} + public static void Merkleize({decl.Name}{(decl.IsStruct ? "" : "?")} container, out UInt256 root) + {{ + {(decl.IsStruct ? "" : @"if(container is null) + { + root = 0; + return; + }")} + Merkleizer merkleizer = new Merkleizer(Merkle.NextPowerOfTwoExponent({decl.Members!.Length})); + switch(container.Selector) {{ +{Shift(3, decl.UnionMembers.Select(m => $"case {decl.Selector!.Type.Name}.{m.Name}: {(m.HandledByStd ? $"merkleizer.Feed(container.{m.Name}{(m.Kind == Kind.List || m.Kind == Kind.BitList ? $", {m.Limit}" : "")});" + : m.Kind == Kind.List ? $"MerkleizeList(container.{m.Name}, {m.Limit}, out UInt256 {VarName(m.Name)}Root); merkleizer.Feed({VarName(m.Name)}Root);" + : m.Kind == Kind.Vector ? $"MerkleizeVector(container.{m.Name}, out UInt256 {VarName(m.Name)}Root); merkleizer.Feed({VarName(m.Name)}Root);" + : $"Merkleize(container.{m.Name}, out UInt256 {VarName(m.Name)}Root); merkleizer.Feed({VarName(m.Name)}Root);")} break;"))} + }}; +{Whitespace} + merkleizer.CalculateRoot(out root); + Merkle.MixIn(ref root, (byte)container.Selector); + }} +{Whitespace} + public static void MerkleizeVector(IList<{decl.Name}>? container, out UInt256 root) + {{ + if(container is null) + {{ + root = 0; + return; + }} +{Whitespace} + UInt256[] subRoots = new UInt256[container.Count]; + for(int i = 0; i < container.Count; i++) + {{ + Merkleize(container[i], out subRoots[i]); + }} +{Whitespace} + Merkle.Ize(out root, subRoots); + }} +{Whitespace} + public static void MerkleizeList(IList<{decl.Name}>? container, ulong limit, out UInt256 root) + {{ + if(container is null || container.Count is 0) + {{ + root = 0; + Merkle.MixIn(ref root, (int)limit); + return; + }} +{Whitespace} + MerkleizeVector(container, out root); + Merkle.MixIn(ref root, (int)limit); + }} +}} +"); +#if DEBUG + Console.WriteLine(WithLineNumbers(result, false)); +#endif + return result; + } + catch (Exception e) + { + return $"/* Failed due to error: {e.Message}*/"; + } + } + +#if DEBUG + static string WithLineNumbers(string input, bool bypass = false) + { + if (bypass) return input; + + string[] lines = input.Split('\n'); + int lineNumberWidth = lines.Length.ToString().Length; + + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < lines.Length; i++) + { + string lineNumber = (i + 1).ToString().PadLeft(lineNumberWidth); + sb.AppendLine($"{lineNumber}: {lines[i]}"); + } + + return sb.ToString(); + } +#endif +} diff --git a/src/Nethermind/Nethermind.Serialization.SszGenerator/SszProperty.cs b/src/Nethermind/Nethermind.Serialization.SszGenerator/SszProperty.cs new file mode 100644 index 00000000000..1277e0b2805 --- /dev/null +++ b/src/Nethermind/Nethermind.Serialization.SszGenerator/SszProperty.cs @@ -0,0 +1,99 @@ +using Microsoft.CodeAnalysis; + +class SszProperty +{ + public override string ToString() + { + return $"prop({Kind},{Type},{Name},{(IsVariable ? "v" : "f")})"; + } + + public static SszProperty From(SemanticModel semanticModel, List types, IPropertySymbol prop) + { + ITypeSymbol? itemType = GetCollectionType(prop.Type, semanticModel.Compilation); + + SszType type = SszType.From(semanticModel, types, itemType ?? prop.Type); + + SszProperty result = new SszProperty { Name = prop.Name, Type = type }; + + if (itemType is not null || prop.Type.Name == "BitArray") + { + var vectorAttr = prop.GetAttributes().FirstOrDefault(a => a.AttributeClass?.Name == "SszVectorAttribute"); + if (vectorAttr is not null) + { + result.Length = vectorAttr.ConstructorArguments.FirstOrDefault().Value as int? ?? 0; + } + var listAttr = prop.GetAttributes().FirstOrDefault(a => a.AttributeClass?.Name == "SszListAttribute"); + if (listAttr is not null) + { + result.Limit = listAttr.ConstructorArguments.FirstOrDefault().Value as int? ?? 0; + } + } + + return result; + } + + private static ITypeSymbol? GetCollectionType(ITypeSymbol typeSymbol, Compilation compilation) + { + if (typeSymbol is IArrayTypeSymbol array) + { + return array.ElementType!; + } + + INamedTypeSymbol? ienumerableOfT = compilation.GetTypeByMetadataName("System.Collections.Generic.IList`1"); + INamedTypeSymbol? enumerable = typeSymbol.AllInterfaces.FirstOrDefault(i => SymbolEqualityComparer.Default.Equals(i.OriginalDefinition, ienumerableOfT)); + if (ienumerableOfT != null && enumerable is not null) + { + return enumerable.TypeArguments.First(); + } + + return null; + } + + + public required string Name { get; init; } + public required SszType Type { get; init; } + public Kind Kind + { + get + { + if (Limit is not null) + { + return Type.Name == "BitArray" ? Kind.BitList : Kind.List; + } + if (Length is not null) + { + return Type.Name == "BitArray" ? Kind.BitVector : Kind.Vector; + } + return Type.Kind; + } + } + + public bool HandledByStd => ((Kind & (Kind.Basic | Kind.BitVector | Kind.BitList)) != Kind.None) || (((Kind & (Kind.Vector | Kind.List)) != Kind.None) && Type.Kind == Kind.Basic); + public bool IsCollection => (Kind & Kind.Collection) != Kind.None; + + public bool IsVariable => (Kind & (Kind.List | Kind.BitList)) != Kind.None || Type.IsVariable; + + public int StaticLength + { + get + { + if (IsVariable) + { + return 4; + } + try + { + return Kind switch + { + Kind.Vector => Length!.Value * Type.StaticLength, + Kind.BitVector => (Length!.Value + 7) / 8, + _ => Type.StaticLength, + }; + } + catch { throw; } + } + } + + public int? Length { get; set; } + public int? Limit { get; set; } +} diff --git a/src/Nethermind/Nethermind.Serialization.SszGenerator/SszType.cs b/src/Nethermind/Nethermind.Serialization.SszGenerator/SszType.cs new file mode 100644 index 00000000000..4965818827b --- /dev/null +++ b/src/Nethermind/Nethermind.Serialization.SszGenerator/SszType.cs @@ -0,0 +1,176 @@ +class SszType +{ + static SszType() + { + BasicTypes.Add(new SszType + { + Namespace = "System", + Name = "Byte", + Kind = Kind.Basic, + StaticLength = sizeof(byte), + }); + + BasicTypes.Add(new SszType + { + Namespace = "System", + Name = "UInt16", + Kind = Kind.Basic, + StaticLength = sizeof(ushort), + }); + + BasicTypes.Add(new SszType + { + Namespace = "System", + Name = "Int32", + Kind = Kind.Basic, + StaticLength = sizeof(int), + }); + + BasicTypes.Add(new SszType + { + Namespace = "System", + Name = "UInt32", + Kind = Kind.Basic, + StaticLength = sizeof(uint), + }); + + BasicTypes.Add(new SszType + { + Namespace = "System", + Name = "Int64", + Kind = Kind.Basic, + StaticLength = sizeof(long), + }); + + BasicTypes.Add(new SszType + { + Namespace = "System", + Name = "UInt64", + Kind = Kind.Basic, + StaticLength = sizeof(ulong), + }); + + BasicTypes.Add(new SszType + { + Namespace = "Nethermind.Int256", + Name = "UInt256", + Kind = Kind.Basic, + StaticLength = 32, + }); + BasicTypes.Add(new SszType + { + Namespace = "System", + Name = "Boolean", + Kind = Kind.Basic, + StaticLength = 1, + }); + + BasicTypes.Add(new SszType + { + Namespace = "System.Collections", + Name = "BitArray", + Kind = Kind.Basic, + }); + } + + public static List BasicTypes { get; set; } = []; + public required string Name { get; init; } + public required string? Namespace { get; init; } + public required Kind Kind { get; init; } + public SszProperty[]? Members { get; set; } = null; + + public bool IsStruct { get; set; } + + public bool HasNone { get; set; } + public IEnumerable? UnionMembers { get => Kind == Kind.Union ? Members.Where(x => x.Name != "Selector") : null; } + public SszProperty? Selector { get => Members.FirstOrDefault(x => x.Name == "Selector"); } + + private int? length = null; + + public SszType? EnumType { get; set; } + public int StaticLength { get => length ?? Members.Sum(x => x.StaticLength); set => length = value; } + + public bool IsVariable => Members is not null && Members.Any(x => x.IsVariable) || Kind is Kind.Union; + + public bool IsSszListItself { get; private set; } + + + public const int PointerLength = 4; + + internal static SszType From(SemanticModel semanticModel, List types, ITypeSymbol type) + { + string? @namespace = GetNamespace(type); + string name = GetTypeName(type); + + SszType? existingType = types.FirstOrDefault(t => t.Name == name && t.Namespace == @namespace); + if (existingType is not null) + { + return existingType; + } + + INamedTypeSymbol? enumType = (type as INamedTypeSymbol)?.EnumUnderlyingType; + + SszType result = new SszType + { + Namespace = @namespace, + Name = name, + Kind = type.GetMembers().OfType().Any(x => x.Name == "Selector" && x.Type.TypeKind == TypeKind.Enum) ? Kind.Union : enumType is not null ? Kind.Basic : Kind.Container, + }; + types.Add(result); + + if (enumType is not null) + { + result.EnumType = BasicTypes.First(x => x.Name == enumType.Name); + result.StaticLength = result.EnumType.StaticLength; + result.HasNone = (type as INamedTypeSymbol)?.MemberNames.Contains("None") == true; + } + + result.Members = result.Kind switch + { + Kind.Container or Kind.Union => type.GetMembers().OfType() + .Where(p => p.GetMethod is not null && p.SetMethod is not null && p.GetMethod.DeclaredAccessibility == Accessibility.Public && p.SetMethod.DeclaredAccessibility == Accessibility.Public) + .Select(prop => SszProperty.From(semanticModel, types, prop)).ToArray() ?? [], + _ => null, + }; + + if (result.Kind == Kind.Union) + { + result.HasNone = result.Members.Any(x => x.Name == "Selector" && x.Type.HasNone); + } + + if ((result.Kind & (Kind.Container | Kind.Union)) != Kind.None && type.TypeKind == TypeKind.Struct) + { + result.IsStruct = true; + } + + if (result.Kind is Kind.Container && result.Members is { Length: 1 } && result.Members[0] is { Kind: Kind.List, HandledByStd: true }) + { + result.IsSszListItself = GetIsCollectionItselfValue(type); + } + + return result; + } + + private static string? GetNamespace(ITypeSymbol syntaxNode) + { + return syntaxNode.ContainingNamespace?.ToString(); + } + + private static string GetTypeName(ITypeSymbol syntaxNode) + { + return string.IsNullOrEmpty(syntaxNode.ContainingNamespace?.ToString()) ? syntaxNode.ToString() : syntaxNode.Name.Replace(syntaxNode.ContainingNamespace!.ToString() + ".", ""); + } + public override string ToString() + { + return $"type({Kind},{Name},{(IsVariable ? "v" : "f")})"; + } + + private static bool GetIsCollectionItselfValue(ITypeSymbol typeSymbol) + { + object? attrValue = typeSymbol + .GetAttributes().FirstOrDefault(a => a.AttributeClass?.Name == "SszSerializableAttribute")? + .ConstructorArguments.FirstOrDefault().Value; + + return attrValue is not null && (bool)attrValue; + } +} diff --git a/src/Nethermind/Nethermind.Shutter.Test/ShutterApiSimulator.cs b/src/Nethermind/Nethermind.Shutter.Test/ShutterApiSimulator.cs index 68616f48c68..d2377ef466e 100644 --- a/src/Nethermind/Nethermind.Shutter.Test/ShutterApiSimulator.cs +++ b/src/Nethermind/Nethermind.Shutter.Test/ShutterApiSimulator.cs @@ -1,6 +1,4 @@ -// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only - using System; using System.Collections.Generic; using System.IO.Abstractions; @@ -21,9 +19,7 @@ using Nethermind.Shutter.Config; using Nethermind.State; using NSubstitute; - namespace Nethermind.Shutter.Test; - public class ShutterApiSimulator( ShutterEventSimulator eventSimulator, IAbiEncoder abiEncoder, @@ -73,7 +69,6 @@ public void InsertShutterReceipts(Block block, in LogEntry[] logs) { var receipts = new TxReceipt[logs.Length]; block.Header.Bloom = new(logs); - // one log per receipt for (int i = 0; i < logs.Length; i++) { diff --git a/src/Nethermind/Nethermind.Shutter/Nethermind.Shutter.csproj b/src/Nethermind/Nethermind.Shutter/Nethermind.Shutter.csproj index f659070d3d2..1f75b9ac946 100644 --- a/src/Nethermind/Nethermind.Shutter/Nethermind.Shutter.csproj +++ b/src/Nethermind/Nethermind.Shutter/Nethermind.Shutter.csproj @@ -1,4 +1,4 @@ - + true diff --git a/src/Nethermind/Nethermind.Shutter/SlotDecryptionIdentites.cs b/src/Nethermind/Nethermind.Shutter/SlotDecryptionIdentites.cs new file mode 100644 index 00000000000..52dfbf69fdd --- /dev/null +++ b/src/Nethermind/Nethermind.Shutter/SlotDecryptionIdentites.cs @@ -0,0 +1,26 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using Nethermind.Serialization.Ssz; +using System.Collections.Generic; + +namespace Nethermind.Shutter; + +[SszSerializable] +public struct SlotDecryptionIdentites +{ + public ulong InstanceID { get; set; } + public ulong Eon { get; set; } + public ulong Slot { get; set; } + public ulong TxPointer { get; set; } + + [SszList(1024)] + public List IdentityPreimages { get; set; } +} + +[SszSerializable] +public struct IdentityPreimage(byte[] data) +{ + [SszVector(52)] + public byte[] Data { get; set; } = data; +} diff --git a/src/Nethermind/Nethermind.sln b/src/Nethermind/Nethermind.sln index 44cf9aa69bd..6651a800390 100644 --- a/src/Nethermind/Nethermind.sln +++ b/src/Nethermind/Nethermind.sln @@ -226,6 +226,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nethermind.Shutter", "Nethe EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nethermind.Shutter.Test", "Nethermind.Shutter.Test\Nethermind.Shutter.Test.csproj", "{CEA1C413-A96C-4339-AC1C-839B603DECC8}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nethermind.Serialization.SszGenerator", "Nethermind.Serialization.SszGenerator\Nethermind.Serialization.SszGenerator.csproj", "{F2F02874-8DF2-4434-A5F7-3418F24E1E56}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nethermind.Serialization.SszGenerator.Test", "Nethermind.Serialization.SszGenerator.Test\Nethermind.Serialization.SszGenerator.Test.csproj", "{E11DA65F-7F52-40DA-BBF4-E6E90932EB68}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -624,6 +628,14 @@ Global {CEA1C413-A96C-4339-AC1C-839B603DECC8}.Debug|Any CPU.Build.0 = Debug|Any CPU {CEA1C413-A96C-4339-AC1C-839B603DECC8}.Release|Any CPU.ActiveCfg = Release|Any CPU {CEA1C413-A96C-4339-AC1C-839B603DECC8}.Release|Any CPU.Build.0 = Release|Any CPU + {F2F02874-8DF2-4434-A5F7-3418F24E1E56}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F2F02874-8DF2-4434-A5F7-3418F24E1E56}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F2F02874-8DF2-4434-A5F7-3418F24E1E56}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F2F02874-8DF2-4434-A5F7-3418F24E1E56}.Release|Any CPU.Build.0 = Release|Any CPU + {E11DA65F-7F52-40DA-BBF4-E6E90932EB68}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E11DA65F-7F52-40DA-BBF4-E6E90932EB68}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E11DA65F-7F52-40DA-BBF4-E6E90932EB68}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E11DA65F-7F52-40DA-BBF4-E6E90932EB68}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -684,6 +696,7 @@ Global {E1E7BEFC-52C0-49ED-B0A7-CB8C3250D120} = {4019B82F-1104-4D2C-9F96-05FD7D3575E8} {89311B58-AF36-4956-883D-54531BC1D5A3} = {78BED57D-720E-4E6C-ABA2-397B73B494F9} {6528010D-7DCE-4935-9785-5270FF515F3E} = {89311B58-AF36-4956-883D-54531BC1D5A3} + {E11DA65F-7F52-40DA-BBF4-E6E90932EB68} = {4019B82F-1104-4D2C-9F96-05FD7D3575E8} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {092CA5E3-6180-4ED7-A3CB-9B57FAC2AA85} diff --git a/src/bench_precompiles b/src/bench_precompiles deleted file mode 160000 index c1089350ef0..00000000000 --- a/src/bench_precompiles +++ /dev/null @@ -1 +0,0 @@ -Subproject commit c1089350ef0daa3f0bd437ce9d1626de973f9a93