diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 000000000000..2115785efd25
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,54 @@
+
+
+# Changelog
+
+## Unreleased
+
+### Improvements
+
+- [#28](https://github.com/evmos/go-ethereum/pull/28) OpCodesHooks for CREATE and CALL opcodes
+- [#26](https://github.com/evmos/go-ethereum/pull/26) PreExecuteCallback for Call opcodes
+- [#23](https://github.com/evmos/go-ethereum/pull/23) Remove `IsStateful` function from the `PrecompiledContract` interface, as this remains unused.
+- [#8](https://github.com/evmos/go-ethereum/pull/8) Add `Address` function to `PrecompiledContract` interface.
+- [#7](https://github.com/evmos/go-ethereum/pull/7) Implement custom active precompiles for the EVM.
+- [#6](https://github.com/evmos/go-ethereum/pull/6) Refactor `Stack` implementation
+- [#3](https://github.com/evmos/go-ethereum/pull/3) Move the `JumpTable` defaults to a separate function.
+- [#2](https://github.com/evmos/go-ethereum/pull/2) Define `Interpreter` interface for the EVM.
+
+### State Machine Breaking
+
+- [#24](https://github.com/evmos/go-ethereum/pull/24) Set `callcode` to use `readOnly` mode for precompiled calls.
+- [#10](https://github.com/evmos/go-ethereum/pull/10) Support stateful precompiled contracts.
diff --git a/core/state_transition.go b/core/state_transition.go
index 9c4f76d1c585..f2be287b3d59 100644
--- a/core/state_transition.go
+++ b/core/state_transition.go
@@ -421,7 +421,7 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) {
// Execute the preparatory steps for state transition which includes:
// - prepare accessList(post-berlin)
// - reset transient storage(eip 1153)
- st.state.Prepare(rules, msg.From, st.evm.Context.Coinbase, msg.To, vm.ActivePrecompiles(rules), msg.AccessList)
+ st.state.Prepare(rules, msg.From, st.evm.Context.Coinbase, msg.To, st.evm.ActivePrecompiles(rules), msg.AccessList)
var (
ret []byte
diff --git a/core/vm/common.go b/core/vm/common.go
index 90ba4a4ad15b..a83fe604a541 100644
--- a/core/vm/common.go
+++ b/core/vm/common.go
@@ -22,6 +22,12 @@ import (
"github.com/holiman/uint256"
)
+// CalcMemSize64 calculates the required memory size, and returns
+// the size and whether the result overflowed uint64
+func CalcMemSize64(off, l *uint256.Int) (uint64, bool) {
+ return calcMemSize64(off, l)
+}
+
// calcMemSize64 calculates the required memory size, and returns
// the size and whether the result overflowed uint64
func calcMemSize64(off, l *uint256.Int) (uint64, bool) {
diff --git a/core/vm/contract.go b/core/vm/contract.go
index 16b669ebca27..a1bf6a28f1e1 100644
--- a/core/vm/contract.go
+++ b/core/vm/contract.go
@@ -23,6 +23,7 @@ import (
// ContractRef is a reference to the contract's backing object
type ContractRef interface {
+ // Address returns the contract's address
Address() common.Address
}
@@ -56,12 +57,13 @@ type Contract struct {
CodeAddr *common.Address
Input []byte
- Gas uint64
- value *uint256.Int
+ Gas uint64
+ value *uint256.Int
+ isPrecompile bool
}
// NewContract returns a new contract environment for the execution of EVM.
-func NewContract(caller ContractRef, object ContractRef, value *uint256.Int, gas uint64) *Contract {
+func NewContract(caller, object ContractRef, value *uint256.Int, gas uint64) *Contract {
c := &Contract{CallerAddress: caller.Address(), caller: caller, self: object}
if parent, ok := caller.(*Contract); ok {
@@ -80,7 +82,34 @@ func NewContract(caller ContractRef, object ContractRef, value *uint256.Int, gas
return c
}
+// NewPrecompile returns a new instance of a precompiled contract environment for the execution of EVM.
+func NewPrecompile(caller, object ContractRef, value *uint256.Int, gas uint64) *Contract {
+ c := &Contract{
+ CallerAddress: caller.Address(),
+ caller: caller,
+ self: object,
+ isPrecompile: true,
+ }
+
+ // Gas should be a pointer so it can safely be reduced through the run
+ // This pointer will be off the state transition
+ c.Gas = gas
+ // ensures a value is set
+ c.value = value
+
+ return c
+}
+
+// IsPrecompile returns true if the contract is a precompiled contract environment
+func (c Contract) IsPrecompile() bool {
+ return c.isPrecompile
+}
+
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.
@@ -97,6 +126,10 @@ 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)
@@ -130,6 +163,9 @@ func (c *Contract) isCode(udest uint64) bool {
// AsDelegate sets the contract to be a delegate call and returns the current
// contract (for chaining calls)
func (c *Contract) AsDelegate() *Contract {
+ if c.isPrecompile {
+ return c
+ }
// NOTE: caller must, at all times be a contract. It should never happen
// that caller is something other than a Contract.
parent := c.caller.(*Contract)
@@ -178,6 +214,10 @@ func (c *Contract) Value() *uint256.Int {
// SetCallCode sets the code of the contract and address of the backing data
// object
func (c *Contract) SetCallCode(addr *common.Address, hash common.Hash, code []byte) {
+ if c.isPrecompile {
+ return
+ }
+
c.Code = code
c.CodeHash = hash
c.CodeAddr = addr
@@ -186,6 +226,10 @@ func (c *Contract) SetCallCode(addr *common.Address, hash common.Hash, code []by
// SetCodeOptionalHash can be used to provide code, but it's optional to provide hash.
// In case hash is not provided, the jumpdest analysis will not be saved to the parent context
func (c *Contract) SetCodeOptionalHash(addr *common.Address, codeAndHash *codeAndHash) {
+ if c.isPrecompile {
+ return
+ }
+
c.Code = codeAndHash.code
c.CodeHash = codeAndHash.hash
c.CodeAddr = addr
diff --git a/core/vm/contracts.go b/core/vm/contracts.go
index 33a867654e71..2e3963d0666b 100644
--- a/core/vm/contracts.go
+++ b/core/vm/contracts.go
@@ -17,6 +17,7 @@
package vm
import (
+ "bytes"
"crypto/sha256"
"encoding/binary"
"errors"
@@ -31,6 +32,7 @@ import (
"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"
)
@@ -38,117 +40,157 @@ import (
// 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
+ ContractRef
+ // RequiredPrice calculates the contract gas used
+ RequiredGas(input []byte) uint64
+ // Run runs the precompiled contract
+ Run(evm *EVM, contract *Contract, readonly bool) ([]byte, error)
}
// PrecompiledContractsHomestead contains the default set of pre-compiled Ethereum
// contracts used in the Frontier and Homestead releases.
var PrecompiledContractsHomestead = map[common.Address]PrecompiledContract{
- common.BytesToAddress([]byte{1}): &ecrecover{},
- common.BytesToAddress([]byte{2}): &sha256hash{},
- common.BytesToAddress([]byte{3}): &ripemd160hash{},
- common.BytesToAddress([]byte{4}): &dataCopy{},
+ 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 = map[common.Address]PrecompiledContract{
- common.BytesToAddress([]byte{1}): &ecrecover{},
- common.BytesToAddress([]byte{2}): &sha256hash{},
- common.BytesToAddress([]byte{3}): &ripemd160hash{},
- common.BytesToAddress([]byte{4}): &dataCopy{},
- common.BytesToAddress([]byte{5}): &bigModExp{eip2565: false},
- common.BytesToAddress([]byte{6}): &bn256AddByzantium{},
- common.BytesToAddress([]byte{7}): &bn256ScalarMulByzantium{},
- common.BytesToAddress([]byte{8}): &bn256PairingByzantium{},
+ ecrecover{}.Address(): &ecrecover{},
+ sha256hash{}.Address(): &sha256hash{},
+ ripemd160hash{}.Address(): &ripemd160hash{},
+ dataCopy{}.Address(): &dataCopy{},
+ bigModExp{}.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 = map[common.Address]PrecompiledContract{
- common.BytesToAddress([]byte{1}): &ecrecover{},
- common.BytesToAddress([]byte{2}): &sha256hash{},
- common.BytesToAddress([]byte{3}): &ripemd160hash{},
- common.BytesToAddress([]byte{4}): &dataCopy{},
- common.BytesToAddress([]byte{5}): &bigModExp{eip2565: false},
- common.BytesToAddress([]byte{6}): &bn256AddIstanbul{},
- common.BytesToAddress([]byte{7}): &bn256ScalarMulIstanbul{},
- common.BytesToAddress([]byte{8}): &bn256PairingIstanbul{},
- common.BytesToAddress([]byte{9}): &blake2F{},
+ ecrecover{}.Address(): &ecrecover{},
+ sha256hash{}.Address(): &sha256hash{},
+ ripemd160hash{}.Address(): &ripemd160hash{},
+ dataCopy{}.Address(): &dataCopy{},
+ bigModExp{}.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 = map[common.Address]PrecompiledContract{
- common.BytesToAddress([]byte{1}): &ecrecover{},
- common.BytesToAddress([]byte{2}): &sha256hash{},
- common.BytesToAddress([]byte{3}): &ripemd160hash{},
- common.BytesToAddress([]byte{4}): &dataCopy{},
- common.BytesToAddress([]byte{5}): &bigModExp{eip2565: true},
- common.BytesToAddress([]byte{6}): &bn256AddIstanbul{},
- common.BytesToAddress([]byte{7}): &bn256ScalarMulIstanbul{},
- common.BytesToAddress([]byte{8}): &bn256PairingIstanbul{},
- common.BytesToAddress([]byte{9}): &blake2F{},
+ ecrecover{}.Address(): &ecrecover{},
+ sha256hash{}.Address(): &sha256hash{},
+ ripemd160hash{}.Address(): &ripemd160hash{},
+ dataCopy{}.Address(): &dataCopy{},
+ bigModExp{}.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 = map[common.Address]PrecompiledContract{
- common.BytesToAddress([]byte{1}): &ecrecover{},
- common.BytesToAddress([]byte{2}): &sha256hash{},
- common.BytesToAddress([]byte{3}): &ripemd160hash{},
- common.BytesToAddress([]byte{4}): &dataCopy{},
- common.BytesToAddress([]byte{5}): &bigModExp{eip2565: true},
- common.BytesToAddress([]byte{6}): &bn256AddIstanbul{},
- common.BytesToAddress([]byte{7}): &bn256ScalarMulIstanbul{},
- common.BytesToAddress([]byte{8}): &bn256PairingIstanbul{},
- common.BytesToAddress([]byte{9}): &blake2F{},
- common.BytesToAddress([]byte{0x0a}): &kzgPointEvaluation{},
+ ecrecover{}.Address(): &ecrecover{},
+ sha256hash{}.Address(): &sha256hash{},
+ ripemd160hash{}.Address(): &ripemd160hash{},
+ dataCopy{}.Address(): &dataCopy{},
+ bigModExp{}.Address(): &bigModExp{eip2565: true},
+ bn256AddIstanbul{}.Address(): &bn256AddIstanbul{},
+ bn256ScalarMulIstanbul{}.Address(): &bn256ScalarMulIstanbul{},
+ bn256PairingIstanbul{}.Address(): &bn256PairingIstanbul{},
+ blake2F{}.Address(): &blake2F{},
+ kzgPointEvaluation{}.Address(): &kzgPointEvaluation{},
}
// PrecompiledContractsBLS contains the set of pre-compiled Ethereum
// contracts specified in EIP-2537. These are exported for testing purposes.
var PrecompiledContractsBLS = map[common.Address]PrecompiledContract{
- common.BytesToAddress([]byte{10}): &bls12381G1Add{},
- common.BytesToAddress([]byte{11}): &bls12381G1Mul{},
- common.BytesToAddress([]byte{12}): &bls12381G1MultiExp{},
- common.BytesToAddress([]byte{13}): &bls12381G2Add{},
- common.BytesToAddress([]byte{14}): &bls12381G2Mul{},
- common.BytesToAddress([]byte{15}): &bls12381G2MultiExp{},
- common.BytesToAddress([]byte{16}): &bls12381Pairing{},
- common.BytesToAddress([]byte{17}): &bls12381MapG1{},
- common.BytesToAddress([]byte{18}): &bls12381MapG2{},
+ bls12381G1Add{}.Address(): &bls12381G1Add{},
+ bls12381G1Mul{}.Address(): &bls12381G1Mul{},
+ bls12381G1MultiExp{}.Address(): &bls12381G1MultiExp{},
+ bls12381G2Add{}.Address(): &bls12381G2Add{},
+ bls12381G2Mul{}.Address(): &bls12381G2Mul{},
+ bls12381G2MultiExp{}.Address(): &bls12381G2MultiExp{},
+ bls12381Pairing{}.Address(): &bls12381Pairing{},
+ bls12381MapG1{}.Address(): &bls12381MapG1{},
+ bls12381MapG2{}.Address(): &bls12381MapG2{},
}
var (
- PrecompiledAddressesCancun []common.Address
- PrecompiledAddressesBerlin []common.Address
- PrecompiledAddressesIstanbul []common.Address
- PrecompiledAddressesByzantium []common.Address
- PrecompiledAddressesHomestead []common.Address
-)
-
-func init() {
- for k := range PrecompiledContractsHomestead {
- PrecompiledAddressesHomestead = append(PrecompiledAddressesHomestead, k)
- }
- for k := range PrecompiledContractsByzantium {
- PrecompiledAddressesByzantium = append(PrecompiledAddressesByzantium, k)
- }
- for k := range PrecompiledContractsIstanbul {
- PrecompiledAddressesIstanbul = append(PrecompiledAddressesIstanbul, k)
+ // PrecompiledAddressesBerlin defines the default set of pre-compiled
+ // Ethereum contract addresses used in the Berlin release.
+ PrecompiledAddressesBerlin = []common.Address{
+ ecrecover{}.Address(),
+ sha256hash{}.Address(),
+ ripemd160hash{}.Address(),
+ dataCopy{}.Address(),
+ bigModExp{}.Address(),
+ bn256AddIstanbul{}.Address(),
+ bn256ScalarMulIstanbul{}.Address(),
+ bn256PairingIstanbul{}.Address(),
+ blake2F{}.Address(),
+ }
+ // PrecompiledAddressesIstanbul defines the default set of pre-compiled
+ // Ethereum contract addresses used in the Istanbul release.
+ PrecompiledAddressesIstanbul = []common.Address{
+ ecrecover{}.Address(),
+ sha256hash{}.Address(),
+ ripemd160hash{}.Address(),
+ dataCopy{}.Address(),
+ bigModExp{}.Address(),
+ bn256AddIstanbul{}.Address(),
+ bn256ScalarMulIstanbul{}.Address(),
+ bn256PairingIstanbul{}.Address(),
+ blake2F{}.Address(),
+ }
+ // PrecompiledAddressesByzantium defines the default set of pre-compiled
+ // Ethereum contract addresses used in the Byzantium release.
+ PrecompiledAddressesByzantium = []common.Address{
+ ecrecover{}.Address(),
+ sha256hash{}.Address(),
+ ripemd160hash{}.Address(),
+ dataCopy{}.Address(),
+ bigModExp{}.Address(),
+ bn256AddByzantium{}.Address(),
+ bn256ScalarMulByzantium{}.Address(),
+ bn256PairingByzantium{}.Address(),
+ }
+ // PrecompiledAddressesHomestead defines the default set of pre-compiled
+ // Ethereum contract addresses used in the Homestead release.
+ PrecompiledAddressesHomestead = []common.Address{
+ ecrecover{}.Address(),
+ sha256hash{}.Address(),
+ ripemd160hash{}.Address(),
+ dataCopy{}.Address(),
+ }
+ // PrecompiledAddressesCancun defines the default set of pre-compiled
+ // Ethereum contract addresses used in the Cancun release.
+ PrecompiledAddressesCancun = []common.Address{
+ ecrecover{}.Address(),
+ sha256hash{}.Address(),
+ ripemd160hash{}.Address(),
+ dataCopy{}.Address(),
+ bigModExp{}.Address(),
+ bn256AddIstanbul{}.Address(),
+ bn256ScalarMulIstanbul{}.Address(),
+ bn256PairingIstanbul{}.Address(),
+ blake2F{}.Address(),
+ kzgPointEvaluation{}.Address(),
}
- for k := range PrecompiledContractsBerlin {
- PrecompiledAddressesBerlin = append(PrecompiledAddressesBerlin, k)
- }
- for k := range PrecompiledContractsCancun {
- PrecompiledAddressesCancun = append(PrecompiledAddressesCancun, k)
- }
-}
+)
-// ActivePrecompiles returns the precompiles enabled with the current configuration.
-func ActivePrecompiles(rules params.Rules) []common.Address {
+// DefaultActivePrecompiles returns the set of precompiles enabled with the default configuration.
+func DefaultActivePrecompiles(rules params.Rules) []common.Address {
switch {
case rules.IsCancun:
return PrecompiledAddressesCancun
@@ -163,50 +205,165 @@ func ActivePrecompiles(rules params.Rules) []common.Address {
}
}
+// DefaultPrecompiles define the mapping of address and precompiles from the default configuration
+func DefaultPrecompiles(rules params.Rules) (precompiles map[common.Address]PrecompiledContract) {
+ switch {
+ case rules.IsCancun:
+ precompiles = PrecompiledContractsCancun
+ case rules.IsBerlin:
+ precompiles = PrecompiledContractsBerlin
+ case rules.IsIstanbul:
+ precompiles = PrecompiledContractsIstanbul
+ case rules.IsByzantium:
+ precompiles = PrecompiledContractsByzantium
+ default:
+ precompiles = PrecompiledContractsHomestead
+ }
+
+ return precompiles
+}
+
+// ActivePrecompiles returns the precompiles enabled with the current configuration.
+//
+// NOTE: The rules argument is ignored as the active precompiles can be set via the WithPrecompiles
+// method according to the chain rules from the current block context.
+func (evm *EVM) ActivePrecompiles(_ params.Rules) []common.Address {
+ return evm.activePrecompiles
+}
+
+// Precompile returns a precompiled contract for the given address. This
+// function returns false if the address is not a registered precompile.
+func (evm *EVM) Precompile(addr common.Address) (PrecompiledContract, bool) {
+ p, ok := evm.precompiles[addr]
+ return p, ok
+}
+
+// WithPrecompiles sets the precompiled contracts and the slice of actives precompiles.
+// IMPORTANT: This function does NOT validate the precompiles provided to the EVM. The caller should
+// use the ValidatePrecompiles function for this purpose prior to calling WithPrecompiles.
+func (evm *EVM) WithPrecompiles(
+ precompiles map[common.Address]PrecompiledContract,
+ activePrecompiles []common.Address,
+) {
+ evm.precompiles = precompiles
+ evm.activePrecompiles = activePrecompiles
+}
+
+// ValidatePrecompiles validates the precompile map against the active
+// precompile slice.
+// It returns an error if the precompiled contract map has a different length
+// than the slice of active contract addresses. This function also checks for
+// duplicates, invalid addresses and empty precompile contract instances.
+func ValidatePrecompiles(
+ precompiles map[common.Address]PrecompiledContract,
+ activePrecompiles []common.Address,
+) error {
+ if len(precompiles) != len(activePrecompiles) {
+ return fmt.Errorf("precompiles length mismatch (expected %d, got %d)", len(precompiles), len(activePrecompiles))
+ }
+
+ dupActivePrecompiles := make(map[common.Address]bool)
+
+ for _, addr := range activePrecompiles {
+ if dupActivePrecompiles[addr] {
+ return fmt.Errorf("duplicate active precompile: %s", addr)
+ }
+
+ precompile, ok := precompiles[addr]
+ if !ok {
+ return fmt.Errorf("active precompile address doesn't exist in precompiles map: %s", addr)
+ }
+
+ if precompile == nil {
+ return fmt.Errorf("precompile contract cannot be nil: %s", addr)
+ }
+
+ if bytes.Equal(addr.Bytes(), common.Address{}.Bytes()) {
+ return fmt.Errorf("precompile cannot be the zero address: %s", addr)
+ }
+
+ dupActivePrecompiles[addr] = true
+ }
+
+ return nil
+}
+
// RunPrecompiledContract runs and evaluates the output of a precompiled contract.
// It returns
// - the returned bytes,
// - the _remaining_ gas,
// - any error that occurred
-func RunPrecompiledContract(p PrecompiledContract, input []byte, suppliedGas uint64) (ret []byte, remainingGas uint64, err error) {
+func (evm *EVM) RunPrecompiledContract(
+ p PrecompiledContract,
+ caller ContractRef,
+ input []byte,
+ suppliedGas uint64,
+ value *uint256.Int,
+ readOnly bool,
+) (ret []byte, remainingGas uint64, err error) {
+ return runPrecompiledContract(evm, p, caller, input, suppliedGas, value, readOnly)
+}
+
+func runPrecompiledContract(
+ evm *EVM,
+ p PrecompiledContract,
+ caller ContractRef,
+ input []byte,
+ suppliedGas uint64,
+ value *uint256.Int,
+ readOnly bool,
+) (ret []byte, remainingGas uint64, err error) {
+ addrCopy := p.Address()
+ inputCopy := make([]byte, len(input))
+ copy(inputCopy, input)
+
+ contract := NewPrecompile(caller, AccountRef(addrCopy), value, suppliedGas)
+ contract.Input = inputCopy
+
gasCost := p.RequiredGas(input)
- if suppliedGas < gasCost {
- return nil, 0, ErrOutOfGas
+ if !contract.UseGas(gasCost) {
+ return nil, contract.Gas, ErrOutOfGas
}
- 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.
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{1})
+}
+
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)
+ contract.Input = common.RightPadBytes(contract.Input, ecRecoverInputLength)
// "input" is (hash, v, r, s), each 32 bytes
// but for ecrecover we want (r, s, v)
- r := new(big.Int).SetBytes(input[64:96])
- s := new(big.Int).SetBytes(input[96:128])
- v := input[63] - 27
+ r := new(big.Int).SetBytes(contract.Input[64:96])
+ s := new(big.Int).SetBytes(contract.Input[96:128])
+ v := contract.Input[63] - 27
// tighter sig s values input homestead only apply to tx sigs
- if !allZero(input[32:63]) || !crypto.ValidateSignatureValues(v, r, s, false) {
+ if !allZero(contract.Input[32:63]) || !crypto.ValidateSignatureValues(v, r, s, false) {
return nil, nil
}
// We must make sure not to modify the 'input', so placing the 'v' along with
// the signature needs to be done on a new allocation
sig := make([]byte, 65)
- copy(sig, input[64:128])
+ copy(sig, contract.Input[64:128])
sig[64] = v
// v needs to be at the end for libsecp256k1
- pubKey, err := crypto.Ecrecover(input[:32], sig)
+ pubKey, err := crypto.Ecrecover(contract.Input[:32], sig)
// make sure the public key is a valid one
if err != nil {
return nil, nil
@@ -219,6 +376,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{2})
+}
+
// 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
@@ -226,14 +389,21 @@ type sha256hash struct{}
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
}
// 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{3})
+}
+
// 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
@@ -241,15 +411,22 @@ type ripemd160hash struct{}
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
}
// 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{4})
+}
+
// 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
@@ -257,8 +434,9 @@ type dataCopy struct{}
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.
@@ -311,6 +489,12 @@ func modexpMultComplexity(x *big.Int) *big.Int {
return x
}
+// 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{5})
+}
+
// RequiredGas returns the gas required to execute the pre-compiled contract.
func (c *bigModExp) RequiredGas(input []byte) uint64 {
var (
@@ -382,16 +566,16 @@ 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) {
var (
- baseLen = new(big.Int).SetBytes(getData(input, 0, 32)).Uint64()
- expLen = new(big.Int).SetBytes(getData(input, 32, 32)).Uint64()
- modLen = new(big.Int).SetBytes(getData(input, 64, 32)).Uint64()
+ baseLen = new(big.Int).SetBytes(getData(contract.Input, 0, 32)).Uint64()
+ expLen = new(big.Int).SetBytes(getData(contract.Input, 32, 32)).Uint64()
+ modLen = new(big.Int).SetBytes(getData(contract.Input, 64, 32)).Uint64()
)
- if len(input) > 96 {
- input = input[96:]
+ if len(contract.Input) > 96 {
+ contract.Input = contract.Input[96:]
} else {
- input = input[:0]
+ contract.Input = contract.Input[:0]
}
// Handle a special case when both the base and mod length is zero
if baseLen == 0 && modLen == 0 {
@@ -399,9 +583,9 @@ func (c *bigModExp) Run(input []byte) ([]byte, error) {
}
// Retrieve the operands and execute the exponentiation
var (
- base = new(big.Int).SetBytes(getData(input, 0, baseLen))
- exp = new(big.Int).SetBytes(getData(input, baseLen, expLen))
- mod = new(big.Int).SetBytes(getData(input, baseLen+expLen, modLen))
+ base = new(big.Int).SetBytes(getData(contract.Input, 0, baseLen))
+ exp = new(big.Int).SetBytes(getData(contract.Input, baseLen, expLen))
+ mod = new(big.Int).SetBytes(getData(contract.Input, baseLen+expLen, modLen))
v []byte
)
switch {
@@ -457,26 +641,38 @@ 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{6})
+}
+
// RequiredGas returns the gas required to execute the pre-compiled contract.
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
// 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{6})
+}
+
// RequiredGas returns the gas required to execute the pre-compiled contract.
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
@@ -495,26 +691,38 @@ 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{7})
+}
+
// RequiredGas returns the gas required to execute the pre-compiled contract.
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
// 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{7})
+}
+
// RequiredGas returns the gas required to execute the pre-compiled contract.
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 (
@@ -563,30 +771,48 @@ 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{8})
+}
+
// 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
}
-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
// 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{8})
+}
+
// 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
}
-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{}
+// 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{9})
+}
+
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.
@@ -607,18 +833,18 @@ 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) {
// Make sure the input is valid (correct length and final flag)
- if len(input) != blake2FInputLength {
+ if len(contract.Input) != blake2FInputLength {
return nil, errBlake2FInvalidInputLength
}
- if input[212] != blake2FNonFinalBlockBytes && input[212] != blake2FFinalBlockBytes {
+ if contract.Input[212] != blake2FNonFinalBlockBytes && contract.Input[212] != blake2FFinalBlockBytes {
return nil, errBlake2FInvalidFinalFlag
}
// Parse the input into the Blake2b call parameters
var (
- rounds = binary.BigEndian.Uint32(input[0:4])
- final = input[212] == blake2FFinalBlockBytes
+ rounds = binary.BigEndian.Uint32(contract.Input[0:4])
+ final = contract.Input[212] == blake2FFinalBlockBytes
h [8]uint64
m [16]uint64
@@ -626,14 +852,14 @@ func (c *blake2F) Run(input []byte) ([]byte, error) {
)
for i := 0; i < 8; i++ {
offset := 4 + i*8
- h[i] = binary.LittleEndian.Uint64(input[offset : offset+8])
+ h[i] = binary.LittleEndian.Uint64(contract.Input[offset : offset+8])
}
for i := 0; i < 16; i++ {
offset := 68 + i*8
- m[i] = binary.LittleEndian.Uint64(input[offset : offset+8])
+ m[i] = binary.LittleEndian.Uint64(contract.Input[offset : offset+8])
}
- t[0] = binary.LittleEndian.Uint64(input[196:204])
- t[1] = binary.LittleEndian.Uint64(input[204:212])
+ t[0] = binary.LittleEndian.Uint64(contract.Input[196:204])
+ t[1] = binary.LittleEndian.Uint64(contract.Input[204:212])
// Execute the compression function, extract and return the result
blake2b.F(&h, m, t, final, rounds)
@@ -656,16 +882,22 @@ 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{10})
+}
+
// RequiredGas returns the gas required to execute the pre-compiled contract.
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) {
// 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).
- if len(input) != 256 {
+ if len(contract.Input) != 256 {
return nil, errBLS12381InvalidInputLength
}
var err error
@@ -675,11 +907,11 @@ func (c *bls12381G1Add) Run(input []byte) ([]byte, error) {
g := bls12381.NewG1()
// Decode G1 point p_0
- if p0, err = g.DecodePoint(input[:128]); err != nil {
+ if p0, err = g.DecodePoint(contract.Input[:128]); err != nil {
return nil, err
}
// Decode G1 point p_1
- if p1, err = g.DecodePoint(input[128:]); err != nil {
+ if p1, err = g.DecodePoint(contract.Input[128:]); err != nil {
return nil, err
}
@@ -694,16 +926,22 @@ func (c *bls12381G1Add) Run(input []byte) ([]byte, error) {
// bls12381G1Mul implements EIP-2537 G1Mul precompile.
type bls12381G1Mul struct{}
+// Address defines the precompiled contract address. This MUST match the address
+// set in the precompiled contract map.
+func (bls12381G1Mul) Address() common.Address {
+ return common.BytesToAddress([]byte{11})
+}
+
// RequiredGas returns the gas required to execute the pre-compiled contract.
func (c *bls12381G1Mul) RequiredGas(input []byte) uint64 {
return params.Bls12381G1MulGas
}
-func (c *bls12381G1Mul) Run(input []byte) ([]byte, error) {
+func (c *bls12381G1Mul) Run(evm *EVM, contract *Contract, readonly bool) ([]byte, error) {
// Implements EIP-2537 G1Mul precompile.
// > G1 multiplication call expects `160` bytes as an input that is interpreted as byte concatenation of encoding of G1 point (`128` bytes) and encoding of a scalar value (`32` bytes).
// > Output is an encoding of multiplication operation result - single G1 point (`128` bytes).
- if len(input) != 160 {
+ if len(contract.Input) != 160 {
return nil, errBLS12381InvalidInputLength
}
var err error
@@ -713,11 +951,11 @@ func (c *bls12381G1Mul) Run(input []byte) ([]byte, error) {
g := bls12381.NewG1()
// Decode G1 point
- if p0, err = g.DecodePoint(input[:128]); err != nil {
+ if p0, err = g.DecodePoint(contract.Input[:128]); err != nil {
return nil, err
}
// Decode scalar value
- e := new(big.Int).SetBytes(input[128:])
+ e := new(big.Int).SetBytes(contract.Input[128:])
// Compute r = e * p_0
r := g.New()
@@ -730,6 +968,12 @@ func (c *bls12381G1Mul) 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{12})
+}
+
// 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
@@ -749,12 +993,12 @@ 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) {
// 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).
- k := len(input) / 160
- if len(input) == 0 || len(input)%160 != 0 {
+ k := len(contract.Input) / 160
+ if len(contract.Input) == 0 || len(contract.Input)%160 != 0 {
return nil, errBLS12381InvalidInputLength
}
var err error
@@ -769,11 +1013,11 @@ func (c *bls12381G1MultiExp) Run(input []byte) ([]byte, error) {
off := 160 * i
t0, t1, t2 := off, off+128, off+160
// Decode G1 point
- if points[i], err = g.DecodePoint(input[t0:t1]); err != nil {
+ if points[i], err = g.DecodePoint(contract.Input[t0:t1]); err != nil {
return nil, err
}
// Decode scalar value
- scalars[i] = new(big.Int).SetBytes(input[t1:t2])
+ scalars[i] = new(big.Int).SetBytes(contract.Input[t1:t2])
}
// Compute r = e_0 * p_0 + e_1 * p_1 + ... + e_(k-1) * p_(k-1)
@@ -787,16 +1031,22 @@ 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{13})
+}
+
// RequiredGas returns the gas required to execute the pre-compiled contract.
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) {
// 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).
- if len(input) != 512 {
+ if len(contract.Input) != 512 {
return nil, errBLS12381InvalidInputLength
}
var err error
@@ -807,11 +1057,11 @@ func (c *bls12381G2Add) Run(input []byte) ([]byte, error) {
r := g.New()
// Decode G2 point p_0
- if p0, err = g.DecodePoint(input[:256]); err != nil {
+ if p0, err = g.DecodePoint(contract.Input[:256]); err != nil {
return nil, err
}
// Decode G2 point p_1
- if p1, err = g.DecodePoint(input[256:]); err != nil {
+ if p1, err = g.DecodePoint(contract.Input[256:]); err != nil {
return nil, err
}
@@ -825,16 +1075,22 @@ func (c *bls12381G2Add) Run(input []byte) ([]byte, error) {
// bls12381G2Mul implements EIP-2537 G2Mul precompile.
type bls12381G2Mul struct{}
+// Address defines the precompiled contract address. This MUST match the address
+// set in the precompiled contract map.
+func (bls12381G2Mul) Address() common.Address {
+ return common.BytesToAddress([]byte{14})
+}
+
// RequiredGas returns the gas required to execute the pre-compiled contract.
func (c *bls12381G2Mul) RequiredGas(input []byte) uint64 {
return params.Bls12381G2MulGas
}
-func (c *bls12381G2Mul) Run(input []byte) ([]byte, error) {
+func (c *bls12381G2Mul) Run(evm *EVM, contract *Contract, readonly bool) ([]byte, error) {
// Implements EIP-2537 G2MUL precompile logic.
// > G2 multiplication call expects `288` bytes as an input that is interpreted as byte concatenation of encoding of G2 point (`256` bytes) and encoding of a scalar value (`32` bytes).
// > Output is an encoding of multiplication operation result - single G2 point (`256` bytes).
- if len(input) != 288 {
+ if len(contract.Input) != 288 {
return nil, errBLS12381InvalidInputLength
}
var err error
@@ -844,11 +1100,11 @@ func (c *bls12381G2Mul) Run(input []byte) ([]byte, error) {
g := bls12381.NewG2()
// Decode G2 point
- if p0, err = g.DecodePoint(input[:256]); err != nil {
+ if p0, err = g.DecodePoint(contract.Input[:256]); err != nil {
return nil, err
}
// Decode scalar value
- e := new(big.Int).SetBytes(input[256:])
+ e := new(big.Int).SetBytes(contract.Input[256:])
// Compute r = e * p_0
r := g.New()
@@ -861,6 +1117,12 @@ func (c *bls12381G2Mul) 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{15})
+}
+
// 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
@@ -880,12 +1142,12 @@ 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) {
// 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).
- k := len(input) / 288
- if len(input) == 0 || len(input)%288 != 0 {
+ k := len(contract.Input) / 288
+ if len(contract.Input) == 0 || len(contract.Input)%288 != 0 {
return nil, errBLS12381InvalidInputLength
}
var err error
@@ -900,11 +1162,11 @@ func (c *bls12381G2MultiExp) Run(input []byte) ([]byte, error) {
off := 288 * i
t0, t1, t2 := off, off+256, off+288
// Decode G1 point
- if points[i], err = g.DecodePoint(input[t0:t1]); err != nil {
+ if points[i], err = g.DecodePoint(contract.Input[t0:t1]); err != nil {
return nil, err
}
// Decode scalar value
- scalars[i] = new(big.Int).SetBytes(input[t1:t2])
+ scalars[i] = new(big.Int).SetBytes(contract.Input[t1:t2])
}
// Compute r = e_0 * p_0 + e_1 * p_1 + ... + e_(k-1) * p_(k-1)
@@ -918,20 +1180,26 @@ 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{16})
+}
+
// 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
}
-func (c *bls12381Pairing) Run(input []byte) ([]byte, error) {
+func (c *bls12381Pairing) Run(evm *EVM, contract *Contract, readonly bool) ([]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
// > - `256` bytes of G2 point encoding
// > Output is a `32` bytes where last single byte is `0x01` if pairing result is equal to multiplicative identity in a pairing target field and `0x00` otherwise
// > (which is equivalent of Big Endian encoding of Solidity values `uint256(1)` and `uin256(0)` respectively).
- k := len(input) / 384
- if len(input) == 0 || len(input)%384 != 0 {
+ k := len(contract.Input) / 384
+ if len(contract.Input) == 0 || len(contract.Input)%384 != 0 {
return nil, errBLS12381InvalidInputLength
}
@@ -945,12 +1213,12 @@ func (c *bls12381Pairing) Run(input []byte) ([]byte, error) {
t0, t1, t2 := off, off+128, off+384
// Decode G1 point
- p1, err := g1.DecodePoint(input[t0:t1])
+ p1, err := g1.DecodePoint(contract.Input[t0:t1])
if err != nil {
return nil, err
}
// Decode G2 point
- p2, err := g2.DecodePoint(input[t1:t2])
+ p2, err := g2.DecodePoint(contract.Input[t1:t2])
if err != nil {
return nil, err
}
@@ -997,21 +1265,27 @@ func decodeBLS12381FieldElement(in []byte) ([]byte, error) {
// 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{17})
+}
+
// RequiredGas returns the gas required to execute the pre-compiled contract.
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) {
// Implements EIP-2537 Map_To_G1 precompile.
// > Field-to-curve call expects `64` bytes an an input that is interpreted as a an element of the base field.
// > Output of this call is `128` bytes and is G1 point following respective encoding rules.
- if len(input) != 64 {
+ if len(contract.Input) != 64 {
return nil, errBLS12381InvalidInputLength
}
// Decode input field element
- fe, err := decodeBLS12381FieldElement(input)
+ fe, err := decodeBLS12381FieldElement(contract.Input)
if err != nil {
return nil, err
}
@@ -1032,27 +1306,33 @@ 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{18})
+}
+
// RequiredGas returns the gas required to execute the pre-compiled contract.
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) {
// Implements EIP-2537 Map_FP2_TO_G2 precompile logic.
// > Field-to-curve call expects `128` bytes an an input that is interpreted as a an element of the quadratic extension field.
// > Output of this call is `256` bytes and is G2 point following respective encoding rules.
- if len(input) != 128 {
+ if len(contract.Input) != 128 {
return nil, errBLS12381InvalidInputLength
}
// Decode input field element
fe := make([]byte, 96)
- c0, err := decodeBLS12381FieldElement(input[:64])
+ c0, err := decodeBLS12381FieldElement(contract.Input[:64])
if err != nil {
return nil, err
}
copy(fe[48:], c0)
- c1, err := decodeBLS12381FieldElement(input[64:])
+ c1, err := decodeBLS12381FieldElement(contract.Input[64:])
if err != nil {
return nil, err
}
@@ -1074,6 +1354,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{0x0a})
+}
+
// RequiredGas estimates the gas required for running the point evaluation precompile.
func (b *kzgPointEvaluation) RequiredGas(input []byte) uint64 {
return params.BlobTxPointEvaluationPrecompileGas
@@ -1092,33 +1378,33 @@ var (
)
// Run executes the point evaluation precompile.
-func (b *kzgPointEvaluation) Run(input []byte) ([]byte, error) {
- if len(input) != blobVerifyInputLength {
+func (b *kzgPointEvaluation) Run(evm *EVM, contract *Contract, readonly bool) ([]byte, error) {
+ if len(contract.Input) != blobVerifyInputLength {
return nil, errBlobVerifyInvalidInputLength
}
// versioned hash: first 32 bytes
var versionedHash common.Hash
- copy(versionedHash[:], input[:])
+ copy(versionedHash[:], contract.Input[:])
var (
point kzg4844.Point
claim kzg4844.Claim
)
// Evaluation point: next 32 bytes
- copy(point[:], input[32:])
+ copy(point[:], contract.Input[32:])
// Expected output: next 32 bytes
- copy(claim[:], input[64:])
+ copy(claim[:], contract.Input[64:])
// input kzg point: next 48 bytes
var commitment kzg4844.Commitment
- copy(commitment[:], input[96:])
+ copy(commitment[:], contract.Input[96:])
if kZGToVersionedHash(commitment) != versionedHash {
return nil, errBlobVerifyMismatchedVersion
}
// Proof: next 48 bytes
var proof kzg4844.Proof
- copy(proof[:], input[144:])
+ copy(proof[:], contract.Input[144:])
if err := kzg4844.VerifyProof(commitment, point, claim, proof); err != nil {
return nil, fmt.Errorf("%w: %v", errBlobVerifyKZGProof, err)
diff --git a/core/vm/contracts_fuzz_test.go b/core/vm/contracts_fuzz_test.go
index 87c1fff7cc81..c28d6d570392 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,8 @@ func FuzzPrecompiledContracts(f *testing.F) {
return
}
inWant := string(input)
- RunPrecompiledContract(p, input, gas)
+
+ runPrecompiledContract(nil, p, AccountRef(common.Address{}), input, gas, new(uint256.Int), false)
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 fc30541d4596..defca9e9c602 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/holiman/uint256"
)
// precompiledTest defines the input/output pairs for precompiled contract tests.
@@ -98,7 +99,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); err != nil {
+ if res, _, err := runPrecompiledContract(nil, p, AccountRef(common.Address{}), in, gas, new(uint256.Int), false); err != nil {
t.Error(err)
} else if common.Bytes2Hex(res) != test.Expected {
t.Errorf("Expected %v, got %v", test.Expected, common.Bytes2Hex(res))
@@ -120,7 +121,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)
+ _, _, err := runPrecompiledContract(nil, p, AccountRef(common.Address{}), in, gas, new(uint256.Int), false)
if err.Error() != "out of gas" {
t.Errorf("Expected error [out of gas], got [%v]", err)
}
@@ -137,7 +138,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)
+ _, _, err := runPrecompiledContract(nil, p, AccountRef(common.Address{}), in, gas, new(uint256.Int), false)
if err.Error() != test.ExpectedError {
t.Errorf("Expected error [%v], got [%v]", test.ExpectedError, err)
}
@@ -169,7 +170,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)
+ res, _, err = runPrecompiledContract(nil, p, AccountRef(common.Address{}), in, reqGas, new(uint256.Int), false)
}
bench.StopTimer()
elapsed := uint64(time.Since(start))
@@ -181,7 +182,7 @@ func benchmarkPrecompiled(addr string, test precompiledTest, bench *testing.B) {
// Keep it as uint64, multiply 100 to get two digit float later
mgasps := (100 * 1000 * gasUsed) / elapsed
bench.ReportMetric(float64(mgasps)/100, "mgas/s")
- //Check if it is correct
+ // Check if it is correct
if err != nil {
bench.Error(err)
return
diff --git a/core/vm/custom_eip.go b/core/vm/custom_eip.go
new file mode 100644
index 000000000000..235ca500a9c4
--- /dev/null
+++ b/core/vm/custom_eip.go
@@ -0,0 +1,151 @@
+package vm
+
+import (
+ "fmt"
+ "sort"
+ "strings"
+
+ "golang.org/x/exp/maps"
+)
+
+// OpCodeInfo contains information required to identify an EVM operation.
+type OpCodeInfo struct {
+ Number OpCode
+ Name string
+}
+
+// Operation is an utility struct that wraps the private type
+// operation.
+type Operation struct {
+ Op *operation
+}
+
+// ExtendActivators allows to merge the go ethereum activators map
+// with additional custom activators.
+func ExtendActivators(eips map[int]func(*JumpTable)) error {
+ // Catch early duplicated eip.
+ keys := make([]int, 0, len(eips))
+ for k := range eips {
+ if ValidEip(k) {
+ return fmt.Errorf("duplicate activation: %d is already present in %s", k, ActivateableEips())
+ }
+ keys = append(keys, k)
+ }
+
+ // Sorting keys to ensure deterministic execution.
+ sort.Ints(keys)
+
+ for _, k := range keys {
+ activators[k] = eips[k]
+ }
+ return nil
+}
+
+// GetActivatorsEipNumbers returns the name of EIPs registered in
+// the activators map.
+// Used only in tests.
+func GetActivatorsEipNumbers() []int {
+ keys := maps.Keys(activators)
+
+ sort.Ints(keys)
+ return keys
+}
+
+// ExtendOperations returns an instance of the new operation and register it in the list
+// of available ones.
+// Return an error if an operation with the same name is already present.
+// This function is used to prevent the overwrite of an already existent operation.
+func ExtendOperations(
+ opInfo OpCodeInfo,
+ execute executionFunc,
+ constantGas uint64,
+ dynamicGas gasFunc,
+ minStack int,
+ maxStack int,
+ memorySize memorySizeFunc,
+) (*Operation, error) {
+ opName := strings.ToUpper(strings.TrimSpace(opInfo.Name))
+ if err := extendOpCodeStringLists(opInfo.Number, opName); err != nil {
+ return nil, err
+ }
+
+ operation := newOperation(execute, constantGas, dynamicGas, minStack, maxStack, memorySize)
+ op := &Operation{operation}
+
+ return op, nil
+}
+
+// newOperation returns an instance of a new EVM operation.
+func newOperation(
+ execute executionFunc,
+ constantGas uint64,
+ dynamicGas gasFunc,
+ minStack int,
+ maxStack int,
+ memorySize memorySizeFunc,
+) *operation {
+ return &operation{
+ execute: execute,
+ constantGas: constantGas,
+ dynamicGas: dynamicGas,
+ minStack: minStack,
+ maxStack: maxStack,
+ memorySize: memorySize,
+ }
+}
+
+// GetConstantGas return the constant gas used by the operation.
+func (o *operation) GetConstantGas() uint64 {
+ return o.constantGas
+}
+
+// SetExecute sets the execution function of the operation.
+func (o *operation) SetExecute(ef executionFunc) {
+ o.execute = ef
+}
+
+// SetConstantGas changes the constant gas of the operation.
+func (o *operation) SetConstantGas(gas uint64) {
+ o.constantGas = gas
+}
+
+// SetDynamicGas sets the dynamic gas function of the operation.
+func (o *operation) SetDynamicGas(gf gasFunc) {
+ o.dynamicGas = gf
+}
+
+// SetMinStack sets the minimum stack size required for the operation.
+func (o *operation) SetMinStack(minStack int) {
+ o.minStack = minStack
+}
+
+// SetMaxStack sets the maximum stack size for the operation.
+func (o *operation) SetMaxStack(maxStack int) {
+ o.maxStack = maxStack
+}
+
+// SetMemorySize sets the memory size function for the operation.
+func (o *operation) SetMemorySize(msf memorySizeFunc) {
+ o.memorySize = msf
+}
+
+// extendOpCodeStringLists updates the lists mapping opcode number to the name
+// and viceversa. Return an error if the key is already set.
+//
+// ASSUMPTION: no opcode is registered as an empty string.
+func extendOpCodeStringLists(newOpCode OpCode, newOpName string) error {
+ opName := opCodeToString[newOpCode]
+ if opName != "" {
+ return fmt.Errorf("opcode %d already exists: %s", newOpCode, opName)
+ }
+ opNumber := stringToOp[newOpName]
+ // We need to check against the STOP opcode name because we have to discriminate
+ // between 0x00 of this opcode and the default value of an empty key.
+ stopName := opCodeToString[STOP]
+ if opNumber != 0x00 || newOpName == stopName {
+ return fmt.Errorf("opcode with name %s already exists", newOpName)
+ }
+ opCodeToString[newOpCode] = newOpName
+ stringToOp[newOpName] = newOpCode
+ return nil
+}
diff --git a/core/vm/custom_eip_test.go b/core/vm/custom_eip_test.go
new file mode 100644
index 000000000000..d49d76fbfb91
--- /dev/null
+++ b/core/vm/custom_eip_test.go
@@ -0,0 +1,192 @@
+package vm
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/require"
+)
+
+func TestExtendActivators(t *testing.T) {
+ eips_snapshot := GetActivatorsEipNumbers()
+
+ testCases := []struct {
+ name string
+ newActivators map[int]func(*JumpTable)
+ expPass bool
+ errContains string
+ postCheck func()
+ }{
+ {
+ "success - nil new activators",
+ nil,
+ true,
+ "",
+ func() {
+ eips := GetActivatorsEipNumbers()
+ require.ElementsMatch(t, eips_snapshot, eips, "expected eips number to be equal")
+ },
+ },
+ {
+ "success - single new activator",
+ map[int]func(*JumpTable){
+ 0o000: func(jt *JumpTable) {},
+ },
+ true,
+ "",
+ func() {
+ eips := GetActivatorsEipNumbers()
+ require.ElementsMatch(t, append(eips_snapshot, 0), eips, "expected eips number to be equal")
+ },
+ },
+ {
+ "success - multiple new activators",
+ map[int]func(*JumpTable){
+ 0o001: func(jt *JumpTable) {},
+ 0o002: func(jt *JumpTable) {},
+ },
+ true,
+ "",
+ func() {
+ eips := GetActivatorsEipNumbers()
+ // since we are working with a global function, tests are not independent
+ require.ElementsMatch(t, append(eips_snapshot, 0, 1, 2), eips,
+ "expected eips number to be equal")
+ },
+ },
+ {
+ "fail - repeated activator",
+ map[int]func(*JumpTable){
+ 3855: func(jt *JumpTable) {},
+ },
+ false,
+ "",
+ func() {
+ eips := GetActivatorsEipNumbers()
+ // since we are working with a global function, tests are not independent
+ require.ElementsMatch(t, append(eips_snapshot, 0, 1, 2), eips,
+ "expected eips number to be equal")
+ },
+ },
+ {
+ "fail - valid activator is not stored if a repeated is present",
+ map[int]func(*JumpTable){
+ 0o003: func(jt *JumpTable) {},
+ 3855: func(jt *JumpTable) {},
+ },
+ false,
+ "",
+ func() {
+ eips := GetActivatorsEipNumbers()
+ // since we are working with a global function, tests are not independent
+ require.ElementsMatch(t, append(eips_snapshot, 0o000, 0o001, 0o002), eips,
+ "expected eips number to be equal")
+ },
+ },
+ }
+
+ for _, tc := range testCases {
+ err := ExtendActivators(tc.newActivators)
+ if tc.expPass {
+ require.NoError(t, err)
+ } else {
+ require.Error(t, err)
+ require.Contains(t, err.Error(), tc.errContains, "expected different error")
+ }
+
+ tc.postCheck()
+ }
+}
+
+func TestAddOperation(t *testing.T) {
+ // Functions used to create an operation.
+ customExecute := func(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
+ // no - op
+ return nil, nil
+ }
+ customDynamicGas := func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
+ // no-op
+ return 0, nil
+ }
+ customMemorySize := func(stack *Stack) (uint64, bool) {
+ // no-op
+ return 0, false
+ }
+
+ const (
+ EXISTENT OpCode = STOP
+ NEW OpCode = 0xf
+ )
+
+ testCases := []struct {
+ name string
+ opName string
+ opNumber OpCode
+ expPass bool
+ errContains string
+ postCheck func()
+ }{
+ {
+ "fail - operation with same number already exists",
+ "TEST",
+ EXISTENT,
+ false,
+ "already exists",
+ func() {
+ name := EXISTENT.String()
+ require.Equal(t, "STOP", name)
+ },
+ },
+ {
+ "fail - operation with same name already exists",
+ "CREATE",
+ NEW,
+ false,
+ "already exists",
+ func() {
+ name := NEW.String()
+ require.Contains(t, name, "not defined")
+ },
+ },
+ {
+ "fail - operation with same name of STOP",
+ "STOP",
+ NEW,
+ false,
+ "already exists",
+ func() {
+ name := NEW.String()
+ require.Contains(t, name, "not defined")
+ },
+ },
+ {
+ "pass - new operation added to the list",
+ "TEST",
+ NEW,
+ true,
+ "",
+ func() {
+ name := NEW.String()
+ require.Equal(t, "TEST", name)
+ },
+ },
+ }
+
+ for _, tc := range testCases {
+ t.Run(tc.name, func(t *testing.T) {
+ opInfo := OpCodeInfo{
+ Number: tc.opNumber,
+ Name: tc.opName,
+ }
+ _, err := ExtendOperations(opInfo, customExecute, 0, customDynamicGas, 0, 0, customMemorySize)
+
+ if tc.expPass {
+ require.NoError(t, err)
+ } else {
+ require.Error(t, err)
+ require.Contains(t, err.Error(), tc.errContains, "expected different error")
+ }
+
+ tc.postCheck()
+ })
+ }
+}
diff --git a/core/vm/custom_eip_testing.go b/core/vm/custom_eip_testing.go
new file mode 100644
index 000000000000..272397990b4e
--- /dev/null
+++ b/core/vm/custom_eip_testing.go
@@ -0,0 +1,30 @@
+//go:build test
+// +build test
+
+// This file is used to allow the testing of EVM configuration initialization
+// without the need to introduce testing requirements in the final binary. In
+// this case, the file provides the possibility to restore the EIP activator
+// functions to the initial state without the need to compile ResetActivators
+// in the final binary.
+
+package vm
+
+var originalActivators = make(map[int]func(*JumpTable))
+
+func init() {
+ keys := GetActivatorsEipNumbers()
+
+ originalActivators = make(map[int]func(*JumpTable), len(keys))
+
+ for _, k := range keys {
+ originalActivators[k] = activators[k]
+ }
+}
+
+// ResetActivators resets activators to the original go ethereum activators map
+func ResetActivators() {
+ activators = make(map[int]func(*JumpTable))
+ for k, v := range originalActivators {
+ activators[k] = v
+ }
+}
diff --git a/core/vm/eips.go b/core/vm/eips.go
index 9f06b2818fee..e9f533ce8794 100644
--- a/core/vm/eips.go
+++ b/core/vm/eips.go
@@ -86,7 +86,7 @@ func enable1884(jt *JumpTable) {
func opSelfBalance(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
balance := interpreter.evm.StateDB.GetBalance(scope.Contract.Address())
- scope.Stack.push(balance)
+ scope.Stack.Push(balance)
return nil, nil
}
@@ -105,7 +105,7 @@ func enable1344(jt *JumpTable) {
// opChainID implements CHAINID opcode
func opChainID(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
chainId, _ := uint256.FromBig(interpreter.evm.chainConfig.ChainID)
- scope.Stack.push(chainId)
+ scope.Stack.Push(chainId)
return nil, nil
}
@@ -195,7 +195,7 @@ func enable1153(jt *JumpTable) {
// opTload implements TLOAD opcode
func opTload(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
- loc := scope.Stack.peek()
+ loc := scope.Stack.Peek()
hash := common.Hash(loc.Bytes32())
val := interpreter.evm.StateDB.GetTransientState(scope.Contract.Address(), hash)
loc.SetBytes(val.Bytes())
@@ -207,8 +207,8 @@ func opTstore(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]b
if interpreter.readOnly {
return nil, ErrWriteProtection
}
- loc := scope.Stack.pop()
- val := scope.Stack.pop()
+ loc := scope.Stack.Pop()
+ val := scope.Stack.Pop()
interpreter.evm.StateDB.SetTransientState(scope.Contract.Address(), loc.Bytes32(), val.Bytes32())
return nil, nil
}
@@ -216,7 +216,7 @@ func opTstore(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]b
// opBaseFee implements BASEFEE opcode
func opBaseFee(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
baseFee, _ := uint256.FromBig(interpreter.evm.Context.BaseFee)
- scope.Stack.push(baseFee)
+ scope.Stack.Push(baseFee)
return nil, nil
}
@@ -233,7 +233,7 @@ func enable3855(jt *JumpTable) {
// opPush0 implements the PUSH0 opcode
func opPush0(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
- scope.Stack.push(new(uint256.Int))
+ scope.Stack.Push(new(uint256.Int))
return nil, nil
}
@@ -260,9 +260,9 @@ func enable5656(jt *JumpTable) {
// opMcopy implements the MCOPY opcode (https://eips.ethereum.org/EIPS/eip-5656)
func opMcopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
var (
- dst = scope.Stack.pop()
- src = scope.Stack.pop()
- length = scope.Stack.pop()
+ dst = scope.Stack.Pop()
+ src = scope.Stack.Pop()
+ length = scope.Stack.Pop()
)
// These values are checked for overflow during memory expansion calculation
// (the memorySize function on the opcode).
@@ -272,7 +272,7 @@ func opMcopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]by
// opBlobHash implements the BLOBHASH opcode
func opBlobHash(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
- index := scope.Stack.peek()
+ index := scope.Stack.Peek()
if index.LtUint64(uint64(len(interpreter.evm.TxContext.BlobHashes))) {
blobHash := interpreter.evm.TxContext.BlobHashes[index.Uint64()]
index.SetBytes32(blobHash[:])
@@ -285,7 +285,7 @@ func opBlobHash(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([
// opBlobBaseFee implements BLOBBASEFEE opcode
func opBlobBaseFee(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
blobBaseFee, _ := uint256.FromBig(interpreter.evm.Context.BlobBaseFee)
- scope.Stack.push(blobBaseFee)
+ scope.Stack.Push(blobBaseFee)
return nil, nil
}
diff --git a/core/vm/evm.go b/core/vm/evm.go
index 16cc8549080a..39e55da7db3e 100644
--- a/core/vm/evm.go
+++ b/core/vm/evm.go
@@ -1,18 +1,5 @@
-// Copyright 2014 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
+// Copyright Tharsis Labs Ltd.(Evmos)
+// SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE)
package vm
@@ -37,24 +24,6 @@ type (
GetHashFunc func(uint64) common.Hash
)
-func (evm *EVM) precompile(addr common.Address) (PrecompiledContract, bool) {
- var precompiles map[common.Address]PrecompiledContract
- switch {
- case evm.chainRules.IsCancun:
- precompiles = PrecompiledContractsCancun
- case evm.chainRules.IsBerlin:
- precompiles = PrecompiledContractsBerlin
- case evm.chainRules.IsIstanbul:
- precompiles = PrecompiledContractsIstanbul
- case evm.chainRules.IsByzantium:
- precompiles = PrecompiledContractsByzantium
- default:
- precompiles = PrecompiledContractsHomestead
- }
- p, ok := precompiles[addr]
- return p, ok
-}
-
// BlockContext provides the EVM with auxiliary information. Once provided
// it shouldn't be modified.
type BlockContext struct {
@@ -114,13 +83,22 @@ type EVM struct {
Config Config
// global (to this context) ethereum virtual machine
// used throughout the execution of the tx.
- interpreter *EVMInterpreter
+ interpreter Interpreter
// abort is used to abort the EVM calling operations
abort atomic.Bool
// callGasTemp holds the gas available for the current call. This is needed because the
// available gas is calculated in gasCall* according to the 63/64 rule and later
// applied in opCall*.
callGasTemp uint64
+ // precompiles defines the precompiled contracts used by the EVM
+ precompiles map[common.Address]PrecompiledContract
+ // activePrecompiles defines the precompiles that are currently active
+ activePrecompiles []common.Address
+
+ // hooks is a set of functions that can be used to intercept and modify the
+ // behavior of the EVM when executing certain opcodes.
+ // The hooks are called before the execution of the respective opcodes.
+ hooks OpCodeHooks
}
// NewEVM returns a new EVM. The returned EVM is not thread safe and should
@@ -144,8 +122,33 @@ func NewEVM(blockCtx BlockContext, txCtx TxContext, statedb StateDB, chainConfig
Config: config,
chainConfig: chainConfig,
chainRules: chainConfig.Rules(blockCtx.BlockNumber, blockCtx.Random != nil, blockCtx.Time),
+ hooks: newNoopOpCodeHooks(),
}
+ // set the default precompiles
+ evm.activePrecompiles = DefaultActivePrecompiles(evm.chainRules)
+ evm.precompiles = DefaultPrecompiles(evm.chainRules)
evm.interpreter = NewEVMInterpreter(evm)
+
+ return evm
+}
+
+// NewEVMWithHooks returns a new EVM and takes a custom OpCodeHooks. The returned EVM is
+// not thread safe and should only ever be used *once*.
+func NewEVMWithHooks(hooks OpCodeHooks, blockCtx BlockContext, txCtx TxContext, statedb StateDB, chainConfig *params.ChainConfig, config Config) *EVM {
+ evm := &EVM{
+ Context: blockCtx,
+ TxContext: txCtx,
+ StateDB: statedb,
+ Config: config,
+ chainConfig: chainConfig,
+ chainRules: chainConfig.Rules(blockCtx.BlockNumber, blockCtx.Random != nil, blockCtx.Time),
+ hooks: hooks,
+ }
+ // set the default precompiles
+ evm.activePrecompiles = DefaultActivePrecompiles(evm.chainRules)
+ evm.precompiles = DefaultPrecompiles(evm.chainRules)
+ evm.interpreter = NewEVMInterpreter(evm)
+
return evm
}
@@ -168,15 +171,24 @@ func (evm *EVM) Cancelled() bool {
}
// Interpreter returns the current interpreter
-func (evm *EVM) Interpreter() *EVMInterpreter {
+func (evm *EVM) Interpreter() Interpreter {
return evm.interpreter
}
+// WithInterpreter sets the interpreter to the EVM instance
+func (evm *EVM) WithInterpreter(interpreter Interpreter) {
+ evm.interpreter = interpreter
+}
+
// Call executes the contract associated with the addr with the given input as
// parameters. It also handles any necessary value transfer required and takes
// the necessary steps to create accounts and reverses the state in case of an
// execution error or failed value transfer.
func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas uint64, value *uint256.Int) (ret []byte, leftOverGas uint64, err error) {
+ if err = evm.hooks.CallHook(evm, caller.Address(), addr); err != nil {
+ return nil, gas, err
+ }
+
// Fail if we're trying to execute above the call depth limit
if evm.depth > int(params.CallCreateDepth) {
return nil, gas, ErrDepth
@@ -185,8 +197,9 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
if !value.IsZero() && !evm.Context.CanTransfer(evm.StateDB, caller.Address(), value) {
return nil, gas, ErrInsufficientBalance
}
+
snapshot := evm.StateDB.Snapshot()
- p, isPrecompile := evm.precompile(addr)
+ p, isPrecompile := evm.Precompile(addr)
debug := evm.Config.Tracer != nil
if !evm.StateDB.Exist(addr) {
@@ -223,8 +236,9 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
}
}
+ // It is allowed to call precompiles, even via call -- as opposed to callcode, staticcall and delegatecall it can also modify state
if isPrecompile {
- ret, gas, err = RunPrecompiledContract(p, input, gas)
+ ret, gas, err = evm.RunPrecompiledContract(p, caller, input, gas, value, 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.
@@ -264,6 +278,10 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
// CallCode differs from Call in the sense that it executes the given address'
// code with the caller as context.
func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte, gas uint64, value *uint256.Int) (ret []byte, leftOverGas uint64, err error) {
+ if err = evm.hooks.CallHook(evm, caller.Address(), addr); err != nil {
+ return nil, gas, err
+ }
+
// Fail if we're trying to execute above the call depth limit
if evm.depth > int(params.CallCreateDepth) {
return nil, gas, ErrDepth
@@ -285,9 +303,9 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte,
}(gas)
}
- // It is allowed to call precompiles, even via delegatecall
- if p, isPrecompile := evm.precompile(addr); isPrecompile {
- ret, gas, err = RunPrecompiledContract(p, input, gas)
+ // It is allowed to call precompiles, even via callcode, but only for reading
+ if p, isPrecompile := evm.Precompile(addr); isPrecompile {
+ ret, gas, err = evm.RunPrecompiledContract(p, caller, input, gas, value, true)
} else {
addrCopy := addr
// Initialise a new contract and set the code that is to be used by the EVM.
@@ -312,6 +330,10 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte,
// DelegateCall differs from CallCode in the sense that it executes the given address'
// code with the caller as context and the caller is set to the caller of the caller.
func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []byte, gas uint64) (ret []byte, leftOverGas uint64, err error) {
+ if err = evm.hooks.CallHook(evm, caller.Address(), addr); err != nil {
+ return nil, gas, err
+ }
+
// Fail if we're trying to execute above the call depth limit
if evm.depth > int(params.CallCreateDepth) {
return nil, gas, ErrDepth
@@ -331,8 +353,8 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by
}
// It is allowed to call precompiles, even via delegatecall
- if p, isPrecompile := evm.precompile(addr); isPrecompile {
- ret, gas, err = RunPrecompiledContract(p, input, gas)
+ if p, isPrecompile := evm.Precompile(addr); isPrecompile {
+ ret, gas, err = evm.RunPrecompiledContract(p, caller, input, gas, nil, true)
} else {
addrCopy := addr
// Initialise a new contract and make initialise the delegate values
@@ -355,6 +377,10 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by
// Opcodes that attempt to perform such modifications will result in exceptions
// instead of performing the modifications.
func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte, gas uint64) (ret []byte, leftOverGas uint64, err error) {
+ if err = evm.hooks.CallHook(evm, caller.Address(), addr); err != nil {
+ return nil, gas, err
+ }
+
// Fail if we're trying to execute above the call depth limit
if evm.depth > int(params.CallCreateDepth) {
return nil, gas, ErrDepth
@@ -380,8 +406,9 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte
}(gas)
}
- if p, isPrecompile := evm.precompile(addr); isPrecompile {
- ret, gas, err = RunPrecompiledContract(p, input, gas)
+ if p, isPrecompile := evm.Precompile(addr); isPrecompile {
+ // Note: delegate call is not allowed to modify state on precompiles
+ ret, gas, err = evm.RunPrecompiledContract(p, caller, input, gas, new(uint256.Int), true)
} else {
// At this point, we use a copy of address. If we don't, the go compiler will
// leak the 'contract' to the outer scope, and make allocation for 'contract'
@@ -511,6 +538,9 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
// Create creates a new contract using code as deployment code.
func (evm *EVM) Create(caller ContractRef, code []byte, gas uint64, value *uint256.Int) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) {
+ if err = evm.hooks.CreateHook(evm, caller.Address()); err != nil {
+ return nil, common.Address{}, gas, err
+ }
contractAddr = crypto.CreateAddress(caller.Address(), evm.StateDB.GetNonce(caller.Address()))
return evm.create(caller, &codeAndHash{code: code}, gas, value, contractAddr, CREATE)
}
@@ -520,6 +550,9 @@ func (evm *EVM) Create(caller ContractRef, code []byte, gas uint64, value *uint2
// The different between Create2 with Create is Create2 uses keccak256(0xff ++ msg.sender ++ salt ++ keccak256(init_code))[12:]
// instead of the usual sender-and-nonce-hash as the address where the contract is initialized at.
func (evm *EVM) Create2(caller ContractRef, code []byte, gas uint64, endowment *uint256.Int, salt *uint256.Int) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) {
+ if err = evm.hooks.CreateHook(evm, caller.Address()); err != nil {
+ return nil, common.Address{}, gas, err
+ }
codeAndHash := &codeAndHash{code: code}
contractAddr = crypto.CreateAddress2(caller.Address(), salt.Bytes32(), codeAndHash.Hash().Bytes())
return evm.create(caller, codeAndHash, gas, endowment, contractAddr, CREATE2)
diff --git a/core/vm/gas_table.go b/core/vm/gas_table.go
index 4b141d8f9a5d..42b495cc70e5 100644
--- a/core/vm/gas_table.go
+++ b/core/vm/gas_table.go
@@ -338,7 +338,7 @@ func gasCreate2Eip3860(evm *EVM, contract *Contract, stack *Stack, mem *Memory,
}
func gasExpFrontier(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
- expByteLen := uint64((stack.data[stack.len()-2].BitLen() + 7) / 8)
+ expByteLen := uint64((stack.Data[stack.Len()-2].BitLen() + 7) / 8)
var (
gas = expByteLen * params.ExpByteFrontier // no overflow check required. Max is 256 * ExpByte gas
@@ -351,7 +351,7 @@ func gasExpFrontier(evm *EVM, contract *Contract, stack *Stack, mem *Memory, mem
}
func gasExpEIP158(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
- expByteLen := uint64((stack.data[stack.len()-2].BitLen() + 7) / 8)
+ expByteLen := uint64((stack.Data[stack.Len()-2].BitLen() + 7) / 8)
var (
gas = expByteLen * params.ExpByteEIP158 // no overflow check required. Max is 256 * ExpByte gas
diff --git a/core/vm/gas_table_test.go b/core/vm/gas_table_test.go
index 4a2545b6edfa..c38ef1ce0caf 100644
--- a/core/vm/gas_table_test.go
+++ b/core/vm/gas_table_test.go
@@ -95,7 +95,8 @@ func TestEIP2200(t *testing.T) {
CanTransfer: func(StateDB, common.Address, *uint256.Int) bool { return true },
Transfer: func(StateDB, common.Address, common.Address, *uint256.Int) {},
}
- vmenv := NewEVM(vmctx, TxContext{}, statedb, params.AllEthashProtocolChanges, Config{ExtraEips: []int{2200}})
+ vmenv := NewEVM(vmctx, TxContext{}, statedb, params.AllEthashProtocolChanges,
+ Config{ExtraEips: []int{2200}})
_, gas, err := vmenv.Call(AccountRef(common.Address{}), address, nil, tt.gaspool, new(uint256.Int))
if err != tt.failure {
diff --git a/core/vm/instructions.go b/core/vm/instructions.go
index b8055de6bce7..402ac8b2570c 100644
--- a/core/vm/instructions.go
+++ b/core/vm/instructions.go
@@ -27,67 +27,67 @@ import (
)
func opAdd(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
- x, y := scope.Stack.pop(), scope.Stack.peek()
+ x, y := scope.Stack.Pop(), scope.Stack.Peek()
y.Add(&x, y)
return nil, nil
}
func opSub(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
- x, y := scope.Stack.pop(), scope.Stack.peek()
+ x, y := scope.Stack.Pop(), scope.Stack.Peek()
y.Sub(&x, y)
return nil, nil
}
func opMul(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
- x, y := scope.Stack.pop(), scope.Stack.peek()
+ x, y := scope.Stack.Pop(), scope.Stack.Peek()
y.Mul(&x, y)
return nil, nil
}
func opDiv(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
- x, y := scope.Stack.pop(), scope.Stack.peek()
+ x, y := scope.Stack.Pop(), scope.Stack.Peek()
y.Div(&x, y)
return nil, nil
}
func opSdiv(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
- x, y := scope.Stack.pop(), scope.Stack.peek()
+ x, y := scope.Stack.Pop(), scope.Stack.Peek()
y.SDiv(&x, y)
return nil, nil
}
func opMod(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
- x, y := scope.Stack.pop(), scope.Stack.peek()
+ x, y := scope.Stack.Pop(), scope.Stack.Peek()
y.Mod(&x, y)
return nil, nil
}
func opSmod(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
- x, y := scope.Stack.pop(), scope.Stack.peek()
+ x, y := scope.Stack.Pop(), scope.Stack.Peek()
y.SMod(&x, y)
return nil, nil
}
func opExp(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
- base, exponent := scope.Stack.pop(), scope.Stack.peek()
+ base, exponent := scope.Stack.Pop(), scope.Stack.Peek()
exponent.Exp(&base, exponent)
return nil, nil
}
func opSignExtend(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
- back, num := scope.Stack.pop(), scope.Stack.peek()
+ back, num := scope.Stack.Pop(), scope.Stack.Peek()
num.ExtendSign(num, &back)
return nil, nil
}
func opNot(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
- x := scope.Stack.peek()
+ x := scope.Stack.Peek()
x.Not(x)
return nil, nil
}
func opLt(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
- x, y := scope.Stack.pop(), scope.Stack.peek()
+ x, y := scope.Stack.Pop(), scope.Stack.Peek()
if x.Lt(y) {
y.SetOne()
} else {
@@ -97,7 +97,7 @@ func opLt(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte,
}
func opGt(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
- x, y := scope.Stack.pop(), scope.Stack.peek()
+ x, y := scope.Stack.Pop(), scope.Stack.Peek()
if x.Gt(y) {
y.SetOne()
} else {
@@ -107,7 +107,7 @@ func opGt(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte,
}
func opSlt(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
- x, y := scope.Stack.pop(), scope.Stack.peek()
+ x, y := scope.Stack.Pop(), scope.Stack.Peek()
if x.Slt(y) {
y.SetOne()
} else {
@@ -117,7 +117,7 @@ func opSlt(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte
}
func opSgt(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
- x, y := scope.Stack.pop(), scope.Stack.peek()
+ x, y := scope.Stack.Pop(), scope.Stack.Peek()
if x.Sgt(y) {
y.SetOne()
} else {
@@ -127,7 +127,7 @@ func opSgt(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte
}
func opEq(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
- x, y := scope.Stack.pop(), scope.Stack.peek()
+ x, y := scope.Stack.Pop(), scope.Stack.Peek()
if x.Eq(y) {
y.SetOne()
} else {
@@ -137,7 +137,7 @@ func opEq(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte,
}
func opIszero(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
- x := scope.Stack.peek()
+ x := scope.Stack.Peek()
if x.IsZero() {
x.SetOne()
} else {
@@ -147,31 +147,31 @@ func opIszero(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]b
}
func opAnd(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
- x, y := scope.Stack.pop(), scope.Stack.peek()
+ x, y := scope.Stack.Pop(), scope.Stack.Peek()
y.And(&x, y)
return nil, nil
}
func opOr(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
- x, y := scope.Stack.pop(), scope.Stack.peek()
+ x, y := scope.Stack.Pop(), scope.Stack.Peek()
y.Or(&x, y)
return nil, nil
}
func opXor(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
- x, y := scope.Stack.pop(), scope.Stack.peek()
+ x, y := scope.Stack.Pop(), scope.Stack.Peek()
y.Xor(&x, y)
return nil, nil
}
func opByte(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
- th, val := scope.Stack.pop(), scope.Stack.peek()
+ th, val := scope.Stack.Pop(), scope.Stack.Peek()
val.Byte(&th)
return nil, nil
}
func opAddmod(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
- x, y, z := scope.Stack.pop(), scope.Stack.pop(), scope.Stack.peek()
+ x, y, z := scope.Stack.Pop(), scope.Stack.Pop(), scope.Stack.Peek()
if z.IsZero() {
z.Clear()
} else {
@@ -181,7 +181,7 @@ func opAddmod(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]b
}
func opMulmod(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
- x, y, z := scope.Stack.pop(), scope.Stack.pop(), scope.Stack.peek()
+ x, y, z := scope.Stack.Pop(), scope.Stack.Pop(), scope.Stack.Peek()
z.MulMod(&x, &y, z)
return nil, nil
}
@@ -191,7 +191,7 @@ func opMulmod(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]b
// and pushes on the stack arg2 shifted to the left by arg1 number of bits.
func opSHL(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
// Note, second operand is left in the stack; accumulate result into it, and no need to push it afterwards
- shift, value := scope.Stack.pop(), scope.Stack.peek()
+ shift, value := scope.Stack.Pop(), scope.Stack.Peek()
if shift.LtUint64(256) {
value.Lsh(value, uint(shift.Uint64()))
} else {
@@ -205,7 +205,7 @@ func opSHL(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte
// and pushes on the stack arg2 shifted to the right by arg1 number of bits with zero fill.
func opSHR(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
// Note, second operand is left in the stack; accumulate result into it, and no need to push it afterwards
- shift, value := scope.Stack.pop(), scope.Stack.peek()
+ shift, value := scope.Stack.Pop(), scope.Stack.Peek()
if shift.LtUint64(256) {
value.Rsh(value, uint(shift.Uint64()))
} else {
@@ -218,7 +218,7 @@ func opSHR(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte
// The SAR instruction (arithmetic shift right) pops 2 values from the stack, first arg1 and then arg2,
// and pushes on the stack arg2 shifted to the right by arg1 number of bits with sign extension.
func opSAR(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
- shift, value := scope.Stack.pop(), scope.Stack.peek()
+ shift, value := scope.Stack.Pop(), scope.Stack.Peek()
if shift.GtUint64(256) {
if value.Sign() >= 0 {
value.Clear()
@@ -234,7 +234,7 @@ func opSAR(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte
}
func opKeccak256(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
- offset, size := scope.Stack.pop(), scope.Stack.peek()
+ offset, size := scope.Stack.Pop(), scope.Stack.Peek()
data := scope.Memory.GetPtr(int64(offset.Uint64()), int64(size.Uint64()))
if interpreter.hasher == nil {
@@ -255,34 +255,34 @@ func opKeccak256(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) (
}
func opAddress(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
- scope.Stack.push(new(uint256.Int).SetBytes(scope.Contract.Address().Bytes()))
+ scope.Stack.Push(new(uint256.Int).SetBytes(scope.Contract.Address().Bytes()))
return nil, nil
}
func opBalance(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
- slot := scope.Stack.peek()
+ slot := scope.Stack.Peek()
address := common.Address(slot.Bytes20())
slot.Set(interpreter.evm.StateDB.GetBalance(address))
return nil, nil
}
func opOrigin(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
- scope.Stack.push(new(uint256.Int).SetBytes(interpreter.evm.Origin.Bytes()))
+ scope.Stack.Push(new(uint256.Int).SetBytes(interpreter.evm.Origin.Bytes()))
return nil, nil
}
func opCaller(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
- scope.Stack.push(new(uint256.Int).SetBytes(scope.Contract.Caller().Bytes()))
+ scope.Stack.Push(new(uint256.Int).SetBytes(scope.Contract.Caller().Bytes()))
return nil, nil
}
func opCallValue(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
- scope.Stack.push(scope.Contract.value)
+ scope.Stack.Push(scope.Contract.value)
return nil, nil
}
func opCallDataLoad(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
- x := scope.Stack.peek()
+ x := scope.Stack.Peek()
if offset, overflow := x.Uint64WithOverflow(); !overflow {
data := getData(scope.Contract.Input, offset, 32)
x.SetBytes(data)
@@ -293,15 +293,15 @@ func opCallDataLoad(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext
}
func opCallDataSize(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
- scope.Stack.push(new(uint256.Int).SetUint64(uint64(len(scope.Contract.Input))))
+ scope.Stack.Push(new(uint256.Int).SetUint64(uint64(len(scope.Contract.Input))))
return nil, nil
}
func opCallDataCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
var (
- memOffset = scope.Stack.pop()
- dataOffset = scope.Stack.pop()
- length = scope.Stack.pop()
+ memOffset = scope.Stack.Pop()
+ dataOffset = scope.Stack.Pop()
+ length = scope.Stack.Pop()
)
dataOffset64, overflow := dataOffset.Uint64WithOverflow()
if overflow {
@@ -316,15 +316,15 @@ func opCallDataCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext
}
func opReturnDataSize(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
- scope.Stack.push(new(uint256.Int).SetUint64(uint64(len(interpreter.returnData))))
+ scope.Stack.Push(new(uint256.Int).SetUint64(uint64(len(interpreter.returnData))))
return nil, nil
}
func opReturnDataCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
var (
- memOffset = scope.Stack.pop()
- dataOffset = scope.Stack.pop()
- length = scope.Stack.pop()
+ memOffset = scope.Stack.Pop()
+ dataOffset = scope.Stack.Pop()
+ length = scope.Stack.Pop()
)
offset64, overflow := dataOffset.Uint64WithOverflow()
@@ -343,21 +343,21 @@ func opReturnDataCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeConte
}
func opExtCodeSize(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
- slot := scope.Stack.peek()
+ slot := scope.Stack.Peek()
slot.SetUint64(uint64(interpreter.evm.StateDB.GetCodeSize(slot.Bytes20())))
return nil, nil
}
func opCodeSize(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
- scope.Stack.push(new(uint256.Int).SetUint64(uint64(len(scope.Contract.Code))))
+ scope.Stack.Push(new(uint256.Int).SetUint64(uint64(len(scope.Contract.Code))))
return nil, nil
}
func opCodeCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
var (
- memOffset = scope.Stack.pop()
- codeOffset = scope.Stack.pop()
- length = scope.Stack.pop()
+ memOffset = scope.Stack.Pop()
+ codeOffset = scope.Stack.Pop()
+ length = scope.Stack.Pop()
)
uint64CodeOffset, overflow := codeOffset.Uint64WithOverflow()
if overflow {
@@ -372,10 +372,10 @@ func opCodeCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([
func opExtCodeCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
var (
stack = scope.Stack
- a = stack.pop()
- memOffset = stack.pop()
- codeOffset = stack.pop()
- length = stack.pop()
+ a = stack.Pop()
+ memOffset = stack.Pop()
+ codeOffset = stack.Pop()
+ length = stack.Pop()
)
uint64CodeOffset, overflow := codeOffset.Uint64WithOverflow()
if overflow {
@@ -392,14 +392,21 @@ func opExtCodeCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext)
// There are several cases when the function is called, while we can relay everything
// to `state.GetCodeHash` function to ensure the correctness.
//
-// 1. Caller tries to get the code hash of a normal contract account, state
-// should return the relative code hash and set it as the result.
+// (1) Caller tries to get the code hash of a normal contract account, state
//
-// 2. Caller tries to get the code hash of a non-existent account, state should
-// return common.Hash{} and zero will be set as the result.
+// should return the relative code hash and set it as the result.
//
-// 3. Caller tries to get the code hash for an account without contract code, state
-// should return emptyCodeHash(0xc5d246...) as the result.
+// (2) Caller tries to get the code hash of a non-existent account, state should
+//
+// return common.Hash{} and zero will be set as the result.
+//
+// (3) Caller tries to get the code hash for an account without contract code,
+//
+// state should return emptyCodeHash(0xc5d246...) as the result.
+//
+// (4) Caller tries to get the code hash of a precompiled account, the result
+//
+// should be zero or emptyCodeHash.
//
// 4. Caller tries to get the code hash of a precompiled account, the result should be
// zero or emptyCodeHash.
@@ -409,13 +416,15 @@ func opExtCodeCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext)
// emptyCodeHash. If the precompile account is not transferred any amount on a private or
// customized chain, the return value will be zero.
//
-// 5. Caller tries to get the code hash for an account which is marked as self-destructed
-// in the current transaction, the code hash of this account should be returned.
+// (5) Caller tries to get the code hash for an account which is marked as suicided
+//
+// in the current transaction, the code hash of this account should be returned.
+//
+// (6) Caller tries to get the code hash for an account which is marked as deleted,
//
-// 6. Caller tries to get the code hash for an account which is marked as deleted, this
-// account should be regarded as a non-existent account and zero should be returned.
+// this account should be regarded as a non-existent account and zero should be returned.
func opExtCodeHash(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
- slot := scope.Stack.peek()
+ slot := scope.Stack.Peek()
address := common.Address(slot.Bytes20())
if interpreter.evm.StateDB.Empty(address) {
slot.Clear()
@@ -427,12 +436,12 @@ func opExtCodeHash(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext)
func opGasprice(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
v, _ := uint256.FromBig(interpreter.evm.GasPrice)
- scope.Stack.push(v)
+ scope.Stack.Push(v)
return nil, nil
}
func opBlockhash(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
- num := scope.Stack.peek()
+ num := scope.Stack.Peek()
num64, overflow := num.Uint64WithOverflow()
if overflow {
num.Clear()
@@ -454,45 +463,45 @@ func opBlockhash(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) (
}
func opCoinbase(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
- scope.Stack.push(new(uint256.Int).SetBytes(interpreter.evm.Context.Coinbase.Bytes()))
+ scope.Stack.Push(new(uint256.Int).SetBytes(interpreter.evm.Context.Coinbase.Bytes()))
return nil, nil
}
func opTimestamp(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
- scope.Stack.push(new(uint256.Int).SetUint64(interpreter.evm.Context.Time))
+ scope.Stack.Push(new(uint256.Int).SetUint64(interpreter.evm.Context.Time))
return nil, nil
}
func opNumber(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
v, _ := uint256.FromBig(interpreter.evm.Context.BlockNumber)
- scope.Stack.push(v)
+ scope.Stack.Push(v)
return nil, nil
}
func opDifficulty(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
v, _ := uint256.FromBig(interpreter.evm.Context.Difficulty)
- scope.Stack.push(v)
+ scope.Stack.Push(v)
return nil, nil
}
func opRandom(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
v := new(uint256.Int).SetBytes(interpreter.evm.Context.Random.Bytes())
- scope.Stack.push(v)
+ scope.Stack.Push(v)
return nil, nil
}
func opGasLimit(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
- scope.Stack.push(new(uint256.Int).SetUint64(interpreter.evm.Context.GasLimit))
+ scope.Stack.Push(new(uint256.Int).SetUint64(interpreter.evm.Context.GasLimit))
return nil, nil
}
func opPop(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
- scope.Stack.pop()
+ scope.Stack.Pop()
return nil, nil
}
func opMload(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
- v := scope.Stack.peek()
+ v := scope.Stack.Peek()
offset := int64(v.Uint64())
v.SetBytes(scope.Memory.GetPtr(offset, 32))
return nil, nil
@@ -500,19 +509,19 @@ func opMload(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]by
func opMstore(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
// pop value of the stack
- mStart, val := scope.Stack.pop(), scope.Stack.pop()
+ mStart, val := scope.Stack.Pop(), scope.Stack.Pop()
scope.Memory.Set32(mStart.Uint64(), &val)
return nil, nil
}
func opMstore8(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
- off, val := scope.Stack.pop(), scope.Stack.pop()
+ off, val := scope.Stack.Pop(), scope.Stack.Pop()
scope.Memory.store[off.Uint64()] = byte(val.Uint64())
return nil, nil
}
func opSload(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
- loc := scope.Stack.peek()
+ loc := scope.Stack.Peek()
hash := common.Hash(loc.Bytes32())
val := interpreter.evm.StateDB.GetState(scope.Contract.Address(), hash)
loc.SetBytes(val.Bytes())
@@ -523,8 +532,8 @@ func opSstore(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]b
if interpreter.readOnly {
return nil, ErrWriteProtection
}
- loc := scope.Stack.pop()
- val := scope.Stack.pop()
+ loc := scope.Stack.Pop()
+ val := scope.Stack.Pop()
interpreter.evm.StateDB.SetState(scope.Contract.Address(), loc.Bytes32(), val.Bytes32())
return nil, nil
}
@@ -533,7 +542,7 @@ func opJump(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byt
if interpreter.evm.abort.Load() {
return nil, errStopToken
}
- pos := scope.Stack.pop()
+ pos := scope.Stack.Pop()
if !scope.Contract.validJumpdest(&pos) {
return nil, ErrInvalidJump
}
@@ -545,7 +554,7 @@ func opJumpi(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]by
if interpreter.evm.abort.Load() {
return nil, errStopToken
}
- pos, cond := scope.Stack.pop(), scope.Stack.pop()
+ pos, cond := scope.Stack.Pop(), scope.Stack.Pop()
if !cond.IsZero() {
if !scope.Contract.validJumpdest(&pos) {
return nil, ErrInvalidJump
@@ -560,17 +569,17 @@ func opJumpdest(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([
}
func opPc(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
- scope.Stack.push(new(uint256.Int).SetUint64(*pc))
+ scope.Stack.Push(new(uint256.Int).SetUint64(*pc))
return nil, nil
}
func opMsize(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
- scope.Stack.push(new(uint256.Int).SetUint64(uint64(scope.Memory.Len())))
+ scope.Stack.Push(new(uint256.Int).SetUint64(uint64(scope.Memory.Len())))
return nil, nil
}
func opGas(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
- scope.Stack.push(new(uint256.Int).SetUint64(scope.Contract.Gas))
+ scope.Stack.Push(new(uint256.Int).SetUint64(scope.Contract.Gas))
return nil, nil
}
@@ -579,8 +588,8 @@ func opCreate(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]b
return nil, ErrWriteProtection
}
var (
- value = scope.Stack.pop()
- offset, size = scope.Stack.pop(), scope.Stack.pop()
+ value = scope.Stack.Pop()
+ offset, size = scope.Stack.Pop(), scope.Stack.Pop()
input = scope.Memory.GetCopy(int64(offset.Uint64()), int64(size.Uint64()))
gas = scope.Contract.Gas
)
@@ -604,7 +613,7 @@ func opCreate(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]b
} else {
stackvalue.SetBytes(addr.Bytes())
}
- scope.Stack.push(&stackvalue)
+ scope.Stack.Push(&stackvalue)
scope.Contract.Gas += returnGas
if suberr == ErrExecutionReverted {
@@ -620,9 +629,9 @@ func opCreate2(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]
return nil, ErrWriteProtection
}
var (
- endowment = scope.Stack.pop()
- offset, size = scope.Stack.pop(), scope.Stack.pop()
- salt = scope.Stack.pop()
+ endowment = scope.Stack.Pop()
+ offset, size = scope.Stack.Pop(), scope.Stack.Pop()
+ salt = scope.Stack.Pop()
input = scope.Memory.GetCopy(int64(offset.Uint64()), int64(size.Uint64()))
gas = scope.Contract.Gas
)
@@ -639,7 +648,7 @@ func opCreate2(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]
} else {
stackvalue.SetBytes(addr.Bytes())
}
- scope.Stack.push(&stackvalue)
+ scope.Stack.Push(&stackvalue)
scope.Contract.Gas += returnGas
if suberr == ErrExecutionReverted {
@@ -654,10 +663,10 @@ func opCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byt
stack := scope.Stack
// Pop gas. The actual gas in interpreter.evm.callGasTemp.
// We can use this as a temporary value
- temp := stack.pop()
+ temp := stack.Pop()
gas := interpreter.evm.callGasTemp
// Pop other call parameters.
- addr, value, inOffset, inSize, retOffset, retSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop()
+ addr, value, inOffset, inSize, retOffset, retSize := stack.Pop(), stack.Pop(), stack.Pop(), stack.Pop(), stack.Pop(), stack.Pop()
toAddr := common.Address(addr.Bytes20())
// Get the arguments from the memory.
args := scope.Memory.GetPtr(int64(inOffset.Uint64()), int64(inSize.Uint64()))
@@ -675,7 +684,7 @@ func opCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byt
} else {
temp.SetOne()
}
- stack.push(&temp)
+ stack.Push(&temp)
if err == nil || err == ErrExecutionReverted {
scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
}
@@ -689,10 +698,10 @@ func opCallCode(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([
// Pop gas. The actual gas is in interpreter.evm.callGasTemp.
stack := scope.Stack
// We use it as a temporary value
- temp := stack.pop()
+ temp := stack.Pop()
gas := interpreter.evm.callGasTemp
// Pop other call parameters.
- addr, value, inOffset, inSize, retOffset, retSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop()
+ addr, value, inOffset, inSize, retOffset, retSize := stack.Pop(), stack.Pop(), stack.Pop(), stack.Pop(), stack.Pop(), stack.Pop()
toAddr := common.Address(addr.Bytes20())
// Get arguments from the memory.
args := scope.Memory.GetPtr(int64(inOffset.Uint64()), int64(inSize.Uint64()))
@@ -707,7 +716,7 @@ func opCallCode(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([
} else {
temp.SetOne()
}
- stack.push(&temp)
+ stack.Push(&temp)
if err == nil || err == ErrExecutionReverted {
scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
}
@@ -721,10 +730,10 @@ func opDelegateCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext
stack := scope.Stack
// Pop gas. The actual gas is in interpreter.evm.callGasTemp.
// We use it as a temporary value
- temp := stack.pop()
+ temp := stack.Pop()
gas := interpreter.evm.callGasTemp
// Pop other call parameters.
- addr, inOffset, inSize, retOffset, retSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop()
+ addr, inOffset, inSize, retOffset, retSize := stack.Pop(), stack.Pop(), stack.Pop(), stack.Pop(), stack.Pop()
toAddr := common.Address(addr.Bytes20())
// Get arguments from the memory.
args := scope.Memory.GetPtr(int64(inOffset.Uint64()), int64(inSize.Uint64()))
@@ -735,7 +744,7 @@ func opDelegateCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext
} else {
temp.SetOne()
}
- stack.push(&temp)
+ stack.Push(&temp)
if err == nil || err == ErrExecutionReverted {
scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
}
@@ -749,10 +758,10 @@ func opStaticCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext)
// Pop gas. The actual gas is in interpreter.evm.callGasTemp.
stack := scope.Stack
// We use it as a temporary value
- temp := stack.pop()
+ temp := stack.Pop()
gas := interpreter.evm.callGasTemp
// Pop other call parameters.
- addr, inOffset, inSize, retOffset, retSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop()
+ addr, inOffset, inSize, retOffset, retSize := stack.Pop(), stack.Pop(), stack.Pop(), stack.Pop(), stack.Pop()
toAddr := common.Address(addr.Bytes20())
// Get arguments from the memory.
args := scope.Memory.GetPtr(int64(inOffset.Uint64()), int64(inSize.Uint64()))
@@ -763,7 +772,7 @@ func opStaticCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext)
} else {
temp.SetOne()
}
- stack.push(&temp)
+ stack.Push(&temp)
if err == nil || err == ErrExecutionReverted {
scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
}
@@ -774,14 +783,14 @@ func opStaticCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext)
}
func opReturn(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
- offset, size := scope.Stack.pop(), scope.Stack.pop()
+ offset, size := scope.Stack.Pop(), scope.Stack.Pop()
ret := scope.Memory.GetPtr(int64(offset.Uint64()), int64(size.Uint64()))
return ret, errStopToken
}
func opRevert(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
- offset, size := scope.Stack.pop(), scope.Stack.pop()
+ offset, size := scope.Stack.Pop(), scope.Stack.Pop()
ret := scope.Memory.GetPtr(int64(offset.Uint64()), int64(size.Uint64()))
interpreter.returnData = ret
@@ -800,7 +809,7 @@ func opSelfdestruct(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext
if interpreter.readOnly {
return nil, ErrWriteProtection
}
- beneficiary := scope.Stack.pop()
+ beneficiary := scope.Stack.Pop()
balance := interpreter.evm.StateDB.GetBalance(scope.Contract.Address())
interpreter.evm.StateDB.AddBalance(beneficiary.Bytes20(), balance)
interpreter.evm.StateDB.SelfDestruct(scope.Contract.Address())
@@ -815,7 +824,7 @@ func opSelfdestruct6780(pc *uint64, interpreter *EVMInterpreter, scope *ScopeCon
if interpreter.readOnly {
return nil, ErrWriteProtection
}
- beneficiary := scope.Stack.pop()
+ beneficiary := scope.Stack.Pop()
balance := interpreter.evm.StateDB.GetBalance(scope.Contract.Address())
interpreter.evm.StateDB.SubBalance(scope.Contract.Address(), balance)
interpreter.evm.StateDB.AddBalance(beneficiary.Bytes20(), balance)
@@ -837,9 +846,9 @@ func makeLog(size int) executionFunc {
}
topics := make([]common.Hash, size)
stack := scope.Stack
- mStart, mSize := stack.pop(), stack.pop()
+ mStart, mSize := stack.Pop(), stack.Pop()
for i := 0; i < size; i++ {
- addr := stack.pop()
+ addr := stack.Pop()
topics[i] = addr.Bytes32()
}
@@ -865,9 +874,9 @@ func opPush1(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]by
)
*pc += 1
if *pc < codeLen {
- scope.Stack.push(integer.SetUint64(uint64(scope.Contract.Code[*pc])))
+ scope.Stack.Push(integer.SetUint64(uint64(scope.Contract.Code[*pc])))
} else {
- scope.Stack.push(integer.Clear())
+ scope.Stack.Push(integer.Clear())
}
return nil, nil
}
@@ -888,7 +897,7 @@ func makePush(size uint64, pushByteSize int) executionFunc {
}
integer := new(uint256.Int)
- scope.Stack.push(integer.SetBytes(common.RightPadBytes(
+ scope.Stack.Push(integer.SetBytes(common.RightPadBytes(
scope.Contract.Code[startMin:endMin], pushByteSize)))
*pc += size
@@ -899,7 +908,7 @@ func makePush(size uint64, pushByteSize int) executionFunc {
// make dup instruction function
func makeDup(size int64) executionFunc {
return func(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
- scope.Stack.dup(int(size))
+ scope.Stack.Dup(int(size))
return nil, nil
}
}
@@ -909,7 +918,7 @@ func makeSwap(size int64) executionFunc {
// switch n + 1 otherwise n would be swapped with n
size++
return func(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
- scope.Stack.swap(int(size))
+ scope.Stack.Swap(int(size))
return nil, nil
}
}
diff --git a/core/vm/instructions_test.go b/core/vm/instructions_test.go
index 8653864d11e4..f30dfd80027d 100644
--- a/core/vm/instructions_test.go
+++ b/core/vm/instructions_test.go
@@ -25,6 +25,8 @@ import (
"strings"
"testing"
+ "github.com/stretchr/testify/require"
+
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/math"
"github.com/ethereum/go-ethereum/core/rawdb"
@@ -105,23 +107,25 @@ func init() {
func testTwoOperandOp(t *testing.T, tests []TwoOperandTestcase, opFn executionFunc, name string) {
var (
- env = NewEVM(BlockContext{}, TxContext{}, nil, params.TestChainConfig, Config{})
- stack = newstack()
- pc = uint64(0)
- evmInterpreter = env.interpreter
+ env = NewEVM(BlockContext{}, TxContext{}, nil, params.TestChainConfig, Config{})
+ stack, err = NewStack()
+ pc = uint64(0)
+ interpreter = env.interpreter
)
+ require.NoError(t, err)
+
for i, test := range tests {
x := new(uint256.Int).SetBytes(common.Hex2Bytes(test.X))
y := new(uint256.Int).SetBytes(common.Hex2Bytes(test.Y))
expected := new(uint256.Int).SetBytes(common.Hex2Bytes(test.Expected))
- stack.push(x)
- stack.push(y)
- opFn(&pc, evmInterpreter, &ScopeContext{nil, stack, nil})
- if len(stack.data) != 1 {
- t.Errorf("Expected one item on stack after %v, got %d: ", name, len(stack.data))
+ stack.Push(x)
+ stack.Push(y)
+ opFn(&pc, interpreter.(*EVMInterpreter), &ScopeContext{nil, stack, nil})
+ if len(stack.Data) != 1 {
+ t.Errorf("Expected one item on stack after %v, got %d: ", name, len(stack.Data))
}
- actual := stack.pop()
+ actual := stack.Pop()
if actual.Cmp(expected) != 0 {
t.Errorf("Testcase %v %d, %v(%x, %x): expected %x, got %x", name, i, name, x, y, expected, actual)
@@ -205,10 +209,13 @@ func TestSAR(t *testing.T) {
func TestAddMod(t *testing.T) {
var (
env = NewEVM(BlockContext{}, TxContext{}, nil, params.TestChainConfig, Config{})
- stack = newstack()
+ stack, err = NewStack()
evmInterpreter = NewEVMInterpreter(env)
pc = uint64(0)
)
+
+ require.NoError(t, err)
+
tests := []struct {
x string
y string
@@ -229,11 +236,11 @@ func TestAddMod(t *testing.T) {
y := new(uint256.Int).SetBytes(common.Hex2Bytes(test.y))
z := new(uint256.Int).SetBytes(common.Hex2Bytes(test.z))
expected := new(uint256.Int).SetBytes(common.Hex2Bytes(test.expected))
- stack.push(z)
- stack.push(y)
- stack.push(x)
+ stack.Push(z)
+ stack.Push(y)
+ stack.Push(x)
opAddmod(&pc, evmInterpreter, &ScopeContext{nil, stack, nil})
- actual := stack.pop()
+ actual := stack.Pop()
if actual.Cmp(expected) != 0 {
t.Errorf("Testcase %d, expected %x, got %x", i, expected, actual)
}
@@ -249,18 +256,21 @@ func TestWriteExpectedValues(t *testing.T) {
getResult := func(args []*twoOperandParams, opFn executionFunc) []TwoOperandTestcase {
var (
env = NewEVM(BlockContext{}, TxContext{}, nil, params.TestChainConfig, Config{})
- stack = newstack()
+ stack, err = NewStack()
pc = uint64(0)
interpreter = env.interpreter
)
+
+ require.NoError(t, err)
+
result := make([]TwoOperandTestcase, len(args))
for i, param := range args {
x := new(uint256.Int).SetBytes(common.Hex2Bytes(param.x))
y := new(uint256.Int).SetBytes(common.Hex2Bytes(param.y))
- stack.push(x)
- stack.push(y)
- opFn(&pc, interpreter, &ScopeContext{nil, stack, nil})
- actual := stack.pop()
+ stack.Push(x)
+ stack.Push(y)
+ opFn(&pc, interpreter.(*EVMInterpreter), &ScopeContext{nil, stack, nil})
+ actual := stack.Pop()
result[i] = TwoOperandTestcase{param.x, param.y, fmt.Sprintf("%064x", actual)}
}
return result
@@ -294,11 +304,10 @@ func TestJsonTestcases(t *testing.T) {
func opBenchmark(bench *testing.B, op executionFunc, args ...string) {
var (
env = NewEVM(BlockContext{}, TxContext{}, nil, params.TestChainConfig, Config{})
- stack = newstack()
+ stack, _ = NewStack()
scope = &ScopeContext{nil, stack, nil}
evmInterpreter = NewEVMInterpreter(env)
)
-
env.interpreter = evmInterpreter
// convert args
intArgs := make([]*uint256.Int, len(args))
@@ -309,10 +318,10 @@ func opBenchmark(bench *testing.B, op executionFunc, args ...string) {
bench.ResetTimer()
for i := 0; i < bench.N; i++ {
for _, arg := range intArgs {
- stack.push(arg)
+ stack.Push(arg)
}
op(&pc, evmInterpreter, scope)
- stack.pop()
+ stack.Pop()
}
bench.StopTimer()
@@ -535,23 +544,25 @@ func BenchmarkOpIsZero(b *testing.B) {
func TestOpMstore(t *testing.T) {
var (
env = NewEVM(BlockContext{}, TxContext{}, nil, params.TestChainConfig, Config{})
- stack = newstack()
+ stack, err = NewStack()
mem = NewMemory()
evmInterpreter = NewEVMInterpreter(env)
)
+ require.NoError(t, err)
+
env.interpreter = evmInterpreter
mem.Resize(64)
pc := uint64(0)
v := "abcdef00000000000000abba000000000deaf000000c0de00100000000133700"
- stack.push(new(uint256.Int).SetBytes(common.Hex2Bytes(v)))
- stack.push(new(uint256.Int))
+ stack.Push(new(uint256.Int).SetBytes(common.Hex2Bytes(v)))
+ stack.Push(new(uint256.Int))
opMstore(&pc, evmInterpreter, &ScopeContext{mem, stack, nil})
if got := common.Bytes2Hex(mem.GetCopy(0, 32)); got != v {
t.Fatalf("Mstore fail, got %v, expected %v", got, v)
}
- stack.push(new(uint256.Int).SetUint64(0x1))
- stack.push(new(uint256.Int))
+ stack.Push(new(uint256.Int).SetUint64(0x1))
+ stack.Push(new(uint256.Int))
opMstore(&pc, evmInterpreter, &ScopeContext{mem, stack, nil})
if common.Bytes2Hex(mem.GetCopy(0, 32)) != "0000000000000000000000000000000000000000000000000000000000000001" {
t.Fatalf("Mstore failed to overwrite previous value")
@@ -561,7 +572,7 @@ func TestOpMstore(t *testing.T) {
func BenchmarkOpMstore(bench *testing.B) {
var (
env = NewEVM(BlockContext{}, TxContext{}, nil, params.TestChainConfig, Config{})
- stack = newstack()
+ stack, _ = NewStack()
mem = NewMemory()
evmInterpreter = NewEVMInterpreter(env)
)
@@ -574,8 +585,8 @@ func BenchmarkOpMstore(bench *testing.B) {
bench.ResetTimer()
for i := 0; i < bench.N; i++ {
- stack.push(value)
- stack.push(memStart)
+ stack.Push(value)
+ stack.Push(memStart)
opMstore(&pc, evmInterpreter, &ScopeContext{mem, stack, nil})
}
}
@@ -584,7 +595,7 @@ func TestOpTstore(t *testing.T) {
var (
statedb, _ = state.New(types.EmptyRootHash, state.NewDatabase(rawdb.NewMemoryDatabase()), nil)
env = NewEVM(BlockContext{}, TxContext{}, statedb, params.TestChainConfig, Config{})
- stack = newstack()
+ stack, err = NewStack()
mem = NewMemory()
evmInterpreter = NewEVMInterpreter(env)
caller = common.Address{}
@@ -594,6 +605,7 @@ func TestOpTstore(t *testing.T) {
scopeContext = ScopeContext{mem, stack, contract}
value = common.Hex2Bytes("abcdef00000000000000abba000000000deaf000000c0de00100000000133700")
)
+ require.NoError(t, err)
// Add a stateObject for the caller and the contract being called
statedb.CreateAccount(caller)
@@ -602,22 +614,22 @@ func TestOpTstore(t *testing.T) {
env.interpreter = evmInterpreter
pc := uint64(0)
// push the value to the stack
- stack.push(new(uint256.Int).SetBytes(value))
+ stack.Push(new(uint256.Int).SetBytes(value))
// push the location to the stack
- stack.push(new(uint256.Int))
+ stack.Push(new(uint256.Int))
opTstore(&pc, evmInterpreter, &scopeContext)
// there should be no elements on the stack after TSTORE
- if stack.len() != 0 {
+ if stack.Len() != 0 {
t.Fatal("stack wrong size")
}
// push the location to the stack
- stack.push(new(uint256.Int))
+ stack.Push(new(uint256.Int))
opTload(&pc, evmInterpreter, &scopeContext)
// there should be one element on the stack after TLOAD
- if stack.len() != 1 {
+ if stack.Len() != 1 {
t.Fatal("stack wrong size")
}
- val := stack.peek()
+ val := stack.Peek()
if !bytes.Equal(val.Bytes(), value) {
t.Fatal("incorrect element read from transient storage")
}
@@ -626,7 +638,7 @@ func TestOpTstore(t *testing.T) {
func BenchmarkOpKeccak256(bench *testing.B) {
var (
env = NewEVM(BlockContext{}, TxContext{}, nil, params.TestChainConfig, Config{})
- stack = newstack()
+ stack, _ = NewStack()
mem = NewMemory()
evmInterpreter = NewEVMInterpreter(env)
)
@@ -637,8 +649,8 @@ func BenchmarkOpKeccak256(bench *testing.B) {
bench.ResetTimer()
for i := 0; i < bench.N; i++ {
- stack.push(uint256.NewInt(32))
- stack.push(start)
+ stack.Push(uint256.NewInt(32))
+ stack.Push(start)
opKeccak256(&pc, evmInterpreter, &ScopeContext{mem, stack, nil})
}
}
@@ -703,9 +715,9 @@ func TestCreate2Addreses(t *testing.T) {
/*
stack := newstack()
// salt, but we don't need that for this test
- stack.push(big.NewInt(int64(len(code)))) //size
- stack.push(big.NewInt(0)) // memstart
- stack.push(big.NewInt(0)) // value
+ stack.Push(big.NewInt(int64(len(code)))) //size
+ stack.Push(big.NewInt(0)) // memstart
+ stack.Push(big.NewInt(0)) // value
gas, _ := gasCreate2(params.GasTable{}, nil, nil, stack, nil, 0)
fmt.Printf("Example %d\n* address `0x%x`\n* salt `0x%x`\n* init_code `0x%x`\n* gas (assuming no mem expansion): `%v`\n* result: `%s`\n\n", i,origin, salt, code, gas, address.String())
*/
@@ -729,16 +741,19 @@ func TestRandom(t *testing.T) {
{name: "hash(0x010203)", random: crypto.Keccak256Hash([]byte{0x01, 0x02, 0x03})},
} {
var (
- env = NewEVM(BlockContext{Random: &tt.random}, TxContext{}, nil, params.TestChainConfig, Config{})
- stack = newstack()
- pc = uint64(0)
- evmInterpreter = env.interpreter
+ env = NewEVM(BlockContext{Random: &tt.random}, TxContext{}, nil, params.TestChainConfig, Config{})
+ stack, err = NewStack()
+ pc = uint64(0)
+ interpreter = env.interpreter
)
- opRandom(&pc, evmInterpreter, &ScopeContext{nil, stack, nil})
- if len(stack.data) != 1 {
- t.Errorf("Expected one item on stack after %v, got %d: ", tt.name, len(stack.data))
+
+ require.NoError(t, err)
+
+ opRandom(&pc, interpreter.(*EVMInterpreter), &ScopeContext{nil, stack, nil})
+ if len(stack.Data) != 1 {
+ t.Errorf("Expected one item on stack after %v, got %d: ", tt.name, len(stack.Data))
}
- actual := stack.pop()
+ actual := stack.Pop()
expected, overflow := uint256.FromBig(new(big.Int).SetBytes(tt.random.Bytes()))
if overflow {
t.Errorf("Testcase %v: invalid overflow", tt.name)
@@ -771,16 +786,17 @@ func TestBlobHash(t *testing.T) {
} {
var (
env = NewEVM(BlockContext{}, TxContext{BlobHashes: tt.hashes}, nil, params.TestChainConfig, Config{})
- stack = newstack()
+ stack, err = NewStack()
pc = uint64(0)
evmInterpreter = env.interpreter
)
- stack.push(uint256.NewInt(tt.idx))
- opBlobHash(&pc, evmInterpreter, &ScopeContext{nil, stack, nil})
- if len(stack.data) != 1 {
- t.Errorf("Expected one item on stack after %v, got %d: ", tt.name, len(stack.data))
+ require.NoError(t, err)
+ stack.Push(uint256.NewInt(tt.idx))
+ opBlobHash(&pc, evmInterpreter.(*EVMInterpreter), &ScopeContext{nil, stack, nil})
+ if len(stack.Data) != 1 {
+ t.Errorf("Expected one item on stack after %v, got %d: ", tt.name, len(stack.Data))
}
- actual := stack.pop()
+ actual := stack.Pop()
expected, overflow := uint256.FromBig(new(big.Int).SetBytes(tt.expect.Bytes()))
if overflow {
t.Errorf("Testcase %v: invalid overflow", tt.name)
@@ -874,10 +890,11 @@ func TestOpMCopy(t *testing.T) {
} {
var (
env = NewEVM(BlockContext{}, TxContext{}, nil, params.TestChainConfig, Config{})
- stack = newstack()
+ stack, err = NewStack()
pc = uint64(0)
evmInterpreter = env.interpreter
)
+ require.NoError(t, err)
data := common.FromHex(strings.ReplaceAll(tc.pre, " ", ""))
// Set pre
mem := NewMemory()
@@ -888,9 +905,9 @@ func TestOpMCopy(t *testing.T) {
src, _ := uint256.FromHex(tc.src)
dst, _ := uint256.FromHex(tc.dst)
- stack.push(len)
- stack.push(src)
- stack.push(dst)
+ stack.Push(len)
+ stack.Push(src)
+ stack.Push(dst)
wantErr := (tc.wantGas == 0)
// Calc mem expansion
var memorySize uint64
@@ -917,7 +934,7 @@ func TestOpMCopy(t *testing.T) {
mem.Resize(memorySize)
}
// Do the copy
- opMcopy(&pc, evmInterpreter, &ScopeContext{mem, stack, nil})
+ opMcopy(&pc, evmInterpreter.(*EVMInterpreter), &ScopeContext{mem, stack, nil})
want := common.FromHex(strings.ReplaceAll(tc.want, " ", ""))
if have := mem.store; !bytes.Equal(want, have) {
t.Errorf("case %d: \nwant: %#x\nhave: %#x\n", i, want, have)
diff --git a/core/vm/interface.go b/core/vm/interface.go
index 25bfa0672067..b03edac647ac 100644
--- a/core/vm/interface.go
+++ b/core/vm/interface.go
@@ -93,3 +93,23 @@ type CallContext interface {
// Create creates a new contract
Create(env *EVM, me ContractRef, data []byte, gas, value *big.Int) ([]byte, common.Address, error)
}
+
+// Interpreter is used to run Ethereum based contracts and will utilize the
+// passed environment to query external sources for state information.
+// The Interpreter will run the byte code VM based on the passed
+// configuration.
+type Interpreter interface {
+ // EVM returns the EVM instance
+ EVM() *EVM
+ // Config returns the configuration of the interpreter
+ Config() Config
+ // ReadOnly returns whether the interpreter is in read-only mode
+ ReadOnly() bool
+ // ReturnData gets the last CALL's return data for subsequent reuse
+ ReturnData() []byte
+ // SetReturnData sets the last CALL's return data
+ SetReturnData([]byte)
+ // Run loops and evaluates the contract's code with the given input data and returns
+ // the return byte-slice and an error if one occurred.
+ Run(contract *Contract, input []byte, static bool) ([]byte, error)
+}
diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go
index 1968289f4eaa..797df8df8e5c 100644
--- a/core/vm/interpreter.go
+++ b/core/vm/interpreter.go
@@ -39,6 +39,8 @@ type ScopeContext struct {
Contract *Contract
}
+var _ Interpreter = &EVMInterpreter{}
+
// EVMInterpreter represents an EVM interpreter
type EVMInterpreter struct {
evm *EVM
@@ -54,37 +56,11 @@ type EVMInterpreter struct {
// NewEVMInterpreter returns a new instance of the Interpreter.
func NewEVMInterpreter(evm *EVM) *EVMInterpreter {
// If jump table was not initialised we set the default one.
- var table *JumpTable
- switch {
- case evm.chainRules.IsCancun:
- table = &cancunInstructionSet
- case evm.chainRules.IsShanghai:
- table = &shanghaiInstructionSet
- case evm.chainRules.IsMerge:
- table = &mergeInstructionSet
- case evm.chainRules.IsLondon:
- table = &londonInstructionSet
- case evm.chainRules.IsBerlin:
- table = &berlinInstructionSet
- case evm.chainRules.IsIstanbul:
- table = &istanbulInstructionSet
- case evm.chainRules.IsConstantinople:
- table = &constantinopleInstructionSet
- case evm.chainRules.IsByzantium:
- table = &byzantiumInstructionSet
- case evm.chainRules.IsEIP158:
- table = &spuriousDragonInstructionSet
- case evm.chainRules.IsEIP150:
- table = &tangerineWhistleInstructionSet
- case evm.chainRules.IsHomestead:
- table = &homesteadInstructionSet
- default:
- table = &frontierInstructionSet
- }
+ table := DefaultJumpTable(evm.chainRules)
var extraEips []int
if len(evm.Config.ExtraEips) > 0 {
// Deep-copy jumptable to prevent modification of opcodes in other tables
- table = copyJumpTable(table)
+ table = CopyJumpTable(table)
}
for _, eip := range evm.Config.ExtraEips {
if err := EnableEIP(eip, table); err != nil {
@@ -98,6 +74,31 @@ func NewEVMInterpreter(evm *EVM) *EVMInterpreter {
return &EVMInterpreter{evm: evm, table: table}
}
+// EVM returns the EVM instance
+func (in *EVMInterpreter) EVM() *EVM {
+ return in.evm
+}
+
+// Config returns the configuration of the interpreter
+func (in EVMInterpreter) Config() Config {
+ return in.evm.Config
+}
+
+// ReadOnly returns whether the interpreter is in read-only mode
+func (in EVMInterpreter) ReadOnly() bool {
+ return in.readOnly
+}
+
+// ReturnData gets the last CALL's return data for subsequent reuse
+func (in *EVMInterpreter) ReturnData() []byte {
+ return in.returnData
+}
+
+// SetReturnData sets the last CALL's return data
+func (in *EVMInterpreter) SetReturnData(data []byte) {
+ in.returnData = data
+}
+
// Run loops and evaluates the contract's code with the given input data and returns
// the return byte-slice and an error if one occurred.
//
@@ -125,19 +126,23 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
return nil, nil
}
+ mem := NewMemory() // bound memory
+ stack, err := NewStack() // local stack
+ if err != nil {
+ return nil, err
+ }
+ callContext := &ScopeContext{
+ Memory: mem,
+ Stack: stack,
+ Contract: contract,
+ }
+
var (
- op OpCode // current opcode
- mem = NewMemory() // bound memory
- stack = newstack() // local stack
- callContext = &ScopeContext{
- Memory: mem,
- Stack: stack,
- Contract: contract,
- }
+ op OpCode // current opcode
// For optimisation reason we're using uint64 as the program counter.
// It's theoretically possible to go above 2^64. The YP defines the PC
// to be uint256. Practically much less so feasible.
- pc = uint64(0) // program counter
+ pc uint64 // program counter
cost uint64
// copies used by tracer
pcCopy uint64 // needed for the deferred EVMLogger
@@ -149,9 +154,7 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
// Don't move this deferred function, it's placed before the capturestate-deferred method,
// so that it gets executed _after_: the capturestate needs the stacks before
// they are returned to the pools
- defer func() {
- returnStack(stack)
- }()
+ defer ReturnNormalStack(stack)
contract.Input = input
if debug {
@@ -180,7 +183,7 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
operation := in.table[op]
cost = operation.constantGas // For tracing
// Validate stack
- if sLen := stack.len(); sLen < operation.minStack {
+ if sLen := stack.Len(); sLen < operation.minStack {
return nil, &ErrStackUnderflow{stackLen: sLen, required: operation.minStack}
} else if sLen > operation.maxStack {
return nil, &ErrStackOverflow{stackLen: sLen, limit: operation.maxStack}
diff --git a/core/vm/jump_table.go b/core/vm/jump_table.go
index 65716f9442af..83419ecbb80f 100644
--- a/core/vm/jump_table.go
+++ b/core/vm/jump_table.go
@@ -24,7 +24,7 @@ import (
type (
executionFunc func(pc *uint64, interpreter *EVMInterpreter, callContext *ScopeContext) ([]byte, error)
- gasFunc func(*EVM, *Contract, *Stack, *Memory, uint64) (uint64, error) // last parameter is the requested memory size as a uint64
+ gasFunc func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error)
// memorySizeFunc returns the required size, and whether the operation overflowed a uint64
memorySizeFunc func(*Stack) (size uint64, overflow bool)
)
@@ -45,27 +45,58 @@ type operation struct {
}
var (
- frontierInstructionSet = newFrontierInstructionSet()
- homesteadInstructionSet = newHomesteadInstructionSet()
- tangerineWhistleInstructionSet = newTangerineWhistleInstructionSet()
- spuriousDragonInstructionSet = newSpuriousDragonInstructionSet()
- byzantiumInstructionSet = newByzantiumInstructionSet()
- constantinopleInstructionSet = newConstantinopleInstructionSet()
- istanbulInstructionSet = newIstanbulInstructionSet()
- berlinInstructionSet = newBerlinInstructionSet()
- londonInstructionSet = newLondonInstructionSet()
- mergeInstructionSet = newMergeInstructionSet()
- shanghaiInstructionSet = newShanghaiInstructionSet()
- cancunInstructionSet = newCancunInstructionSet()
+ FrontierInstructionSet = newFrontierInstructionSet()
+ HomesteadInstructionSet = newHomesteadInstructionSet()
+ TangerineWhistleInstructionSet = newTangerineWhistleInstructionSet()
+ SpuriousDragonInstructionSet = newSpuriousDragonInstructionSet()
+ ByzantiumInstructionSet = newByzantiumInstructionSet()
+ ConstantinopleInstructionSet = newConstantinopleInstructionSet()
+ IstanbulInstructionSet = newIstanbulInstructionSet()
+ BerlinInstructionSet = newBerlinInstructionSet()
+ LondonInstructionSet = newLondonInstructionSet()
+ MergeInstructionSet = newMergeInstructionSet()
+ ShanghaiInstructionSet = newShanghaiInstructionSet()
+ CancunInstructionSet = newCancunInstructionSet()
)
// JumpTable contains the EVM opcodes supported at a given fork.
type JumpTable [256]*operation
-func validate(jt JumpTable) JumpTable {
+// DefaultJumpTable defines the default jump table used by the EVM interpreter.
+func DefaultJumpTable(rules params.Rules) (table *JumpTable) {
+ switch {
+ case rules.IsCancun:
+ table = &CancunInstructionSet
+ case rules.IsShanghai:
+ table = &ShanghaiInstructionSet
+ case rules.IsMerge:
+ table = &MergeInstructionSet
+ case rules.IsLondon:
+ table = &LondonInstructionSet
+ case rules.IsBerlin:
+ table = &BerlinInstructionSet
+ case rules.IsIstanbul:
+ table = &IstanbulInstructionSet
+ case rules.IsConstantinople:
+ table = &ConstantinopleInstructionSet
+ case rules.IsByzantium:
+ table = &ByzantiumInstructionSet
+ case rules.IsEIP158:
+ table = &SpuriousDragonInstructionSet
+ case rules.IsEIP150:
+ table = &TangerineWhistleInstructionSet
+ case rules.IsHomestead:
+ table = &HomesteadInstructionSet
+ default:
+ table = &FrontierInstructionSet
+ }
+ return table
+}
+
+func (jt JumpTable) Validate() error {
for i, op := range jt {
if op == nil {
- panic(fmt.Sprintf("op %#x is not set", i))
+ return fmt.Errorf("op %#x is not set", i)
}
// The interpreter has an assumption that if the memorySize function is
// set, then the dynamicGas function is also set. This is a somewhat
@@ -74,10 +105,17 @@ func validate(jt JumpTable) JumpTable {
// in there, this little sanity check prevents us from merging in a
// change which violates it.
if op.memorySize != nil && op.dynamicGas == nil {
- panic(fmt.Sprintf("op %v has dynamic memory but not dynamic gas", OpCode(i).String()))
+ return fmt.Errorf("op %v has dynamic memory but not dynamic gas", OpCode(i).String())
}
}
- return jt
+ return nil
+}
+
+// MustValidate panics if the operations are not valid.
+func (jt JumpTable) MustValidate() {
+ if err := jt.Validate(); err != nil {
+ panic(err)
+ }
}
func newCancunInstructionSet() JumpTable {
@@ -87,7 +125,8 @@ func newCancunInstructionSet() JumpTable {
enable1153(&instructionSet) // EIP-1153 "Transient Storage"
enable5656(&instructionSet) // EIP-5656 (MCOPY opcode)
enable6780(&instructionSet) // EIP-6780 SELFDESTRUCT only in same transaction
- return validate(instructionSet)
+ instructionSet.MustValidate()
+ return instructionSet
}
func newShanghaiInstructionSet() JumpTable {
@@ -95,7 +134,8 @@ func newShanghaiInstructionSet() JumpTable {
enable3855(&instructionSet) // PUSH0 instruction
enable3860(&instructionSet) // Limit and meter initcode
- return validate(instructionSet)
+ instructionSet.MustValidate()
+ return instructionSet
}
func newMergeInstructionSet() JumpTable {
@@ -106,7 +146,8 @@ func newMergeInstructionSet() JumpTable {
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
}
- return validate(instructionSet)
+ instructionSet.MustValidate()
+ return instructionSet
}
// newLondonInstructionSet returns the frontier, homestead, byzantium,
@@ -115,7 +156,8 @@ func newLondonInstructionSet() JumpTable {
instructionSet := newBerlinInstructionSet()
enable3529(&instructionSet) // EIP-3529: Reduction in refunds https://eips.ethereum.org/EIPS/eip-3529
enable3198(&instructionSet) // Base fee opcode https://eips.ethereum.org/EIPS/eip-3198
- return validate(instructionSet)
+ instructionSet.MustValidate()
+ return instructionSet
}
// newBerlinInstructionSet returns the frontier, homestead, byzantium,
@@ -123,7 +165,8 @@ func newLondonInstructionSet() JumpTable {
func newBerlinInstructionSet() JumpTable {
instructionSet := newIstanbulInstructionSet()
enable2929(&instructionSet) // Gas cost increases for state access opcodes https://eips.ethereum.org/EIPS/eip-2929
- return validate(instructionSet)
+ instructionSet.MustValidate()
+ return instructionSet
}
// newIstanbulInstructionSet returns the frontier, homestead, byzantium,
@@ -135,7 +178,8 @@ func newIstanbulInstructionSet() JumpTable {
enable1884(&instructionSet) // Reprice reader opcodes - https://eips.ethereum.org/EIPS/eip-1884
enable2200(&instructionSet) // Net metered SSTORE - https://eips.ethereum.org/EIPS/eip-2200
- return validate(instructionSet)
+ instructionSet.MustValidate()
+ return instructionSet
}
// newConstantinopleInstructionSet returns the frontier, homestead,
@@ -174,7 +218,8 @@ func newConstantinopleInstructionSet() JumpTable {
maxStack: maxStack(4, 1),
memorySize: memoryCreate2,
}
- return validate(instructionSet)
+ instructionSet.MustValidate()
+ return instructionSet
}
// newByzantiumInstructionSet returns the frontier, homestead and
@@ -210,14 +255,16 @@ func newByzantiumInstructionSet() JumpTable {
maxStack: maxStack(2, 0),
memorySize: memoryRevert,
}
- return validate(instructionSet)
+ instructionSet.MustValidate()
+ return instructionSet
}
// EIP 158 a.k.a Spurious Dragon
func newSpuriousDragonInstructionSet() JumpTable {
instructionSet := newTangerineWhistleInstructionSet()
instructionSet[EXP].dynamicGas = gasExpEIP158
- return validate(instructionSet)
+ instructionSet.MustValidate()
+ return instructionSet
}
// EIP 150 a.k.a Tangerine Whistle
@@ -230,7 +277,8 @@ func newTangerineWhistleInstructionSet() JumpTable {
instructionSet[CALL].constantGas = params.CallGasEIP150
instructionSet[CALLCODE].constantGas = params.CallGasEIP150
instructionSet[DELEGATECALL].constantGas = params.CallGasEIP150
- return validate(instructionSet)
+ instructionSet.MustValidate()
+ return instructionSet
}
// newHomesteadInstructionSet returns the frontier and homestead
@@ -245,7 +293,8 @@ func newHomesteadInstructionSet() JumpTable {
maxStack: maxStack(6, 1),
memorySize: memoryDelegateCall,
}
- return validate(instructionSet)
+ instructionSet.MustValidate()
+ return instructionSet
}
// newFrontierInstructionSet returns the frontier instructions
@@ -1061,10 +1110,11 @@ func newFrontierInstructionSet() JumpTable {
}
}
- return validate(tbl)
+ tbl.MustValidate()
+ return tbl
}
-func copyJumpTable(source *JumpTable) *JumpTable {
+func CopyJumpTable(source *JumpTable) *JumpTable {
dest := *source
for i, op := range source {
if op != nil {
diff --git a/core/vm/jump_table_test.go b/core/vm/jump_table_test.go
index 02558035c0e9..addffa4b1db9 100644
--- a/core/vm/jump_table_test.go
+++ b/core/vm/jump_table_test.go
@@ -28,7 +28,7 @@ func TestJumpTableCopy(t *testing.T) {
require.Equal(t, uint64(0), tbl[SLOAD].constantGas)
// a deep copy won't modify the shared jump table
- deepCopy := copyJumpTable(&tbl)
+ deepCopy := CopyJumpTable(&tbl)
deepCopy[SLOAD].constantGas = 100
require.Equal(t, uint64(100), deepCopy[SLOAD].constantGas)
require.Equal(t, uint64(0), tbl[SLOAD].constantGas)
diff --git a/core/vm/memory.go b/core/vm/memory.go
index e0202fd7c020..eb028f069ff5 100644
--- a/core/vm/memory.go
+++ b/core/vm/memory.go
@@ -114,3 +114,9 @@ func (m *Memory) Copy(dst, src, len uint64) {
}
copy(m.store[dst:], m.store[src:src+len])
}
+
+// GasCost calculates the quadratic gas for memory expansion. It does so
+// only for the memory region that is expanded, not the total memory.
+func (m *Memory) GasCost(newMemSize uint64) (uint64, error) {
+ return memoryGasCost(m, newMemSize)
+}
diff --git a/core/vm/opcode_hooks.go b/core/vm/opcode_hooks.go
new file mode 100644
index 000000000000..afb8b2cd9e39
--- /dev/null
+++ b/core/vm/opcode_hooks.go
@@ -0,0 +1,48 @@
+// Copyright 2014 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+package vm
+
+import "github.com/ethereum/go-ethereum/common"
+
+// OpCodeHooks is a set of hooks that can be used to intercept and modify the
+// behavior of the EVM when executing certain opcodes.
+// The hooks are called before the execution of the respective opcodes.
+type OpCodeHooks interface {
+ // CallHook is called before executing a CALL, CALLCODE, DELEGATECALL and STATICCALL opcodes.
+ CallHook(evm *EVM, caller common.Address, recipient common.Address) error
+ // CreateHook is called before executing a CREATE and CREATE2 opcodes.
+ CreateHook(evm *EVM, caller common.Address) error
+}
+
+type NoopOpCodeHooks struct {
+}
+
+func (NoopOpCodeHooks) CallHook(evm *EVM, caller common.Address, recipient common.Address) error {
+ return nil
+}
+
+func (NoopOpCodeHooks) CreateHook(evm *EVM, caller common.Address) error {
+ return nil
+}
+
+func newNoopOpCodeHooks() OpCodeHooks {
+ return NoopOpCodeHooks{}
+}
+
+func NewDefaultOpCodeHooks() OpCodeHooks {
+ return newNoopOpCodeHooks()
+}
diff --git a/core/vm/operations_acl.go b/core/vm/operations_acl.go
index f420a241058b..040ff328e18d 100644
--- a/core/vm/operations_acl.go
+++ b/core/vm/operations_acl.go
@@ -32,7 +32,7 @@ func makeGasSStoreFunc(clearingRefund uint64) gasFunc {
}
// Gas sentry honoured, do the actual gas calculation based on the stored value
var (
- y, x = stack.Back(1), stack.peek()
+ y, x = stack.Back(1), stack.Peek()
slot = common.Hash(x.Bytes32())
current = evm.StateDB.GetState(contract.Address(), slot)
cost = uint64(0)
@@ -101,7 +101,7 @@ func makeGasSStoreFunc(clearingRefund uint64) gasFunc {
// charge 2100 gas and add the pair to accessed_storage_keys.
// If the pair is already in accessed_storage_keys, charge 100 gas.
func gasSLoadEIP2929(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
- loc := stack.peek()
+ loc := stack.Peek()
slot := common.Hash(loc.Bytes32())
// Check slot presence in the access list
if _, slotPresent := evm.StateDB.SlotInAccessList(contract.Address(), slot); !slotPresent {
@@ -124,7 +124,7 @@ func gasExtCodeCopyEIP2929(evm *EVM, contract *Contract, stack *Stack, mem *Memo
if err != nil {
return 0, err
}
- addr := common.Address(stack.peek().Bytes20())
+ addr := common.Address(stack.Peek().Bytes20())
// Check slot presence in the access list
if !evm.StateDB.AddressInAccessList(addr) {
evm.StateDB.AddAddressToAccessList(addr)
@@ -146,7 +146,7 @@ func gasExtCodeCopyEIP2929(evm *EVM, contract *Contract, stack *Stack, mem *Memo
// - extcodesize,
// - (ext) balance
func gasEip2929AccountCheck(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
- addr := common.Address(stack.peek().Bytes20())
+ addr := common.Address(stack.Peek().Bytes20())
// Check slot presence in the access list
if !evm.StateDB.AddressInAccessList(addr) {
// If the caller cannot afford the cost, this change will be rolled back
@@ -229,7 +229,7 @@ func makeSelfdestructGasFn(refundsEnabled bool) gasFunc {
gasFunc := func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
var (
gas uint64
- address = common.Address(stack.peek().Bytes20())
+ address = common.Address(stack.Peek().Bytes20())
)
if !evm.StateDB.AddressInAccessList(address) {
// If the caller cannot afford the cost, this change will be rolled back
diff --git a/core/vm/runtime/runtime.go b/core/vm/runtime/runtime.go
index 46f2bb5d5f64..a0b150ae943b 100644
--- a/core/vm/runtime/runtime.go
+++ b/core/vm/runtime/runtime.go
@@ -126,7 +126,7 @@ func Execute(code, input []byte, cfg *Config) ([]byte, *state.StateDB, error) {
// Execute the preparatory steps for state transition which includes:
// - prepare accessList(post-berlin)
// - reset transient storage(eip 1153)
- cfg.State.Prepare(rules, cfg.Origin, cfg.Coinbase, &address, vm.ActivePrecompiles(rules), nil)
+ cfg.State.Prepare(rules, cfg.Origin, cfg.Coinbase, &address, vm.DefaultActivePrecompiles(rules), nil)
cfg.State.CreateAccount(address)
// set the receiver's (the executing contract) code for execution.
cfg.State.SetCode(address, code)
@@ -159,7 +159,7 @@ func Create(input []byte, cfg *Config) ([]byte, common.Address, uint64, error) {
// Execute the preparatory steps for state transition which includes:
// - prepare accessList(post-berlin)
// - reset transient storage(eip 1153)
- cfg.State.Prepare(rules, cfg.Origin, cfg.Coinbase, nil, vm.ActivePrecompiles(rules), nil)
+ cfg.State.Prepare(rules, cfg.Origin, cfg.Coinbase, nil, vm.DefaultActivePrecompiles(rules), nil)
// Call the code with the given configuration.
code, address, leftOverGas, err := vmenv.Create(
sender,
@@ -187,7 +187,7 @@ func Call(address common.Address, input []byte, cfg *Config) ([]byte, uint64, er
// Execute the preparatory steps for state transition which includes:
// - prepare accessList(post-berlin)
// - reset transient storage(eip 1153)
- statedb.Prepare(rules, cfg.Origin, cfg.Coinbase, &address, vm.ActivePrecompiles(rules), nil)
+ statedb.Prepare(rules, cfg.Origin, cfg.Coinbase, &address, vm.DefaultActivePrecompiles(rules), nil)
// Call the code with the given configuration.
ret, leftOverGas, err := vmenv.Call(
diff --git a/core/vm/stack.go b/core/vm/stack.go
index e1a957e2445a..bd54b95fd29d 100644
--- a/core/vm/stack.go
+++ b/core/vm/stack.go
@@ -17,6 +17,7 @@
package vm
import (
+ "fmt"
"sync"
"github.com/holiman/uint256"
@@ -24,7 +25,7 @@ import (
var stackPool = sync.Pool{
New: func() interface{} {
- return &Stack{data: make([]uint256.Int, 0, 16)}
+ return &Stack{Data: make([]uint256.Int, 0, 16)}
},
}
@@ -32,51 +33,114 @@ var stackPool = sync.Pool{
// expected to be changed and modified. stack does not take care of adding newly
// initialised objects.
type Stack struct {
- data []uint256.Int
+ Data []uint256.Int
}
-func newstack() *Stack {
- return stackPool.Get().(*Stack)
+func NewStack() (*Stack, error) {
+ stack, ok := stackPool.Get().(*Stack)
+ if !ok {
+ return nil, fmt.Errorf("Type assertion failure: cannot get Stack pointer from stackPool")
+ }
+ return stack, nil
}
-func returnStack(s *Stack) {
- s.data = s.data[:0]
- stackPool.Put(s)
-}
-
-// Data returns the underlying uint256.Int array.
-func (st *Stack) Data() []uint256.Int {
- return st.data
+func (st *Stack) Push(d *uint256.Int) {
+ // NOTE push limit (1024) is checked in baseCheck
+ st.Data = append(st.Data, *d)
}
-func (st *Stack) push(d *uint256.Int) {
- // NOTE push limit (1024) is checked in baseCheck
- st.data = append(st.data, *d)
+func (st *Stack) PushN(ds ...uint256.Int) {
+ // FIXME: Is there a way to pass args by pointers.
+ st.Data = append(st.Data, ds...)
}
-func (st *Stack) pop() (ret uint256.Int) {
- ret = st.data[len(st.data)-1]
- st.data = st.data[:len(st.data)-1]
+func (st *Stack) Pop() (ret uint256.Int) {
+ ret = st.Data[len(st.Data)-1]
+ st.Data = st.Data[:len(st.Data)-1]
return
}
-func (st *Stack) len() int {
- return len(st.data)
+func (st *Stack) Cap() int {
+ return cap(st.Data)
}
-func (st *Stack) swap(n int) {
- st.data[st.len()-n], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-n]
+func (st *Stack) Swap(n int) {
+ st.Data[st.Len()-n], st.Data[st.Len()-1] = st.Data[st.Len()-1], st.Data[st.Len()-n]
}
-func (st *Stack) dup(n int) {
- st.push(&st.data[st.len()-n])
+func (st *Stack) Dup(n int) {
+ st.Push(&st.Data[st.Len()-n])
}
-func (st *Stack) peek() *uint256.Int {
- return &st.data[st.len()-1]
+func (st *Stack) Peek() *uint256.Int {
+ return &st.Data[st.Len()-1]
}
-// Back returns the n'th item in stack
func (st *Stack) Back(n int) *uint256.Int {
- return &st.data[st.len()-n-1]
+ return &st.Data[st.Len()-n-1]
+}
+
+func (st *Stack) Reset() {
+ st.Data = st.Data[:0]
+}
+
+func (st *Stack) Len() int {
+ return len(st.Data)
+}
+
+// Print dumps the content of the stack
+func (st *Stack) Print() {
+ fmt.Println("### stack ###")
+ if len(st.Data) > 0 {
+ for i, val := range st.Data {
+ fmt.Printf("%-3d %v\n", i, val)
+ }
+ } else {
+ fmt.Println("-- empty --")
+ }
+ fmt.Println("#############")
+}
+
+func ReturnNormalStack(s *Stack) {
+ s.Data = s.Data[:0]
+ stackPool.Put(s)
+}
+
+var rStackPool = sync.Pool{
+ New: func() interface{} {
+ return &ReturnStack{data: make([]uint32, 0, 10)}
+ },
+}
+
+func ReturnRStack(rs *ReturnStack) {
+ rs.data = rs.data[:0]
+ rStackPool.Put(rs)
+}
+
+// ReturnStack is an object for basic return stack operations.
+type ReturnStack struct {
+ data []uint32
+}
+
+func NewReturnStack() (*ReturnStack, error) {
+ rStack, ok := rStackPool.Get().(*ReturnStack)
+ if !ok {
+ return nil, fmt.Errorf("type assertion failure: cannot get ReturnStack pointer from rStackPool")
+ }
+ return rStack, nil
+}
+
+func (st *ReturnStack) Push(d uint32) {
+ st.data = append(st.data, d)
+}
+
+// Pop A uint32 is sufficient as for code below 4.2G
+func (st *ReturnStack) Pop() (ret uint32) {
+ ret = st.data[len(st.data)-1]
+ st.data = st.data[:len(st.data)-1]
+ return
+}
+
+func (st *ReturnStack) Data() []uint32 {
+ return st.data
}
diff --git a/eth/tracers/js/goja.go b/eth/tracers/js/goja.go
index 07c138bae47b..f65961c60754 100644
--- a/eth/tracers/js/goja.go
+++ b/eth/tracers/js/goja.go
@@ -57,9 +57,11 @@ func init() {
// hex strings into big ints.
var bigIntProgram = goja.MustCompile("bigInt", bigIntegerJS, false)
-type toBigFn = func(vm *goja.Runtime, val string) (goja.Value, error)
-type toBufFn = func(vm *goja.Runtime, val []byte) (goja.Value, error)
-type fromBufFn = func(vm *goja.Runtime, buf goja.Value, allowString bool) ([]byte, error)
+type (
+ toBigFn = func(vm *goja.Runtime, val string) (goja.Value, error)
+ toBufFn = func(vm *goja.Runtime, val []byte) (goja.Value, error)
+ fromBufFn = func(vm *goja.Runtime, buf goja.Value, allowString bool) ([]byte, error)
+)
func toBuf(vm *goja.Runtime, bufType goja.Value, val []byte) (goja.Value, error) {
// bufType is usually Uint8Array. This is equivalent to `new Uint8Array(val)` in JS.
@@ -280,7 +282,7 @@ func (t *jsTracer) CaptureStart(env *vm.EVM, from common.Address, to common.Addr
t.ctx["block"] = t.vm.ToValue(env.Context.BlockNumber.Uint64())
// Update list of precompiles based on current block
rules := env.ChainConfig().Rules(env.Context.BlockNumber, env.Context.Random != nil, env.Context.Time)
- t.activePrecompiles = vm.ActivePrecompiles(rules)
+ t.activePrecompiles = env.ActivePrecompiles(rules)
}
// CaptureState implements the Tracer interface to trace a single step of VM execution.
@@ -669,14 +671,14 @@ func (s *stackObj) Peek(idx int) goja.Value {
// peek returns the nth-from-the-top element of the stack.
func (s *stackObj) peek(idx int) (*big.Int, error) {
- if len(s.stack.Data()) <= idx || idx < 0 {
- return nil, fmt.Errorf("tracer accessed out of bound stack: size %d, index %d", len(s.stack.Data()), idx)
+ if len(s.stack.Data) <= idx || idx < 0 {
+ return nil, fmt.Errorf("tracer accessed out of bound stack: size %d, index %d", len(s.stack.Data), idx)
}
return s.stack.Back(idx).ToBig(), nil
}
func (s *stackObj) Length() int {
- return len(s.stack.Data())
+ return len(s.stack.Data)
}
func (s *stackObj) setupObject() *goja.Object {
diff --git a/eth/tracers/logger/access_list_tracer.go b/eth/tracers/logger/access_list_tracer.go
index 766ee4e4b95c..7ec021165950 100644
--- a/eth/tracers/logger/access_list_tracer.go
+++ b/eth/tracers/logger/access_list_tracer.go
@@ -138,7 +138,7 @@ func (a *AccessListTracer) CaptureStart(env *vm.EVM, from common.Address, to com
// CaptureState captures all opcodes that touch storage or addresses and adds them to the accesslist.
func (a *AccessListTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) {
stack := scope.Stack
- stackData := stack.Data()
+ stackData := stack.Data
stackLen := len(stackData)
if (op == vm.SLOAD || op == vm.SSTORE) && stackLen >= 1 {
slot := common.Hash(stackData[stackLen-1].Bytes32())
diff --git a/eth/tracers/logger/logger.go b/eth/tracers/logger/logger.go
index 2b36f9f4922f..919103a87941 100644
--- a/eth/tracers/logger/logger.go
+++ b/eth/tracers/logger/logger.go
@@ -170,12 +170,12 @@ func (l *StructLogger) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, s
// Copy a snapshot of the current stack state to a new buffer
var stck []uint256.Int
if !l.cfg.DisableStack {
- stck = make([]uint256.Int, len(stack.Data()))
- for i, item := range stack.Data() {
+ stck = make([]uint256.Int, len(stack.Data))
+ for i, item := range stack.Data {
stck[i] = item
}
}
- stackData := stack.Data()
+ stackData := stack.Data
stackLen := len(stackData)
// Copy a snapshot of the current storage to a new container
var storage Storage
@@ -368,7 +368,7 @@ func (t *mdLogger) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope
if !t.cfg.DisableStack {
// format stack
var a []string
- for _, elem := range stack.Data() {
+ for _, elem := range stack.Data {
a = append(a, elem.Hex())
}
b := fmt.Sprintf("[%v]", strings.Join(a, ","))
diff --git a/eth/tracers/logger/logger_json.go b/eth/tracers/logger/logger_json.go
index a2cb4cd9fc59..8feb992f56f7 100644
--- a/eth/tracers/logger/logger_json.go
+++ b/eth/tracers/logger/logger_json.go
@@ -70,7 +70,7 @@ func (l *JSONLogger) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, sco
log.Memory = memory.Data()
}
if !l.cfg.DisableStack {
- log.Stack = stack.Data()
+ log.Stack = stack.Data
}
if l.cfg.EnableReturnData {
log.ReturnData = rData
diff --git a/eth/tracers/native/4byte.go b/eth/tracers/native/4byte.go
index 5a2c4f91115f..4da983df028b 100644
--- a/eth/tracers/native/4byte.go
+++ b/eth/tracers/native/4byte.go
@@ -82,7 +82,7 @@ func (t *fourByteTracer) store(id []byte, size int) {
func (t *fourByteTracer) CaptureStart(env *vm.EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) {
// Update list of precompiles based on current block
rules := env.ChainConfig().Rules(env.Context.BlockNumber, env.Context.Random != nil, env.Context.Time)
- t.activePrecompiles = vm.ActivePrecompiles(rules)
+ t.activePrecompiles = env.ActivePrecompiles(rules)
// Save the outer calldata also
if len(input) >= 4 {
diff --git a/eth/tracers/native/call.go b/eth/tracers/native/call.go
index be9b58a4cd3c..ceddc1790fca 100644
--- a/eth/tracers/native/call.go
+++ b/eth/tracers/native/call.go
@@ -173,7 +173,7 @@ func (t *callTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, sco
size := int(op - vm.LOG0)
stack := scope.Stack
- stackData := stack.Data()
+ stackData := stack.Data
// Don't modify the stack
mStart := stackData[len(stackData)-1]
diff --git a/eth/tracers/native/call_flat.go b/eth/tracers/native/call_flat.go
index 266ab9900146..78fd834822fc 100644
--- a/eth/tracers/native/call_flat.go
+++ b/eth/tracers/native/call_flat.go
@@ -148,7 +148,7 @@ func (t *flatCallTracer) CaptureStart(env *vm.EVM, from common.Address, to commo
t.tracer.CaptureStart(env, from, to, create, input, gas, value)
// Update list of precompiles based on current block
rules := env.ChainConfig().Rules(env.Context.BlockNumber, env.Context.Random != nil, env.Context.Time)
- t.activePrecompiles = vm.ActivePrecompiles(rules)
+ t.activePrecompiles = vm.DefaultActivePrecompiles(rules)
}
// CaptureEnd is called after the call finishes to finalize the tracing.
diff --git a/eth/tracers/native/prestate.go b/eth/tracers/native/prestate.go
index d7e10173cf27..1a081aeda49b 100644
--- a/eth/tracers/native/prestate.go
+++ b/eth/tracers/native/prestate.go
@@ -142,7 +142,7 @@ func (t *prestateTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64,
return
}
stack := scope.Stack
- stackData := stack.Data()
+ stackData := stack.Data
stackLen := len(stackData)
caller := scope.Contract.Address()
switch {
diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go
index 863849f4da6a..a509919829ea 100644
--- a/internal/ethapi/api.go
+++ b/internal/ethapi/api.go
@@ -238,7 +238,7 @@ func (s *TxPoolAPI) Inspect() map[string]map[string]map[string]string {
pending, queue := s.b.TxPoolContent()
// Define a formatter to flatten a transaction into a string
- var format = func(tx *types.Transaction) string {
+ format := func(tx *types.Transaction) string {
if to := tx.To(); to != nil {
return fmt.Sprintf("%s: %v wei + %v gas × %v wei", tx.To().Hex(), tx.Value(), tx.Gas(), tx.GasPrice())
}
@@ -1498,7 +1498,7 @@ func AccessList(ctx context.Context, b Backend, blockNrOrHash rpc.BlockNumberOrH
}
isPostMerge := header.Difficulty.Cmp(common.Big0) == 0
// Retrieve the precompiles since they don't need to be added to the access list
- precompiles := vm.ActivePrecompiles(b.ChainConfig().Rules(header.Number, isPostMerge, header.Time))
+ precompiles := vm.DefaultActivePrecompiles(b.ChainConfig().Rules(header.Number, isPostMerge, header.Time))
// Create an initial tracer
prevTracer := logger.NewAccessListTracer(nil, args.from(), to, precompiles)
@@ -1934,11 +1934,11 @@ func (s *TransactionAPI) Resend(ctx context.Context, sendArgs TransactionArgs, g
matchTx := sendArgs.toTransaction()
// Before replacing the old transaction, ensure the _new_ transaction fee is reasonable.
- var price = matchTx.GasPrice()
+ price := matchTx.GasPrice()
if gasPrice != nil {
price = gasPrice.ToInt()
}
- var gas = matchTx.Gas()
+ gas := matchTx.Gas()
if gasLimit != nil {
gas = uint64(*gasLimit)
}
diff --git a/tests/fuzzers/bls12381/precompile_fuzzer.go b/tests/fuzzers/bls12381/precompile_fuzzer.go
index 763ed56e9f7a..b1d861de9480 100644
--- a/tests/fuzzers/bls12381/precompile_fuzzer.go
+++ b/tests/fuzzers/bls12381/precompile_fuzzer.go
@@ -82,7 +82,9 @@ func fuzz(id byte, data []byte) int {
}
cpy := make([]byte, len(data))
copy(cpy, data)
- _, err := precompile.Run(cpy)
+ contract := vm.NewPrecompile(vm.AccountRef(common.Address{}), precompile, common.U2560, gas)
+ contract.Input = cpy
+ _, err := precompile.Run(nil, contract, false)
if !bytes.Equal(cpy, data) {
panic(fmt.Sprintf("input data modified, precompile %d: %x %x", id, data, cpy))
}
diff --git a/tests/state_test.go b/tests/state_test.go
index 1d749d8bcf52..d703166f24c1 100644
--- a/tests/state_test.go
+++ b/tests/state_test.go
@@ -301,7 +301,7 @@ func runBenchmark(b *testing.B, t *StateTest) {
b.ResetTimer()
for n := 0; n < b.N; n++ {
snapshot := state.StateDB.Snapshot()
- state.StateDB.Prepare(rules, msg.From, context.Coinbase, msg.To, vm.ActivePrecompiles(rules), msg.AccessList)
+ state.StateDB.Prepare(rules, msg.From, context.Coinbase, msg.To, vm.DefaultActivePrecompiles(rules), msg.AccessList)
b.StartTimer()
start := time.Now()