refactor: stateful precompiles

This commit is contained in:
MiniFrenchBread 2025-03-25 05:39:15 +08:00
parent 6f3bb41e10
commit eca91aa939
9 changed files with 120 additions and 106 deletions

View file

@ -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
}

View file

@ -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)
}

View file

@ -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))

View file

@ -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.

View file

@ -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
}

View file

@ -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

View file

@ -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
}

View file

@ -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{},
}

View file

@ -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)