From eca91aa939d4330ba3d5cde682b8fd763b97f0be Mon Sep 17 00:00:00 2001 From: MiniFrenchBread <103425574+MiniFrenchBread@users.noreply.github.com> Date: Tue, 25 Mar 2025 05:39:15 +0800 Subject: [PATCH] refactor: stateful precompiles --- core/vm/contracts.go | 144 +++++++++++++++------- core/vm/contracts_fuzz_test.go | 3 +- core/vm/contracts_test.go | 12 +- core/vm/evm.go | 15 ++- 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 +- 9 files changed, 120 insertions(+), 106 deletions(-) delete mode 100644 core/vm/precompiles/precompiles.go diff --git a/core/vm/contracts.go b/core/vm/contracts.go index 2202fcca85..438833a56e 100644 --- a/core/vm/contracts.go +++ b/core/vm/contracts.go @@ -31,18 +31,46 @@ 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" "github.com/ethereum/go-ethereum/crypto/kzg4844" "github.com/ethereum/go-ethereum/params" + "github.com/holiman/uint256" "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 { + Address() common.Address + RequiredGas(input []byte) uint64 // RequiredPrice calculates the contract gas use + Run(evm *EVM, contract *Contract, readonly bool) ([]byte, error) +} + +// 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 +} + // PrecompiledContractsHomestead contains the default set of pre-compiled Ethereum // contracts used in the Frontier and Homestead releases. -var PrecompiledContractsHomestead = precompiles.WithCustomPrecompiles(precompiles.PrecompiledContracts{ +var PrecompiledContractsHomestead = WithCustomPrecompiles(PrecompiledContracts{ (&ecrecover{}).Address(): &ecrecover{}, (&sha256hash{}).Address(): &sha256hash{}, (&ripemd160hash{}).Address(): &ripemd160hash{}, @@ -51,7 +79,7 @@ var PrecompiledContractsHomestead = precompiles.WithCustomPrecompiles(precompile // PrecompiledContractsByzantium contains the default set of pre-compiled Ethereum // contracts used in the Byzantium release. -var PrecompiledContractsByzantium = precompiles.WithCustomPrecompiles(precompiles.PrecompiledContracts{ +var PrecompiledContractsByzantium = WithCustomPrecompiles(PrecompiledContracts{ (&ecrecover{}).Address(): &ecrecover{}, (&sha256hash{}).Address(): &sha256hash{}, (&ripemd160hash{}).Address(): &ripemd160hash{}, @@ -64,7 +92,7 @@ var PrecompiledContractsByzantium = precompiles.WithCustomPrecompiles(precompile // PrecompiledContractsIstanbul contains the default set of pre-compiled Ethereum // contracts used in the Istanbul release. -var PrecompiledContractsIstanbul = precompiles.WithCustomPrecompiles(precompiles.PrecompiledContracts{ +var PrecompiledContractsIstanbul = WithCustomPrecompiles(PrecompiledContracts{ (&ecrecover{}).Address(): &ecrecover{}, (&sha256hash{}).Address(): &sha256hash{}, (&ripemd160hash{}).Address(): &ripemd160hash{}, @@ -78,7 +106,7 @@ var PrecompiledContractsIstanbul = precompiles.WithCustomPrecompiles(precompiles // PrecompiledContractsBerlin contains the default set of pre-compiled Ethereum // contracts used in the Berlin release. -var PrecompiledContractsBerlin = precompiles.WithCustomPrecompiles(precompiles.PrecompiledContracts{ +var PrecompiledContractsBerlin = WithCustomPrecompiles(PrecompiledContracts{ (&ecrecover{}).Address(): &ecrecover{}, (&sha256hash{}).Address(): &sha256hash{}, (&ripemd160hash{}).Address(): &ripemd160hash{}, @@ -92,7 +120,7 @@ var PrecompiledContractsBerlin = precompiles.WithCustomPrecompiles(precompiles.P // PrecompiledContractsCancun contains the default set of pre-compiled Ethereum // contracts used in the Cancun release. -var PrecompiledContractsCancun = precompiles.WithCustomPrecompiles(precompiles.PrecompiledContracts{ +var PrecompiledContractsCancun = WithCustomPrecompiles(PrecompiledContracts{ (&ecrecover{}).Address(): &ecrecover{}, (&sha256hash{}).Address(): &sha256hash{}, (&ripemd160hash{}).Address(): &ripemd160hash{}, @@ -107,7 +135,7 @@ var PrecompiledContractsCancun = precompiles.WithCustomPrecompiles(precompiles.P // PrecompiledContractsPrague contains the set of pre-compiled Ethereum // contracts used in the Prague release. -var PrecompiledContractsPrague = precompiles.WithCustomPrecompiles(precompiles.PrecompiledContracts{ +var PrecompiledContractsPrague = WithCustomPrecompiles(PrecompiledContracts{ (&ecrecover{}).Address(): &ecrecover{}, (&sha256hash{}).Address(): &sha256hash{}, (&ripemd160hash{}).Address(): &ripemd160hash{}, @@ -161,7 +189,7 @@ func init() { } } -func activePrecompiledContracts(rules params.Rules) precompiles.PrecompiledContracts { +func activePrecompiledContracts(rules params.Rules) PrecompiledContracts { switch { case rules.IsVerkle: return PrecompiledContractsVerkle @@ -181,7 +209,7 @@ func activePrecompiledContracts(rules params.Rules) precompiles.PrecompiledContr } // ActivePrecompiledContracts returns a copy of precompiled contracts enabled with the current configuration. -func ActivePrecompiledContracts(rules params.Rules) precompiles.PrecompiledContracts { +func ActivePrecompiledContracts(rules params.Rules) PrecompiledContracts { return maps.Clone(activePrecompiledContracts(rules)) } @@ -208,17 +236,29 @@ func ActivePrecompiles(rules params.Rules) []common.Address { // - the returned bytes, // - the _remaining_ gas, // - any error that occurred -func RunPrecompiledContract(p precompiles.PrecompiledContract, input []byte, suppliedGas uint64, logger *tracing.Hooks) (ret []byte, remainingGas uint64, err error) { +func RunPrecompiledContract( + evm *EVM, + p PrecompiledContract, + caller common.Address, + input []byte, + suppliedGas uint64, + value *uint256.Int, + readOnly bool, + logger *tracing.Hooks, +) (ret []byte, remainingGas uint64, err error) { + inputCopy := make([]byte, len(input)) + copy(inputCopy, input) + + contract := NewContract(caller, p.Address(), value, suppliedGas, evm.jumpDests) + contract.Input = inputCopy + gasCost := p.RequiredGas(input) - if suppliedGas < gasCost { + if !contract.UseGas(gasCost, logger, tracing.GasChangeCallPrecompiledContract) { return nil, 0, ErrOutOfGas } - if logger != nil && logger.OnGasChange != nil { - logger.OnGasChange(suppliedGas, suppliedGas-gasCost, tracing.GasChangeCallPrecompiledContract) - } - suppliedGas -= gasCost - output, err := p.Run(input) - return output, suppliedGas, err + + output, err := p.Run(evm, contract, readOnly) + return output, contract.Gas, err } // ecrecover implemented as a native contract. @@ -234,10 +274,10 @@ func (c *ecrecover) RequiredGas(input []byte) uint64 { return params.EcrecoverGas } -func (c *ecrecover) Run(input []byte) ([]byte, error) { +func (c *ecrecover) Run(evm *EVM, contract *Contract, readonly bool) ([]byte, error) { const ecRecoverInputLength = 128 - input = common.RightPadBytes(input, ecRecoverInputLength) + input := common.RightPadBytes(contract.Input, ecRecoverInputLength) // "input" is (hash, v, r, s), each 32 bytes // but for ecrecover we want (r, s, v) @@ -281,8 +321,8 @@ func (*sha256hash) Address() common.Address { func (c *sha256hash) RequiredGas(input []byte) uint64 { return uint64(len(input)+31)/32*params.Sha256PerWordGas + params.Sha256BaseGas } -func (c *sha256hash) Run(input []byte) ([]byte, error) { - h := sha256.Sum256(input) +func (c *sha256hash) Run(evm *EVM, contract *Contract, readonly bool) ([]byte, error) { + h := sha256.Sum256(contract.Input) return h[:], nil } @@ -302,9 +342,9 @@ func (*ripemd160hash) Address() common.Address { func (c *ripemd160hash) RequiredGas(input []byte) uint64 { return uint64(len(input)+31)/32*params.Ripemd160PerWordGas + params.Ripemd160BaseGas } -func (c *ripemd160hash) Run(input []byte) ([]byte, error) { +func (c *ripemd160hash) Run(evm *EVM, contract *Contract, readonly bool) ([]byte, error) { ripemd := ripemd160.New() - ripemd.Write(input) + ripemd.Write(contract.Input) return common.LeftPadBytes(ripemd.Sum(nil), 32), nil } @@ -324,8 +364,8 @@ func (*dataCopy) Address() common.Address { func (c *dataCopy) RequiredGas(input []byte) uint64 { return uint64(len(input)+31)/32*params.IdentityPerWordGas + params.IdentityBaseGas } -func (c *dataCopy) Run(in []byte) ([]byte, error) { - return common.CopyBytes(in), nil +func (c *dataCopy) Run(evm *EVM, contract *Contract, readonly bool) ([]byte, error) { + return common.CopyBytes(contract.Input), nil } // bigModExp implements a native big integer exponential modular operation. @@ -461,7 +501,8 @@ func (c *bigModExp) RequiredGas(input []byte) uint64 { return gas.Uint64() } -func (c *bigModExp) Run(input []byte) ([]byte, error) { +func (c *bigModExp) Run(evm *EVM, contract *Contract, readonly bool) ([]byte, error) { + input := contract.Input var ( baseLen = new(big.Int).SetBytes(getData(input, 0, 32)).Uint64() expLen = new(big.Int).SetBytes(getData(input, 32, 32)).Uint64() @@ -547,8 +588,8 @@ func (c *bn256AddIstanbul) RequiredGas(input []byte) uint64 { return params.Bn256AddGasIstanbul } -func (c *bn256AddIstanbul) Run(input []byte) ([]byte, error) { - return runBn256Add(input) +func (c *bn256AddIstanbul) Run(evm *EVM, contract *Contract, readonly bool) ([]byte, error) { + return runBn256Add(contract.Input) } // bn256AddByzantium implements a native elliptic curve point addition @@ -566,8 +607,8 @@ func (c *bn256AddByzantium) RequiredGas(input []byte) uint64 { return params.Bn256AddGasByzantium } -func (c *bn256AddByzantium) Run(input []byte) ([]byte, error) { - return runBn256Add(input) +func (c *bn256AddByzantium) Run(evm *EVM, contract *Contract, readonly bool) ([]byte, error) { + return runBn256Add(contract.Input) } // runBn256ScalarMul implements the Bn256ScalarMul precompile, referenced by @@ -597,8 +638,8 @@ func (c *bn256ScalarMulIstanbul) RequiredGas(input []byte) uint64 { return params.Bn256ScalarMulGasIstanbul } -func (c *bn256ScalarMulIstanbul) Run(input []byte) ([]byte, error) { - return runBn256ScalarMul(input) +func (c *bn256ScalarMulIstanbul) Run(evm *EVM, contract *Contract, readonly bool) ([]byte, error) { + return runBn256ScalarMul(contract.Input) } // bn256ScalarMulByzantium implements a native elliptic curve scalar @@ -616,8 +657,8 @@ func (c *bn256ScalarMulByzantium) RequiredGas(input []byte) uint64 { return params.Bn256ScalarMulGasByzantium } -func (c *bn256ScalarMulByzantium) Run(input []byte) ([]byte, error) { - return runBn256ScalarMul(input) +func (c *bn256ScalarMulByzantium) Run(evm *EVM, contract *Contract, readonly bool) ([]byte, error) { + return runBn256ScalarMul(contract.Input) } var ( @@ -677,8 +718,8 @@ func (c *bn256PairingIstanbul) RequiredGas(input []byte) uint64 { return params.Bn256PairingBaseGasIstanbul + uint64(len(input)/192)*params.Bn256PairingPerPointGasIstanbul } -func (c *bn256PairingIstanbul) Run(input []byte) ([]byte, error) { - return runBn256Pairing(input) +func (c *bn256PairingIstanbul) Run(evm *EVM, contract *Contract, readonly bool) ([]byte, error) { + return runBn256Pairing(contract.Input) } // bn256PairingByzantium implements a pairing pre-compile for the bn256 curve @@ -696,8 +737,8 @@ func (c *bn256PairingByzantium) RequiredGas(input []byte) uint64 { return params.Bn256PairingBaseGasByzantium + uint64(len(input)/192)*params.Bn256PairingPerPointGasByzantium } -func (c *bn256PairingByzantium) Run(input []byte) ([]byte, error) { - return runBn256Pairing(input) +func (c *bn256PairingByzantium) Run(evm *EVM, contract *Contract, readonly bool) ([]byte, error) { + return runBn256Pairing(contract.Input) } type blake2F struct{} @@ -728,7 +769,8 @@ var ( errBlake2FInvalidFinalFlag = errors.New("invalid final flag") ) -func (c *blake2F) Run(input []byte) ([]byte, error) { +func (c *blake2F) Run(evm *EVM, contract *Contract, readonly bool) ([]byte, error) { + input := contract.Input // Make sure the input is valid (correct length and final flag) if len(input) != blake2FInputLength { return nil, errBlake2FInvalidInputLength @@ -788,7 +830,8 @@ func (c *bls12381G1Add) RequiredGas(input []byte) uint64 { return params.Bls12381G1AddGas } -func (c *bls12381G1Add) Run(input []byte) ([]byte, error) { +func (c *bls12381G1Add) Run(evm *EVM, contract *Contract, readonly bool) ([]byte, error) { + input := contract.Input // Implements EIP-2537 G1Add precompile. // > G1 addition call expects `256` bytes as an input that is interpreted as byte concatenation of two G1 points (`128` bytes each). // > Output is an encoding of addition operation result - single G1 point (`128` bytes). @@ -844,7 +887,8 @@ func (c *bls12381G1MultiExp) RequiredGas(input []byte) uint64 { return (uint64(k) * params.Bls12381G1MulGas * discount) / 1000 } -func (c *bls12381G1MultiExp) Run(input []byte) ([]byte, error) { +func (c *bls12381G1MultiExp) Run(evm *EVM, contract *Contract, readonly bool) ([]byte, error) { + input := contract.Input // Implements EIP-2537 G1MultiExp precompile. // G1 multiplication call expects `160*k` bytes as an input that is interpreted as byte concatenation of `k` slices each of them being a byte concatenation of encoding of G1 point (`128` bytes) and encoding of a scalar value (`32` bytes). // Output is an encoding of multiexponentiation operation result - single G1 point (`128` bytes). @@ -896,7 +940,8 @@ func (c *bls12381G2Add) RequiredGas(input []byte) uint64 { return params.Bls12381G2AddGas } -func (c *bls12381G2Add) Run(input []byte) ([]byte, error) { +func (c *bls12381G2Add) Run(evm *EVM, contract *Contract, readonly bool) ([]byte, error) { + input := contract.Input // Implements EIP-2537 G2Add precompile. // > G2 addition call expects `512` bytes as an input that is interpreted as byte concatenation of two G2 points (`256` bytes each). // > Output is an encoding of addition operation result - single G2 point (`256` bytes). @@ -953,7 +998,8 @@ func (c *bls12381G2MultiExp) RequiredGas(input []byte) uint64 { return (uint64(k) * params.Bls12381G2MulGas * discount) / 1000 } -func (c *bls12381G2MultiExp) Run(input []byte) ([]byte, error) { +func (c *bls12381G2MultiExp) Run(evm *EVM, contract *Contract, readonly bool) ([]byte, error) { + input := contract.Input // Implements EIP-2537 G2MultiExp precompile logic // > G2 multiplication call expects `288*k` bytes as an input that is interpreted as byte concatenation of `k` slices each of them being a byte concatenation of encoding of G2 point (`256` bytes) and encoding of a scalar value (`32` bytes). // > Output is an encoding of multiexponentiation operation result - single G2 point (`256` bytes). @@ -1005,7 +1051,8 @@ func (c *bls12381Pairing) RequiredGas(input []byte) uint64 { return params.Bls12381PairingBaseGas + uint64(len(input)/384)*params.Bls12381PairingPerPairGas } -func (c *bls12381Pairing) Run(input []byte) ([]byte, error) { +func (c *bls12381Pairing) Run(evm *EVM, contract *Contract, readonly bool) ([]byte, error) { + input := contract.Input // Implements EIP-2537 Pairing precompile logic. // > Pairing call expects `384*k` bytes as an inputs that is interpreted as byte concatenation of `k` slices. Each slice has the following structure: // > - `128` bytes of G1 point encoding @@ -1163,7 +1210,8 @@ func (c *bls12381MapG1) RequiredGas(input []byte) uint64 { return params.Bls12381MapG1Gas } -func (c *bls12381MapG1) Run(input []byte) ([]byte, error) { +func (c *bls12381MapG1) Run(evm *EVM, contract *Contract, readonly bool) ([]byte, error) { + input := contract.Input // Implements EIP-2537 Map_To_G1 precompile. // > Field-to-curve call expects an `64` bytes input that is interpreted as an element of the base field. // > Output of this call is `128` bytes and is G1 point following respective encoding rules. @@ -1198,7 +1246,8 @@ func (c *bls12381MapG2) RequiredGas(input []byte) uint64 { return params.Bls12381MapG2Gas } -func (c *bls12381MapG2) Run(input []byte) ([]byte, error) { +func (c *bls12381MapG2) Run(evm *EVM, contract *Contract, readonly bool) ([]byte, error) { + input := contract.Input // Implements EIP-2537 Map_FP2_TO_G2 precompile logic. // > Field-to-curve call expects an `128` bytes input that is interpreted as an element of the quadratic extension field. // > Output of this call is `256` bytes and is G2 point following respective encoding rules. @@ -1250,7 +1299,8 @@ var ( ) // Run executes the point evaluation precompile. -func (b *kzgPointEvaluation) Run(input []byte) ([]byte, error) { +func (b *kzgPointEvaluation) Run(evm *EVM, contract *Contract, readonly bool) ([]byte, error) { + input := contract.Input if len(input) != blobVerifyInputLength { return nil, errBlobVerifyInvalidInputLength } diff --git a/core/vm/contracts_fuzz_test.go b/core/vm/contracts_fuzz_test.go index 1e5cc80074..d1a32f8ea2 100644 --- a/core/vm/contracts_fuzz_test.go +++ b/core/vm/contracts_fuzz_test.go @@ -20,6 +20,7 @@ import ( "testing" "github.com/ethereum/go-ethereum/common" + "github.com/holiman/uint256" ) func FuzzPrecompiledContracts(f *testing.F) { @@ -36,7 +37,7 @@ func FuzzPrecompiledContracts(f *testing.F) { return } inWant := string(input) - RunPrecompiledContract(p, input, gas, nil) + RunPrecompiledContract(nil, p, common.Address{}, input, gas, uint256.NewInt(0), false, nil) if inHave := string(input); inWant != inHave { t.Errorf("Precompiled %v modified input data", a) } diff --git a/core/vm/contracts_test.go b/core/vm/contracts_test.go index e9c063e036..69957783c5 100644 --- a/core/vm/contracts_test.go +++ b/core/vm/contracts_test.go @@ -25,7 +25,7 @@ import ( "time" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/vm/precompiles" + "github.com/holiman/uint256" ) // precompiledTest defines the input/output pairs for precompiled contract tests. @@ -46,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]precompiles.PrecompiledContract{ +var allPrecompiles = map[common.Address]PrecompiledContract{ common.BytesToAddress([]byte{1}): &ecrecover{}, common.BytesToAddress([]byte{2}): &sha256hash{}, common.BytesToAddress([]byte{3}): &ripemd160hash{}, @@ -97,7 +97,7 @@ func testPrecompiled(addr string, test precompiledTest, t *testing.T) { in := common.Hex2Bytes(test.Input) gas := p.RequiredGas(in) t.Run(fmt.Sprintf("%s-Gas=%d", test.Name, gas), func(t *testing.T) { - if res, _, err := RunPrecompiledContract(p, in, gas, nil); err != nil { + if res, _, err := RunPrecompiledContract(nil, p, common.Address{}, in, gas, uint256.NewInt(0), false, nil); err != nil { t.Error(err) } else if common.Bytes2Hex(res) != test.Expected { t.Errorf("Expected %v, got %v", test.Expected, common.Bytes2Hex(res)) @@ -119,7 +119,7 @@ func testPrecompiledOOG(addr string, test precompiledTest, t *testing.T) { gas := p.RequiredGas(in) - 1 t.Run(fmt.Sprintf("%s-Gas=%d", test.Name, gas), func(t *testing.T) { - _, _, err := RunPrecompiledContract(p, in, gas, nil) + _, _, err := RunPrecompiledContract(nil, p, common.Address{}, in, gas, uint256.NewInt(0), false, nil) if err.Error() != "out of gas" { t.Errorf("Expected error [out of gas], got [%v]", err) } @@ -136,7 +136,7 @@ func testPrecompiledFailure(addr string, test precompiledFailureTest, t *testing in := common.Hex2Bytes(test.Input) gas := p.RequiredGas(in) t.Run(test.Name, func(t *testing.T) { - _, _, err := RunPrecompiledContract(p, in, gas, nil) + _, _, err := RunPrecompiledContract(nil, p, common.Address{}, in, gas, uint256.NewInt(0), false, nil) if err.Error() != test.ExpectedError { t.Errorf("Expected error [%v], got [%v]", test.ExpectedError, err) } @@ -168,7 +168,7 @@ func benchmarkPrecompiled(addr string, test precompiledTest, bench *testing.B) { bench.ResetTimer() for i := 0; i < bench.N; i++ { copy(data, in) - res, _, err = RunPrecompiledContract(p, data, reqGas, nil) + res, _, err = RunPrecompiledContract(nil, p, common.Address{}, data, reqGas, uint256.NewInt(0), false, nil) } bench.StopTimer() elapsed := uint64(time.Since(start)) diff --git a/core/vm/evm.go b/core/vm/evm.go index 18ceb98fad..6b60812d92 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -25,7 +25,6 @@ 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" @@ -41,7 +40,7 @@ type ( GetHashFunc func(uint64) common.Hash ) -func (evm *EVM) precompile(addr common.Address) (precompiles.PrecompiledContract, bool) { +func (evm *EVM) precompile(addr common.Address) (PrecompiledContract, bool) { p, ok := evm.precompiles[addr] return p, ok } @@ -121,7 +120,7 @@ type EVM struct { callGasTemp uint64 // precompiles holds the precompiled contracts for the current epoch - precompiles map[common.Address]precompiles.PrecompiledContract + precompiles map[common.Address]PrecompiledContract // jumpDests is the aggregated result of JUMPDEST analysis made through // the life cycle of EVM. @@ -149,7 +148,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 precompiles.PrecompiledContracts) { +func (evm *EVM) SetPrecompiles(precompiles PrecompiledContracts) { evm.precompiles = precompiles } @@ -225,7 +224,7 @@ func (evm *EVM) Call(caller common.Address, addr common.Address, input []byte, g evm.Context.Transfer(evm.StateDB, caller, addr, value) if isPrecompile { - ret, gas, err = RunPrecompiledContract(p, input, gas, evm.Config.Tracer) + ret, gas, err = RunPrecompiledContract(evm, p, caller, input, gas, value, false, evm.Config.Tracer) } else { // Initialise a new contract and set the code that is to be used by the EVM. code := evm.resolveCode(addr) @@ -289,7 +288,7 @@ func (evm *EVM) CallCode(caller common.Address, addr common.Address, input []byt // It is allowed to call precompiles, even via delegatecall if p, isPrecompile := evm.precompile(addr); isPrecompile { - ret, gas, err = RunPrecompiledContract(p, input, gas, evm.Config.Tracer) + ret, gas, err = RunPrecompiledContract(evm, p, caller, input, gas, value, true, evm.Config.Tracer) } else { // Initialise a new contract and set the code that is to be used by the EVM. // The contract is a scoped environment for this execution context only. @@ -332,7 +331,7 @@ func (evm *EVM) DelegateCall(originCaller common.Address, caller common.Address, // It is allowed to call precompiles, even via delegatecall if p, isPrecompile := evm.precompile(addr); isPrecompile { - ret, gas, err = RunPrecompiledContract(p, input, gas, evm.Config.Tracer) + ret, gas, err = RunPrecompiledContract(evm, p, caller, input, gas, value, true, evm.Config.Tracer) } else { // Initialise a new contract and make initialise the delegate values // @@ -384,7 +383,7 @@ func (evm *EVM) StaticCall(caller common.Address, addr common.Address, input []b evm.StateDB.AddBalance(addr, new(uint256.Int), tracing.BalanceChangeTouchAccount) if p, isPrecompile := evm.precompile(addr); isPrecompile { - ret, gas, err = RunPrecompiledContract(p, input, gas, evm.Config.Tracer) + ret, gas, err = RunPrecompiledContract(evm, p, caller, input, gas, new(uint256.Int), true, evm.Config.Tracer) } else { // Initialise a new contract and set the code that is to be used by the EVM. // The contract is a scoped environment for this execution context only. diff --git a/core/vm/precompiles/precompiles.go b/core/vm/precompiles/precompiles.go deleted file mode 100644 index 8a841502a3..0000000000 --- a/core/vm/precompiles/precompiles.go +++ /dev/null @@ -1,35 +0,0 @@ -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 ca1a1eef93..f3975d35a0 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -37,7 +37,6 @@ 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" @@ -689,7 +688,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 precompiles.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 vm.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 5f425577c9..f6a8a94ffd 100644 --- a/internal/ethapi/override/override.go +++ b/internal/ethapi/override/override.go @@ -26,7 +26,6 @@ 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" ) @@ -54,7 +53,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 precompiles.PrecompiledContracts) error { +func (diff *StateOverride) Apply(statedb *state.StateDB, precompiles vm.PrecompiledContracts) error { if diff == nil { return nil } diff --git a/internal/ethapi/override/override_test.go b/internal/ethapi/override/override_test.go index a6321467c8..ec151f7dd5 100644 --- a/internal/ethapi/override/override_test.go +++ b/internal/ethapi/override/override_test.go @@ -25,7 +25,7 @@ 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/precompiles" + "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/triedb" ) @@ -37,7 +37,9 @@ func (precompileContract) Address() common.Address { func (p *precompileContract) RequiredGas(input []byte) uint64 { return 0 } -func (p *precompileContract) Run(input []byte) ([]byte, error) { return nil, nil } +func (p *precompileContract) Run(evm *vm.EVM, contract *vm.Contract, readonly bool) ([]byte, error) { + return nil, nil +} func TestStateOverrideMovePrecompile(t *testing.T) { db := state.NewDatabase(triedb.NewDatabase(rawdb.NewMemoryDatabase(), nil), nil) @@ -45,7 +47,7 @@ func TestStateOverrideMovePrecompile(t *testing.T) { if err != nil { t.Fatalf("failed to create statedb: %v", err) } - precompiles := map[common.Address]precompiles.PrecompiledContract{ + precompiles := map[common.Address]vm.PrecompiledContract{ common.BytesToAddress([]byte{0x1}): &precompileContract{}, common.BytesToAddress([]byte{0x2}): &precompileContract{}, } diff --git a/internal/ethapi/simulate.go b/internal/ethapi/simulate.go index e8a931e187..ac76372424 100644 --- a/internal/ethapi/simulate.go +++ b/internal/ethapi/simulate.go @@ -33,7 +33,6 @@ 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" @@ -316,7 +315,7 @@ func (sim *simulator) sanitizeCall(call *TransactionArgs, state vm.StateDB, head return nil } -func (sim *simulator) activePrecompiles(base *types.Header) precompiles.PrecompiledContracts { +func (sim *simulator) activePrecompiles(base *types.Header) vm.PrecompiledContracts { var ( isMerge = (base.Difficulty.Sign() == 0) rules = sim.chainConfig.Rules(base.Number, isMerge, base.Time)