From 6f3bb41e10bbee3ee06124830cb32d31fbd10c6e Mon Sep 17 00:00:00 2001 From: MiniFrenchBread <103425574+MiniFrenchBread@users.noreply.github.com> Date: Tue, 25 Mar 2025 05:04:43 +0800 Subject: [PATCH] feat: refactor precompiles --- core/vm/contracts.go | 276 +++++++++++++++------- core/vm/contracts_test.go | 3 +- core/vm/evm.go | 7 +- core/vm/precompiles/precompiles.go | 35 +++ internal/ethapi/api.go | 3 +- internal/ethapi/override/override.go | 3 +- internal/ethapi/override/override_test.go | 8 +- internal/ethapi/simulate.go | 3 +- 8 files changed, 246 insertions(+), 92 deletions(-) create mode 100644 core/vm/precompiles/precompiles.go diff --git a/core/vm/contracts.go b/core/vm/contracts.go index 06849e65ad..2202fcca85 100644 --- a/core/vm/contracts.go +++ b/core/vm/contracts.go @@ -31,6 +31,7 @@ import ( "github.com/consensys/gnark-crypto/ecc/bls12-381/fr" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/tracing" + "github.com/ethereum/go-ethereum/core/vm/precompiles" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto/blake2b" "github.com/ethereum/go-ethereum/crypto/bn256" @@ -39,103 +40,92 @@ import ( "golang.org/x/crypto/ripemd160" ) -// PrecompiledContract is the basic interface for native Go contracts. The implementation -// requires a deterministic gas count based on the input size of the Run method of the -// contract. -type PrecompiledContract interface { - RequiredGas(input []byte) uint64 // RequiredPrice calculates the contract gas use - Run(input []byte) ([]byte, error) // Run runs the precompiled contract -} - -// PrecompiledContracts contains the precompiled contracts supported at the given fork. -type PrecompiledContracts map[common.Address]PrecompiledContract - // PrecompiledContractsHomestead contains the default set of pre-compiled Ethereum // contracts used in the Frontier and Homestead releases. -var PrecompiledContractsHomestead = PrecompiledContracts{ - common.BytesToAddress([]byte{0x1}): &ecrecover{}, - common.BytesToAddress([]byte{0x2}): &sha256hash{}, - common.BytesToAddress([]byte{0x3}): &ripemd160hash{}, - common.BytesToAddress([]byte{0x4}): &dataCopy{}, -} +var PrecompiledContractsHomestead = precompiles.WithCustomPrecompiles(precompiles.PrecompiledContracts{ + (&ecrecover{}).Address(): &ecrecover{}, + (&sha256hash{}).Address(): &sha256hash{}, + (&ripemd160hash{}).Address(): &ripemd160hash{}, + (&dataCopy{}).Address(): &dataCopy{}, +}) // PrecompiledContractsByzantium contains the default set of pre-compiled Ethereum // contracts used in the Byzantium release. -var PrecompiledContractsByzantium = PrecompiledContracts{ - common.BytesToAddress([]byte{0x1}): &ecrecover{}, - common.BytesToAddress([]byte{0x2}): &sha256hash{}, - common.BytesToAddress([]byte{0x3}): &ripemd160hash{}, - common.BytesToAddress([]byte{0x4}): &dataCopy{}, - common.BytesToAddress([]byte{0x5}): &bigModExp{eip2565: false}, - common.BytesToAddress([]byte{0x6}): &bn256AddByzantium{}, - common.BytesToAddress([]byte{0x7}): &bn256ScalarMulByzantium{}, - common.BytesToAddress([]byte{0x8}): &bn256PairingByzantium{}, -} +var PrecompiledContractsByzantium = precompiles.WithCustomPrecompiles(precompiles.PrecompiledContracts{ + (&ecrecover{}).Address(): &ecrecover{}, + (&sha256hash{}).Address(): &sha256hash{}, + (&ripemd160hash{}).Address(): &ripemd160hash{}, + (&dataCopy{}).Address(): &dataCopy{}, + (&bigModExp{eip2565: false}).Address(): &bigModExp{eip2565: false}, + (&bn256AddByzantium{}).Address(): &bn256AddByzantium{}, + (&bn256ScalarMulByzantium{}).Address(): &bn256ScalarMulByzantium{}, + (&bn256PairingByzantium{}).Address(): &bn256PairingByzantium{}, +}) // PrecompiledContractsIstanbul contains the default set of pre-compiled Ethereum // contracts used in the Istanbul release. -var PrecompiledContractsIstanbul = PrecompiledContracts{ - common.BytesToAddress([]byte{0x1}): &ecrecover{}, - common.BytesToAddress([]byte{0x2}): &sha256hash{}, - common.BytesToAddress([]byte{0x3}): &ripemd160hash{}, - common.BytesToAddress([]byte{0x4}): &dataCopy{}, - common.BytesToAddress([]byte{0x5}): &bigModExp{eip2565: false}, - common.BytesToAddress([]byte{0x6}): &bn256AddIstanbul{}, - common.BytesToAddress([]byte{0x7}): &bn256ScalarMulIstanbul{}, - common.BytesToAddress([]byte{0x8}): &bn256PairingIstanbul{}, - common.BytesToAddress([]byte{0x9}): &blake2F{}, -} +var PrecompiledContractsIstanbul = precompiles.WithCustomPrecompiles(precompiles.PrecompiledContracts{ + (&ecrecover{}).Address(): &ecrecover{}, + (&sha256hash{}).Address(): &sha256hash{}, + (&ripemd160hash{}).Address(): &ripemd160hash{}, + (&dataCopy{}).Address(): &dataCopy{}, + (&bigModExp{eip2565: false}).Address(): &bigModExp{eip2565: false}, + (&bn256AddIstanbul{}).Address(): &bn256AddIstanbul{}, + (&bn256ScalarMulIstanbul{}).Address(): &bn256ScalarMulIstanbul{}, + (&bn256PairingIstanbul{}).Address(): &bn256PairingIstanbul{}, + (&blake2F{}).Address(): &blake2F{}, +}) // PrecompiledContractsBerlin contains the default set of pre-compiled Ethereum // contracts used in the Berlin release. -var PrecompiledContractsBerlin = PrecompiledContracts{ - common.BytesToAddress([]byte{0x1}): &ecrecover{}, - common.BytesToAddress([]byte{0x2}): &sha256hash{}, - common.BytesToAddress([]byte{0x3}): &ripemd160hash{}, - common.BytesToAddress([]byte{0x4}): &dataCopy{}, - common.BytesToAddress([]byte{0x5}): &bigModExp{eip2565: true}, - common.BytesToAddress([]byte{0x6}): &bn256AddIstanbul{}, - common.BytesToAddress([]byte{0x7}): &bn256ScalarMulIstanbul{}, - common.BytesToAddress([]byte{0x8}): &bn256PairingIstanbul{}, - common.BytesToAddress([]byte{0x9}): &blake2F{}, -} +var PrecompiledContractsBerlin = precompiles.WithCustomPrecompiles(precompiles.PrecompiledContracts{ + (&ecrecover{}).Address(): &ecrecover{}, + (&sha256hash{}).Address(): &sha256hash{}, + (&ripemd160hash{}).Address(): &ripemd160hash{}, + (&dataCopy{}).Address(): &dataCopy{}, + (&bigModExp{eip2565: true}).Address(): &bigModExp{eip2565: true}, + (&bn256AddIstanbul{}).Address(): &bn256AddIstanbul{}, + (&bn256ScalarMulIstanbul{}).Address(): &bn256ScalarMulIstanbul{}, + (&bn256PairingIstanbul{}).Address(): &bn256PairingIstanbul{}, + (&blake2F{}).Address(): &blake2F{}, +}) // PrecompiledContractsCancun contains the default set of pre-compiled Ethereum // contracts used in the Cancun release. -var PrecompiledContractsCancun = PrecompiledContracts{ - common.BytesToAddress([]byte{0x1}): &ecrecover{}, - common.BytesToAddress([]byte{0x2}): &sha256hash{}, - common.BytesToAddress([]byte{0x3}): &ripemd160hash{}, - common.BytesToAddress([]byte{0x4}): &dataCopy{}, - common.BytesToAddress([]byte{0x5}): &bigModExp{eip2565: true}, - common.BytesToAddress([]byte{0x6}): &bn256AddIstanbul{}, - common.BytesToAddress([]byte{0x7}): &bn256ScalarMulIstanbul{}, - common.BytesToAddress([]byte{0x8}): &bn256PairingIstanbul{}, - common.BytesToAddress([]byte{0x9}): &blake2F{}, - common.BytesToAddress([]byte{0xa}): &kzgPointEvaluation{}, -} +var PrecompiledContractsCancun = precompiles.WithCustomPrecompiles(precompiles.PrecompiledContracts{ + (&ecrecover{}).Address(): &ecrecover{}, + (&sha256hash{}).Address(): &sha256hash{}, + (&ripemd160hash{}).Address(): &ripemd160hash{}, + (&dataCopy{}).Address(): &dataCopy{}, + (&bigModExp{eip2565: true}).Address(): &bigModExp{eip2565: true}, + (&bn256AddIstanbul{}).Address(): &bn256AddIstanbul{}, + (&bn256ScalarMulIstanbul{}).Address(): &bn256ScalarMulIstanbul{}, + (&bn256PairingIstanbul{}).Address(): &bn256PairingIstanbul{}, + (&blake2F{}).Address(): &blake2F{}, + (&kzgPointEvaluation{}).Address(): &kzgPointEvaluation{}, +}) // PrecompiledContractsPrague contains the set of pre-compiled Ethereum // contracts used in the Prague release. -var PrecompiledContractsPrague = PrecompiledContracts{ - common.BytesToAddress([]byte{0x01}): &ecrecover{}, - common.BytesToAddress([]byte{0x02}): &sha256hash{}, - common.BytesToAddress([]byte{0x03}): &ripemd160hash{}, - common.BytesToAddress([]byte{0x04}): &dataCopy{}, - common.BytesToAddress([]byte{0x05}): &bigModExp{eip2565: true}, - common.BytesToAddress([]byte{0x06}): &bn256AddIstanbul{}, - common.BytesToAddress([]byte{0x07}): &bn256ScalarMulIstanbul{}, - common.BytesToAddress([]byte{0x08}): &bn256PairingIstanbul{}, - common.BytesToAddress([]byte{0x09}): &blake2F{}, - common.BytesToAddress([]byte{0x0a}): &kzgPointEvaluation{}, - common.BytesToAddress([]byte{0x0b}): &bls12381G1Add{}, - common.BytesToAddress([]byte{0x0c}): &bls12381G1MultiExp{}, - common.BytesToAddress([]byte{0x0d}): &bls12381G2Add{}, - common.BytesToAddress([]byte{0x0e}): &bls12381G2MultiExp{}, - common.BytesToAddress([]byte{0x0f}): &bls12381Pairing{}, - common.BytesToAddress([]byte{0x10}): &bls12381MapG1{}, - common.BytesToAddress([]byte{0x11}): &bls12381MapG2{}, -} +var PrecompiledContractsPrague = precompiles.WithCustomPrecompiles(precompiles.PrecompiledContracts{ + (&ecrecover{}).Address(): &ecrecover{}, + (&sha256hash{}).Address(): &sha256hash{}, + (&ripemd160hash{}).Address(): &ripemd160hash{}, + (&dataCopy{}).Address(): &dataCopy{}, + (&bigModExp{eip2565: true}).Address(): &bigModExp{eip2565: true}, + (&bn256AddIstanbul{}).Address(): &bn256AddIstanbul{}, + (&bn256ScalarMulIstanbul{}).Address(): &bn256ScalarMulIstanbul{}, + (&bn256PairingIstanbul{}).Address(): &bn256PairingIstanbul{}, + (&blake2F{}).Address(): &blake2F{}, + (&kzgPointEvaluation{}).Address(): &kzgPointEvaluation{}, + (&bls12381G1Add{}).Address(): &bls12381G1Add{}, + (&bls12381G1MultiExp{}).Address(): &bls12381G1MultiExp{}, + (&bls12381G2Add{}).Address(): &bls12381G2Add{}, + (&bls12381G2MultiExp{}).Address(): &bls12381G2MultiExp{}, + (&bls12381Pairing{}).Address(): &bls12381Pairing{}, + (&bls12381MapG1{}).Address(): &bls12381MapG1{}, + (&bls12381MapG2{}).Address(): &bls12381MapG2{}, +}) var PrecompiledContractsBLS = PrecompiledContractsPrague @@ -171,7 +161,7 @@ func init() { } } -func activePrecompiledContracts(rules params.Rules) PrecompiledContracts { +func activePrecompiledContracts(rules params.Rules) precompiles.PrecompiledContracts { switch { case rules.IsVerkle: return PrecompiledContractsVerkle @@ -191,7 +181,7 @@ func activePrecompiledContracts(rules params.Rules) PrecompiledContracts { } // ActivePrecompiledContracts returns a copy of precompiled contracts enabled with the current configuration. -func ActivePrecompiledContracts(rules params.Rules) PrecompiledContracts { +func ActivePrecompiledContracts(rules params.Rules) precompiles.PrecompiledContracts { return maps.Clone(activePrecompiledContracts(rules)) } @@ -218,7 +208,7 @@ func ActivePrecompiles(rules params.Rules) []common.Address { // - the returned bytes, // - the _remaining_ gas, // - any error that occurred -func RunPrecompiledContract(p PrecompiledContract, input []byte, suppliedGas uint64, logger *tracing.Hooks) (ret []byte, remainingGas uint64, err error) { +func RunPrecompiledContract(p precompiles.PrecompiledContract, input []byte, suppliedGas uint64, logger *tracing.Hooks) (ret []byte, remainingGas uint64, err error) { gasCost := p.RequiredGas(input) if suppliedGas < gasCost { return nil, 0, ErrOutOfGas @@ -234,6 +224,12 @@ func RunPrecompiledContract(p PrecompiledContract, input []byte, suppliedGas uin // ecrecover implemented as a native contract. type ecrecover struct{} +// Address defines the precompiled contract address. This MUST match the address +// set in the precompiled contract map. +func (*ecrecover) Address() common.Address { + return common.BytesToAddress([]byte{0x1}) +} + func (c *ecrecover) RequiredGas(input []byte) uint64 { return params.EcrecoverGas } @@ -272,6 +268,12 @@ func (c *ecrecover) Run(input []byte) ([]byte, error) { // SHA256 implemented as a native contract. type sha256hash struct{} +// Address defines the precompiled contract address. This MUST match the address +// set in the precompiled contract map. +func (*sha256hash) Address() common.Address { + return common.BytesToAddress([]byte{0x2}) +} + // RequiredGas returns the gas required to execute the pre-compiled contract. // // This method does not require any overflow checking as the input size gas costs @@ -287,6 +289,12 @@ func (c *sha256hash) Run(input []byte) ([]byte, error) { // RIPEMD160 implemented as a native contract. type ripemd160hash struct{} +// Address defines the precompiled contract address. This MUST match the address +// set in the precompiled contract map. +func (*ripemd160hash) Address() common.Address { + return common.BytesToAddress([]byte{0x3}) +} + // RequiredGas returns the gas required to execute the pre-compiled contract. // // This method does not require any overflow checking as the input size gas costs @@ -303,6 +311,12 @@ func (c *ripemd160hash) Run(input []byte) ([]byte, error) { // data copy implemented as a native contract. type dataCopy struct{} +// Address defines the precompiled contract address. This MUST match the address +// set in the precompiled contract map. +func (*dataCopy) Address() common.Address { + return common.BytesToAddress([]byte{0x4}) +} + // RequiredGas returns the gas required to execute the pre-compiled contract. // // This method does not require any overflow checking as the input size gas costs @@ -319,6 +333,12 @@ type bigModExp struct { eip2565 bool } +// Address defines the precompiled contract address. This MUST match the address +// set in the precompiled contract map. +func (*bigModExp) Address() common.Address { + return common.BytesToAddress([]byte{0x5}) +} + var ( big1 = big.NewInt(1) big3 = big.NewInt(3) @@ -516,6 +536,12 @@ func runBn256Add(input []byte) ([]byte, error) { // Istanbul consensus rules. type bn256AddIstanbul struct{} +// Address defines the precompiled contract address. This MUST match the address +// set in the precompiled contract map. +func (*bn256AddIstanbul) Address() common.Address { + return common.BytesToAddress([]byte{0x6}) +} + // RequiredGas returns the gas required to execute the pre-compiled contract. func (c *bn256AddIstanbul) RequiredGas(input []byte) uint64 { return params.Bn256AddGasIstanbul @@ -529,6 +555,12 @@ func (c *bn256AddIstanbul) Run(input []byte) ([]byte, error) { // conforming to Byzantium consensus rules. type bn256AddByzantium struct{} +// Address defines the precompiled contract address. This MUST match the address +// set in the precompiled contract map. +func (*bn256AddByzantium) Address() common.Address { + return common.BytesToAddress([]byte{0x6}) +} + // RequiredGas returns the gas required to execute the pre-compiled contract. func (c *bn256AddByzantium) RequiredGas(input []byte) uint64 { return params.Bn256AddGasByzantium @@ -554,6 +586,12 @@ func runBn256ScalarMul(input []byte) ([]byte, error) { // multiplication conforming to Istanbul consensus rules. type bn256ScalarMulIstanbul struct{} +// Address defines the precompiled contract address. This MUST match the address +// set in the precompiled contract map. +func (*bn256ScalarMulIstanbul) Address() common.Address { + return common.BytesToAddress([]byte{0x7}) +} + // RequiredGas returns the gas required to execute the pre-compiled contract. func (c *bn256ScalarMulIstanbul) RequiredGas(input []byte) uint64 { return params.Bn256ScalarMulGasIstanbul @@ -567,6 +605,12 @@ func (c *bn256ScalarMulIstanbul) Run(input []byte) ([]byte, error) { // multiplication conforming to Byzantium consensus rules. type bn256ScalarMulByzantium struct{} +// Address defines the precompiled contract address. This MUST match the address +// set in the precompiled contract map. +func (*bn256ScalarMulByzantium) Address() common.Address { + return common.BytesToAddress([]byte{0x7}) +} + // RequiredGas returns the gas required to execute the pre-compiled contract. func (c *bn256ScalarMulByzantium) RequiredGas(input []byte) uint64 { return params.Bn256ScalarMulGasByzantium @@ -622,6 +666,12 @@ func runBn256Pairing(input []byte) ([]byte, error) { // conforming to Istanbul consensus rules. type bn256PairingIstanbul struct{} +// Address defines the precompiled contract address. This MUST match the address +// set in the precompiled contract map. +func (*bn256PairingIstanbul) Address() common.Address { + return common.BytesToAddress([]byte{0x8}) +} + // RequiredGas returns the gas required to execute the pre-compiled contract. func (c *bn256PairingIstanbul) RequiredGas(input []byte) uint64 { return params.Bn256PairingBaseGasIstanbul + uint64(len(input)/192)*params.Bn256PairingPerPointGasIstanbul @@ -635,6 +685,12 @@ func (c *bn256PairingIstanbul) Run(input []byte) ([]byte, error) { // conforming to Byzantium consensus rules. type bn256PairingByzantium struct{} +// Address defines the precompiled contract address. This MUST match the address +// set in the precompiled contract map. +func (*bn256PairingByzantium) Address() common.Address { + return common.BytesToAddress([]byte{0x8}) +} + // RequiredGas returns the gas required to execute the pre-compiled contract. func (c *bn256PairingByzantium) RequiredGas(input []byte) uint64 { return params.Bn256PairingBaseGasByzantium + uint64(len(input)/192)*params.Bn256PairingPerPointGasByzantium @@ -646,6 +702,12 @@ func (c *bn256PairingByzantium) Run(input []byte) ([]byte, error) { type blake2F struct{} +// Address defines the precompiled contract address. This MUST match the address +// set in the precompiled contract map. +func (*blake2F) Address() common.Address { + return common.BytesToAddress([]byte{0x9}) +} + func (c *blake2F) RequiredGas(input []byte) uint64 { // If the input is malformed, we can't calculate the gas, return 0 and let the // actual call choke and fault. @@ -715,6 +777,12 @@ var ( // bls12381G1Add implements EIP-2537 G1Add precompile. type bls12381G1Add struct{} +// Address defines the precompiled contract address. This MUST match the address +// set in the precompiled contract map. +func (*bls12381G1Add) Address() common.Address { + return common.BytesToAddress([]byte{0x0b}) +} + // RequiredGas returns the gas required to execute the pre-compiled contract. func (c *bls12381G1Add) RequiredGas(input []byte) uint64 { return params.Bls12381G1AddGas @@ -751,6 +819,12 @@ func (c *bls12381G1Add) Run(input []byte) ([]byte, error) { // bls12381G1MultiExp implements EIP-2537 G1MultiExp precompile. type bls12381G1MultiExp struct{} +// Address defines the precompiled contract address. This MUST match the address +// set in the precompiled contract map. +func (*bls12381G1MultiExp) Address() common.Address { + return common.BytesToAddress([]byte{0x0c}) +} + // RequiredGas returns the gas required to execute the pre-compiled contract. func (c *bls12381G1MultiExp) RequiredGas(input []byte) uint64 { // Calculate G1 point, scalar value pair length @@ -811,6 +885,12 @@ func (c *bls12381G1MultiExp) Run(input []byte) ([]byte, error) { // bls12381G2Add implements EIP-2537 G2Add precompile. type bls12381G2Add struct{} +// Address defines the precompiled contract address. This MUST match the address +// set in the precompiled contract map. +func (*bls12381G2Add) Address() common.Address { + return common.BytesToAddress([]byte{0x0d}) +} + // RequiredGas returns the gas required to execute the pre-compiled contract. func (c *bls12381G2Add) RequiredGas(input []byte) uint64 { return params.Bls12381G2AddGas @@ -848,6 +928,12 @@ func (c *bls12381G2Add) Run(input []byte) ([]byte, error) { // bls12381G2MultiExp implements EIP-2537 G2MultiExp precompile. type bls12381G2MultiExp struct{} +// Address defines the precompiled contract address. This MUST match the address +// set in the precompiled contract map. +func (*bls12381G2MultiExp) Address() common.Address { + return common.BytesToAddress([]byte{0x0e}) +} + // RequiredGas returns the gas required to execute the pre-compiled contract. func (c *bls12381G2MultiExp) RequiredGas(input []byte) uint64 { // Calculate G2 point, scalar value pair length @@ -908,6 +994,12 @@ func (c *bls12381G2MultiExp) Run(input []byte) ([]byte, error) { // bls12381Pairing implements EIP-2537 Pairing precompile. type bls12381Pairing struct{} +// Address defines the precompiled contract address. This MUST match the address +// set in the precompiled contract map. +func (*bls12381Pairing) Address() common.Address { + return common.BytesToAddress([]byte{0x0f}) +} + // RequiredGas returns the gas required to execute the pre-compiled contract. func (c *bls12381Pairing) RequiredGas(input []byte) uint64 { return params.Bls12381PairingBaseGas + uint64(len(input)/384)*params.Bls12381PairingPerPairGas @@ -1060,6 +1152,12 @@ func encodePointG2(p *bls12381.G2Affine) []byte { // bls12381MapG1 implements EIP-2537 MapG1 precompile. type bls12381MapG1 struct{} +// Address defines the precompiled contract address. This MUST match the address +// set in the precompiled contract map. +func (*bls12381MapG1) Address() common.Address { + return common.BytesToAddress([]byte{0x10}) +} + // RequiredGas returns the gas required to execute the pre-compiled contract. func (c *bls12381MapG1) RequiredGas(input []byte) uint64 { return params.Bls12381MapG1Gas @@ -1089,6 +1187,12 @@ func (c *bls12381MapG1) Run(input []byte) ([]byte, error) { // bls12381MapG2 implements EIP-2537 MapG2 precompile. type bls12381MapG2 struct{} +// Address defines the precompiled contract address. This MUST match the address +// set in the precompiled contract map. +func (*bls12381MapG2) Address() common.Address { + return common.BytesToAddress([]byte{0x11}) +} + // RequiredGas returns the gas required to execute the pre-compiled contract. func (c *bls12381MapG2) RequiredGas(input []byte) uint64 { return params.Bls12381MapG2Gas @@ -1122,6 +1226,12 @@ func (c *bls12381MapG2) Run(input []byte) ([]byte, error) { // kzgPointEvaluation implements the EIP-4844 point evaluation precompile. type kzgPointEvaluation struct{} +// Address defines the precompiled contract address. This MUST match the address +// set in the precompiled contract map. +func (*kzgPointEvaluation) Address() common.Address { + return common.BytesToAddress([]byte{0xa}) +} + // RequiredGas estimates the gas required for running the point evaluation precompile. func (b *kzgPointEvaluation) RequiredGas(input []byte) uint64 { return params.BlobTxPointEvaluationPrecompileGas diff --git a/core/vm/contracts_test.go b/core/vm/contracts_test.go index b627f2ada5..e9c063e036 100644 --- a/core/vm/contracts_test.go +++ b/core/vm/contracts_test.go @@ -25,6 +25,7 @@ import ( "time" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/vm/precompiles" ) // precompiledTest defines the input/output pairs for precompiled contract tests. @@ -45,7 +46,7 @@ type precompiledFailureTest struct { // allPrecompiles does not map to the actual set of precompiles, as it also contains // repriced versions of precompiles at certain slots -var allPrecompiles = map[common.Address]PrecompiledContract{ +var allPrecompiles = map[common.Address]precompiles.PrecompiledContract{ common.BytesToAddress([]byte{1}): &ecrecover{}, common.BytesToAddress([]byte{2}): &sha256hash{}, common.BytesToAddress([]byte{3}): &ripemd160hash{}, diff --git a/core/vm/evm.go b/core/vm/evm.go index c28dcb2554..18ceb98fad 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -25,6 +25,7 @@ import ( "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/tracing" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm/precompiles" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/params" "github.com/holiman/uint256" @@ -40,7 +41,7 @@ type ( GetHashFunc func(uint64) common.Hash ) -func (evm *EVM) precompile(addr common.Address) (PrecompiledContract, bool) { +func (evm *EVM) precompile(addr common.Address) (precompiles.PrecompiledContract, bool) { p, ok := evm.precompiles[addr] return p, ok } @@ -120,7 +121,7 @@ type EVM struct { callGasTemp uint64 // precompiles holds the precompiled contracts for the current epoch - precompiles map[common.Address]PrecompiledContract + precompiles map[common.Address]precompiles.PrecompiledContract // jumpDests is the aggregated result of JUMPDEST analysis made through // the life cycle of EVM. @@ -148,7 +149,7 @@ func NewEVM(blockCtx BlockContext, statedb StateDB, chainConfig *params.ChainCon // SetPrecompiles sets the precompiled contracts for the EVM. // This method is only used through RPC calls. // It is not thread-safe. -func (evm *EVM) SetPrecompiles(precompiles PrecompiledContracts) { +func (evm *EVM) SetPrecompiles(precompiles precompiles.PrecompiledContracts) { evm.precompiles = precompiles } diff --git a/core/vm/precompiles/precompiles.go b/core/vm/precompiles/precompiles.go new file mode 100644 index 0000000000..8a841502a3 --- /dev/null +++ b/core/vm/precompiles/precompiles.go @@ -0,0 +1,35 @@ +package precompiles + +import ( + "maps" + + "github.com/ethereum/go-ethereum/common" +) + +// PrecompiledContract is the basic interface for native Go contracts. The implementation +// requires a deterministic gas count based on the input size of the Run method of the +// contract. +type PrecompiledContract interface { + Address() common.Address + RequiredGas(input []byte) uint64 // RequiredPrice calculates the contract gas use + Run(input []byte) ([]byte, error) // Run runs the precompiled contract +} + +// PrecompiledContracts contains the precompiled contracts supported at the given fork. +type PrecompiledContracts map[common.Address]PrecompiledContract + +var CustomPrecompiledContracts = PrecompiledContracts{} + +// WithCustomPrecompiles merge given precompiles with custom precompiles +func WithCustomPrecompiles(base PrecompiledContracts) PrecompiledContracts { + result := make(PrecompiledContracts) + maps.Copy(result, base) + for k, v := range CustomPrecompiledContracts { + if _, exists := result[k]; !exists { + result[k] = v + } else { + panic("Precompile address collision") + } + } + return result +} diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index f3975d35a0..ca1a1eef93 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -37,6 +37,7 @@ import ( "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/core/vm/precompiles" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/eth/gasestimator" "github.com/ethereum/go-ethereum/eth/tracers/logger" @@ -688,7 +689,7 @@ func doCall(ctx context.Context, b Backend, args TransactionArgs, state *state.S return applyMessage(ctx, b, args, state, header, timeout, gp, &blockCtx, &vm.Config{NoBaseFee: true}, precompiles, true) } -func applyMessage(ctx context.Context, b Backend, args TransactionArgs, state *state.StateDB, header *types.Header, timeout time.Duration, gp *core.GasPool, blockContext *vm.BlockContext, vmConfig *vm.Config, precompiles vm.PrecompiledContracts, skipChecks bool) (*core.ExecutionResult, error) { +func applyMessage(ctx context.Context, b Backend, args TransactionArgs, state *state.StateDB, header *types.Header, timeout time.Duration, gp *core.GasPool, blockContext *vm.BlockContext, vmConfig *vm.Config, precompiles precompiles.PrecompiledContracts, skipChecks bool) (*core.ExecutionResult, error) { // Get a new instance of the EVM. if err := args.CallDefaults(gp.Gas(), blockContext.BaseFee, b.ChainConfig().ChainID); err != nil { return nil, err diff --git a/internal/ethapi/override/override.go b/internal/ethapi/override/override.go index f6a8a94ffd..5f425577c9 100644 --- a/internal/ethapi/override/override.go +++ b/internal/ethapi/override/override.go @@ -26,6 +26,7 @@ import ( "github.com/ethereum/go-ethereum/core/tracing" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/core/vm/precompiles" "github.com/holiman/uint256" ) @@ -53,7 +54,7 @@ func (diff *StateOverride) has(address common.Address) bool { } // Apply overrides the fields of specified accounts into the given state. -func (diff *StateOverride) Apply(statedb *state.StateDB, precompiles vm.PrecompiledContracts) error { +func (diff *StateOverride) Apply(statedb *state.StateDB, precompiles precompiles.PrecompiledContracts) error { if diff == nil { return nil } diff --git a/internal/ethapi/override/override_test.go b/internal/ethapi/override/override_test.go index 02a17c1331..a6321467c8 100644 --- a/internal/ethapi/override/override_test.go +++ b/internal/ethapi/override/override_test.go @@ -25,12 +25,16 @@ import ( "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/core/vm/precompiles" "github.com/ethereum/go-ethereum/triedb" ) type precompileContract struct{} +func (precompileContract) Address() common.Address { + return common.BytesToAddress([]byte{0x1}) +} + func (p *precompileContract) RequiredGas(input []byte) uint64 { return 0 } func (p *precompileContract) Run(input []byte) ([]byte, error) { return nil, nil } @@ -41,7 +45,7 @@ func TestStateOverrideMovePrecompile(t *testing.T) { if err != nil { t.Fatalf("failed to create statedb: %v", err) } - precompiles := map[common.Address]vm.PrecompiledContract{ + precompiles := map[common.Address]precompiles.PrecompiledContract{ common.BytesToAddress([]byte{0x1}): &precompileContract{}, common.BytesToAddress([]byte{0x2}): &precompileContract{}, } diff --git a/internal/ethapi/simulate.go b/internal/ethapi/simulate.go index ac76372424..e8a931e187 100644 --- a/internal/ethapi/simulate.go +++ b/internal/ethapi/simulate.go @@ -33,6 +33,7 @@ import ( "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/core/vm/precompiles" "github.com/ethereum/go-ethereum/internal/ethapi/override" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rpc" @@ -315,7 +316,7 @@ func (sim *simulator) sanitizeCall(call *TransactionArgs, state vm.StateDB, head return nil } -func (sim *simulator) activePrecompiles(base *types.Header) vm.PrecompiledContracts { +func (sim *simulator) activePrecompiles(base *types.Header) precompiles.PrecompiledContracts { var ( isMerge = (base.Difficulty.Sign() == 0) rules = sim.chainConfig.Rules(base.Number, isMerge, base.Time)