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; + } } ///