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