diff --git a/src/Nethermind/Nethermind.Blockchain/Tracing/GethStyle/Custom/JavaScript/Engine.cs b/src/Nethermind/Nethermind.Blockchain/Tracing/GethStyle/Custom/JavaScript/Engine.cs index 87e7cb8b9ae..10f71db7cb8 100644 --- a/src/Nethermind/Nethermind.Blockchain/Tracing/GethStyle/Custom/JavaScript/Engine.cs +++ b/src/Nethermind/Nethermind.Blockchain/Tracing/GethStyle/Custom/JavaScript/Engine.cs @@ -127,7 +127,7 @@ public Engine(IReleaseSpec spec) /// /// Checks if contract at given address is a precompile /// - private bool IsPrecompiled(object address) => address.ToAddress().IsPrecompile(_spec); + private bool IsPrecompiled(object address) => _spec.IsPrecompile(address.ToAddress()); /// /// Returns a slice of input diff --git a/src/Nethermind/Nethermind.Core.Test/AddressTests.cs b/src/Nethermind/Nethermind.Core.Test/AddressTests.cs index bd6cba869b2..bb029f46be5 100644 --- a/src/Nethermind/Nethermind.Core.Test/AddressTests.cs +++ b/src/Nethermind/Nethermind.Core.Test/AddressTests.cs @@ -120,7 +120,7 @@ public void Is_precompiled_1() byte[] addressBytes = new byte[20]; addressBytes[19] = 1; Address address = new(addressBytes); - Assert.That(address.IsPrecompile(Frontier.Instance), Is.True); + Assert.That(Frontier.Instance.IsPrecompile(address), Is.True); } [Test] @@ -129,7 +129,7 @@ public void Is_precompiled_4_regression() byte[] addressBytes = new byte[20]; addressBytes[19] = 4; Address address = new(addressBytes); - Assert.That(address.IsPrecompile(Frontier.Instance), Is.True); + Assert.That(Frontier.Instance.IsPrecompile(address), Is.True); } [Test] @@ -138,7 +138,7 @@ public void Is_precompiled_5_frontier() byte[] addressBytes = new byte[20]; addressBytes[19] = 5; Address address = new(addressBytes); - Assert.That(address.IsPrecompile(Frontier.Instance), Is.False); + Assert.That(Frontier.Instance.IsPrecompile(address), Is.False); } [Test] @@ -147,7 +147,7 @@ public void Is_precompiled_5_byzantium() byte[] addressBytes = new byte[20]; addressBytes[19] = 5; Address address = new(addressBytes); - Assert.That(address.IsPrecompile(Byzantium.Instance), Is.True); + Assert.That(Byzantium.Instance.IsPrecompile(address), Is.True); } [Test] @@ -156,7 +156,7 @@ public void Is_precompiled_9_byzantium() byte[] addressBytes = new byte[20]; addressBytes[19] = 9; Address address = new(addressBytes); - Assert.That(address.IsPrecompile(Byzantium.Instance), Is.False); + Assert.That(Byzantium.Instance.IsPrecompile(address), Is.False); } [TestCase(0, false)] @@ -165,7 +165,7 @@ public void Is_precompiled_9_byzantium() public void From_number_for_precompile(int number, bool isPrecompile) { Address address = Address.FromNumber((UInt256)number); - Assert.That(address.IsPrecompile(Byzantium.Instance), Is.EqualTo(isPrecompile)); + Assert.That(Byzantium.Instance.IsPrecompile(address), Is.EqualTo(isPrecompile)); } [TestCase(0, "0x24cd2edba056b7c654a50e8201b619d4f624fdda")] @@ -178,7 +178,7 @@ public void Of_contract(long nonce, string expectedAddress) [TestCaseSource(nameof(PointEvaluationPrecompileTestCases))] public bool Is_PointEvaluationPrecompile_properly_activated(IReleaseSpec spec) => - Address.FromNumber(0x0a).IsPrecompile(spec); + spec.IsPrecompile(Address.FromNumber(0x0a)); [TestCase(Address.SystemUserHex, false)] [TestCase("2" + Address.SystemUserHex, false)] diff --git a/src/Nethermind/Nethermind.Core/Precompiles/PrecompiledAddresses.cs b/src/Nethermind/Nethermind.Core/Precompiles/PrecompiledAddresses.cs new file mode 100644 index 00000000000..756bd0bfea1 --- /dev/null +++ b/src/Nethermind/Nethermind.Core/Precompiles/PrecompiledAddresses.cs @@ -0,0 +1,30 @@ +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +namespace Nethermind.Core.Precompiles; + +/// +/// Contains static references to all known Ethereum precompile addresses. +/// +public static class PrecompiledAddresses +{ + public static readonly AddressAsKey EcRecover = Address.FromNumber(0x01); + public static readonly AddressAsKey Sha256 = Address.FromNumber(0x02); + public static readonly AddressAsKey Ripemd160 = Address.FromNumber(0x03); + public static readonly AddressAsKey Identity = Address.FromNumber(0x04); + public static readonly AddressAsKey ModExp = Address.FromNumber(0x05); + public static readonly AddressAsKey Bn128Add = Address.FromNumber(0x06); + public static readonly AddressAsKey Bn128Mul = Address.FromNumber(0x07); + public static readonly AddressAsKey Bn128Pairing = Address.FromNumber(0x08); + public static readonly AddressAsKey Blake2F = Address.FromNumber(0x09); + public static readonly AddressAsKey PointEvaluation = Address.FromNumber(0x0a); + public static readonly AddressAsKey Bls12G1Add = Address.FromNumber(0x0b); + public static readonly AddressAsKey Bls12G1Mul = Address.FromNumber(0x0c); + public static readonly AddressAsKey Bls12G1MultiExp = Address.FromNumber(0x0d); + public static readonly AddressAsKey Bls12G2Add = Address.FromNumber(0x0e); + public static readonly AddressAsKey Bls12G2Mul = Address.FromNumber(0x0f); + public static readonly AddressAsKey Bls12G2MultiExp = Address.FromNumber(0x10); + public static readonly AddressAsKey Bls12Pairing = Address.FromNumber(0x11); + public static readonly AddressAsKey P256Verify = Address.FromNumber(0x0100); + public static readonly AddressAsKey L1Sload = Address.FromNumber(0x10001); +} diff --git a/src/Nethermind/Nethermind.Core/Specs/IReleaseSpec.cs b/src/Nethermind/Nethermind.Core/Specs/IReleaseSpec.cs index 591bdf1753a..0c1d0e3b884 100644 --- a/src/Nethermind/Nethermind.Core/Specs/IReleaseSpec.cs +++ b/src/Nethermind/Nethermind.Core/Specs/IReleaseSpec.cs @@ -2,6 +2,7 @@ // SPDX-License-Identifier: LGPL-3.0-only using System; +using System.Collections.Generic; using Nethermind.Int256; namespace Nethermind.Core.Specs @@ -496,6 +497,19 @@ public interface IReleaseSpec : IEip1559Spec, IReceiptSpec /// public Array? EvmInstructionsTraced { get; set; } + /// + /// Determines whether the specified address is a precompiled contract for this release specification. + /// + /// The address to check for precompile status. + /// True if the address is a precompiled contract; otherwise, false. + bool IsPrecompile(Address address) => Precompiles.Contains(address); + + /// + /// Gets a cached set of all precompiled contract addresses for this release specification. + /// Chain-specific implementations can override this to include their own precompiled contracts. + /// + HashSet Precompiles { get; } + public ProofVersion BlobProofVersion => IsEip7594Enabled ? ProofVersion.V1 : ProofVersion.V0; /// diff --git a/src/Nethermind/Nethermind.Core/Specs/ReleaseSpecDecorator.cs b/src/Nethermind/Nethermind.Core/Specs/ReleaseSpecDecorator.cs index c86f8284b0c..5726746303c 100644 --- a/src/Nethermind/Nethermind.Core/Specs/ReleaseSpecDecorator.cs +++ b/src/Nethermind/Nethermind.Core/Specs/ReleaseSpecDecorator.cs @@ -2,6 +2,7 @@ // SPDX-License-Identifier: LGPL-3.0-only using System; +using System.Collections.Generic; using Nethermind.Int256; namespace Nethermind.Core.Specs; @@ -150,6 +151,7 @@ public class ReleaseSpecDecorator(IReleaseSpec spec) : IReleaseSpec public virtual bool ValidateReceipts => spec.ValidateReceipts; Array? IReleaseSpec.EvmInstructionsNoTrace { get => spec.EvmInstructionsNoTrace; set => spec.EvmInstructionsNoTrace = value; } Array? IReleaseSpec.EvmInstructionsTraced { get => spec.EvmInstructionsTraced; set => spec.EvmInstructionsTraced = value; } + HashSet IReleaseSpec.Precompiles => spec.Precompiles; public bool IsEip7939Enabled => spec.IsEip7939Enabled; public bool IsEip7907Enabled => spec.IsEip7907Enabled; public bool IsRip7728Enabled => spec.IsRip7728Enabled; diff --git a/src/Nethermind/Nethermind.Evm.Test/Eip1052Tests.cs b/src/Nethermind/Nethermind.Evm.Test/Eip1052Tests.cs index 4840c5bc85b..40819ab8d1a 100644 --- a/src/Nethermind/Nethermind.Evm.Test/Eip1052Tests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/Eip1052Tests.cs @@ -59,7 +59,7 @@ public void Non_existing_account_returns_0() public void Non_existing_precompile_returns_0() { Address precompileAddress = Sha256Precompile.Address; - Assert.That(precompileAddress.IsPrecompile(Spec), Is.True); + Assert.That(Spec.IsPrecompile(precompileAddress), Is.True); byte[] code = Prepare.EvmCode .PushData(precompileAddress) @@ -76,7 +76,7 @@ public void Non_existing_precompile_returns_0() public void Existing_precompile_returns_empty_data_hash() { Address precompileAddress = Sha256Precompile.Address; - Assert.That(precompileAddress.IsPrecompile(Spec), Is.True); + Assert.That(Spec.IsPrecompile(precompileAddress), Is.True); TestState.CreateAccount(precompileAddress, 1.Wei()); diff --git a/src/Nethermind/Nethermind.Evm.Test/Eip152Tests.cs b/src/Nethermind/Nethermind.Evm.Test/Eip152Tests.cs index b93be39bd7f..2431e6ae167 100644 --- a/src/Nethermind/Nethermind.Evm.Test/Eip152Tests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/Eip152Tests.cs @@ -28,7 +28,7 @@ public void before_istanbul() { _blockNumberAdjustment = -1; Address precompileAddress = Blake2FPrecompile.Address; - Assert.That(precompileAddress.IsPrecompile(Spec), Is.False); + Assert.That(Spec.IsPrecompile(precompileAddress), Is.False); } [Test] diff --git a/src/Nethermind/Nethermind.Evm.Test/Eip2537Tests.cs b/src/Nethermind/Nethermind.Evm.Test/Eip2537Tests.cs index 9a079cd770c..b91632f29ab 100644 --- a/src/Nethermind/Nethermind.Evm.Test/Eip2537Tests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/Eip2537Tests.cs @@ -27,13 +27,13 @@ public override void TearDown() public void Test_g1_add_before_prague() { _timestampAdjustment = -12; - Assert.That(G1AddPrecompile.Address.IsPrecompile(Spec), Is.False); + Assert.That(Spec.IsPrecompile(G1AddPrecompile.Address), Is.False); } [Test] public void Test_g1_add_after_prague() { - Assert.That(G1AddPrecompile.Address.IsPrecompile(Spec), Is.True); + Assert.That(Spec.IsPrecompile(G1AddPrecompile.Address), Is.True); byte[] code = Prepare.EvmCode .CallWithInput(G1AddPrecompile.Address, 1000L, new byte[256]) @@ -55,13 +55,13 @@ public void Test_g1_add_after_prague() public void Test_g2_add_before_prague() { _timestampAdjustment = -12; - Assert.That(G2AddPrecompile.Address.IsPrecompile(Spec), Is.False); + Assert.That(Spec.IsPrecompile(G2AddPrecompile.Address), Is.False); } [Test] public void Test_g2_add_after_prague() { - Assert.That(G2AddPrecompile.Address.IsPrecompile(Spec), Is.True); + Assert.That(Spec.IsPrecompile(G2AddPrecompile.Address), Is.True); byte[] code = Prepare.EvmCode .CallWithInput(G2AddPrecompile.Address, 1000L, new byte[512]) @@ -83,13 +83,13 @@ public void Test_g2_add_after_prague() public void Test_g1_msm_before_prague() { _timestampAdjustment = -12; - Assert.That(G1MSMPrecompile.Address.IsPrecompile(Spec), Is.False); + Assert.That(Spec.IsPrecompile(G1MSMPrecompile.Address), Is.False); } [Test] public void Test_g1_msm_after_prague() { - Assert.That(G1MSMPrecompile.Address.IsPrecompile(Spec), Is.True); + Assert.That(Spec.IsPrecompile(G1MSMPrecompile.Address), Is.True); byte[] code = Prepare.EvmCode .CallWithInput(G1MSMPrecompile.Address, 100000L, new byte[160]) @@ -111,13 +111,13 @@ public void Test_g1_msm_after_prague() public void Test_g2_msm_before_prague() { _timestampAdjustment = -12; - Assert.That(G2MSMPrecompile.Address.IsPrecompile(Spec), Is.False); + Assert.That(Spec.IsPrecompile(G2MSMPrecompile.Address), Is.False); } [Test] public void Test_g2_msm_after_prague() { - Assert.That(G2MSMPrecompile.Address.IsPrecompile(Spec), Is.True); + Assert.That(Spec.IsPrecompile(G2MSMPrecompile.Address), Is.True); byte[] code = Prepare.EvmCode .CallWithInput(G2MSMPrecompile.Address, 100000L, new byte[288]) @@ -139,13 +139,13 @@ public void Test_g2_msm_after_prague() public void Test_pairing_before_prague() { _timestampAdjustment = -12; - Assert.That(PairingCheckPrecompile.Address.IsPrecompile(Spec), Is.False); + Assert.That(Spec.IsPrecompile(PairingCheckPrecompile.Address), Is.False); } [Test] public void Test_pairing_check_after_prague() { - Assert.That(PairingCheckPrecompile.Address.IsPrecompile(Spec), Is.True); + Assert.That(Spec.IsPrecompile(PairingCheckPrecompile.Address), Is.True); byte[] code = Prepare.EvmCode .CallWithInput(PairingCheckPrecompile.Address, 100000L, new byte[384]) @@ -167,13 +167,13 @@ public void Test_pairing_check_after_prague() public void Test_map_fp_to_g1_before_prague() { _timestampAdjustment = -12; - Assert.That(MapFpToG1Precompile.Address.IsPrecompile(Spec), Is.False); + Assert.That(Spec.IsPrecompile(MapFpToG1Precompile.Address), Is.False); } [Test] public void Test_map_fp_to_g1_after_prague() { - Assert.That(MapFpToG1Precompile.Address.IsPrecompile(Spec), Is.True); + Assert.That(Spec.IsPrecompile(MapFpToG1Precompile.Address), Is.True); byte[] code = Prepare.EvmCode .CallWithInput(MapFpToG1Precompile.Address, 10000L, new byte[64]) @@ -195,13 +195,13 @@ public void Test_map_fp_to_g1_after_prague() public void Test_map_fp2_to_g2_before_prague() { _timestampAdjustment = -12; - Assert.That(MapFp2ToG2Precompile.Address.IsPrecompile(Spec), Is.False); + Assert.That(Spec.IsPrecompile(MapFp2ToG2Precompile.Address), Is.False); } [Test] public void Test_map_fp2_to_g2_after_prague() { - Assert.That(MapFp2ToG2Precompile.Address.IsPrecompile(Spec), Is.True); + Assert.That(Spec.IsPrecompile(MapFp2ToG2Precompile.Address), Is.True); byte[] code = Prepare.EvmCode .CallWithInput(MapFp2ToG2Precompile.Address, 100000L, new byte[128]) diff --git a/src/Nethermind/Nethermind.Evm.Test/L1SloadPrecompileTests.cs b/src/Nethermind/Nethermind.Evm.Test/L1SloadPrecompileTests.cs index 769f4c483bf..eb986f3493a 100644 --- a/src/Nethermind/Nethermind.Evm.Test/L1SloadPrecompileTests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/L1SloadPrecompileTests.cs @@ -145,15 +145,15 @@ public void Run_With_Provider_Returning_Null_Should_Fail() [Test] public void IsPrecompile_Active_With_Rip7728() { - var enabledSpec = new ReleaseSpec { IsRip7728Enabled = true }; - var disabledSpec = new ReleaseSpec { IsRip7728Enabled = false }; + IReleaseSpec enabledSpec = new ReleaseSpec { IsRip7728Enabled = true }; + IReleaseSpec disabledSpec = new ReleaseSpec { IsRip7728Enabled = false }; Address? precompileAddress = L1SloadPrecompile.Address; - Assert.That(precompileAddress.IsPrecompile(enabledSpec), Is.True, + Assert.That(enabledSpec.IsPrecompile(precompileAddress), Is.True, "L1SloadPrecompile address should be identified as precompile when RIP-7728 is enabled"); - Assert.That(precompileAddress.IsPrecompile(disabledSpec), Is.False, + Assert.That(disabledSpec.IsPrecompile(precompileAddress), Is.False, "L1SloadPrecompile address should not be identified as precompile when RIP-7728 is disabled"); } diff --git a/src/Nethermind/Nethermind.Evm/CodeInfoRepository.cs b/src/Nethermind/Nethermind.Evm/CodeInfoRepository.cs index c23fe3aad4e..034c580b789 100644 --- a/src/Nethermind/Nethermind.Evm/CodeInfoRepository.cs +++ b/src/Nethermind/Nethermind.Evm/CodeInfoRepository.cs @@ -28,7 +28,7 @@ protected CodeInfoRepository(FrozenDictionary prec } public bool IsPrecompile(Address address, IReleaseSpec spec) => - address.IsPrecompile(spec) && _localPrecompiles.ContainsKey(address); + spec.IsPrecompile(address) && _localPrecompiles.ContainsKey(address); public ICodeInfo GetCachedCodeInfo(IWorldState worldState, Address codeSource, bool followDelegation, IReleaseSpec vmSpec, out Address? delegationAddress) { diff --git a/src/Nethermind/Nethermind.Evm/Precompiles/AddressExtensions.cs b/src/Nethermind/Nethermind.Evm/Precompiles/AddressExtensions.cs deleted file mode 100644 index b60b6b4540a..00000000000 --- a/src/Nethermind/Nethermind.Evm/Precompiles/AddressExtensions.cs +++ /dev/null @@ -1,55 +0,0 @@ -// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited -// SPDX-License-Identifier: LGPL-3.0-only - -using System; -using System.Runtime.InteropServices; -using Nethermind.Core; -using Nethermind.Core.Specs; - -namespace Nethermind.Evm.Precompiles; - -public static class AddressExtensions -{ - public static bool IsPrecompile(this Address address, IReleaseSpec releaseSpec) - { - Span data = MemoryMarshal.Cast(address.Bytes.AsSpan()); - - return (data[4] & 0x000000ff) == 0 - && data[3] == 0 && data[2] == 0 && data[1] == 0 && data[0] == 0 - && ((data[4] >>> 8) & 0xffff) switch - { - 0x0000 => (data[4] >>> 24) switch - { - 0x01 => true, - 0x02 => true, - 0x03 => true, - 0x04 => true, - 0x05 => releaseSpec.ModExpEnabled, - 0x06 => releaseSpec.BN254Enabled, - 0x07 => releaseSpec.BN254Enabled, - 0x08 => releaseSpec.BN254Enabled, - 0x09 => releaseSpec.BlakeEnabled, - 0x0a => releaseSpec.IsEip4844Enabled, - 0x0b => releaseSpec.Bls381Enabled, - 0x0c => releaseSpec.Bls381Enabled, - 0x0d => releaseSpec.Bls381Enabled, - 0x0e => releaseSpec.Bls381Enabled, - 0x0f => releaseSpec.Bls381Enabled, - 0x10 => releaseSpec.Bls381Enabled, - 0x11 => releaseSpec.Bls381Enabled, - _ => false - }, - 0x0001 => (data[4] >>> 24) switch // L2 precompiles, starts from 0x10001 - { - 0x01 => releaseSpec.IsRip7728Enabled, - _ => false - }, - 0x0100 => (data[4] >>> 24) switch - { - 0x00 => releaseSpec.IsEip7951Enabled || releaseSpec.IsRip7212Enabled, - _ => false - }, - _ => false - }; - } -} diff --git a/src/Nethermind/Nethermind.Specs.Test/OverridableReleaseSpec.cs b/src/Nethermind/Nethermind.Specs.Test/OverridableReleaseSpec.cs index 6eda90b1946..34ba27bd2f5 100644 --- a/src/Nethermind/Nethermind.Specs.Test/OverridableReleaseSpec.cs +++ b/src/Nethermind/Nethermind.Specs.Test/OverridableReleaseSpec.cs @@ -2,6 +2,7 @@ // SPDX-License-Identifier: LGPL-3.0-only using System; +using System.Collections.Generic; using Nethermind.Core; using Nethermind.Core.Specs; using Nethermind.Int256; @@ -198,5 +199,6 @@ public ulong Eip4844TransitionTimestamp public bool IsEip7939Enabled => spec.IsEip7939Enabled; public bool IsEip7907Enabled => spec.IsEip7907Enabled; public bool IsRip7728Enabled => spec.IsRip7728Enabled; + HashSet IReleaseSpec.Precompiles => spec.Precompiles; } } diff --git a/src/Nethermind/Nethermind.Specs/ReleaseSpec.cs b/src/Nethermind/Nethermind.Specs/ReleaseSpec.cs index a5af1609919..180858465f6 100644 --- a/src/Nethermind/Nethermind.Specs/ReleaseSpec.cs +++ b/src/Nethermind/Nethermind.Specs/ReleaseSpec.cs @@ -2,7 +2,9 @@ // SPDX-License-Identifier: LGPL-3.0-only using System; +using System.Collections.Generic; using Nethermind.Core; +using Nethermind.Core.Precompiles; using Nethermind.Core.Specs; using Nethermind.Int256; @@ -160,5 +162,42 @@ public Address Eip2935ContractAddress Array? IReleaseSpec.EvmInstructionsTraced { get; set; } public bool IsEip7939Enabled { get; set; } public bool IsRip7728Enabled { get; set; } + + private HashSet? _precompiles; + HashSet IReleaseSpec.Precompiles => _precompiles ??= BuildPrecompilesCache(); + + public virtual HashSet BuildPrecompilesCache() + { + HashSet cache = new(); + + cache.Add(PrecompiledAddresses.EcRecover); + cache.Add(PrecompiledAddresses.Sha256); + cache.Add(PrecompiledAddresses.Ripemd160); + cache.Add(PrecompiledAddresses.Identity); + + if (IsEip198Enabled) cache.Add(PrecompiledAddresses.ModExp); + if (IsEip196Enabled && IsEip197Enabled) + { + cache.Add(PrecompiledAddresses.Bn128Add); + cache.Add(PrecompiledAddresses.Bn128Mul); + cache.Add(PrecompiledAddresses.Bn128Pairing); + } + if (IsEip152Enabled) cache.Add(PrecompiledAddresses.Blake2F); + if (IsEip4844Enabled) cache.Add(PrecompiledAddresses.PointEvaluation); + if (IsEip2537Enabled) + { + cache.Add(PrecompiledAddresses.Bls12G1Add); + cache.Add(PrecompiledAddresses.Bls12G1Mul); + cache.Add(PrecompiledAddresses.Bls12G1MultiExp); + cache.Add(PrecompiledAddresses.Bls12G2Add); + cache.Add(PrecompiledAddresses.Bls12G2Mul); + cache.Add(PrecompiledAddresses.Bls12G2MultiExp); + cache.Add(PrecompiledAddresses.Bls12Pairing); + } + if (IsRip7212Enabled || IsEip7951Enabled) cache.Add(PrecompiledAddresses.P256Verify); + if (IsRip7728Enabled) cache.Add(PrecompiledAddresses.L1Sload); + + return cache; + } } }