Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions src/Nethermind/Nethermind.Core.Test/KeccakTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Buffers;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.Intrinsics.X86;
using Nethermind.Core.Crypto;
using Nethermind.Core.Extensions;
using Nethermind.Serialization.Rlp;
Expand Down Expand Up @@ -168,6 +169,39 @@ public void Computes_known_hash_for_span_and_array()
}
}

[Test]
public void Avx512_permutation_matches_scalar()
{
if (!Avx512F.IsSupported)
{
Assert.Ignore("AVX-512F intrinsics are not supported on this machine.");
Comment thread
benaadams marked this conversation as resolved.
}

const int stateLength = 25;
ulong[] expected = new ulong[stateLength];
ulong[] actual = new ulong[stateLength];

for (int testCase = 0; testCase < 64; testCase++)
{
for (int lane = 0; lane < stateLength; lane++)
{
actual[lane] = testCase switch
{
0 => 0,
1 => ulong.MaxValue,
_ => unchecked((ulong)(testCase * stateLength + lane + 1) * 0x9e3779b97f4a7c15UL)
};
}

actual.CopyTo(expected, 0);

KeccakHash.KeccakF1600Scalar(ref expected[0]);
KeccakHash.KeccakF1600Avx512F(ref actual[0]);

Assert.That(actual, Is.EqualTo(expected), $"Permutation mismatch for test case {testCase}.");
}
Comment thread
benaadams marked this conversation as resolved.
}

[TestCase("0x", "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470")]
public void Sanity_check(string hexString, string expected)
{
Expand Down
2 changes: 2 additions & 0 deletions src/Nethermind/Nethermind.Core/Crypto/Keccak.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ public static ValueHash256 Compute(string input)
}

[DebuggerStepThrough]
[SkipLocalsInit]
public static ValueHash256 Compute(ReadOnlySpan<byte> input)
{
if (input.Length == 0)
Expand All @@ -61,6 +62,7 @@ public static ValueHash256 Compute(ReadOnlySpan<byte> input)
return keccak;
}

[SkipLocalsInit]
internal static ValueHash256 InternalCompute(byte[] input)
{
Unsafe.SkipInit(out ValueHash256 keccak);
Expand Down
19 changes: 17 additions & 2 deletions src/Nethermind/Nethermind.Core/Crypto/KeccakHash.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ public sealed partial class KeccakHash
{
private const int HASH_SIZE = 32;
private const int STATE_SIZE = 200;
private const int STATE_LANES = STATE_SIZE / sizeof(ulong);
private const int HASH_DATA_AREA = 136;

private byte[] _remainderBuffer = [];
Expand Down Expand Up @@ -69,6 +70,7 @@ private KeccakHash(int size)

public KeccakHash Copy() => new(this);

[SkipLocalsInit]
public static void ComputeHash(ReadOnlySpan<byte> input, Span<byte> output)
{
if ((uint)(output.Length - 1) >= STATE_SIZE)
Expand All @@ -83,7 +85,10 @@ public static void ComputeHash(ReadOnlySpan<byte> input, Span<byte> output)
#endif
int roundSize = GetRoundSize(output.Length);

Span<ulong> state = stackalloc ulong[STATE_SIZE / sizeof(ulong)];
// A struct local rather than stackalloc: localloc would pin this method at Tier0-FullOpts
// (no tiering or dynamic PGO) and add GS-cookie and stack-probe overhead per call.
KeccakState stateBuffer = default; // the sponge state must start all-zero
Span<ulong> state = stateBuffer;
Span<byte> stateBytes = MemoryMarshal.AsBytes(state);

if (input.Length == Address.Size)
Expand Down Expand Up @@ -173,13 +178,15 @@ public static uint[] ComputeBytesToUint(ReadOnlySpan<byte> input, int size)
return output;
}

[SkipLocalsInit]
public ValueHash256 GenerateValueHash()
{
Unsafe.SkipInit(out ValueHash256 output);
UpdateFinalTo(output.BytesAsSpan);
return output;
}

[SkipLocalsInit]
public void Update(ReadOnlySpan<byte> input)
{
if (_hash is not null)
Expand Down Expand Up @@ -259,6 +266,7 @@ public void Update(ReadOnlySpan<byte> input)
}
}

[SkipLocalsInit]
public void UpdateFinalTo(Span<byte> output)
{
if (_hash is not null)
Expand Down Expand Up @@ -364,7 +372,8 @@ public void ResetTo(KeccakHash original)

private static partial void KeccakF(Span<ulong> st);

private static int GetRoundSize(int hashSize) => checked(STATE_SIZE - 2 * hashSize);
// Callers bound hashSize to [1, STATE_SIZE], so the arithmetic cannot overflow.
private static int GetRoundSize(int hashSize) => STATE_SIZE - 2 * hashSize;

private byte[] GenerateHash()
{
Expand Down Expand Up @@ -469,6 +478,12 @@ private static unsafe void XorVectors(Span<byte> state, ReadOnlySpan<byte> input
private static void ThrowInvalidOutputSize(int length) => throw new ArgumentOutOfRangeException(
nameof(length), length, $"Must be between 1 and {STATE_SIZE}.");

[InlineArray(STATE_LANES)]
private struct KeccakState
{
private ulong _lane0;
}

private static class Pool
{
private const int MaxPooledPerThread = 4;
Expand Down
Loading
Loading