diff --git a/.github/workflows/dependency_review.yml b/.github/workflows/dependency_review.yml
new file mode 100644
index 00000000..eda9c496
--- /dev/null
+++ b/.github/workflows/dependency_review.yml
@@ -0,0 +1,16 @@
+name: 'Dependency Review'
+on: [pull_request]
+
+permissions:
+ contents: read
+
+jobs:
+ dependency-review:
+ runs-on: ubuntu-latest
+ steps:
+ - name: 'Checkout Repository'
+ uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 #v4.2.0
+ - name: 'Dependency Review'
+ uses: actions/dependency-review-action@4081bf99e2866ebe428fc0477b69eb4fcda7220a #v4.4.0
+ with:
+ fail-on-severity: high
diff --git a/src/Paprika.Tests/Chain/RawStateTests.cs b/src/Paprika.Tests/Chain/RawStateTests.cs
index 5abd58f3..e3edc098 100644
--- a/src/Paprika.Tests/Chain/RawStateTests.cs
+++ b/src/Paprika.Tests/Chain/RawStateTests.cs
@@ -150,4 +150,79 @@ public async Task DeleteByPrefix()
using var read = db.BeginReadOnlyBatch();
read.TryGet(Key.Account(account), out _).Should().BeFalse();
}
+
+ [Test]
+ public async Task DeleteByShortPrefix()
+ {
+ var account1 = new Keccak(new byte[]
+ { 1, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, });
+
+ var account2 = new Keccak(new byte[]
+ { 18, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, });
+
+ using var db = PagedDb.NativeMemoryDb(64 * 1024, 2);
+ var merkle = new ComputeMerkleBehavior();
+
+ await using var blockchain = new Blockchain(db, merkle);
+
+ using var raw = blockchain.StartRaw();
+
+ raw.SetAccount(account1, new Account(1, 1));
+ raw.SetAccount(account2, new Account(2, 2));
+ raw.Commit();
+
+ raw.RegisterDeleteByPrefix(Key.Account(NibblePath.FromKey(account2).SliceTo(1)));
+ raw.Commit();
+
+ raw.Finalize(1);
+
+ //check account 1 is still present and account 2 is deleted
+ using var read = db.BeginReadOnlyBatch();
+ read.TryGet(Key.Account(account1), out _).Should().BeTrue();
+ read.TryGet(Key.Account(account2), out _).Should().BeFalse();
+
+ //let's re-add 2nd account and delete using empty prefix
+ using var raw2 = blockchain.StartRaw();
+
+ raw2.SetAccount(account2, new Account(2, 2));
+ raw2.Commit();
+
+ raw2.RegisterDeleteByPrefix(Key.Account(NibblePath.Empty));
+ raw2.Commit();
+
+ raw2.Finalize(2);
+
+ //no accounts should be present
+ using var read2 = db.BeginReadOnlyBatch();
+ read2.TryGet(Key.Account(account1), out _).Should().BeFalse();
+ read2.TryGet(Key.Account(account2), out _).Should().BeFalse();
+ }
+
+ [Test]
+ public void DeleteByPrefixStorage()
+ {
+ var account = Values.Key1;
+
+ using var db = PagedDb.NativeMemoryDb(256 * 1024, 2);
+ var merkle = new ComputeMerkleBehavior();
+
+ var blockchain = new Blockchain(db, merkle);
+
+ using var raw = blockchain.StartRaw();
+
+ raw.SetAccount(account, new Account(1, 1));
+ raw.SetStorage(account, Values.Key2, new byte[] { 1, 2, 3, 4, 5 });
+ raw.Commit();
+
+ using var read = db.BeginReadOnlyBatch();
+ read.TryGet(Key.StorageCell(NibblePath.FromKey(account), Values.Key2), out _).Should().BeTrue();
+
+ raw.RegisterDeleteByPrefix(Key.StorageCell(NibblePath.FromKey(account), NibblePath.Empty));
+ raw.Commit();
+
+ raw.Finalize(1);
+
+ using var read2 = db.BeginReadOnlyBatch();
+ read2.TryGet(Key.StorageCell(NibblePath.FromKey(account), Values.Key2), out _).Should().BeFalse();
+ }
}
diff --git a/src/Paprika/Crypto/KeccakHash.cs b/src/Paprika/Crypto/KeccakHash.cs
index 4437b0fb..74ba3c7e 100644
--- a/src/Paprika/Crypto/KeccakHash.cs
+++ b/src/Paprika/Crypto/KeccakHash.cs
@@ -15,9 +15,10 @@
// along with the Nethermind. If not, see .
using System.Diagnostics;
-using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
+using System.Runtime.Intrinsics.X86;
+using System.Runtime.Intrinsics;
using static System.Numerics.BitOperations;
// ReSharper disable InconsistentNaming
@@ -41,6 +42,18 @@ public sealed class KeccakHash
// update the state with given number of rounds
private static void KeccakF(Span st)
+ {
+ if (Avx512F.IsSupported)
+ {
+ KeccakF1600Avx512F(st);
+ }
+ else
+ {
+ KeccakF1600(st);
+ }
+ }
+
+ private static void KeccakF1600(Span st)
{
Debug.Assert(st.Length == 25);
@@ -57,36 +70,31 @@ private static void KeccakF(Span st)
ulong ema, eme, emi, emo, emu;
ulong esa, ese, esi, eso, esu;
- {
- // Access last element to perform range check once
- // and not for every ascending access
- _ = st[24];
- }
- aba = st[0];
- abe = st[1];
- abi = st[2];
- abo = st[3];
- abu = st[4];
- aga = st[5];
- age = st[6];
- agi = st[7];
- ago = st[8];
- agu = st[9];
- aka = st[10];
- ake = st[11];
- aki = st[12];
- ako = st[13];
- aku = st[14];
- ama = st[15];
- ame = st[16];
- ami = st[17];
- amo = st[18];
- amu = st[19];
- asa = st[20];
- ase = st[21];
- asi = st[22];
- aso = st[23];
asu = st[24];
+ aso = st[23];
+ asi = st[22];
+ ase = st[21];
+ asa = st[20];
+ amu = st[19];
+ amo = st[18];
+ ami = st[17];
+ ame = st[16];
+ ama = st[15];
+ aku = st[14];
+ ako = st[13];
+ aki = st[12];
+ ake = st[11];
+ aka = st[10];
+ agu = st[9];
+ ago = st[8];
+ agi = st[7];
+ age = st[6];
+ aga = st[5];
+ abu = st[4];
+ abo = st[3];
+ abi = st[2];
+ abe = st[1];
+ aba = st[0];
for (int round = 0; round < ROUNDS; round += 2)
{
@@ -104,253 +112,467 @@ private static void KeccakF(Span st)
@do = bCi ^ RotateLeft(bCu, 1);
du = bCo ^ RotateLeft(bCa, 1);
- aba ^= da;
- bCa = aba;
- age ^= de;
- bCe = RotateLeft(age, 44);
- aki ^= di;
- bCi = RotateLeft(aki, 43);
- amo ^= @do;
- bCo = RotateLeft(amo, 21);
- asu ^= du;
- bCu = RotateLeft(asu, 14);
- eba = bCa ^ ((~bCe) & bCi);
- eba ^= RoundConstants[round];
+ bCa = aba ^ da;
+ bCe = RotateLeft(age ^ de, 44);
+ bCi = RotateLeft(aki ^ di, 43);
+ eba = bCa ^ ((~bCe) & bCi) ^ RoundConstants[round];
+ bCo = RotateLeft(amo ^ @do, 21);
ebe = bCe ^ ((~bCi) & bCo);
+ bCu = RotateLeft(asu ^ du, 14);
ebi = bCi ^ ((~bCo) & bCu);
ebo = bCo ^ ((~bCu) & bCa);
ebu = bCu ^ ((~bCa) & bCe);
- abo ^= @do;
- bCa = RotateLeft(abo, 28);
- agu ^= du;
- bCe = RotateLeft(agu, 20);
- aka ^= da;
- bCi = RotateLeft(aka, 3);
- ame ^= de;
- bCo = RotateLeft(ame, 45);
- asi ^= di;
- bCu = RotateLeft(asi, 61);
+ bCa = RotateLeft(abo ^ @do, 28);
+ bCe = RotateLeft(agu ^ du, 20);
+ bCi = RotateLeft(aka ^ da, 3);
ega = bCa ^ ((~bCe) & bCi);
+ bCo = RotateLeft(ame ^ de, 45);
ege = bCe ^ ((~bCi) & bCo);
+ bCu = RotateLeft(asi ^ di, 61);
egi = bCi ^ ((~bCo) & bCu);
ego = bCo ^ ((~bCu) & bCa);
egu = bCu ^ ((~bCa) & bCe);
- abe ^= de;
- bCa = RotateLeft(abe, 1);
- agi ^= di;
- bCe = RotateLeft(agi, 6);
- ako ^= @do;
- bCi = RotateLeft(ako, 25);
- amu ^= du;
- bCo = RotateLeft(amu, 8);
- asa ^= da;
- bCu = RotateLeft(asa, 18);
+ bCa = RotateLeft(abe ^ de, 1);
+ bCe = RotateLeft(agi ^ di, 6);
+ bCi = RotateLeft(ako ^ @do, 25);
eka = bCa ^ ((~bCe) & bCi);
+ bCo = RotateLeft(amu ^ du, 8);
eke = bCe ^ ((~bCi) & bCo);
+ bCu = RotateLeft(asa ^ da, 18);
eki = bCi ^ ((~bCo) & bCu);
eko = bCo ^ ((~bCu) & bCa);
eku = bCu ^ ((~bCa) & bCe);
- abu ^= du;
- bCa = RotateLeft(abu, 27);
- aga ^= da;
- bCe = RotateLeft(aga, 36);
- ake ^= de;
- bCi = RotateLeft(ake, 10);
- ami ^= di;
- bCo = RotateLeft(ami, 15);
- aso ^= @do;
- bCu = RotateLeft(aso, 56);
+ bCa = RotateLeft(abu ^ du, 27);
+ bCe = RotateLeft(aga ^ da, 36);
+ bCi = RotateLeft(ake ^ de, 10);
ema = bCa ^ ((~bCe) & bCi);
+ bCo = RotateLeft(ami ^ di, 15);
eme = bCe ^ ((~bCi) & bCo);
+ bCu = RotateLeft(aso ^ @do, 56);
emi = bCi ^ ((~bCo) & bCu);
emo = bCo ^ ((~bCu) & bCa);
emu = bCu ^ ((~bCa) & bCe);
- abi ^= di;
- bCa = RotateLeft(abi, 62);
- ago ^= @do;
- bCe = RotateLeft(ago, 55);
- aku ^= du;
- bCi = RotateLeft(aku, 39);
- ama ^= da;
- bCo = RotateLeft(ama, 41);
- ase ^= de;
- bCu = RotateLeft(ase, 2);
+ bCa = RotateLeft(abi ^ di, 62);
+ bCe = RotateLeft(ago ^ @do, 55);
+ bCi = RotateLeft(aku ^ du, 39);
esa = bCa ^ ((~bCe) & bCi);
+ bCo = RotateLeft(ama ^ da, 41);
ese = bCe ^ ((~bCi) & bCo);
+ bCu = RotateLeft(ase ^ de, 2);
esi = bCi ^ ((~bCo) & bCu);
eso = bCo ^ ((~bCu) & bCa);
esu = bCu ^ ((~bCa) & bCe);
// prepareTheta
- bCa = eba ^ ega ^ eka ^ ema ^ esa;
+
bCe = ebe ^ ege ^ eke ^ eme ^ ese;
- bCi = ebi ^ egi ^ eki ^ emi ^ esi;
- bCo = ebo ^ ego ^ eko ^ emo ^ eso;
bCu = ebu ^ egu ^ eku ^ emu ^ esu;
-
//thetaRhoPiChiIotaPrepareTheta(round+1, E, A)
da = bCu ^ RotateLeft(bCe, 1);
+ bCa = eba ^ ega ^ eka ^ ema ^ esa;
+ bCi = ebi ^ egi ^ eki ^ emi ^ esi;
de = bCa ^ RotateLeft(bCi, 1);
+ bCo = ebo ^ ego ^ eko ^ emo ^ eso;
di = bCe ^ RotateLeft(bCo, 1);
@do = bCi ^ RotateLeft(bCu, 1);
du = bCo ^ RotateLeft(bCa, 1);
- eba ^= da;
- bCa = eba;
- ege ^= de;
- bCe = RotateLeft(ege, 44);
- eki ^= di;
- bCi = RotateLeft(eki, 43);
- emo ^= @do;
- bCo = RotateLeft(emo, 21);
- esu ^= du;
- bCu = RotateLeft(esu, 14);
- aba = bCa ^ ((~bCe) & bCi);
- aba ^= RoundConstants[round + 1];
+
+ bCi = RotateLeft(eki ^ di, 43);
+ bCe = RotateLeft(ege ^ de, 44);
+ bCa = eba ^ da;
+ aba = bCa ^ ((~bCe) & bCi) ^ RoundConstants[round + 1];
+ bCo = RotateLeft(emo ^ @do, 21);
abe = bCe ^ ((~bCi) & bCo);
+ bCu = RotateLeft(esu ^ du, 14);
abi = bCi ^ ((~bCo) & bCu);
abo = bCo ^ ((~bCu) & bCa);
abu = bCu ^ ((~bCa) & bCe);
- ebo ^= @do;
- bCa = RotateLeft(ebo, 28);
- egu ^= du;
- bCe = RotateLeft(egu, 20);
- eka ^= da;
- bCi = RotateLeft(eka, 3);
- eme ^= de;
- bCo = RotateLeft(eme, 45);
- esi ^= di;
- bCu = RotateLeft(esi, 61);
+ bCa = RotateLeft(ebo ^ @do, 28);
+ bCe = RotateLeft(egu ^ du, 20);
+ bCi = RotateLeft(eka ^ da, 3);
aga = bCa ^ ((~bCe) & bCi);
+ bCo = RotateLeft(eme ^ de, 45);
age = bCe ^ ((~bCi) & bCo);
+ bCu = RotateLeft(esi ^ di, 61);
agi = bCi ^ ((~bCo) & bCu);
ago = bCo ^ ((~bCu) & bCa);
agu = bCu ^ ((~bCa) & bCe);
- ebe ^= de;
- bCa = RotateLeft(ebe, 1);
- egi ^= di;
- bCe = RotateLeft(egi, 6);
- eko ^= @do;
- bCi = RotateLeft(eko, 25);
- emu ^= du;
- bCo = RotateLeft(emu, 8);
- esa ^= da;
- bCu = RotateLeft(esa, 18);
+ bCa = RotateLeft(ebe ^ de, 1);
+ bCe = RotateLeft(egi ^ di, 6);
+ bCi = RotateLeft(eko ^ @do, 25);
aka = bCa ^ ((~bCe) & bCi);
+ bCo = RotateLeft(emu ^ du, 8);
ake = bCe ^ ((~bCi) & bCo);
+ bCu = RotateLeft(esa ^ da, 18);
aki = bCi ^ ((~bCo) & bCu);
ako = bCo ^ ((~bCu) & bCa);
aku = bCu ^ ((~bCa) & bCe);
- ebu ^= du;
- bCa = RotateLeft(ebu, 27);
- ega ^= da;
- bCe = RotateLeft(ega, 36);
- eke ^= de;
- bCi = RotateLeft(eke, 10);
- emi ^= di;
- bCo = RotateLeft(emi, 15);
- eso ^= @do;
- bCu = RotateLeft(eso, 56);
+ bCa = RotateLeft(ebu ^ du, 27);
+ bCe = RotateLeft(ega ^ da, 36);
+ bCi = RotateLeft(eke ^ de, 10);
ama = bCa ^ ((~bCe) & bCi);
+ bCo = RotateLeft(emi ^ di, 15);
ame = bCe ^ ((~bCi) & bCo);
+ bCu = RotateLeft(eso ^ @do, 56);
ami = bCi ^ ((~bCo) & bCu);
amo = bCo ^ ((~bCu) & bCa);
amu = bCu ^ ((~bCa) & bCe);
- ebi ^= di;
- bCa = RotateLeft(ebi, 62);
- ego ^= @do;
- bCe = RotateLeft(ego, 55);
- eku ^= du;
- bCi = RotateLeft(eku, 39);
- ema ^= da;
- bCo = RotateLeft(ema, 41);
- ese ^= de;
- bCu = RotateLeft(ese, 2);
+ bCa = RotateLeft(ebi ^ di, 62);
+ bCe = RotateLeft(ego ^ @do, 55);
+ bCi = RotateLeft(eku ^ du, 39);
asa = bCa ^ ((~bCe) & bCi);
+ bCo = RotateLeft(ema ^ da, 41);
ase = bCe ^ ((~bCi) & bCo);
+ bCu = RotateLeft(ese ^ de, 2);
asi = bCi ^ ((~bCo) & bCu);
aso = bCo ^ ((~bCu) & bCa);
asu = bCu ^ ((~bCa) & bCe);
}
//copyToState(state, A)
- st[0] = aba;
- st[1] = abe;
- st[2] = abi;
- st[3] = abo;
- st[4] = abu;
- st[5] = aga;
- st[6] = age;
- st[7] = agi;
- st[8] = ago;
- st[9] = agu;
- st[10] = aka;
- st[11] = ake;
- st[12] = aki;
- st[13] = ako;
- st[14] = aku;
- st[15] = ama;
- st[16] = ame;
- st[17] = ami;
- st[18] = amo;
- st[19] = amu;
- st[20] = asa;
- st[21] = ase;
- st[22] = asi;
- st[23] = aso;
st[24] = asu;
+ st[23] = aso;
+ st[22] = asi;
+ st[21] = ase;
+ st[20] = asa;
+ st[19] = amu;
+ st[18] = amo;
+ st[17] = ami;
+ st[16] = ame;
+ st[15] = ama;
+ st[14] = aku;
+ st[13] = ako;
+ st[12] = aki;
+ st[11] = ake;
+ st[10] = aka;
+ st[9] = agu;
+ st[8] = ago;
+ st[7] = agi;
+ st[6] = age;
+ st[5] = aga;
+ st[4] = abu;
+ st[3] = abo;
+ st[2] = abi;
+ st[1] = abe;
+ st[0] = aba;
}
// compute a Keccak hash (md) of given byte length from "in"
public static void ComputeHash(ReadOnlySpan input, out Keccak output)
{
+ const int AddressSize = 20;
const int stateSize = 200;
const int roundSize = 136;
const int ulongSize = stateSize / sizeof(ulong);
- const int ulongRounds = roundSize / sizeof(ulong);
Span state = stackalloc ulong[ulongSize];
+ Span stateBytes = MemoryMarshal.AsBytes(state);
- int loopLength = input.Length / roundSize * roundSize;
- if (loopLength > 0)
+ if (input.Length == AddressSize)
+ {
+ // Hashing Address, 20 bytes which is uint+Vector128
+ Unsafe.As(ref MemoryMarshal.GetReference(stateBytes)) =
+ Unsafe.As(ref MemoryMarshal.GetReference(input));
+ Unsafe.As>(ref Unsafe.Add(ref MemoryMarshal.GetReference(stateBytes), sizeof(uint))) =
+ Unsafe.As>(ref Unsafe.Add(ref MemoryMarshal.GetReference(input), sizeof(uint)));
+ }
+ else if (input.Length == Vector256.Count)
+ {
+ // Hashing Hash256 or UInt256, 32 bytes
+ Unsafe.As>(ref MemoryMarshal.GetReference(stateBytes)) =
+ Unsafe.As>(ref MemoryMarshal.GetReference(input));
+ }
+ else if (input.Length >= roundSize)
{
- ReadOnlySpan input64 = MemoryMarshal.Cast(input[..loopLength]);
- input = input.Slice(loopLength);
- while (input64.Length > 0)
+ // Process full rounds
+ do
{
- for (int i = 0; i < ulongRounds; i++)
- {
- state[i] ^= input64[i];
- }
-
- input64 = input64[ulongRounds..];
+ XorVectors(stateBytes, input.Slice(0, roundSize));
KeccakF(state);
+ input = input.Slice(roundSize);
+ } while (input.Length >= roundSize);
+
+ if (input.Length > 0)
+ {
+ // XOR the remaining input bytes into the state
+ XorVectors(stateBytes, input);
}
}
-
- Span temp = stackalloc byte[roundSize];
- input.CopyTo(temp);
- temp[input.Length] = 1;
- temp[roundSize - 1] |= 0x80;
-
- Span tempU64 = MemoryMarshal.Cast(temp);
- for (int i = 0; i < tempU64.Length; i++)
+ else
{
- state[i] ^= tempU64[i];
+ input.CopyTo(stateBytes);
}
+ // Apply terminator markers within the current block
+ stateBytes[input.Length] ^= 0x01; // Append bit '1' after remaining input
+ stateBytes[roundSize - 1] ^= 0x80; // Set the last bit of the round to '1'
+
+ // Process the final block
KeccakF(state);
+
output = Unsafe.As(ref MemoryMarshal.GetReference(state));
}
- [DoesNotReturn]
- private static void ThrowBadKeccak() => throw new ArgumentException("Bad Keccak use");
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static unsafe void XorVectors(Span state, ReadOnlySpan input)
+ {
+ ref byte stateRef = ref MemoryMarshal.GetReference(state);
+ if (Vector512.IsSupported && input.Length >= Vector512.Count)
+ {
+ // Convert to uint for the mod else the Jit does a more complicated signed mod
+ // whereas as uint it just does an And
+ int vectorLength = input.Length - (int)((uint)input.Length % (uint)Vector512.Count);
+ ref byte inputRef = ref MemoryMarshal.GetReference(input);
+ for (int i = 0; i < vectorLength; i += Vector512.Count)
+ {
+ ref Vector512 state256 = ref Unsafe.As>(ref Unsafe.Add(ref stateRef, i));
+ Vector512 input256 = Unsafe.As>(ref Unsafe.Add(ref inputRef, i));
+ state256 = Vector512.Xor(state256, input256);
+ }
+
+ input = input.Slice(vectorLength);
+ stateRef = ref Unsafe.Add(ref stateRef, vectorLength);
+ }
+
+ if (Vector256.IsSupported && input.Length >= Vector256.Count)
+ {
+ // Convert to uint for the mod else the Jit does a more complicated signed mod
+ // whereas as uint it just does an And
+ int vectorLength = input.Length - (int)((uint)input.Length % (uint)Vector256.Count);
+ ref byte inputRef = ref MemoryMarshal.GetReference(input);
+ for (int i = 0; i < vectorLength; i += Vector256.Count)
+ {
+ ref Vector256 state256 = ref Unsafe.As>(ref Unsafe.Add(ref stateRef, i));
+ Vector256 input256 = Unsafe.As>(ref Unsafe.Add(ref inputRef, i));
+ state256 = Vector256.Xor(state256, input256);
+ }
+
+ input = input.Slice(vectorLength);
+ stateRef = ref Unsafe.Add(ref stateRef, vectorLength);
+ }
+
+ if (Vector128.IsSupported && input.Length >= Vector128.Count)
+ {
+ int vectorLength = input.Length - (int)((uint)input.Length % (uint)Vector128.Count);
+ ref byte inputRef = ref MemoryMarshal.GetReference(input);
+ for (int i = 0; i < vectorLength; i += Vector128.Count)
+ {
+ ref Vector128 state128 = ref Unsafe.As>(ref Unsafe.Add(ref stateRef, i));
+ Vector128 input128 = Unsafe.As>(ref Unsafe.Add(ref inputRef, i));
+ state128 = Vector128.Xor(state128, input128);
+ }
+
+ input = input.Slice(vectorLength);
+ stateRef = ref Unsafe.Add(ref stateRef, vectorLength);
+ }
+
+ // Handle remaining elements
+ for (int i = 0; i < input.Length; i++)
+ {
+ Unsafe.Add(ref stateRef, i) ^= input[i];
+ }
+ }
+
+ [SkipLocalsInit]
+ public static void KeccakF1600Avx512F(Span state)
+ {
+ {
+ // Redundant statement that removes all the in loop bounds checks
+ _ = state[24];
+ }
+
+ // Can straight load and over-read for start elements
+ Vector512 mask = Vector512.Create(ulong.MaxValue, ulong.MaxValue, ulong.MaxValue, ulong.MaxValue, ulong.MaxValue, 0UL, 0UL, 0UL);
+ Vector512 c0 = Unsafe.As>(ref MemoryMarshal.GetReference(state));
+ // Clear the over-read values from first vectors
+ c0 = Vector512.BitwiseAnd(mask, c0);
+ Vector512 c1 = Unsafe.As>(ref Unsafe.Add(ref MemoryMarshal.GetReference(state), 5));
+ c1 = Vector512.BitwiseAnd(mask, c1);
+ Vector512 c2 = Unsafe.As>(ref Unsafe.Add(ref MemoryMarshal.GetReference(state), 10));
+ c2 = Vector512.BitwiseAnd(mask, c2);
+ Vector512 c3 = Unsafe.As>(ref Unsafe.Add(ref MemoryMarshal.GetReference(state), 15));
+ c3 = Vector512.BitwiseAnd(mask, c3);
+
+ // Can't over-read for the last elements (8 items in vector 5 to be remaining)
+ // so read a Vector256 and ulong then combine
+ Vector256 c4a = Unsafe.As>(ref Unsafe.Add(ref MemoryMarshal.GetReference(state), 20));
+ Vector256 c4b = Vector256.Create(state[24], 0UL, 0UL, 0UL);
+ Vector512 c4 = Vector512.Create(c4a, c4b);
+
+ Vector512 permute1 = Vector512.Create(1UL, 2UL, 3UL, 4UL, 0UL, 5UL, 6UL, 7UL);
+ Vector512 permute2 = Vector512.Create(2UL, 3UL, 4UL, 0UL, 1UL, 5UL, 6UL, 7UL);
+ ulong[] roundConstants = RoundConstants;
+ // Use constant for loop so Jit expects to loop; unroll once
+ for (int round = 0; round < ROUNDS; round += 2)
+ {
+ // Iteration 1
+ {
+ ulong roundConstant = Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(roundConstants), round);
+ // Theta step
+ Vector512 parity = Avx512F.TernaryLogic(Avx512F.TernaryLogic(c0, c1, c2, 0x96), c3, c4, 0x96);
+
+ // Compute Theta
+ Vector512 bVecRot1Rotated = Avx512F.RotateLeft(Avx512F.PermuteVar8x64(parity, Vector512.Create(1UL, 2UL, 3UL, 4UL, 0UL, 5UL, 6UL, 7UL)), 1);
+ Vector512 bVecRot4 = Avx512F.PermuteVar8x64(parity, Vector512.Create(4UL, 0UL, 1UL, 2UL, 3UL, 5UL, 6UL, 7UL));
+ Vector512 theta = Avx512F.Xor(bVecRot4, bVecRot1Rotated);
+
+ c0 = Avx512F.Xor(c0, theta);
+ c1 = Avx512F.Xor(c1, theta);
+ c2 = Avx512F.Xor(c2, theta);
+ c3 = Avx512F.Xor(c3, theta);
+ c4 = Avx512F.Xor(c4, theta);
+
+ // Rho step
+ Vector512 rhoVec0 = Vector512.Create(0UL, 1UL, 62UL, 28UL, 27UL, 0UL, 0UL, 0UL);
+ c0 = Avx512F.RotateLeftVariable(c0, rhoVec0);
+
+ Vector512 rhoVec1 = Vector512.Create(36UL, 44UL, 6UL, 55UL, 20UL, 0UL, 0UL, 0UL);
+ c1 = Avx512F.RotateLeftVariable(c1, rhoVec1);
+
+ Vector512 rhoVec2 = Vector512.Create(3UL, 10UL, 43UL, 25UL, 39UL, 0UL, 0UL, 0UL);
+ c2 = Avx512F.RotateLeftVariable(c2, rhoVec2);
+
+ Vector512 rhoVec3 = Vector512.Create(41UL, 45UL, 15UL, 21UL, 8UL, 0UL, 0UL, 0UL);
+ c3 = Avx512F.RotateLeftVariable(c3, rhoVec3);
+
+ Vector512 rhoVec4 = Vector512.Create(18UL, 2UL, 61UL, 56UL, 14UL, 0UL, 0UL, 0UL);
+ c4 = Avx512F.RotateLeftVariable(c4, rhoVec4);
+
+ // Pi step
+ Vector512 c0Pi = Avx512F.PermuteVar8x64x2(c0, Vector512.Create(0UL, 8 + 1, 2, 3, 4, 5, 6, 7), c1);
+ c0Pi = Avx512F.PermuteVar8x64x2(c0Pi, Vector512.Create(0UL, 1, 8 + 2, 3, 4, 5, 6, 7), c2);
+ c0Pi = Avx512F.PermuteVar8x64x2(c0Pi, Vector512.Create(0UL, 1, 2, 8 + 3, 4, 5, 6, 7), c3);
+ c0Pi = Avx512F.PermuteVar8x64x2(c0Pi, Vector512.Create(0UL, 1, 2, 3, 8 + 4, 5, 6, 7), c4);
+
+ Vector512 c1Pi = Avx512F.PermuteVar8x64x2(c0, Vector512.Create(3UL, 8 + 4, 2, 3, 4, 5, 6, 7), c1);
+ c1Pi = Avx512F.PermuteVar8x64x2(c1Pi, Vector512.Create(0UL, 1, 8 + 0, 3, 4, 5, 6, 7), c2);
+ c1Pi = Avx512F.PermuteVar8x64x2(c1Pi, Vector512.Create(0UL, 1, 2, 8 + 1, 4, 5, 6, 7), c3);
+ c1Pi = Avx512F.PermuteVar8x64x2(c1Pi, Vector512.Create(0UL, 1, 2, 3, 8 + 2, 5, 6, 7), c4);
+
+ Vector512 c2Pi = Avx512F.PermuteVar8x64x2(c0, Vector512.Create(1UL, 8 + 2, 2, 3, 4, 5, 6, 7), c1);
+ c2Pi = Avx512F.PermuteVar8x64x2(c2Pi, Vector512.Create(0UL, 1, 8 + 3, 3, 4, 5, 6, 7), c2);
+ c2Pi = Avx512F.PermuteVar8x64x2(c2Pi, Vector512.Create(0UL, 1, 2, 8 + 4, 4, 5, 6, 7), c3);
+ c2Pi = Avx512F.PermuteVar8x64x2(c2Pi, Vector512.Create(0UL, 1, 2, 3, 8 + 0, 5, 6, 7), c4);
+
+ Vector512 c3Pi = Avx512F.PermuteVar8x64x2(c0, Vector512.Create(4UL, 8 + 0, 2, 3, 4, 5, 6, 7), c1);
+ c3Pi = Avx512F.PermuteVar8x64x2(c3Pi, Vector512.Create(0UL, 1, 8 + 1, 3, 4, 5, 6, 7), c2);
+ c3Pi = Avx512F.PermuteVar8x64x2(c3Pi, Vector512.Create(0UL, 1, 2, 8 + 2, 4, 5, 6, 7), c3);
+ c3Pi = Avx512F.PermuteVar8x64x2(c3Pi, Vector512.Create(0UL, 1, 2, 3, 8 + 3, 5, 6, 7), c4);
+
+ Vector512 c4Pi = Avx512F.PermuteVar8x64x2(c0, Vector512.Create(2UL, 8 + 3, 2, 3, 4, 5, 6, 7), c1);
+ c0 = c0Pi;
+ c1 = c1Pi;
+ c4Pi = Avx512F.PermuteVar8x64x2(c4Pi, Vector512.Create(0UL, 1, 8 + 4, 3, 4, 5, 6, 7), c2);
+ c2 = c2Pi;
+ c4Pi = Avx512F.PermuteVar8x64x2(c4Pi, Vector512.Create(0UL, 1, 2, 8 + 0, 4, 5, 6, 7), c3);
+ c3 = c3Pi;
+ c4Pi = Avx512F.PermuteVar8x64x2(c4Pi, Vector512.Create(0UL, 1, 2, 3, 8 + 1, 5, 6, 7), c4);
+ c4 = c4Pi;
+
+ // Chi step
+
+ c0 = Avx512F.TernaryLogic(c0, Avx512F.PermuteVar8x64(c0, permute1), Avx512F.PermuteVar8x64(c0, permute2), 0xD2);
+ c1 = Avx512F.TernaryLogic(c1, Avx512F.PermuteVar8x64(c1, permute1), Avx512F.PermuteVar8x64(c1, permute2), 0xD2);
+ c2 = Avx512F.TernaryLogic(c2, Avx512F.PermuteVar8x64(c2, permute1), Avx512F.PermuteVar8x64(c2, permute2), 0xD2);
+ c3 = Avx512F.TernaryLogic(c3, Avx512F.PermuteVar8x64(c3, permute1), Avx512F.PermuteVar8x64(c3, permute2), 0xD2);
+ c4 = Avx512F.TernaryLogic(c4, Avx512F.PermuteVar8x64(c4, permute1), Avx512F.PermuteVar8x64(c4, permute2), 0xD2);
+
+ // Iota step
+ c0 = Vector512.Xor(c0, Vector512.Create(roundConstant, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL));
+ }
+ // Iteration 2
+ {
+ ulong roundConstant = Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(roundConstants), round + 1);
+ // Theta step
+ Vector512 parity = Avx512F.TernaryLogic(Avx512F.TernaryLogic(c0, c1, c2, 0x96), c3, c4, 0x96);
+
+ // Compute Theta
+ Vector512 bVecRot1Rotated = Avx512F.RotateLeft(Avx512F.PermuteVar8x64(parity, Vector512.Create(1UL, 2UL, 3UL, 4UL, 0UL, 5UL, 6UL, 7UL)), 1);
+ Vector512 bVecRot4 = Avx512F.PermuteVar8x64(parity, Vector512.Create(4UL, 0UL, 1UL, 2UL, 3UL, 5UL, 6UL, 7UL));
+ Vector512 theta = Avx512F.Xor(bVecRot4, bVecRot1Rotated);
+
+ c0 = Avx512F.Xor(c0, theta);
+ c1 = Avx512F.Xor(c1, theta);
+ c2 = Avx512F.Xor(c2, theta);
+ c3 = Avx512F.Xor(c3, theta);
+ c4 = Avx512F.Xor(c4, theta);
+
+ // Rho step
+ Vector512 rhoVec0 = Vector512.Create(0UL, 1UL, 62UL, 28UL, 27UL, 0UL, 0UL, 0UL);
+ c0 = Avx512F.RotateLeftVariable(c0, rhoVec0);
+
+ Vector512 rhoVec1 = Vector512.Create(36UL, 44UL, 6UL, 55UL, 20UL, 0UL, 0UL, 0UL);
+ c1 = Avx512F.RotateLeftVariable(c1, rhoVec1);
+
+ Vector512 rhoVec2 = Vector512.Create(3UL, 10UL, 43UL, 25UL, 39UL, 0UL, 0UL, 0UL);
+ c2 = Avx512F.RotateLeftVariable(c2, rhoVec2);
+
+ Vector512 rhoVec3 = Vector512.Create(41UL, 45UL, 15UL, 21UL, 8UL, 0UL, 0UL, 0UL);
+ c3 = Avx512F.RotateLeftVariable(c3, rhoVec3);
+
+ Vector512 rhoVec4 = Vector512.Create(18UL, 2UL, 61UL, 56UL, 14UL, 0UL, 0UL, 0UL);
+ c4 = Avx512F.RotateLeftVariable(c4, rhoVec4);
+
+ // Pi step
+ Vector512 c0Pi = Avx512F.PermuteVar8x64x2(c0, Vector512.Create(0UL, 8 + 1, 2, 3, 4, 5, 6, 7), c1);
+ c0Pi = Avx512F.PermuteVar8x64x2(c0Pi, Vector512.Create(0UL, 1, 8 + 2, 3, 4, 5, 6, 7), c2);
+ c0Pi = Avx512F.PermuteVar8x64x2(c0Pi, Vector512.Create(0UL, 1, 2, 8 + 3, 4, 5, 6, 7), c3);
+ c0Pi = Avx512F.PermuteVar8x64x2(c0Pi, Vector512.Create(0UL, 1, 2, 3, 8 + 4, 5, 6, 7), c4);
+
+ Vector512 c1Pi = Avx512F.PermuteVar8x64x2(c0, Vector512.Create(3UL, 8 + 4, 2, 3, 4, 5, 6, 7), c1);
+ c1Pi = Avx512F.PermuteVar8x64x2(c1Pi, Vector512.Create(0UL, 1, 8 + 0, 3, 4, 5, 6, 7), c2);
+ c1Pi = Avx512F.PermuteVar8x64x2(c1Pi, Vector512.Create(0UL, 1, 2, 8 + 1, 4, 5, 6, 7), c3);
+ c1Pi = Avx512F.PermuteVar8x64x2(c1Pi, Vector512.Create(0UL, 1, 2, 3, 8 + 2, 5, 6, 7), c4);
+
+ Vector512 c2Pi = Avx512F.PermuteVar8x64x2(c0, Vector512.Create(1UL, 8 + 2, 2, 3, 4, 5, 6, 7), c1);
+ c2Pi = Avx512F.PermuteVar8x64x2(c2Pi, Vector512.Create(0UL, 1, 8 + 3, 3, 4, 5, 6, 7), c2);
+ c2Pi = Avx512F.PermuteVar8x64x2(c2Pi, Vector512.Create(0UL, 1, 2, 8 + 4, 4, 5, 6, 7), c3);
+ c2Pi = Avx512F.PermuteVar8x64x2(c2Pi, Vector512.Create(0UL, 1, 2, 3, 8 + 0, 5, 6, 7), c4);
+
+ Vector512 c3Pi = Avx512F.PermuteVar8x64x2(c0, Vector512.Create(4UL, 8 + 0, 2, 3, 4, 5, 6, 7), c1);
+ c3Pi = Avx512F.PermuteVar8x64x2(c3Pi, Vector512.Create(0UL, 1, 8 + 1, 3, 4, 5, 6, 7), c2);
+ c3Pi = Avx512F.PermuteVar8x64x2(c3Pi, Vector512.Create(0UL, 1, 2, 8 + 2, 4, 5, 6, 7), c3);
+ c3Pi = Avx512F.PermuteVar8x64x2(c3Pi, Vector512.Create(0UL, 1, 2, 3, 8 + 3, 5, 6, 7), c4);
+
+ Vector512 c4Pi = Avx512F.PermuteVar8x64x2(c0, Vector512.Create(2UL, 8 + 3, 2, 3, 4, 5, 6, 7), c1);
+ c0 = c0Pi;
+ c1 = c1Pi;
+ c4Pi = Avx512F.PermuteVar8x64x2(c4Pi, Vector512.Create(0UL, 1, 8 + 4, 3, 4, 5, 6, 7), c2);
+ c2 = c2Pi;
+ c4Pi = Avx512F.PermuteVar8x64x2(c4Pi, Vector512.Create(0UL, 1, 2, 8 + 0, 4, 5, 6, 7), c3);
+ c3 = c3Pi;
+ c4Pi = Avx512F.PermuteVar8x64x2(c4Pi, Vector512.Create(0UL, 1, 2, 3, 8 + 1, 5, 6, 7), c4);
+ c4 = c4Pi;
+
+ // Chi step
+
+ c0 = Avx512F.TernaryLogic(c0, Avx512F.PermuteVar8x64(c0, permute1), Avx512F.PermuteVar8x64(c0, permute2), 0xD2);
+ c1 = Avx512F.TernaryLogic(c1, Avx512F.PermuteVar8x64(c1, permute1), Avx512F.PermuteVar8x64(c1, permute2), 0xD2);
+ c2 = Avx512F.TernaryLogic(c2, Avx512F.PermuteVar8x64(c2, permute1), Avx512F.PermuteVar8x64(c2, permute2), 0xD2);
+ c3 = Avx512F.TernaryLogic(c3, Avx512F.PermuteVar8x64(c3, permute1), Avx512F.PermuteVar8x64(c3, permute2), 0xD2);
+ c4 = Avx512F.TernaryLogic(c4, Avx512F.PermuteVar8x64(c4, permute1), Avx512F.PermuteVar8x64(c4, permute2), 0xD2);
+
+ // Iota step
+ c0 = Vector512.Xor(c0, Vector512.Create(roundConstant, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL));
+ }
+ }
+
+ // Can over-write for first elements
+ Unsafe.As>(ref MemoryMarshal.GetReference(state)) = c0;
+ Unsafe.As>(ref Unsafe.Add(ref MemoryMarshal.GetReference(state), 5)) = c1;
+ Unsafe.As>(ref Unsafe.Add(ref MemoryMarshal.GetReference(state), 10)) = c2;
+ Unsafe.As>(ref Unsafe.Add(ref MemoryMarshal.GetReference(state), 15)) = c3;
+ // Can't over-write for last elements so write the upper Vector256 and then ulong
+ Unsafe.As>(ref Unsafe.Add(ref MemoryMarshal.GetReference(state), 20)) = c4.GetLower();
+ state[24] = c4.GetElement(4);
+ }
}
diff --git a/src/Paprika/Data/Key.cs b/src/Paprika/Data/Key.cs
index 9b993844..df426b60 100644
--- a/src/Paprika/Data/Key.cs
+++ b/src/Paprika/Data/Key.cs
@@ -55,6 +55,9 @@ public static Key StorageCell(NibblePath path, in Keccak keccak) =>
public static Key StorageCell(NibblePath path, ReadOnlySpan keccak) =>
new(path, DataType.StorageCell, NibblePath.FromKey(keccak));
+ public static Key StorageCell(NibblePath path, NibblePath storagePath) =>
+ new(path, DataType.StorageCell, storagePath);
+
[DebuggerStepThrough]
public Key SliceFrom(int nibbles) => new(Path.SliceFrom(nibbles), Type, StoragePath);
diff --git a/src/Paprika/Data/SlottedArray.cs b/src/Paprika/Data/SlottedArray.cs
index 56a28751..9bd023ba 100644
--- a/src/Paprika/Data/SlottedArray.cs
+++ b/src/Paprika/Data/SlottedArray.cs
@@ -122,7 +122,7 @@ public void DeleteByPrefix(in NibblePath prefix)
{
if (prefix.Length == 0)
{
- Delete(prefix);
+ Clear();
}
else if (prefix.Length == 1)
{
diff --git a/src/Paprika/Store/StateRootPage.cs b/src/Paprika/Store/StateRootPage.cs
index 93c2adb1..9b335689 100644
--- a/src/Paprika/Store/StateRootPage.cs
+++ b/src/Paprika/Store/StateRootPage.cs
@@ -74,7 +74,27 @@ public Page DeleteByPrefix(in NibblePath prefix, IBatchContext batch)
return new StateRootPage(writable).DeleteByPrefix(prefix, batch);
}
- Debug.Assert(prefix.Length > ConsumedNibbles, "Removing prefix at the root? Something went wrong.");
+ if (prefix.Length < ConsumedNibbles)
+ {
+ if (prefix.Length == 1)
+ {
+ var idx = prefix.Nibble0 << NibblePath.NibbleShift;
+ for (int i = 0; i < 16; i++)
+ {
+ ref var addrShort = ref DeleteAll(Data.Buckets, idx + i);
+ }
+ }
+ else if (prefix.IsEmpty)
+ {
+ //can all pages be freed here?
+ for (int i = 0; i < BucketCount; i++)
+ {
+ ref var addrShort = ref DeleteAll(Data.Buckets, i);
+ }
+ }
+
+ return page;
+ }
var index = GetIndex(prefix);
var sliced = prefix.SliceFrom(ConsumedNibbles);
@@ -86,6 +106,18 @@ public Page DeleteByPrefix(in NibblePath prefix, IBatchContext batch)
}
return page;
+
+ ref DbAddress DeleteAll(Span buckets, int addrIndex)
+ {
+ ref var addrShort = ref buckets[addrIndex];
+
+ if (!addrShort.IsNull)
+ {
+ addrShort = batch.GetAddress(new DataPage(batch.GetAt(addrShort)).DeleteByPrefix(NibblePath.Empty, batch));
+ }
+
+ return ref addrShort;
+ }
}
///