From 7c547bf355f70a12660fa2a75e8ede234bdb6f27 Mon Sep 17 00:00:00 2001 From: yihuang Date: Tue, 16 Sep 2025 11:33:11 +0800 Subject: [PATCH 1/2] Problem: precompile can't distinguish call types Solution: - fill in the precompile Contract parameters in the same way as EVM - context: https://github.com/cosmos/evm/issues/605 --- core/vm/contracts.go | 23 +++++++++++++---------- core/vm/contracts_fuzz_test.go | 3 ++- core/vm/contracts_test.go | 12 ++++++++---- core/vm/evm.go | 18 +++++++++--------- 4 files changed, 32 insertions(+), 24 deletions(-) diff --git a/core/vm/contracts.go b/core/vm/contracts.go index 09819fb9f03f..761beca71b98 100644 --- a/core/vm/contracts.go +++ b/core/vm/contracts.go @@ -37,7 +37,6 @@ import ( "github.com/ethereum/go-ethereum/crypto/kzg4844" "github.com/ethereum/go-ethereum/crypto/secp256r1" "github.com/ethereum/go-ethereum/params" - "github.com/holiman/uint256" "golang.org/x/crypto/ripemd160" ) @@ -259,25 +258,29 @@ func ActivePrecompiles(rules params.Rules) []common.Address { // - the returned bytes, // - the _remaining_ gas, // - any error that occurred +// +// readOnly is true for StaticCall, it's the equivalence of `interpreter.readOnly` for precompiles. func (evm *EVM) RunPrecompiledContract( p PrecompiledContract, - caller common.Address, + contract *Contract, input []byte, - suppliedGas uint64, - value *uint256.Int, readOnly bool, - logger *tracing.Hooks, ) (ret []byte, remainingGas uint64, err error) { - return runPrecompiledContract(evm, p, caller, input, suppliedGas, value, readOnly, logger) + return runPrecompiledContract(evm, p, contract, input, readOnly, evm.Config.Tracer) } -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) { - addrCopy := p.Address() +func runPrecompiledContract( + evm *EVM, + p PrecompiledContract, + contract *Contract, + input []byte, + readOnly bool, + logger *tracing.Hooks, +) (ret []byte, remainingGas uint64, err error) { inputCopy := make([]byte, len(input)) copy(inputCopy, input) - contract := NewPrecompile(caller, addrCopy, value, suppliedGas) + contract.isPrecompile = true contract.Input = inputCopy gasCost := p.RequiredGas(input) diff --git a/core/vm/contracts_fuzz_test.go b/core/vm/contracts_fuzz_test.go index 59c62ad14958..087d33ff3491 100644 --- a/core/vm/contracts_fuzz_test.go +++ b/core/vm/contracts_fuzz_test.go @@ -38,7 +38,8 @@ func FuzzPrecompiledContracts(f *testing.F) { } inWant := string(input) - runPrecompiledContract(nil, p, common.Address{}, input, gas, new(uint256.Int), false, nil) + contract := NewContract(common.Address{}, p.Address(), new(uint256.Int), gas, nil) + runPrecompiledContract(nil, p, contract, input, 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 b50d9e9e6bab..9ec059c0b1b6 100644 --- a/core/vm/contracts_test.go +++ b/core/vm/contracts_test.go @@ -100,7 +100,8 @@ 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(nil, p, common.Address{}, in, gas, new(uint256.Int), false, nil); err != nil { + contract := NewContract(common.Address{}, p.Address(), new(uint256.Int), gas, nil) + if res, _, err := runPrecompiledContract(nil, p, contract, in, 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)) @@ -122,7 +123,8 @@ 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(nil, p, common.Address{}, in, gas, new(uint256.Int), false, nil) + contract := NewContract(common.Address{}, p.Address(), new(uint256.Int), gas, nil) + _, _, err := runPrecompiledContract(nil, p, contract, in, false, nil) if err.Error() != "out of gas" { t.Errorf("Expected error [out of gas], got [%v]", err) } @@ -139,7 +141,8 @@ 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(nil, p, common.Address{}, in, gas, new(uint256.Int), false, nil) + contract := NewContract(common.Address{}, p.Address(), new(uint256.Int), gas, nil) + _, _, err := runPrecompiledContract(nil, p, contract, in, false, nil) if err.Error() != test.ExpectedError { t.Errorf("Expected error [%v], got [%v]", test.ExpectedError, err) } @@ -171,7 +174,8 @@ func benchmarkPrecompiled(addr string, test precompiledTest, bench *testing.B) { bench.ResetTimer() for i := 0; i < bench.N; i++ { copy(data, in) - res, _, err = runPrecompiledContract(nil, p, common.Address{}, in, reqGas, new(uint256.Int), false, nil) + contract := NewContract(common.Address{}, p.Address(), new(uint256.Int), reqGas, nil) + res, _, err = runPrecompiledContract(nil, p, contract, in, false, nil) } bench.StopTimer() elapsed := uint64(time.Since(start)) diff --git a/core/vm/evm.go b/core/vm/evm.go index c283c041e976..59eff902c134 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -259,16 +259,16 @@ func (evm *EVM) Call(caller common.Address, addr common.Address, input []byte, g } evm.Context.Transfer(evm.StateDB, caller, addr, value) + // The contract is a scoped environment for this execution context only. + contract := NewContract(caller, addr, value, gas, evm.jumpDests) if isPrecompile { - ret, gas, err = evm.RunPrecompiledContract(p, caller, input, gas, value, false, evm.Config.Tracer) + ret, gas, err = evm.RunPrecompiledContract(p, contract, input, false) } else { // Initialise a new contract and set the code that is to be used by the EVM. code := evm.resolveCode(addr) if len(code) == 0 { ret, err = nil, nil // gas is unchanged } else { - // The contract is a scoped environment for this execution context only. - contract := NewContract(caller, addr, value, gas, evm.jumpDests) contract.IsSystemCall = isSystemCall(caller) contract.SetCallCode(evm.resolveCodeHash(addr), code) ret, err = evm.interpreter.Run(contract, input, false) @@ -326,12 +326,12 @@ func (evm *EVM) CallCode(caller common.Address, addr common.Address, input []byt var snapshot = evm.StateDB.Snapshot() // It is allowed to call precompiles, even via delegatecall + contract := NewContract(caller, caller, value, gas, evm.jumpDests) if p, isPrecompile := evm.Precompile(addr); isPrecompile { - ret, gas, err = evm.RunPrecompiledContract(p, caller, input, gas, value, true, evm.Config.Tracer) + ret, gas, err = evm.RunPrecompiledContract(p, contract, input, false) } 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. - contract := NewContract(caller, caller, value, gas, evm.jumpDests) contract.SetCallCode(evm.resolveCodeHash(addr), evm.resolveCode(addr)) ret, err = evm.interpreter.Run(contract, input, false) gas = contract.Gas @@ -372,13 +372,13 @@ func (evm *EVM) DelegateCall(originCaller common.Address, caller common.Address, var snapshot = evm.StateDB.Snapshot() // It is allowed to call precompiles, even via delegatecall + contract := NewContract(originCaller, caller, value, gas, evm.jumpDests) if p, isPrecompile := evm.Precompile(addr); isPrecompile { - ret, gas, err = evm.RunPrecompiledContract(p, caller, input, gas, nil, true, evm.Config.Tracer) + ret, gas, err = evm.RunPrecompiledContract(p, contract, input, false) } else { // Initialise a new contract and make initialise the delegate values // // Note: The value refers to the original value from the parent call. - contract := NewContract(originCaller, caller, value, gas, evm.jumpDests) contract.SetCallCode(evm.resolveCodeHash(addr), evm.resolveCode(addr)) ret, err = evm.interpreter.Run(contract, input, false) gas = contract.Gas @@ -427,12 +427,12 @@ func (evm *EVM) StaticCall(caller common.Address, addr common.Address, input []b // future scenarios evm.StateDB.AddBalance(addr, new(uint256.Int), tracing.BalanceChangeTouchAccount) + contract := NewContract(caller, addr, new(uint256.Int), gas, evm.jumpDests) if p, isPrecompile := evm.Precompile(addr); isPrecompile { - ret, gas, err = evm.RunPrecompiledContract(p, caller, input, gas, new(uint256.Int), true, evm.Config.Tracer) + ret, gas, err = evm.RunPrecompiledContract(p, contract, input, true) } 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. - contract := NewContract(caller, addr, new(uint256.Int), gas, evm.jumpDests) contract.SetCallCode(evm.resolveCodeHash(addr), evm.resolveCode(addr)) // When an error was returned by the EVM or when setting the creation code From 7f6efc24bec7c29275549b2751669ae4c0cffb0f Mon Sep 17 00:00:00 2001 From: yihuang Date: Tue, 11 Nov 2025 21:29:08 +0800 Subject: [PATCH 2/2] precompile share interpreter context --- core/vm/contract.go | 23 ++++------ core/vm/contracts.go | 86 ++++++++++---------------------------- core/vm/custom_contract.go | 3 +- core/vm/evm.go | 38 +++++++++-------- core/vm/interpreter.go | 13 ++++++ 5 files changed, 66 insertions(+), 97 deletions(-) diff --git a/core/vm/contract.go b/core/vm/contract.go index 77e29faae4e8..0862e44998e4 100644 --- a/core/vm/contract.go +++ b/core/vm/contract.go @@ -42,9 +42,11 @@ type Contract struct { IsDeployment bool IsSystemCall bool - Gas uint64 - value *uint256.Int - isPrecompile bool + Gas uint64 + value *uint256.Int + + // Precompile is exclusive with Code + Precompile PrecompiledContract } // NewContract returns a new contract environment for the execution of EVM. @@ -63,10 +65,6 @@ func NewContract(caller common.Address, address common.Address, value *uint256.I } func (c *Contract) validJumpdest(dest *uint256.Int) bool { - if c.isPrecompile { - return false - } - udest, overflow := dest.Uint64WithOverflow() // PC cannot go beyond len(code) and certainly can't be bigger than 63bits. // Don't bother checking for JUMPDEST in that case. @@ -83,10 +81,6 @@ func (c *Contract) validJumpdest(dest *uint256.Int) bool { // isCode returns true if the provided PC location is an actual opcode, as // opposed to a data-segment following a PUSHN operation. func (c *Contract) isCode(udest uint64) bool { - if c.isPrecompile { - return false - } - // Do we already have an analysis laying around? if c.analysis != nil { return c.analysis.codeSegment(udest) @@ -169,9 +163,10 @@ func (c *Contract) Value() *uint256.Int { // SetCallCode sets the code of the contract, func (c *Contract) SetCallCode(hash common.Hash, code []byte) { - if c.isPrecompile { - return - } c.Code = code c.CodeHash = hash } + +func (c *Contract) SetPrecompile(pc PrecompiledContract) { + c.Precompile = pc +} diff --git a/core/vm/contracts.go b/core/vm/contracts.go index 761beca71b98..9fdca1c48d2b 100644 --- a/core/vm/contracts.go +++ b/core/vm/contracts.go @@ -30,7 +30,6 @@ import ( "github.com/consensys/gnark-crypto/ecc/bls12-381/fp" "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/crypto" "github.com/ethereum/go-ethereum/crypto/blake2b" "github.com/ethereum/go-ethereum/crypto/bn256" @@ -45,8 +44,8 @@ import ( // 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) // Run runs the precompiled contract + RequiredGas(input []byte) uint64 // RequiredPrice calculates the contract gas use + Run(evm *EVM, contract *Contract) ([]byte, error) // Run runs the precompiled contract } // PrecompiledContracts contains the precompiled contracts supported at the given fork. @@ -253,45 +252,6 @@ func ActivePrecompiles(rules params.Rules) []common.Address { } } -// RunPrecompiledContract runs and evaluates the output of a precompiled contract. -// It returns -// - the returned bytes, -// - the _remaining_ gas, -// - any error that occurred -// -// readOnly is true for StaticCall, it's the equivalence of `interpreter.readOnly` for precompiles. -func (evm *EVM) RunPrecompiledContract( - p PrecompiledContract, - contract *Contract, - input []byte, - readOnly bool, -) (ret []byte, remainingGas uint64, err error) { - return runPrecompiledContract(evm, p, contract, input, readOnly, evm.Config.Tracer) -} - -func runPrecompiledContract( - evm *EVM, - p PrecompiledContract, - contract *Contract, - input []byte, - readOnly bool, - logger *tracing.Hooks, -) (ret []byte, remainingGas uint64, err error) { - inputCopy := make([]byte, len(input)) - copy(inputCopy, input) - - contract.isPrecompile = true - contract.Input = inputCopy - - gasCost := p.RequiredGas(input) - if !contract.UseGas(gasCost, logger, tracing.GasChangeCallPrecompiledContract) { - return nil, 0, ErrOutOfGas - } - - output, err := p.Run(evm, contract, readOnly) - return output, contract.Gas, err -} - // ecrecover implemented as a native contract. type ecrecover struct{} @@ -305,7 +265,7 @@ func (c *ecrecover) RequiredGas(input []byte) uint64 { return params.EcrecoverGas } -func (c *ecrecover) Run(evm *EVM, contract *Contract, readonly bool) ([]byte, error) { +func (c *ecrecover) Run(evm *EVM, contract *Contract) ([]byte, error) { const ecRecoverInputLength = 128 contract.Input = common.RightPadBytes(contract.Input, ecRecoverInputLength) @@ -353,7 +313,7 @@ func (c *sha256hash) RequiredGas(input []byte) uint64 { return uint64(len(input)+31)/32*params.Sha256PerWordGas + params.Sha256BaseGas } -func (c *sha256hash) Run(evm *EVM, contract *Contract, readonly bool) ([]byte, error) { +func (c *sha256hash) Run(evm *EVM, contract *Contract) ([]byte, error) { h := sha256.Sum256(contract.Input) return h[:], nil } @@ -375,7 +335,7 @@ func (c *ripemd160hash) RequiredGas(input []byte) uint64 { return uint64(len(input)+31)/32*params.Ripemd160PerWordGas + params.Ripemd160BaseGas } -func (c *ripemd160hash) Run(evm *EVM, contract *Contract, readonly bool) ([]byte, error) { +func (c *ripemd160hash) Run(evm *EVM, contract *Contract) ([]byte, error) { ripemd := ripemd160.New() ripemd.Write(contract.Input) return common.LeftPadBytes(ripemd.Sum(nil), 32), nil @@ -398,7 +358,7 @@ func (c *dataCopy) RequiredGas(input []byte) uint64 { return uint64(len(input)+31)/32*params.IdentityPerWordGas + params.IdentityBaseGas } -func (c *dataCopy) Run(evm *EVM, contract *Contract, readonly bool) ([]byte, error) { +func (c *dataCopy) Run(evm *EVM, contract *Contract) ([]byte, error) { return common.CopyBytes(contract.Input), nil } @@ -553,7 +513,7 @@ func (c *bigModExp) RequiredGas(input []byte) uint64 { return gas.Uint64() } -func (c *bigModExp) Run(evm *EVM, contract *Contract, readonly bool) ([]byte, error) { +func (c *bigModExp) Run(evm *EVM, contract *Contract) ([]byte, error) { var ( baseLen = new(big.Int).SetBytes(getData(contract.Input, 0, 32)).Uint64() expLen = new(big.Int).SetBytes(getData(contract.Input, 32, 32)).Uint64() @@ -643,7 +603,7 @@ func (c *bn256AddIstanbul) RequiredGas(input []byte) uint64 { return params.Bn256AddGasIstanbul } -func (c *bn256AddIstanbul) Run(evm *EVM, contract *Contract, readonly bool) ([]byte, error) { +func (c *bn256AddIstanbul) Run(evm *EVM, contract *Contract) ([]byte, error) { return runBn256Add(contract.Input) } @@ -662,7 +622,7 @@ func (c *bn256AddByzantium) RequiredGas(input []byte) uint64 { return params.Bn256AddGasByzantium } -func (c *bn256AddByzantium) Run(evm *EVM, contract *Contract, readonly bool) ([]byte, error) { +func (c *bn256AddByzantium) Run(evm *EVM, contract *Contract) ([]byte, error) { return runBn256Add(contract.Input) } @@ -693,7 +653,7 @@ func (c *bn256ScalarMulIstanbul) RequiredGas(input []byte) uint64 { return params.Bn256ScalarMulGasIstanbul } -func (c *bn256ScalarMulIstanbul) Run(evm *EVM, contract *Contract, readonly bool) ([]byte, error) { +func (c *bn256ScalarMulIstanbul) Run(evm *EVM, contract *Contract) ([]byte, error) { return runBn256ScalarMul(contract.Input) } @@ -712,7 +672,7 @@ func (c *bn256ScalarMulByzantium) RequiredGas(input []byte) uint64 { return params.Bn256ScalarMulGasByzantium } -func (c *bn256ScalarMulByzantium) Run(evm *EVM, contract *Contract, readonly bool) ([]byte, error) { +func (c *bn256ScalarMulByzantium) Run(evm *EVM, contract *Contract) ([]byte, error) { return runBn256ScalarMul(contract.Input) } @@ -773,7 +733,7 @@ func (c *bn256PairingIstanbul) RequiredGas(input []byte) uint64 { return params.Bn256PairingBaseGasIstanbul + uint64(len(input)/192)*params.Bn256PairingPerPointGasIstanbul } -func (c *bn256PairingIstanbul) Run(evm *EVM, contract *Contract, readonly bool) ([]byte, error) { +func (c *bn256PairingIstanbul) Run(evm *EVM, contract *Contract) ([]byte, error) { return runBn256Pairing(contract.Input) } @@ -792,7 +752,7 @@ func (c *bn256PairingByzantium) RequiredGas(input []byte) uint64 { return params.Bn256PairingBaseGasByzantium + uint64(len(input)/192)*params.Bn256PairingPerPointGasByzantium } -func (c *bn256PairingByzantium) Run(evm *EVM, contract *Contract, readonly bool) ([]byte, error) { +func (c *bn256PairingByzantium) Run(evm *EVM, contract *Contract) ([]byte, error) { return runBn256Pairing(contract.Input) } @@ -824,7 +784,7 @@ var ( errBlake2FInvalidFinalFlag = errors.New("invalid final flag") ) -func (c *blake2F) Run(evm *EVM, contract *Contract, readonly bool) ([]byte, error) { +func (c *blake2F) Run(evm *EVM, contract *Contract) ([]byte, error) { // Make sure the input is valid (correct length and final flag) if len(contract.Input) != blake2FInputLength { return nil, errBlake2FInvalidInputLength @@ -884,7 +844,7 @@ func (c *bls12381G1Add) RequiredGas(input []byte) uint64 { return params.Bls12381G1AddGas } -func (c *bls12381G1Add) Run(evm *EVM, contract *Contract, readonly bool) ([]byte, error) { +func (c *bls12381G1Add) Run(evm *EVM, contract *Contract) ([]byte, error) { // 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). @@ -940,7 +900,7 @@ func (c *bls12381G1MultiExp) RequiredGas(input []byte) uint64 { return (uint64(k) * params.Bls12381G1MulGas * discount) / 1000 } -func (c *bls12381G1MultiExp) Run(evm *EVM, contract *Contract, readonly bool) ([]byte, error) { +func (c *bls12381G1MultiExp) Run(evm *EVM, contract *Contract) ([]byte, error) { // 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). @@ -992,7 +952,7 @@ func (c *bls12381G2Add) RequiredGas(input []byte) uint64 { return params.Bls12381G2AddGas } -func (c *bls12381G2Add) Run(evm *EVM, contract *Contract, readonly bool) ([]byte, error) { +func (c *bls12381G2Add) Run(evm *EVM, contract *Contract) ([]byte, error) { // 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). @@ -1049,7 +1009,7 @@ func (c *bls12381G2MultiExp) RequiredGas(input []byte) uint64 { return (uint64(k) * params.Bls12381G2MulGas * discount) / 1000 } -func (c *bls12381G2MultiExp) Run(evm *EVM, contract *Contract, readonly bool) ([]byte, error) { +func (c *bls12381G2MultiExp) Run(evm *EVM, contract *Contract) ([]byte, error) { // 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). @@ -1101,7 +1061,7 @@ func (c *bls12381Pairing) RequiredGas(input []byte) uint64 { return params.Bls12381PairingBaseGas + uint64(len(input)/384)*params.Bls12381PairingPerPairGas } -func (c *bls12381Pairing) Run(evm *EVM, contract *Contract, readonly bool) ([]byte, error) { +func (c *bls12381Pairing) Run(evm *EVM, contract *Contract) ([]byte, error) { // 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 @@ -1259,7 +1219,7 @@ func (c *bls12381MapG1) RequiredGas(input []byte) uint64 { return params.Bls12381MapG1Gas } -func (c *bls12381MapG1) Run(evm *EVM, contract *Contract, readonly bool) ([]byte, error) { +func (c *bls12381MapG1) Run(evm *EVM, contract *Contract) ([]byte, error) { // 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. @@ -1294,7 +1254,7 @@ func (c *bls12381MapG2) RequiredGas(input []byte) uint64 { return params.Bls12381MapG2Gas } -func (c *bls12381MapG2) Run(evm *EVM, contract *Contract, readonly bool) ([]byte, error) { +func (c *bls12381MapG2) Run(evm *EVM, contract *Contract) ([]byte, error) { // 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. @@ -1346,7 +1306,7 @@ var ( ) // Run executes the point evaluation precompile. -func (b *kzgPointEvaluation) Run(evm *EVM, contract *Contract, readonly bool) ([]byte, error) { +func (b *kzgPointEvaluation) Run(evm *EVM, contract *Contract) ([]byte, error) { if len(contract.Input) != blobVerifyInputLength { return nil, errBlobVerifyInvalidInputLength } @@ -1399,7 +1359,7 @@ func (c *p256Verify) RequiredGas(input []byte) uint64 { } // Run executes the precompiled contract with given 160 bytes of param, returning the output and the used gas -func (c *p256Verify) Run(evm *EVM, contract *Contract, readonly bool) ([]byte, error) { +func (c *p256Verify) Run(evm *EVM, contract *Contract) ([]byte, error) { const p256VerifyInputLength = 160 if len(contract.Input) != p256VerifyInputLength { return nil, nil diff --git a/core/vm/custom_contract.go b/core/vm/custom_contract.go index de644431fc60..2dfb22442f6c 100644 --- a/core/vm/custom_contract.go +++ b/core/vm/custom_contract.go @@ -20,11 +20,10 @@ func (ar AccountRef) Address() common.Address { return (common.Address)(ar) } // NewPrecompile returns a new instance of a precompiled contract environment for the execution of EVM. func NewPrecompile(caller, address common.Address, value *uint256.Int, gas uint64) *Contract { c := NewContract(caller, address, value, gas, nil) - c.isPrecompile = true return c } // IsPrecompile returns true if the contract is a precompiled contract environment func (c *Contract) IsPrecompile() bool { - return c.isPrecompile + return c.Precompile != nil } diff --git a/core/vm/evm.go b/core/vm/evm.go index 59eff902c134..5efb427a2feb 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -261,20 +261,22 @@ func (evm *EVM) Call(caller common.Address, addr common.Address, input []byte, g // The contract is a scoped environment for this execution context only. contract := NewContract(caller, addr, value, gas, evm.jumpDests) + contract.IsSystemCall = isSystemCall(caller) if isPrecompile { - ret, gas, err = evm.RunPrecompiledContract(p, contract, input, false) + contract.SetPrecompile(p) } else { // Initialise a new contract and set the code that is to be used by the EVM. code := evm.resolveCode(addr) if len(code) == 0 { - ret, err = nil, nil // gas is unchanged + return nil, gas, nil // gas is unchanged } else { - contract.IsSystemCall = isSystemCall(caller) contract.SetCallCode(evm.resolveCodeHash(addr), code) - ret, err = evm.interpreter.Run(contract, input, false) - gas = contract.Gas } } + + ret, err = evm.interpreter.Run(contract, input, false) + gas = contract.Gas + // When an error was returned by the EVM or when setting the creation code // above we revert to the snapshot and consume any gas remaining. Additionally, // when we're in homestead this also counts for code storage gas errors. @@ -328,14 +330,14 @@ func (evm *EVM) CallCode(caller common.Address, addr common.Address, input []byt // It is allowed to call precompiles, even via delegatecall contract := NewContract(caller, caller, value, gas, evm.jumpDests) if p, isPrecompile := evm.Precompile(addr); isPrecompile { - ret, gas, err = evm.RunPrecompiledContract(p, contract, input, false) + contract.SetPrecompile(p) } 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. contract.SetCallCode(evm.resolveCodeHash(addr), evm.resolveCode(addr)) - ret, err = evm.interpreter.Run(contract, input, false) - gas = contract.Gas } + ret, err = evm.interpreter.Run(contract, input, false) + gas = contract.Gas if err != nil { evm.StateDB.RevertToSnapshot(snapshot) if err != ErrExecutionReverted { @@ -374,15 +376,15 @@ func (evm *EVM) DelegateCall(originCaller common.Address, caller common.Address, // It is allowed to call precompiles, even via delegatecall contract := NewContract(originCaller, caller, value, gas, evm.jumpDests) if p, isPrecompile := evm.Precompile(addr); isPrecompile { - ret, gas, err = evm.RunPrecompiledContract(p, contract, input, false) + contract.SetPrecompile(p) } else { // Initialise a new contract and make initialise the delegate values // // Note: The value refers to the original value from the parent call. contract.SetCallCode(evm.resolveCodeHash(addr), evm.resolveCode(addr)) - ret, err = evm.interpreter.Run(contract, input, false) - gas = contract.Gas } + ret, err = evm.interpreter.Run(contract, input, false) + gas = contract.Gas if err != nil { evm.StateDB.RevertToSnapshot(snapshot) if err != ErrExecutionReverted { @@ -429,18 +431,18 @@ func (evm *EVM) StaticCall(caller common.Address, addr common.Address, input []b contract := NewContract(caller, addr, new(uint256.Int), gas, evm.jumpDests) if p, isPrecompile := evm.Precompile(addr); isPrecompile { - ret, gas, err = evm.RunPrecompiledContract(p, contract, input, true) + contract.SetPrecompile(p) } 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. contract.SetCallCode(evm.resolveCodeHash(addr), evm.resolveCode(addr)) - - // When an error was returned by the EVM or when setting the creation code - // above we revert to the snapshot and consume any gas remaining. Additionally - // when we're in Homestead this also counts for code storage gas errors. - ret, err = evm.interpreter.Run(contract, input, true) - gas = contract.Gas } + + // When an error was returned by the EVM or when setting the creation code + // above we revert to the snapshot and consume any gas remaining. Additionally + // when we're in Homestead this also counts for code storage gas errors. + ret, err = evm.interpreter.Run(contract, input, true) + gas = contract.Gas if err != nil { evm.StateDB.RevertToSnapshot(snapshot) if err != ErrExecutionReverted { diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go index 289c94ce3721..3bf4f9fdf1ce 100644 --- a/core/vm/interpreter.go +++ b/core/vm/interpreter.go @@ -179,6 +179,19 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) ( // as every returning call will return new data anyway. in.returnData = nil + if contract.Precompile != nil { + contract.Input = input + + // consume static gas + gasCost := contract.Precompile.RequiredGas(input) + if !contract.UseGas(gasCost, in.evm.Config.Tracer, tracing.GasChangeCallPrecompiledContract) { + return nil, ErrOutOfGas + } + + // execute precompile + return contract.Precompile.Run(in.evm, contract) + } + // Don't bother with the execution if there's no code. if len(contract.Code) == 0 { return nil, nil