diff --git a/chain/chain_config.go b/chain/chain_config.go
index b728ab150e8..10ecd5b3e7c 100644
--- a/chain/chain_config.go
+++ b/chain/chain_config.go
@@ -393,6 +393,7 @@ type Rules struct {
IsByzantium, IsConstantinople, IsPetersburg, IsIstanbul bool
IsBerlin, IsLondon, IsShanghai, IsCancun, IsPrague bool
IsEip1559FeeCollector, IsAura, IsMordor bool
+ IsZkEVMForkID4 bool
}
// Rules ensures c's ChainID is not nil and returns a new Rules instance
diff --git a/core/vm/contracts.go b/core/vm/contracts.go
index a8276a0608b..a1edfdb2879 100644
--- a/core/vm/contracts.go
+++ b/core/vm/contracts.go
@@ -17,8 +17,9 @@
package vm
import (
+ "crypto/sha256"
+ "encoding/binary"
"errors"
- "fmt"
"math/big"
"github.com/holiman/uint256"
@@ -26,11 +27,15 @@ import (
"github.com/ledgerwatch/erigon/chain"
"github.com/ledgerwatch/erigon/common"
+ "github.com/ledgerwatch/erigon/common/math"
"github.com/ledgerwatch/erigon/crypto"
+ "github.com/ledgerwatch/erigon/crypto/blake2b"
"github.com/ledgerwatch/erigon/crypto/bls12381"
"github.com/ledgerwatch/erigon/crypto/bn256"
"github.com/ledgerwatch/erigon/params"
+
//lint:ignore SA1019 Needed for precompile
+ "golang.org/x/crypto/ripemd160"
)
// PrecompiledContract is the basic interface for native Go contracts. The implementation
@@ -166,12 +171,6 @@ func (c *ecrecover) RequiredGas(input []byte) uint64 {
func (c *ecrecover) Run(input []byte) ([]byte, error) {
const ecRecoverInputLength = 128
- // [zkevm] - this was a bug prior to forkId6
- // this is the address that belongs to pvtKey = 0
- // occurs on testnet block number 2963608
- if fmt.Sprintf("%x", input) == "0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000001bc6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee57fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a1" {
- return libcommon.HexToHash("0x3f17f1962b36e491b30a40b2405849e597ba5fb5").Bytes(), nil
- }
input = common.RightPadBytes(input, ecRecoverInputLength)
// "input" is (hash, v, r, s), each 32 bytes
// but for ecrecover we want (r, s, v)
@@ -208,15 +207,11 @@ type sha256hash struct{}
// This method does not require any overflow checking as the input size gas costs
// required for anything significant is so high it's impossible to pay for.
func (c *sha256hash) RequiredGas(input []byte) uint64 {
- //[zkevm]
- return 0
- // return uint64(len(input)+31)/32*params.Sha256PerWordGas + params.Sha256BaseGas
+ return uint64(len(input)+31)/32*params.Sha256PerWordGas + params.Sha256BaseGas
}
func (c *sha256hash) Run(input []byte) ([]byte, error) {
- //[zkevm]
- return []byte{}, ErrExecutionReverted
- // h := sha256.Sum256(input)
- // return h[:], nil
+ h := sha256.Sum256(input)
+ return h[:], nil
}
// RIPEMD160 implemented as a native contract.
@@ -227,16 +222,12 @@ type ripemd160hash struct{}
// This method does not require any overflow checking as the input size gas costs
// required for anything significant is so high it's impossible to pay for.
func (c *ripemd160hash) RequiredGas(input []byte) uint64 {
- //[zkevm]
- return 0
- // return uint64(len(input)+31)/32*params.Ripemd160PerWordGas + params.Ripemd160BaseGas
+ return uint64(len(input)+31)/32*params.Ripemd160PerWordGas + params.Ripemd160BaseGas
}
func (c *ripemd160hash) Run(input []byte) ([]byte, error) {
- //[zkevm]
- // ripemd := ripemd160.New()
- // ripemd.Write(input)
- // return common.LeftPadBytes(ripemd.Sum(nil), 32), nil
- return []byte{}, ErrExecutionReverted
+ ripemd := ripemd160.New()
+ ripemd.Write(input)
+ return common.LeftPadBytes(ripemd.Sum(nil), 32), nil
}
// data copy implemented as a native contract.
@@ -306,117 +297,112 @@ func modexpMultComplexity(x *big.Int) *big.Int {
// RequiredGas returns the gas required to execute the pre-compiled contract.
func (c *bigModExp) RequiredGas(input []byte) uint64 {
- //[zkevm]
- return 0
- // var (
- // baseLen = new(big.Int).SetBytes(getData(input, 0, 32))
- // expLen = new(big.Int).SetBytes(getData(input, 32, 32))
- // modLen = new(big.Int).SetBytes(getData(input, 64, 32))
- // )
- // if len(input) > 96 {
- // input = input[96:]
- // } else {
- // input = input[:0]
- // }
- // // Retrieve the head 32 bytes of exp for the adjusted exponent length
- // var expHead *big.Int
- // if big.NewInt(int64(len(input))).Cmp(baseLen) <= 0 {
- // expHead = new(big.Int)
- // } else {
- // if expLen.Cmp(big32) > 0 {
- // expHead = new(big.Int).SetBytes(getData(input, baseLen.Uint64(), 32))
- // } else {
- // expHead = new(big.Int).SetBytes(getData(input, baseLen.Uint64(), expLen.Uint64()))
- // }
- // }
- // // Calculate the adjusted exponent length
- // var msb int
- // if bitlen := expHead.BitLen(); bitlen > 0 {
- // msb = bitlen - 1
- // }
- // adjExpLen := new(big.Int)
- // if expLen.Cmp(big32) > 0 {
- // adjExpLen.Sub(expLen, big32)
- // adjExpLen.Mul(big8, adjExpLen)
- // }
- // adjExpLen.Add(adjExpLen, big.NewInt(int64(msb)))
- // // Calculate the gas cost of the operation
- // gas := new(big.Int).Set(math.BigMax(modLen, baseLen))
- // if c.eip2565 {
- // // EIP-2565 has three changes
- // // 1. Different multComplexity (inlined here)
- // // in EIP-2565 (https://eips.ethereum.org/EIPS/eip-2565):
- // //
- // // def mult_complexity(x):
- // // ceiling(x/8)^2
- // //
- // //where is x is max(length_of_MODULUS, length_of_BASE)
- // gas = gas.Add(gas, big7)
- // gas = gas.Div(gas, big8)
- // gas.Mul(gas, gas)
-
- // gas.Mul(gas, math.BigMax(adjExpLen, big1))
- // // 2. Different divisor (`GQUADDIVISOR`) (3)
- // gas.Div(gas, big3)
- // if gas.BitLen() > 64 {
- // return math.MaxUint64
- // }
- // // 3. Minimum price of 200 gas
- // if gas.Uint64() < 200 {
- // return 200
- // }
- // return gas.Uint64()
- // }
- // gas = modexpMultComplexity(gas)
- // gas.Mul(gas, math.BigMax(adjExpLen, big1))
- // gas.Div(gas, big20)
-
- // if gas.BitLen() > 64 {
- // return math.MaxUint64
- // }
- // return gas.Uint64()
+ var (
+ baseLen = new(big.Int).SetBytes(getData(input, 0, 32))
+ expLen = new(big.Int).SetBytes(getData(input, 32, 32))
+ modLen = new(big.Int).SetBytes(getData(input, 64, 32))
+ )
+ if len(input) > 96 {
+ input = input[96:]
+ } else {
+ input = input[:0]
+ }
+ // Retrieve the head 32 bytes of exp for the adjusted exponent length
+ var expHead *big.Int
+ if big.NewInt(int64(len(input))).Cmp(baseLen) <= 0 {
+ expHead = new(big.Int)
+ } else {
+ if expLen.Cmp(big32) > 0 {
+ expHead = new(big.Int).SetBytes(getData(input, baseLen.Uint64(), 32))
+ } else {
+ expHead = new(big.Int).SetBytes(getData(input, baseLen.Uint64(), expLen.Uint64()))
+ }
+ }
+ // Calculate the adjusted exponent length
+ var msb int
+ if bitlen := expHead.BitLen(); bitlen > 0 {
+ msb = bitlen - 1
+ }
+ adjExpLen := new(big.Int)
+ if expLen.Cmp(big32) > 0 {
+ adjExpLen.Sub(expLen, big32)
+ adjExpLen.Mul(big8, adjExpLen)
+ }
+ adjExpLen.Add(adjExpLen, big.NewInt(int64(msb)))
+ // Calculate the gas cost of the operation
+ gas := new(big.Int).Set(math.BigMax(modLen, baseLen))
+ if c.eip2565 {
+ // EIP-2565 has three changes
+ // 1. Different multComplexity (inlined here)
+ // in EIP-2565 (https://eips.ethereum.org/EIPS/eip-2565):
+ //
+ // def mult_complexity(x):
+ // ceiling(x/8)^2
+ //
+ //where is x is max(length_of_MODULUS, length_of_BASE)
+ gas = gas.Add(gas, big7)
+ gas = gas.Div(gas, big8)
+ gas.Mul(gas, gas)
+
+ gas.Mul(gas, math.BigMax(adjExpLen, big1))
+ // 2. Different divisor (`GQUADDIVISOR`) (3)
+ gas.Div(gas, big3)
+ if gas.BitLen() > 64 {
+ return math.MaxUint64
+ }
+ // 3. Minimum price of 200 gas
+ if gas.Uint64() < 200 {
+ return 200
+ }
+ return gas.Uint64()
+ }
+ gas = modexpMultComplexity(gas)
+ gas.Mul(gas, math.BigMax(adjExpLen, big1))
+ gas.Div(gas, big20)
+
+ if gas.BitLen() > 64 {
+ return math.MaxUint64
+ }
+ return gas.Uint64()
}
func (c *bigModExp) Run(input []byte) ([]byte, error) {
- //[zkevm]
- return []byte{}, ErrExecutionReverted
-
- // 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()
- // )
- // if len(input) > 96 {
- // input = input[96:]
- // } else {
- // input = input[:0]
- // }
- // // Handle a special case when both the base and mod length is zero
- // if baseLen == 0 && modLen == 0 {
- // return []byte{}, nil
- // }
- // // 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))
- // v []byte
- // )
- // switch {
- // case mod.BitLen() == 0:
- // // Modulo 0 is undefined, return zero
- // return common.LeftPadBytes([]byte{}, int(modLen)), nil
- // case base.Cmp(libcommon.Big1) == 0:
- // //If base == 1, then we can just return base % mod (if mod >= 1, which it is)
- // v = base.Mod(base, mod).Bytes()
- // //case mod.Bit(0) == 0:
- // // // Modulo is even
- // // v = math.FastExp(base, exp, mod).Bytes()
- // default:
- // // Modulo is odd
- // v = base.Exp(base, exp, mod).Bytes()
- // }
- // return common.LeftPadBytes(v, int(modLen)), nil
+ 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()
+ )
+ if len(input) > 96 {
+ input = input[96:]
+ } else {
+ input = input[:0]
+ }
+ // Handle a special case when both the base and mod length is zero
+ if baseLen == 0 && modLen == 0 {
+ return []byte{}, nil
+ }
+ // 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))
+ v []byte
+ )
+ switch {
+ case mod.BitLen() == 0:
+ // Modulo 0 is undefined, return zero
+ return common.LeftPadBytes([]byte{}, int(modLen)), nil
+ case base.Cmp(libcommon.Big1) == 0:
+ //If base == 1, then we can just return base % mod (if mod >= 1, which it is)
+ v = base.Mod(base, mod).Bytes()
+ //case mod.Bit(0) == 0:
+ // // Modulo is even
+ // v = math.FastExp(base, exp, mod).Bytes()
+ default:
+ // Modulo is odd
+ v = base.Exp(base, exp, mod).Bytes()
+ }
+ return common.LeftPadBytes(v, int(modLen)), nil
}
// newCurvePoint unmarshals a binary blob into a bn256 elliptic curve point,
@@ -461,16 +447,11 @@ type bn256AddIstanbul struct{}
// RequiredGas returns the gas required to execute the pre-compiled contract.
func (c *bn256AddIstanbul) RequiredGas(input []byte) uint64 {
- //[zkevm]
- return 0
- // return params.Bn256AddGasIstanbul
+ return params.Bn256AddGasIstanbul
}
func (c *bn256AddIstanbul) Run(input []byte) ([]byte, error) {
- //[zkevm]
- return []byte{}, ErrExecutionReverted
-
- // return runBn256Add(input)
+ return runBn256Add(input)
}
// bn256AddByzantium implements a native elliptic curve point addition
@@ -504,16 +485,11 @@ type bn256ScalarMulIstanbul struct{}
// RequiredGas returns the gas required to execute the pre-compiled contract.
func (c *bn256ScalarMulIstanbul) RequiredGas(input []byte) uint64 {
- //[zkevm]
- return 0
- // return params.Bn256ScalarMulGasIstanbul
+ return params.Bn256ScalarMulGasIstanbul
}
func (c *bn256ScalarMulIstanbul) Run(input []byte) ([]byte, error) {
- //[zkevm]
- return []byte{}, ErrExecutionReverted
-
- // return runBn256ScalarMul(input)
+ return runBn256ScalarMul(input)
}
// bn256ScalarMulByzantium implements a native elliptic curve scalar
@@ -577,16 +553,11 @@ type bn256PairingIstanbul struct{}
// RequiredGas returns the gas required to execute the pre-compiled contract.
func (c *bn256PairingIstanbul) RequiredGas(input []byte) uint64 {
- //[zkevm]
- return 0
- // return params.Bn256PairingBaseGasIstanbul + uint64(len(input)/192)*params.Bn256PairingPerPointGasIstanbul
+ return params.Bn256PairingBaseGasIstanbul + uint64(len(input)/192)*params.Bn256PairingPerPointGasIstanbul
}
func (c *bn256PairingIstanbul) Run(input []byte) ([]byte, error) {
- //[zkevm]
- return []byte{}, ErrExecutionReverted
-
- // return runBn256Pairing(input)
+ return runBn256Pairing(input)
}
// bn256PairingByzantium implements a pairing pre-compile for the bn256 curve
@@ -605,14 +576,12 @@ func (c *bn256PairingByzantium) Run(input []byte) ([]byte, error) {
type blake2F struct{}
func (c *blake2F) RequiredGas(input []byte) uint64 {
- //[zkevm]
- return 0
// If the input is malformed, we can't calculate the gas, return 0 and let the
// actual call choke and fault.
- // if len(input) != blake2FInputLength {
- // return 0
- // }
- // return uint64(binary.BigEndian.Uint32(input[0:4]))
+ if len(input) != blake2FInputLength {
+ return 0
+ }
+ return uint64(binary.BigEndian.Uint32(input[0:4]))
}
const (
@@ -627,45 +596,42 @@ var (
)
func (c *blake2F) Run(input []byte) ([]byte, error) {
- //[zkevm]
- return []byte{}, ErrExecutionReverted
-
- // // Make sure the input is valid (correct length and final flag)
- // if len(input) != blake2FInputLength {
- // return nil, errBlake2FInvalidInputLength
- // }
- // if input[212] != blake2FNonFinalBlockBytes && 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
-
- // h [8]uint64
- // m [16]uint64
- // t [2]uint64
- // )
- // for i := 0; i < 8; i++ {
- // offset := 4 + i*8
- // h[i] = binary.LittleEndian.Uint64(input[offset : offset+8])
- // }
- // for i := 0; i < 16; i++ {
- // offset := 68 + i*8
- // m[i] = binary.LittleEndian.Uint64(input[offset : offset+8])
- // }
- // t[0] = binary.LittleEndian.Uint64(input[196:204])
- // t[1] = binary.LittleEndian.Uint64(input[204:212])
-
- // // Execute the compression function, extract and return the result
- // blake2b.F(&h, m, t, final, rounds)
-
- // output := make([]byte, 64)
- // for i := 0; i < 8; i++ {
- // offset := i * 8
- // binary.LittleEndian.PutUint64(output[offset:offset+8], h[i])
- // }
- // return output, nil
+ // Make sure the input is valid (correct length and final flag)
+ if len(input) != blake2FInputLength {
+ return nil, errBlake2FInvalidInputLength
+ }
+ if input[212] != blake2FNonFinalBlockBytes && 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
+
+ h [8]uint64
+ m [16]uint64
+ t [2]uint64
+ )
+ for i := 0; i < 8; i++ {
+ offset := 4 + i*8
+ h[i] = binary.LittleEndian.Uint64(input[offset : offset+8])
+ }
+ for i := 0; i < 16; i++ {
+ offset := 68 + i*8
+ m[i] = binary.LittleEndian.Uint64(input[offset : offset+8])
+ }
+ t[0] = binary.LittleEndian.Uint64(input[196:204])
+ t[1] = binary.LittleEndian.Uint64(input[204:212])
+
+ // Execute the compression function, extract and return the result
+ blake2b.F(&h, m, t, final, rounds)
+
+ output := make([]byte, 64)
+ for i := 0; i < 8; i++ {
+ offset := i * 8
+ binary.LittleEndian.PutUint64(output[offset:offset+8], h[i])
+ }
+ return output, nil
}
var (
diff --git a/core/vm/contracts_zkevm.go b/core/vm/contracts_zkevm.go
new file mode 100644
index 00000000000..5c8a2450f86
--- /dev/null
+++ b/core/vm/contracts_zkevm.go
@@ -0,0 +1,814 @@
+// 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 (
+ "fmt"
+ "math/big"
+
+ "github.com/holiman/uint256"
+ libcommon "github.com/ledgerwatch/erigon-lib/common"
+
+ "github.com/ledgerwatch/erigon/common"
+ "github.com/ledgerwatch/erigon/crypto"
+ "github.com/ledgerwatch/erigon/crypto/bls12381"
+ "github.com/ledgerwatch/erigon/params"
+ //lint:ignore SA1019 Needed for precompile
+)
+
+// PrecompiledContractsZKEVMDragonfruit contains the default set of pre-compiled zkEVM Dragonfruit
+var PrecompiledContractsZKEVMDragonfruit = map[libcommon.Address]PrecompiledContract{
+ libcommon.BytesToAddress([]byte{1}): &ecrecover_zkevm{},
+ libcommon.BytesToAddress([]byte{2}): &sha256hash_zkevm{},
+ libcommon.BytesToAddress([]byte{3}): &ripemd160hash_zkevm{},
+ libcommon.BytesToAddress([]byte{4}): &dataCopy_zkevm{},
+ libcommon.BytesToAddress([]byte{5}): &bigModExp_zkevm{eip2565: true},
+ libcommon.BytesToAddress([]byte{6}): &bn256AddIstanbul_zkevm{},
+ libcommon.BytesToAddress([]byte{7}): &bn256ScalarMulIstanbul_zkevm{},
+ libcommon.BytesToAddress([]byte{8}): &bn256PairingIstanbul_zkevm{},
+ libcommon.BytesToAddress([]byte{9}): &blake2F_zkevm{},
+}
+
+// ECRECOVER implemented as a native contract.
+type ecrecover_zkevm struct{}
+
+func (c *ecrecover_zkevm) RequiredGas(input []byte) uint64 {
+ return params.EcrecoverGas
+}
+
+func (c *ecrecover_zkevm) Run(input []byte) ([]byte, error) {
+ const ecRecoverInputLength = 128
+
+ // [zkevm] - this was a bug prior to forkId6
+ // this is the address that belongs to pvtKey = 0
+ // occurs on testnet block number 2963608
+ if fmt.Sprintf("%x", input) == "0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000001bc6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee57fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a1" {
+ return libcommon.HexToHash("0x3f17f1962b36e491b30a40b2405849e597ba5fb5").Bytes(), nil
+ }
+ input = common.RightPadBytes(input, ecRecoverInputLength)
+ // "input" is (hash, v, r, s), each 32 bytes
+ // but for ecrecover we want (r, s, v)
+
+ r := new(uint256.Int).SetBytes(input[64:96])
+ s := new(uint256.Int).SetBytes(input[96:128])
+ v := 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) {
+ 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])
+ sig[64] = v
+ // v needs to be at the end for libsecp256k1
+ pubKey, err := crypto.Ecrecover(input[:32], sig)
+ // make sure the public key is a valid one
+ if err != nil {
+ return nil, nil
+ }
+
+ // the first byte of pubkey is bitcoin heritage
+ return common.LeftPadBytes(crypto.Keccak256(pubKey[1:])[12:], 32), nil
+}
+
+// SHA256 implemented as a native contract.
+type sha256hash_zkevm struct{}
+
+// 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
+// required for anything significant is so high it's impossible to pay for.
+func (c *sha256hash_zkevm) RequiredGas(input []byte) uint64 {
+ //[zkevm]
+ return 0
+ // return uint64(len(input)+31)/32*params.Sha256PerWordGas + params.Sha256BaseGas
+}
+func (c *sha256hash_zkevm) Run(input []byte) ([]byte, error) {
+ //[zkevm]
+ return []byte{}, ErrExecutionReverted
+ // h := sha256.Sum256(input)
+ // return h[:], nil
+}
+
+// RIPEMD160 implemented as a native contract.
+type ripemd160hash_zkevm struct{}
+
+// 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
+// required for anything significant is so high it's impossible to pay for.
+func (c *ripemd160hash_zkevm) RequiredGas(input []byte) uint64 {
+ //[zkevm]
+ return 0
+ // return uint64(len(input)+31)/32*params.Ripemd160PerWordGas + params.Ripemd160BaseGas
+}
+func (c *ripemd160hash_zkevm) Run(input []byte) ([]byte, error) {
+ //[zkevm]
+ // ripemd := ripemd160.New()
+ // ripemd.Write(input)
+ // return common.LeftPadBytes(ripemd.Sum(nil), 32), nil
+ return []byte{}, ErrExecutionReverted
+}
+
+// data copy implemented as a native contract.
+type dataCopy_zkevm struct{}
+
+// 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
+// required for anything significant is so high it's impossible to pay for.
+func (c *dataCopy_zkevm) RequiredGas(input []byte) uint64 {
+ return uint64(len(input)+31)/32*params.IdentityPerWordGas + params.IdentityBaseGas
+}
+func (c *dataCopy_zkevm) Run(in []byte) ([]byte, error) {
+ return in, nil
+}
+
+// bigModExp implements a native big integer exponential modular operation.
+type bigModExp_zkevm struct {
+ eip2565 bool
+}
+
+// RequiredGas returns the gas required to execute the pre-compiled contract.
+func (c *bigModExp_zkevm) RequiredGas(input []byte) uint64 {
+ //[zkevm]
+ return 0
+ // var (
+ // baseLen = new(big.Int).SetBytes(getData(input, 0, 32))
+ // expLen = new(big.Int).SetBytes(getData(input, 32, 32))
+ // modLen = new(big.Int).SetBytes(getData(input, 64, 32))
+ // )
+ // if len(input) > 96 {
+ // input = input[96:]
+ // } else {
+ // input = input[:0]
+ // }
+ // // Retrieve the head 32 bytes of exp for the adjusted exponent length
+ // var expHead *big.Int
+ // if big.NewInt(int64(len(input))).Cmp(baseLen) <= 0 {
+ // expHead = new(big.Int)
+ // } else {
+ // if expLen.Cmp(big32) > 0 {
+ // expHead = new(big.Int).SetBytes(getData(input, baseLen.Uint64(), 32))
+ // } else {
+ // expHead = new(big.Int).SetBytes(getData(input, baseLen.Uint64(), expLen.Uint64()))
+ // }
+ // }
+ // // Calculate the adjusted exponent length
+ // var msb int
+ // if bitlen := expHead.BitLen(); bitlen > 0 {
+ // msb = bitlen - 1
+ // }
+ // adjExpLen := new(big.Int)
+ // if expLen.Cmp(big32) > 0 {
+ // adjExpLen.Sub(expLen, big32)
+ // adjExpLen.Mul(big8, adjExpLen)
+ // }
+ // adjExpLen.Add(adjExpLen, big.NewInt(int64(msb)))
+ // // Calculate the gas cost of the operation
+ // gas := new(big.Int).Set(math.BigMax(modLen, baseLen))
+ // if c.eip2565 {
+ // // EIP-2565 has three changes
+ // // 1. Different multComplexity (inlined here)
+ // // in EIP-2565 (https://eips.ethereum.org/EIPS/eip-2565):
+ // //
+ // // def mult_complexity(x):
+ // // ceiling(x/8)^2
+ // //
+ // //where is x is max(length_of_MODULUS, length_of_BASE)
+ // gas = gas.Add(gas, big7)
+ // gas = gas.Div(gas, big8)
+ // gas.Mul(gas, gas)
+
+ // gas.Mul(gas, math.BigMax(adjExpLen, big1))
+ // // 2. Different divisor (`GQUADDIVISOR`) (3)
+ // gas.Div(gas, big3)
+ // if gas.BitLen() > 64 {
+ // return math.MaxUint64
+ // }
+ // // 3. Minimum price of 200 gas
+ // if gas.Uint64() < 200 {
+ // return 200
+ // }
+ // return gas.Uint64()
+ // }
+ // gas = modexpMultComplexity(gas)
+ // gas.Mul(gas, math.BigMax(adjExpLen, big1))
+ // gas.Div(gas, big20)
+
+ // if gas.BitLen() > 64 {
+ // return math.MaxUint64
+ // }
+ // return gas.Uint64()
+}
+
+func (c *bigModExp_zkevm) Run(input []byte) ([]byte, error) {
+ //[zkevm]
+ return []byte{}, ErrExecutionReverted
+
+ // 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()
+ // )
+ // if len(input) > 96 {
+ // input = input[96:]
+ // } else {
+ // input = input[:0]
+ // }
+ // // Handle a special case when both the base and mod length is zero
+ // if baseLen == 0 && modLen == 0 {
+ // return []byte{}, nil
+ // }
+ // // 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))
+ // v []byte
+ // )
+ // switch {
+ // case mod.BitLen() == 0:
+ // // Modulo 0 is undefined, return zero
+ // return common.LeftPadBytes([]byte{}, int(modLen)), nil
+ // case base.Cmp(libcommon.Big1) == 0:
+ // //If base == 1, then we can just return base % mod (if mod >= 1, which it is)
+ // v = base.Mod(base, mod).Bytes()
+ // //case mod.Bit(0) == 0:
+ // // // Modulo is even
+ // // v = math.FastExp(base, exp, mod).Bytes()
+ // default:
+ // // Modulo is odd
+ // v = base.Exp(base, exp, mod).Bytes()
+ // }
+ // return common.LeftPadBytes(v, int(modLen)), nil
+}
+
+// bn256Add implements a native elliptic curve point addition conforming to
+// Istanbul consensus rules.
+type bn256AddIstanbul_zkevm struct{}
+
+// RequiredGas returns the gas required to execute the pre-compiled contract.
+func (c *bn256AddIstanbul_zkevm) RequiredGas(input []byte) uint64 {
+ //[zkevm]
+ return 0
+ // return params.Bn256AddGasIstanbul
+}
+
+func (c *bn256AddIstanbul_zkevm) Run(input []byte) ([]byte, error) {
+ //[zkevm]
+ return []byte{}, ErrExecutionReverted
+
+ // return runBn256Add(input)
+}
+
+// bn256AddByzantium implements a native elliptic curve point addition
+// conforming to Byzantium consensus rules.
+type bn256AddByzantium_zkevm struct{}
+
+// RequiredGas returns the gas required to execute the pre-compiled contract.
+func (c *bn256AddByzantium_zkevm) RequiredGas(input []byte) uint64 {
+ return params.Bn256AddGasByzantium
+}
+
+func (c *bn256AddByzantium_zkevm) Run(input []byte) ([]byte, error) {
+ return runBn256Add(input)
+}
+
+// bn256ScalarMulIstanbul implements a native elliptic curve scalar
+// multiplication conforming to Istanbul consensus rules.
+type bn256ScalarMulIstanbul_zkevm struct{}
+
+// RequiredGas returns the gas required to execute the pre-compiled contract.
+func (c *bn256ScalarMulIstanbul_zkevm) RequiredGas(input []byte) uint64 {
+ //[zkevm]
+ return 0
+ // return params.Bn256ScalarMulGasIstanbul
+}
+
+func (c *bn256ScalarMulIstanbul_zkevm) Run(input []byte) ([]byte, error) {
+ //[zkevm]
+ return []byte{}, ErrExecutionReverted
+
+ // return runBn256ScalarMul(input)
+}
+
+// bn256ScalarMulByzantium implements a native elliptic curve scalar
+// multiplication conforming to Byzantium consensus rules.
+type bn256ScalarMulByzantium_zkevm struct{}
+
+// RequiredGas returns the gas required to execute the pre-compiled contract.
+func (c *bn256ScalarMulByzantium_zkevm) RequiredGas(input []byte) uint64 {
+ return params.Bn256ScalarMulGasByzantium
+}
+
+func (c *bn256ScalarMulByzantium_zkevm) Run(input []byte) ([]byte, error) {
+ return runBn256ScalarMul(input)
+}
+
+// bn256PairingIstanbul implements a pairing pre-compile for the bn256 curve
+// conforming to Istanbul consensus rules.
+type bn256PairingIstanbul_zkevm struct{}
+
+// RequiredGas returns the gas required to execute the pre-compiled contract.
+func (c *bn256PairingIstanbul_zkevm) RequiredGas(input []byte) uint64 {
+ //[zkevm]
+ return 0
+ // return params.Bn256PairingBaseGasIstanbul + uint64(len(input)/192)*params.Bn256PairingPerPointGasIstanbul
+}
+
+func (c *bn256PairingIstanbul_zkevm) Run(input []byte) ([]byte, error) {
+ //[zkevm]
+ return []byte{}, ErrExecutionReverted
+
+ // return runBn256Pairing(input)
+}
+
+// bn256PairingByzantium implements a pairing pre-compile for the bn256 curve
+// conforming to Byzantium consensus rules.
+type bn256PairingByzantium_zkevm struct{}
+
+// RequiredGas returns the gas required to execute the pre-compiled contract.
+func (c *bn256PairingByzantium_zkevm) RequiredGas(input []byte) uint64 {
+ return params.Bn256PairingBaseGasByzantium + uint64(len(input)/192)*params.Bn256PairingPerPointGasByzantium
+}
+
+func (c *bn256PairingByzantium_zkevm) Run(input []byte) ([]byte, error) {
+ return runBn256Pairing(input)
+}
+
+type blake2F_zkevm struct{}
+
+func (c *blake2F_zkevm) RequiredGas(input []byte) uint64 {
+ //[zkevm]
+ return 0
+ // If the input is malformed, we can't calculate the gas, return 0 and let the
+ // actual call choke and fault.
+ // if len(input) != blake2FInputLength {
+ // return 0
+ // }
+ // return uint64(binary.BigEndian.Uint32(input[0:4]))
+}
+
+func (c *blake2F_zkevm) Run(input []byte) ([]byte, error) {
+ //[zkevm]
+ return []byte{}, ErrExecutionReverted
+
+ // // Make sure the input is valid (correct length and final flag)
+ // if len(input) != blake2FInputLength {
+ // return nil, errBlake2FInvalidInputLength
+ // }
+ // if input[212] != blake2FNonFinalBlockBytes && 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
+
+ // h [8]uint64
+ // m [16]uint64
+ // t [2]uint64
+ // )
+ // for i := 0; i < 8; i++ {
+ // offset := 4 + i*8
+ // h[i] = binary.LittleEndian.Uint64(input[offset : offset+8])
+ // }
+ // for i := 0; i < 16; i++ {
+ // offset := 68 + i*8
+ // m[i] = binary.LittleEndian.Uint64(input[offset : offset+8])
+ // }
+ // t[0] = binary.LittleEndian.Uint64(input[196:204])
+ // t[1] = binary.LittleEndian.Uint64(input[204:212])
+
+ // // Execute the compression function, extract and return the result
+ // blake2b.F(&h, m, t, final, rounds)
+
+ // output := make([]byte, 64)
+ // for i := 0; i < 8; i++ {
+ // offset := i * 8
+ // binary.LittleEndian.PutUint64(output[offset:offset+8], h[i])
+ // }
+ // return output, nil
+}
+
+// bls12381G1Add implements EIP-2537 G1Add precompile.
+type bls12381G1Add_zkevm struct{}
+
+// RequiredGas returns the gas required to execute the pre-compiled contract.
+func (c *bls12381G1Add_zkevm) RequiredGas(input []byte) uint64 {
+ return params.Bls12381G1AddGas
+}
+
+func (c *bls12381G1Add_zkevm) Run(input []byte) ([]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 {
+ return nil, errBLS12381InvalidInputLength
+ }
+ var err error
+ var p0, p1 *bls12381.PointG1
+
+ // Initialize G1
+ g := bls12381.NewG1()
+
+ // Decode G1 point p_0
+ if p0, err = g.DecodePoint(input[:128]); err != nil {
+ return nil, err
+ }
+ // Decode G1 point p_1
+ if p1, err = g.DecodePoint(input[128:]); err != nil {
+ return nil, err
+ }
+
+ // Compute r = p_0 + p_1
+ r := g.New()
+ g.Add(r, p0, p1)
+
+ // Encode the G1 point result into 128 bytes
+ return g.EncodePoint(r), nil
+}
+
+// bls12381G1Mul implements EIP-2537 G1Mul precompile.
+type bls12381G1Mul_zkevm struct{}
+
+// RequiredGas returns the gas required to execute the pre-compiled contract.
+func (c *bls12381G1Mul_zkevm) RequiredGas(input []byte) uint64 {
+ return params.Bls12381G1MulGas
+}
+
+func (c *bls12381G1Mul_zkevm) Run(input []byte) ([]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 {
+ return nil, errBLS12381InvalidInputLength
+ }
+ var err error
+ var p0 *bls12381.PointG1
+
+ // Initialize G1
+ g := bls12381.NewG1()
+
+ // Decode G1 point
+ if p0, err = g.DecodePoint(input[:128]); err != nil {
+ return nil, err
+ }
+ // Decode scalar value
+ e := new(big.Int).SetBytes(input[128:])
+
+ // Compute r = e * p_0
+ r := g.New()
+ g.MulScalar(r, p0, e)
+
+ // Encode the G1 point into 128 bytes
+ return g.EncodePoint(r), nil
+}
+
+// bls12381G1MultiExp implements EIP-2537 G1MultiExp precompile.
+type bls12381G1MultiExp_zkevm struct{}
+
+// RequiredGas returns the gas required to execute the pre-compiled contract.
+func (c *bls12381G1MultiExp_zkevm) RequiredGas(input []byte) uint64 {
+ // Calculate G1 point, scalar value pair length
+ k := len(input) / 160
+ if k == 0 {
+ // Return 0 gas for small input length
+ return 0
+ }
+ // Lookup discount value for G1 point, scalar value pair length
+ var discount uint64
+ if dLen := len(params.Bls12381MultiExpDiscountTable); k < dLen {
+ discount = params.Bls12381MultiExpDiscountTable[k-1]
+ } else {
+ discount = params.Bls12381MultiExpDiscountTable[dLen-1]
+ }
+ // Calculate gas and return the result
+ return (uint64(k) * params.Bls12381G1MulGas * discount) / 1000
+}
+
+func (c *bls12381G1MultiExp_zkevm) Run(input []byte) ([]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 {
+ return nil, errBLS12381InvalidInputLength
+ }
+ var err error
+ points := make([]*bls12381.PointG1, k)
+ scalars := make([]*big.Int, k)
+
+ // Initialize G1
+ g := bls12381.NewG1()
+
+ // Decode point scalar pairs
+ for i := 0; i < k; i++ {
+ 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 {
+ return nil, err
+ }
+ // Decode scalar value
+ scalars[i] = new(big.Int).SetBytes(input[t1:t2])
+ }
+
+ // Compute r = e_0 * p_0 + e_1 * p_1 + ... + e_(k-1) * p_(k-1)
+ r := g.New()
+ if _, err = g.MultiExp(r, points, scalars); err != nil {
+ return nil, err
+ }
+
+ // Encode the G1 point to 128 bytes
+ return g.EncodePoint(r), nil
+}
+
+// bls12381G2Add implements EIP-2537 G2Add precompile.
+type bls12381G2Add_zkevm struct{}
+
+// RequiredGas returns the gas required to execute the pre-compiled contract.
+func (c *bls12381G2Add_zkevm) RequiredGas(input []byte) uint64 {
+ return params.Bls12381G2AddGas
+}
+
+func (c *bls12381G2Add_zkevm) Run(input []byte) ([]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 {
+ return nil, errBLS12381InvalidInputLength
+ }
+ var err error
+ var p0, p1 *bls12381.PointG2
+
+ // Initialize G2
+ g := bls12381.NewG2()
+ r := g.New()
+
+ // Decode G2 point p_0
+ if p0, err = g.DecodePoint(input[:256]); err != nil {
+ return nil, err
+ }
+ // Decode G2 point p_1
+ if p1, err = g.DecodePoint(input[256:]); err != nil {
+ return nil, err
+ }
+
+ // Compute r = p_0 + p_1
+ g.Add(r, p0, p1)
+
+ // Encode the G2 point into 256 bytes
+ return g.EncodePoint(r), nil
+}
+
+// bls12381G2Mul implements EIP-2537 G2Mul precompile.
+type bls12381G2Mul_zkevm struct{}
+
+// RequiredGas returns the gas required to execute the pre-compiled contract.
+func (c *bls12381G2Mul_zkevm) RequiredGas(input []byte) uint64 {
+ return params.Bls12381G2MulGas
+}
+
+func (c *bls12381G2Mul_zkevm) Run(input []byte) ([]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 {
+ return nil, errBLS12381InvalidInputLength
+ }
+ var err error
+ var p0 *bls12381.PointG2
+
+ // Initialize G2
+ g := bls12381.NewG2()
+
+ // Decode G2 point
+ if p0, err = g.DecodePoint(input[:256]); err != nil {
+ return nil, err
+ }
+ // Decode scalar value
+ e := new(big.Int).SetBytes(input[256:])
+
+ // Compute r = e * p_0
+ r := g.New()
+ g.MulScalar(r, p0, e)
+
+ // Encode the G2 point into 256 bytes
+ return g.EncodePoint(r), nil
+}
+
+// bls12381G2MultiExp implements EIP-2537 G2MultiExp precompile.
+type bls12381G2MultiExp_zkevm struct{}
+
+// RequiredGas returns the gas required to execute the pre-compiled contract.
+func (c *bls12381G2MultiExp_zkevm) RequiredGas(input []byte) uint64 {
+ // Calculate G2 point, scalar value pair length
+ k := len(input) / 288
+ if k == 0 {
+ // Return 0 gas for small input length
+ return 0
+ }
+ // Lookup discount value for G2 point, scalar value pair length
+ var discount uint64
+ if dLen := len(params.Bls12381MultiExpDiscountTable); k < dLen {
+ discount = params.Bls12381MultiExpDiscountTable[k-1]
+ } else {
+ discount = params.Bls12381MultiExpDiscountTable[dLen-1]
+ }
+ // Calculate gas and return the result
+ return (uint64(k) * params.Bls12381G2MulGas * discount) / 1000
+}
+
+func (c *bls12381G2MultiExp_zkevm) Run(input []byte) ([]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 {
+ return nil, errBLS12381InvalidInputLength
+ }
+ var err error
+ points := make([]*bls12381.PointG2, k)
+ scalars := make([]*big.Int, k)
+
+ // Initialize G2
+ g := bls12381.NewG2()
+
+ // Decode point scalar pairs
+ for i := 0; i < k; i++ {
+ 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 {
+ return nil, err
+ }
+ // Decode scalar value
+ scalars[i] = new(big.Int).SetBytes(input[t1:t2])
+ }
+
+ // Compute r = e_0 * p_0 + e_1 * p_1 + ... + e_(k-1) * p_(k-1)
+ r := g.New()
+ if _, err := g.MultiExp(r, points, scalars); err != nil {
+ return nil, err
+ }
+
+ // Encode the G2 point to 256 bytes.
+ return g.EncodePoint(r), nil
+}
+
+// bls12381Pairing implements EIP-2537 Pairing precompile.
+type bls12381Pairing_zkevm struct{}
+
+// RequiredGas returns the gas required to execute the pre-compiled contract.
+func (c *bls12381Pairing_zkevm) RequiredGas(input []byte) uint64 {
+ return params.Bls12381PairingBaseGas + uint64(len(input)/384)*params.Bls12381PairingPerPairGas
+}
+
+func (c *bls12381Pairing_zkevm) Run(input []byte) ([]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 {
+ return nil, errBLS12381InvalidInputLength
+ }
+
+ // Initialize BLS12-381 pairing engine
+ e := bls12381.NewPairingEngine()
+ g1, g2 := e.G1, e.G2
+
+ // Decode pairs
+ for i := 0; i < k; i++ {
+ off := 384 * i
+ t0, t1, t2 := off, off+128, off+384
+
+ // Decode G1 point
+ p1, err := g1.DecodePoint(input[t0:t1])
+ if err != nil {
+ return nil, err
+ }
+ // Decode G2 point
+ p2, err := g2.DecodePoint(input[t1:t2])
+ if err != nil {
+ return nil, err
+ }
+
+ // 'point is on curve' check already done,
+ // Here we need to apply subgroup checks.
+ if !g1.InCorrectSubgroup(p1) {
+ return nil, errBLS12381G1PointSubgroup
+ }
+ if !g2.InCorrectSubgroup(p2) {
+ return nil, errBLS12381G2PointSubgroup
+ }
+
+ // Update pairing engine with G1 and G2 ponits
+ e.AddPair(p1, p2)
+ }
+ // Prepare 32 byte output
+ out := make([]byte, 32)
+
+ // Compute pairing and set the result
+ if e.Check() {
+ out[31] = 1
+ }
+ return out, nil
+}
+
+// bls12381MapG1 implements EIP-2537 MapG1 precompile.
+type bls12381MapG1_zkevm struct{}
+
+// RequiredGas returns the gas required to execute the pre-compiled contract.
+func (c *bls12381MapG1_zkevm) RequiredGas(input []byte) uint64 {
+ return params.Bls12381MapG1Gas
+}
+
+func (c *bls12381MapG1_zkevm) Run(input []byte) ([]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 {
+ return nil, errBLS12381InvalidInputLength
+ }
+
+ // Decode input field element
+ fe, err := decodeBLS12381FieldElement(input)
+ if err != nil {
+ return nil, err
+ }
+
+ // Initialize G1
+ g := bls12381.NewG1()
+
+ // Compute mapping
+ r, err := g.MapToCurve(fe)
+ if err != nil {
+ return nil, err
+ }
+
+ // Encode the G1 point to 128 bytes
+ return g.EncodePoint(r), nil
+}
+
+// bls12381MapG2 implements EIP-2537 MapG2 precompile.
+type bls12381MapG2_zkevm struct{}
+
+// RequiredGas returns the gas required to execute the pre-compiled contract.
+func (c *bls12381MapG2_zkevm) RequiredGas(input []byte) uint64 {
+ return params.Bls12381MapG2Gas
+}
+
+func (c *bls12381MapG2_zkevm) Run(input []byte) ([]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 {
+ return nil, errBLS12381InvalidInputLength
+ }
+
+ // Decode input field element
+ fe := make([]byte, 96)
+ c0, err := decodeBLS12381FieldElement(input[:64])
+ if err != nil {
+ return nil, err
+ }
+ copy(fe[48:], c0)
+ c1, err := decodeBLS12381FieldElement(input[64:])
+ if err != nil {
+ return nil, err
+ }
+ copy(fe[:48], c1)
+
+ // Initialize G2
+ g := bls12381.NewG2()
+
+ // Compute mapping
+ r, err := g.MapToCurve(fe)
+ if err != nil {
+ return nil, err
+ }
+
+ // Encode the G2 point to 256 bytes
+ return g.EncodePoint(r), nil
+}
diff --git a/core/vm/eips.go b/core/vm/eips.go
index 600fccefcbd..1109aa5148a 100644
--- a/core/vm/eips.go
+++ b/core/vm/eips.go
@@ -149,7 +149,6 @@ func enable2929(jt *JumpTable) {
// factor here
jt[SELFDESTRUCT].constantGas = params.SelfdestructGasEIP150
jt[SELFDESTRUCT].dynamicGas = gasSelfdestructEIP2929
- jt[SENDALL].dynamicGas = gasSelfdestructEIP2929
}
func enable3529(jt *JumpTable) {
diff --git a/core/vm/eips_zkevm.go b/core/vm/eips_zkevm.go
new file mode 100644
index 00000000000..1eb2a7ffb49
--- /dev/null
+++ b/core/vm/eips_zkevm.go
@@ -0,0 +1,53 @@
+package vm
+
+import "github.com/ledgerwatch/erigon/params"
+
+// Dynamically enable EIP-2929 and EIP-3529 for ZKEVM
+func init() {
+ activators[2929] = enable2929_zkevm
+ activators[3529] = enable3529_zkevm
+}
+
+// enable2929 enables "EIP-2929: Gas cost increases for state access opcodes"
+// https://eips.ethereum.org/EIPS/eip-2929
+func enable2929_zkevm(jt *JumpTable) {
+ jt[SSTORE].dynamicGas = gasSStoreEIP2929
+
+ jt[SLOAD].constantGas = 0
+ jt[SLOAD].dynamicGas = gasSLoadEIP2929
+
+ jt[EXTCODECOPY].constantGas = params.WarmStorageReadCostEIP2929
+ jt[EXTCODECOPY].dynamicGas = gasExtCodeCopyEIP2929
+
+ jt[EXTCODESIZE].constantGas = params.WarmStorageReadCostEIP2929
+ jt[EXTCODESIZE].dynamicGas = gasEip2929AccountCheck
+
+ jt[EXTCODEHASH].constantGas = params.WarmStorageReadCostEIP2929
+ jt[EXTCODEHASH].dynamicGas = gasEip2929AccountCheck
+
+ jt[BALANCE].constantGas = params.WarmStorageReadCostEIP2929
+ jt[BALANCE].dynamicGas = gasEip2929AccountCheck
+
+ jt[CALL].constantGas = params.WarmStorageReadCostEIP2929
+ jt[CALL].dynamicGas = gasCallEIP2929_zkevm
+
+ jt[CALLCODE].constantGas = params.WarmStorageReadCostEIP2929
+ jt[CALLCODE].dynamicGas = gasCallCodeEIP2929_zkevm
+
+ jt[STATICCALL].constantGas = params.WarmStorageReadCostEIP2929
+ jt[STATICCALL].dynamicGas = gasStaticCallEIP2929_zkevm
+
+ jt[DELEGATECALL].constantGas = params.WarmStorageReadCostEIP2929
+ jt[DELEGATECALL].dynamicGas = gasDelegateCallEIP2929_zkevm
+
+ // This was previously part of the dynamic cost, but we're using it as a constantGas
+ // factor here
+ jt[SELFDESTRUCT].constantGas = params.SelfdestructGasEIP150
+ jt[SELFDESTRUCT].dynamicGas = gasSelfdestructEIP2929_zkevm
+ jt[SENDALL].dynamicGas = gasSelfdestructEIP2929_zkevm
+}
+
+func enable3529_zkevm(jt *JumpTable) {
+ jt[SSTORE].dynamicGas = gasSStoreEIP3529
+ jt[SELFDESTRUCT].dynamicGas = gasSelfdestructEIP3529_zkevm
+}
diff --git a/core/vm/evm.go b/core/vm/evm.go
index 29d0ff93836..1eb687d57a9 100644
--- a/core/vm/evm.go
+++ b/core/vm/evm.go
@@ -34,7 +34,7 @@ import (
// deployed contract addresses (relevant after the account abstraction).
var emptyCodeHash = crypto.Keccak256Hash(nil)
-func (evm *EVM) precompile(addr libcommon.Address) (PrecompiledContract, bool) {
+func (evm *EVM) precompile_disabled(addr libcommon.Address) (PrecompiledContract, bool) {
var precompiles map[libcommon.Address]PrecompiledContract
switch {
case evm.chainRules.IsBerlin:
@@ -102,7 +102,8 @@ func NewEVM(blockCtx evmtypes.BlockContext, txCtx evmtypes.TxContext, state evmt
chainRules: chainConfig.Rules(blockCtx.BlockNumber, blockCtx.Time),
}
- evm.interpreter = NewEVMInterpreter(evm, vmConfig)
+ // [zkevm] change
+ evm.interpreter = NewZKEVMInterpreter(evm, vmConfig)
return evm
}
@@ -124,7 +125,8 @@ func (evm *EVM) ResetBetweenBlocks(blockCtx evmtypes.BlockContext, txCtx evmtype
evm.config = vmConfig
evm.chainRules = chainRules
- evm.interpreter = NewEVMInterpreter(evm, vmConfig)
+ // [zkevm] change
+ evm.interpreter = NewZKEVMInterpreter(evm, vmConfig)
// ensure the evm is reset to be used again
atomic.StoreInt32(&evm.abort, 0)
@@ -323,8 +325,14 @@ func (c *codeAndHash) Hash() libcommon.Hash {
return c.hash
}
-// create creates a new contract using code as deployment code.
func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, value *uint256.Int, address libcommon.Address, typ OpCode, incrementNonce bool) ([]byte, libcommon.Address, uint64, error) {
+ // hack to support zkeVM
+ return evm.createZkEvm(caller, codeAndHash, gas, value, address, typ, incrementNonce)
+}
+
+// create creates a new contract using code as deployment code.
+func (evm *EVM) create_disabled(caller ContractRef, codeAndHash *codeAndHash, gas uint64, value *uint256.Int, address libcommon.Address, typ OpCode, incrementNonce bool) ([]byte, libcommon.Address, uint64, error) {
+
var ret []byte
var err error
var gasConsumption uint64
@@ -362,23 +370,17 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
}
evm.intraBlockState.SetNonce(caller.Address(), nonce+1)
}
- //[zkevm] - moved after err check, because zkevm reverts the address add
// We add this to the access list _before_ taking a snapshot. Even if the creation fails,
// the access-list change should not be rolled back
- // if evm.chainRules.IsBerlin {
- // evm.intraBlockState.AddAddressToAccessList(address)
- // }
+ if evm.chainRules.IsBerlin {
+ evm.intraBlockState.AddAddressToAccessList(address)
+ }
// Ensure there's no existing contract already at the designated address
contractHash := evm.intraBlockState.GetCodeHash(address)
if evm.intraBlockState.GetNonce(address) != 0 || (contractHash != (libcommon.Hash{}) && contractHash != emptyCodeHash) {
err = ErrContractAddressCollision
return nil, libcommon.Address{}, 0, err
}
-
- if evm.chainRules.IsBerlin {
- evm.intraBlockState.AddAddressToAccessList(address)
- }
-
// Create a new account on the state
snapshot := evm.intraBlockState.Snapshot()
evm.intraBlockState.CreateAccount(address, true)
@@ -408,7 +410,7 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
}
// Reject code starting with 0xEF if EIP-3541 is enabled.
- if err == nil && len(ret) >= 1 && ret[0] == 0xEF {
+ if err == nil && evm.chainRules.IsLondon && len(ret) >= 1 && ret[0] == 0xEF {
err = ErrInvalidCode
}
// if the contract creation ran successfully and no errors were returned
diff --git a/core/vm/evm_zkevm.go b/core/vm/evm_zkevm.go
new file mode 100644
index 00000000000..f5a1afd1cf0
--- /dev/null
+++ b/core/vm/evm_zkevm.go
@@ -0,0 +1,151 @@
+// 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/holiman/uint256"
+ libcommon "github.com/ledgerwatch/erigon-lib/common"
+ "github.com/ledgerwatch/erigon/params"
+)
+
+// [zkevm] contains the list of zkevm precompiles
+func (evm *EVM) precompile(addr libcommon.Address) (PrecompiledContract, bool) {
+ var precompiles map[libcommon.Address]PrecompiledContract
+ switch {
+ default:
+ precompiles = PrecompiledContractsZKEVMDragonfruit
+ }
+ p, ok := precompiles[addr]
+ return p, ok
+}
+
+// create creates a new contract using code as deployment code.
+func (evm *EVM) createZkEvm(caller ContractRef, codeAndHash *codeAndHash, gas uint64, value *uint256.Int, address libcommon.Address, typ OpCode, incrementNonce bool) ([]byte, libcommon.Address, uint64, error) {
+ var ret []byte
+ var err error
+ var gasConsumption uint64
+ depth := evm.interpreter.Depth()
+
+ if evm.config.Debug {
+ if depth == 0 {
+ evm.config.Tracer.CaptureStart(evm, caller.Address(), address, false /* precompile */, true /* create */, codeAndHash.code, gas, value, nil)
+ defer func() {
+ evm.config.Tracer.CaptureEnd(ret, gasConsumption, err)
+ }()
+ } else {
+ evm.config.Tracer.CaptureEnter(typ, caller.Address(), address, false /* precompile */, true /* create */, codeAndHash.code, gas, value, nil)
+ defer func() {
+ evm.config.Tracer.CaptureExit(ret, gasConsumption, err)
+ }()
+ }
+ }
+
+ // Depth check execution. Fail if we're trying to execute above the
+ // limit.
+ if depth > int(params.CallCreateDepth) {
+ err = ErrDepth
+ return nil, libcommon.Address{}, gas, err
+ }
+ if !evm.context.CanTransfer(evm.intraBlockState, caller.Address(), value) {
+ err = ErrInsufficientBalance
+ return nil, libcommon.Address{}, gas, err
+ }
+ if incrementNonce {
+ nonce := evm.intraBlockState.GetNonce(caller.Address())
+ if nonce+1 < nonce {
+ err = ErrNonceUintOverflow
+ return nil, libcommon.Address{}, gas, err
+ }
+ evm.intraBlockState.SetNonce(caller.Address(), nonce+1)
+ }
+ //[zkevm] - moved after err check, because zkevm reverts the address add
+ // We add this to the access list _before_ taking a snapshot. Even if the creation fails,
+ // the access-list change should not be rolled back
+ // if evm.chainRules.IsBerlin {
+ // evm.intraBlockState.AddAddressToAccessList(address)
+ // }
+ // Ensure there's no existing contract already at the designated address
+ contractHash := evm.intraBlockState.GetCodeHash(address)
+ if evm.intraBlockState.GetNonce(address) != 0 || (contractHash != (libcommon.Hash{}) && contractHash != emptyCodeHash) {
+ err = ErrContractAddressCollision
+ return nil, libcommon.Address{}, 0, err
+ }
+
+ if evm.chainRules.IsBerlin {
+ evm.intraBlockState.AddAddressToAccessList(address)
+ }
+
+ // Create a new account on the state
+ snapshot := evm.intraBlockState.Snapshot()
+ evm.intraBlockState.CreateAccount(address, true)
+ if evm.chainRules.IsSpuriousDragon {
+ evm.intraBlockState.SetNonce(address, 1)
+ }
+ evm.context.Transfer(evm.intraBlockState, caller.Address(), address, value, false /* bailout */)
+
+ // Initialise a new contract and set the code that is to be used by the EVM.
+ // The contract is a scoped environment for this execution context only.
+ contract := NewContract(caller, AccountRef(address), value, gas, evm.config.SkipAnalysis)
+ contract.SetCodeOptionalHash(&address, codeAndHash)
+
+ if evm.config.NoRecursion && depth > 0 {
+ return nil, address, gas, nil
+ }
+
+ ret, err = run(evm, contract, nil, false)
+
+ // EIP-170: Contract code size limit
+ if err == nil && evm.chainRules.IsSpuriousDragon && len(ret) > params.MaxCodeSize {
+ // Gnosis Chain prior to Shanghai didn't have EIP-170 enabled,
+ // but EIP-3860 (part of Shanghai) requires EIP-170.
+ if !evm.chainRules.IsAura || evm.config.HasEip3860(evm.chainRules) {
+ err = ErrMaxCodeSizeExceeded
+ }
+ }
+
+ // Reject code starting with 0xEF if EIP-3541 is enabled.
+ if err == nil && len(ret) >= 1 && ret[0] == 0xEF {
+ err = ErrInvalidCode
+ }
+ // if the contract creation ran successfully and no errors were returned
+ // calculate the gas required to store the code. If the code could not
+ // be stored due to not enough gas set an error and let it be handled
+ // by the error checking condition below.
+ if err == nil {
+ createDataGas := uint64(len(ret)) * params.CreateDataGas
+ if contract.UseGas(createDataGas) {
+ evm.intraBlockState.SetCode(address, ret)
+ } else if evm.chainRules.IsHomestead {
+ err = ErrCodeStoreOutOfGas
+ }
+ }
+
+ // When an error was returned by the EVM or when setting the creation code
+ // above we revert to the snapshot and consume any gas remaining. Additionally
+ // when we're in homestead this also counts for code storage gas errors.
+ if err != nil && (evm.chainRules.IsHomestead || err != ErrCodeStoreOutOfGas) {
+ evm.intraBlockState.RevertToSnapshot(snapshot)
+ if err != ErrExecutionReverted {
+ contract.UseGas(contract.Gas)
+ }
+ }
+
+ // calculate gasConsumption for deferred captures
+ gasConsumption = gas - contract.Gas
+
+ return ret, address, contract.Gas, err
+}
diff --git a/core/vm/evmtypes/evmtypes.go b/core/vm/evmtypes/evmtypes.go
index f8c676dc591..efc59612021 100644
--- a/core/vm/evmtypes/evmtypes.go
+++ b/core/vm/evmtypes/evmtypes.go
@@ -56,7 +56,6 @@ type (
// IntraBlockState is an EVM database for full state querying.
type IntraBlockState interface {
CreateAccount(libcommon.Address, bool)
- GetTxCount() (uint64, error)
SubBalance(libcommon.Address, *uint256.Int)
AddBalance(libcommon.Address, *uint256.Int)
diff --git a/core/vm/evmtypes/evmtypes_zkevm.go b/core/vm/evmtypes/evmtypes_zkevm.go
new file mode 100644
index 00000000000..f983d588ebc
--- /dev/null
+++ b/core/vm/evmtypes/evmtypes_zkevm.go
@@ -0,0 +1,7 @@
+package evmtypes
+
+// IntraBlockState is an EVM database for full state querying.
+type ZKIntraBlockState interface {
+ IntraBlockState
+ GetTxCount() (uint64, error)
+}
diff --git a/core/vm/gas_table.go b/core/vm/gas_table.go
index ba7a67affd7..60e94e40e5a 100644
--- a/core/vm/gas_table.go
+++ b/core/vm/gas_table.go
@@ -17,7 +17,6 @@
package vm
import (
- "bytes"
"errors"
"github.com/holiman/uint256"
@@ -480,25 +479,10 @@ func gasStaticCall(evm VMInterpreter, contract *Contract, stack *stack.Stack, me
func gasSelfdestruct(evm VMInterpreter, contract *Contract, stack *stack.Stack, mem *Memory, memorySize uint64) (uint64, error) {
var gas uint64
- var address = libcommon.Address(stack.Back(0).Bytes20())
-
- callerAddr := contract.Address()
- balance := evm.IntraBlockState().GetBalance(callerAddr)
-
- //[zkevm] - cold access gas if there was a transfer
- if !evm.IntraBlockState().AddressInAccessList(address) {
- evm.IntraBlockState().AddAddressToAccessList(address)
-
- // in this case there was no transfer, so don't take cold access gas
- if !bytes.Equal(callerAddr.Bytes(), address.Bytes()) && balance.GtUint64(0) {
- // If the caller cannot afford the cost, this change will be rolled back
- gas += params.ColdAccountAccessCostEIP2929
- }
- }
-
// TangerineWhistle (EIP150) gas reprice fork:
if evm.ChainRules().IsTangerineWhistle {
- gas += params.SelfdestructGasEIP150
+ gas = params.SelfdestructGasEIP150
+ var address = libcommon.Address(stack.Back(0).Bytes20())
if evm.ChainRules().IsSpuriousDragon {
// if empty and transfers value
@@ -509,9 +493,9 @@ func gasSelfdestruct(evm VMInterpreter, contract *Contract, stack *stack.Stack,
gas += params.CreateBySelfdestructGas
}
}
- //[zkevm] - according to eip-4758 this is removed
- // if !evm.IntraBlockState().HasSelfdestructed(contract.Address()) {
- // evm.IntraBlockState().AddRefund(params.SelfdestructRefundGas)
- // }
+
+ if !evm.IntraBlockState().HasSelfdestructed(contract.Address()) {
+ evm.IntraBlockState().AddRefund(params.SelfdestructRefundGas)
+ }
return gas, nil
}
diff --git a/core/vm/gas_table_zkevm.go b/core/vm/gas_table_zkevm.go
new file mode 100644
index 00000000000..7a3387f8260
--- /dev/null
+++ b/core/vm/gas_table_zkevm.go
@@ -0,0 +1,47 @@
+package vm
+
+import (
+ "bytes"
+
+ libcommon "github.com/ledgerwatch/erigon-lib/common"
+ "github.com/ledgerwatch/erigon/core/vm/stack"
+ "github.com/ledgerwatch/erigon/params"
+)
+
+func gasSelfdestruct_zkevm(evm VMInterpreter, contract *Contract, stack *stack.Stack, mem *Memory, memorySize uint64) (uint64, error) {
+ var gas uint64
+ var address = libcommon.Address(stack.Back(0).Bytes20())
+
+ callerAddr := contract.Address()
+ balance := evm.IntraBlockState().GetBalance(callerAddr)
+
+ //[zkevm] - cold access gas if there was a transfer
+ if !evm.IntraBlockState().AddressInAccessList(address) {
+ evm.IntraBlockState().AddAddressToAccessList(address)
+
+ // in this case there was no transfer, so don't take cold access gas
+ if !bytes.Equal(callerAddr.Bytes(), address.Bytes()) && balance.GtUint64(0) {
+ // If the caller cannot afford the cost, this change will be rolled back
+ gas += params.ColdAccountAccessCostEIP2929
+ }
+ }
+
+ // TangerineWhistle (EIP150) gas reprice fork:
+ if evm.ChainRules().IsTangerineWhistle {
+ gas += params.SelfdestructGasEIP150
+
+ if evm.ChainRules().IsSpuriousDragon {
+ // if empty and transfers value
+ if evm.IntraBlockState().Empty(address) && !evm.IntraBlockState().GetBalance(contract.Address()).IsZero() {
+ gas += params.CreateBySelfdestructGas
+ }
+ } else if !evm.IntraBlockState().Exist(address) {
+ gas += params.CreateBySelfdestructGas
+ }
+ }
+ //[zkevm] - according to eip-4758 this is removed
+ // if !evm.IntraBlockState().HasSelfdestructed(contract.Address()) {
+ // evm.IntraBlockState().AddRefund(params.SelfdestructRefundGas)
+ // }
+ return gas, nil
+}
diff --git a/core/vm/instructions.go b/core/vm/instructions.go
index 498b44789f7..691620a4b5a 100644
--- a/core/vm/instructions.go
+++ b/core/vm/instructions.go
@@ -18,9 +18,6 @@ package vm
import (
"fmt"
- "math/big"
-
- "github.com/iden3/go-iden3-crypto/keccak256"
"github.com/holiman/uint256"
libcommon "github.com/ledgerwatch/erigon-lib/common"
@@ -311,20 +308,6 @@ func opCallValue(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) (
}
func opCallDataLoad(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
- x := scope.Stack.Peek()
- if offset, overflow := x.Uint64WithOverflow(); !overflow {
- data := getData(scope.Contract.Input, offset, 32)
- if len(scope.Contract.Input) == 0 {
- data = getData(scope.Contract.Code, offset, 32)
- }
- x.SetBytes(data)
- } else {
- x.Clear()
- }
- return nil, nil
-}
-
-func opCallDataLoadFixed(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
x := scope.Stack.Peek()
if offset, overflow := x.Uint64WithOverflow(); !overflow {
data := getData(scope.Contract.Input, offset, 32)
@@ -340,25 +323,6 @@ func opCallDataSize(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext
return nil, nil
}
-func opCallDataCopyFixed(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
- var (
- memOffset = scope.Stack.Pop()
- dataOffset = scope.Stack.Pop()
- length = scope.Stack.Pop()
- )
- dataOffset64, overflow := dataOffset.Uint64WithOverflow()
- if overflow {
- dataOffset64 = 0xffffffffffffffff
- }
- // These values are checked for overflow during gas cost calculation
- memOffset64 := memOffset.Uint64()
- length64 := length.Uint64()
-
- scope.Memory.Set(memOffset64, length64, getData(scope.Contract.Input, dataOffset64, length64))
-
- return nil, nil
-}
-
func opCallDataCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
var (
memOffset = scope.Stack.Pop()
@@ -372,13 +336,7 @@ func opCallDataCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext
// These values are checked for overflow during gas cost calculation
memOffset64 := memOffset.Uint64()
length64 := length.Uint64()
-
- if len(scope.Contract.Input) == 0 {
- scope.Memory.Set(memOffset64, length64, getData(scope.Contract.Code, dataOffset64, length64))
- } else {
- scope.Memory.Set(memOffset64, length64, getData(scope.Contract.Input, dataOffset64, length64))
- }
-
+ scope.Memory.Set(memOffset64, length64, getData(scope.Contract.Input, dataOffset64, length64))
return nil, nil
}
@@ -500,18 +458,6 @@ func opExtCodeHash(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext)
return nil, nil
}
-func opExtCodeHashV2(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
- slot := scope.Stack.Peek()
- address := libcommon.Address(slot.Bytes20())
- ibs := interpreter.evm.IntraBlockState()
- if ibs.GetCodeSize(address) == 0 {
- slot.SetBytes(libcommon.Hash{}.Bytes())
- } else {
- slot.SetBytes(ibs.GetCodeHash(address).Bytes())
- }
- return nil, nil
-}
-
func opGasprice(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
scope.Stack.Push(interpreter.evm.TxContext().GasPrice)
return nil, nil
@@ -539,31 +485,6 @@ func opBlockhash(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) (
return nil, nil
}
-func opBlockhashV2(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
- num := scope.Stack.Peek()
- _, overflow := num.Uint64WithOverflow()
- if overflow {
- num.Clear()
- return nil, nil
- }
-
- ibs := interpreter.evm.IntraBlockState()
- saddr := libcommon.HexToAddress("0x000000000000000000000000000000005ca1ab1e")
-
- d1 := common.LeftPadBytes(num.Bytes(), 32)
- d2 := common.LeftPadBytes(uint256.NewInt(1).Bytes(), 32)
- mapKey := keccak256.Hash(d1, d2)
- mkh := libcommon.BytesToHash(mapKey)
-
- // set mapping of keccak256(txnum,1) -> smt root
- blockHash := uint256.NewInt(0)
- ibs.GetState(saddr, &mkh, blockHash)
-
- num.SetBytes(blockHash.Bytes())
-
- return nil, nil
-}
-
func opCoinbase(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
scope.Stack.Push(new(uint256.Int).SetBytes(interpreter.evm.Context().Coinbase.Bytes()))
return nil, nil
@@ -581,18 +502,6 @@ func opNumber(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]b
return nil, nil
}
-func opNumberV2(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
- ibs := interpreter.evm.IntraBlockState()
- saddr := libcommon.HexToAddress("0x000000000000000000000000000000005ca1ab1e")
- sl0 := libcommon.HexToHash("0x0")
-
- txNum := uint256.NewInt(0)
- ibs.GetState(saddr, &sl0, txNum)
-
- scope.Stack.Push(txNum)
- return nil, nil
-}
-
func opDifficulty(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
var v *uint256.Int
if interpreter.evm.Context().PrevRanDao != nil {
@@ -609,13 +518,6 @@ func opDifficulty(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext)
return nil, nil
}
-func opDifficultyV2(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
- zeroInt := new(big.Int).SetUint64(0)
- v, _ := uint256.FromBig(zeroInt)
- scope.Stack.Push(v)
- return nil, nil
-}
-
func opGasLimit(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
if interpreter.evm.Context().MaxGasLimit {
scope.Stack.Push(new(uint256.Int).SetAllOne())
@@ -913,36 +815,6 @@ func opDelegateCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext
return ret, nil
}
-func opStaticCallForkId5(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
- // Pop gas. The actual gas is in interpreter.evm.callGasTemp.
- stack := scope.Stack
- // We use it as a temporary value
- 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()
- toAddr := libcommon.Address(addr.Bytes20())
- // Get arguments from the memory.
- args := scope.Memory.GetPtr(int64(inOffset.Uint64()), int64(inSize.Uint64()))
- ret, returnGas, err := interpreter.evm.StaticCall(scope.Contract, toAddr, args, gas)
- if err != nil {
- temp.Clear()
- } else {
- temp.SetOne()
- }
- stack.Push(&temp)
- if err == nil || err == ErrExecutionReverted {
- ret = common.CopyBytes(ret)
- scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
- }
-
- scope.Contract.Gas += returnGas
-
- interpreter.returnData = ret
-
- return ret, nil
-}
-
func opStaticCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
// Pop gas. The actual gas is in interpreter.evm.callGasTemp.
stack := scope.Stack
@@ -954,6 +826,7 @@ func opStaticCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext)
toAddr := libcommon.Address(addr.Bytes20())
// Get arguments from the memory.
args := scope.Memory.GetPtr(int64(inOffset.Uint64()), int64(inSize.Uint64()))
+
ret, returnGas, err := interpreter.evm.StaticCall(scope.Contract, toAddr, args, gas)
if err != nil {
temp.Clear()
@@ -968,11 +841,7 @@ func opStaticCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext)
scope.Contract.Gas += returnGas
- //[zkevm] do not overryde returnData if reverted
- if err != ErrExecutionReverted {
- interpreter.returnData = ret
- }
-
+ interpreter.returnData = ret
return ret, nil
}
@@ -997,29 +866,6 @@ func opStop(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byt
return nil, errStopToken
}
-// removed the actual self destruct at the end
-func opSendAll(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
- if interpreter.readOnly {
- return nil, ErrWriteProtection
- }
- beneficiary := scope.Stack.Pop()
- callerAddr := scope.Contract.Address()
- beneficiaryAddr := libcommon.Address(beneficiary.Bytes20())
- balance := interpreter.evm.IntraBlockState().GetBalance(callerAddr)
- if interpreter.evm.Config().Debug {
- if interpreter.cfg.Debug {
- interpreter.cfg.Tracer.CaptureEnter(SELFDESTRUCT, callerAddr, beneficiaryAddr, false /* precompile */, false /* create */, []byte{}, 0, balance, nil /* code */)
- interpreter.cfg.Tracer.CaptureExit([]byte{}, 0, nil)
- }
- }
-
- if beneficiaryAddr != callerAddr {
- interpreter.evm.IntraBlockState().AddBalance(beneficiaryAddr, balance)
- interpreter.evm.IntraBlockState().SubBalance(callerAddr, balance)
- }
- return nil, errStopToken
-}
-
func opSelfdestruct(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
if interpreter.readOnly {
return nil, ErrWriteProtection
diff --git a/core/vm/instructions_test.go b/core/vm/instructions_test.go
index b430bab620c..b375be2e92a 100644
--- a/core/vm/instructions_test.go
+++ b/core/vm/instructions_test.go
@@ -245,7 +245,7 @@ func TestBlockhashV2(t *testing.T) {
var (
env = NewEVM(evmtypes.BlockContext{GetHash: gethashFn}, evmtypes.TxContext{}, TestIntraBlockState{}, params.TestChainConfig, Config{})
stack = stack.New()
- evmInterpreter = NewEVMInterpreter(env, env.Config())
+ evmInterpreter = NewZKEVMInterpreter(env, env.Config())
pc = uint64(0)
)
@@ -270,7 +270,7 @@ func TestBlockhashV2(t *testing.T) {
blockNumberBytes := new(uint256.Int).SetUint64(test.blockNumber)
stack.Push(blockNumberBytes)
- opBlockhashV2(&pc, evmInterpreter, &ScopeContext{nil, stack, nil})
+ opBlockhash_zkevm(&pc, evmInterpreter, &ScopeContext{nil, stack, nil})
actual := stack.Pop()
fmt.Println(actual)
@@ -303,7 +303,7 @@ func TestDifficultyV2(t *testing.T) {
for i, test := range tests {
expected := new(uint256.Int).SetBytes(test.expected.Bytes())
- opDifficultyV2(&pc, evmInterpreter, &ScopeContext{nil, stack, nil})
+ opDifficulty_zkevm(&pc, evmInterpreter, &ScopeContext{nil, stack, nil})
actual := stack.Pop()
if actual.Cmp(expected) != 0 {
@@ -334,7 +334,7 @@ func TestExtCodeHashV2(t *testing.T) {
address := new(uint256.Int).SetBytes(common.Hex2Bytes(test.address))
expected := new(uint256.Int).SetBytes(test.expected.Bytes())
stack.Push(address)
- opExtCodeHashV2(&pc, evmInterpreter, &ScopeContext{nil, stack, nil})
+ opExtCodeHash_zkevm(&pc, evmInterpreter, &ScopeContext{nil, stack, nil})
actual := stack.Pop()
if actual.Cmp(expected) != 0 {
t.Errorf("Testcase %d, expected %x, got %x", i, expected, actual)
diff --git a/core/vm/instructions_zkevm.go b/core/vm/instructions_zkevm.go
new file mode 100644
index 00000000000..a8742deda58
--- /dev/null
+++ b/core/vm/instructions_zkevm.go
@@ -0,0 +1,160 @@
+package vm
+
+import (
+ "math/big"
+
+ "github.com/iden3/go-iden3-crypto/keccak256"
+
+ "github.com/holiman/uint256"
+ libcommon "github.com/ledgerwatch/erigon-lib/common"
+ "github.com/ledgerwatch/erigon/common"
+)
+
+func opCallDataLoad_zkevmIncompatible(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
+ x := scope.Stack.Peek()
+ if offset, overflow := x.Uint64WithOverflow(); !overflow {
+ data := getData(scope.Contract.Input, offset, 32)
+ if len(scope.Contract.Input) == 0 {
+ data = getData(scope.Contract.Code, offset, 32)
+ }
+ x.SetBytes(data)
+ } else {
+ x.Clear()
+ }
+ return nil, nil
+}
+
+func opCallDataCopy_zkevmIncompatible(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
+ var (
+ memOffset = scope.Stack.Pop()
+ dataOffset = scope.Stack.Pop()
+ length = scope.Stack.Pop()
+ )
+ dataOffset64, overflow := dataOffset.Uint64WithOverflow()
+ if overflow {
+ dataOffset64 = 0xffffffffffffffff
+ }
+ // These values are checked for overflow during gas cost calculation
+ memOffset64 := memOffset.Uint64()
+ length64 := length.Uint64()
+
+ if len(scope.Contract.Input) == 0 {
+ scope.Memory.Set(memOffset64, length64, getData(scope.Contract.Code, dataOffset64, length64))
+ } else {
+ scope.Memory.Set(memOffset64, length64, getData(scope.Contract.Input, dataOffset64, length64))
+ }
+
+ return nil, nil
+}
+
+func opExtCodeHash_zkevm(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
+ slot := scope.Stack.Peek()
+ address := libcommon.Address(slot.Bytes20())
+ ibs := interpreter.evm.IntraBlockState()
+ if ibs.GetCodeSize(address) == 0 {
+ slot.SetBytes(libcommon.Hash{}.Bytes())
+ } else {
+ slot.SetBytes(ibs.GetCodeHash(address).Bytes())
+ }
+ return nil, nil
+}
+
+func opBlockhash_zkevm(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
+ num := scope.Stack.Peek()
+ _, overflow := num.Uint64WithOverflow()
+ if overflow {
+ num.Clear()
+ return nil, nil
+ }
+
+ ibs := interpreter.evm.IntraBlockState()
+ saddr := libcommon.HexToAddress("0x000000000000000000000000000000005ca1ab1e")
+
+ d1 := common.LeftPadBytes(num.Bytes(), 32)
+ d2 := common.LeftPadBytes(uint256.NewInt(1).Bytes(), 32)
+ mapKey := keccak256.Hash(d1, d2)
+ mkh := libcommon.BytesToHash(mapKey)
+
+ // set mapping of keccak256(txnum,1) -> smt root
+ blockHash := uint256.NewInt(0)
+ ibs.GetState(saddr, &mkh, blockHash)
+
+ num.SetBytes(blockHash.Bytes())
+
+ return nil, nil
+}
+
+func opNumber_zkevm(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
+ ibs := interpreter.evm.IntraBlockState()
+ saddr := libcommon.HexToAddress("0x000000000000000000000000000000005ca1ab1e")
+ sl0 := libcommon.HexToHash("0x0")
+
+ txNum := uint256.NewInt(0)
+ ibs.GetState(saddr, &sl0, txNum)
+
+ scope.Stack.Push(txNum)
+ return nil, nil
+}
+
+func opDifficulty_zkevm(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
+ zeroInt := new(big.Int).SetUint64(0)
+ v, _ := uint256.FromBig(zeroInt)
+ scope.Stack.Push(v)
+ return nil, nil
+}
+
+func opStaticCall_zkevm(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
+ // Pop gas. The actual gas is in interpreter.evm.callGasTemp.
+ stack := scope.Stack
+ // We use it as a temporary value
+ 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()
+ toAddr := libcommon.Address(addr.Bytes20())
+ // Get arguments from the memory.
+ args := scope.Memory.GetPtr(int64(inOffset.Uint64()), int64(inSize.Uint64()))
+ ret, returnGas, err := interpreter.evm.StaticCall(scope.Contract, toAddr, args, gas)
+ if err != nil {
+ temp.Clear()
+ } else {
+ temp.SetOne()
+ }
+ stack.Push(&temp)
+ if err == nil || err == ErrExecutionReverted {
+ ret = common.CopyBytes(ret)
+ scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
+ }
+
+ scope.Contract.Gas += returnGas
+
+ //[zkevm] do not overryde returnData if reverted
+ if err != ErrExecutionReverted {
+ interpreter.returnData = ret
+ }
+
+ return ret, nil
+}
+
+// removed the actual self destruct at the end
+func opSendAll_zkevm(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
+ if interpreter.readOnly {
+ return nil, ErrWriteProtection
+ }
+ beneficiary := scope.Stack.Pop()
+ callerAddr := scope.Contract.Address()
+ beneficiaryAddr := libcommon.Address(beneficiary.Bytes20())
+ balance := interpreter.evm.IntraBlockState().GetBalance(callerAddr)
+ if interpreter.evm.Config().Debug {
+ if interpreter.cfg.Debug {
+ interpreter.cfg.Tracer.CaptureEnter(SELFDESTRUCT, callerAddr, beneficiaryAddr, false /* precompile */, false /* create */, []byte{}, 0, balance, nil /* code */)
+ interpreter.cfg.Tracer.CaptureExit([]byte{}, 0, nil)
+ }
+ }
+
+ if beneficiaryAddr != callerAddr {
+ interpreter.evm.IntraBlockState().AddBalance(beneficiaryAddr, balance)
+ interpreter.evm.IntraBlockState().SubBalance(callerAddr, balance)
+ }
+ return nil, errStopToken
+}
diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go
index 091b6d80e98..cff454d0f3a 100644
--- a/core/vm/interpreter.go
+++ b/core/vm/interpreter.go
@@ -124,13 +124,16 @@ func copyJumpTable(jt *JumpTable) *JumpTable {
func NewEVMInterpreter(evm VMInterpreter, cfg Config) *EVMInterpreter {
var jt *JumpTable
switch {
- // to add our own IsRohan chain rule, we would need to fork or code or chain.Config
- // that is why we hard code it here for POC
- // our fork extends berlin anyways and starts from block 1
- case evm.ChainRules().IsMordor:
- jt = &mordorInstructionSet
+ case evm.ChainRules().IsPrague:
+ jt = &pragueInstructionSet
+ case evm.ChainRules().IsCancun:
+ jt = &cancunInstructionSet
+ case evm.ChainRules().IsShanghai:
+ jt = &shanghaiInstructionSet
+ case evm.ChainRules().IsLondon:
+ jt = &londonInstructionSet
case evm.ChainRules().IsBerlin:
- jt = &rohanInstructionSet
+ jt = &berlinInstructionSet
case evm.ChainRules().IsIstanbul:
jt = &istanbulInstructionSet
case evm.ChainRules().IsConstantinople:
@@ -252,10 +255,6 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
// Get the operation from the jump table and validate the stack to ensure there are
// enough stack items available to perform the operation.
op = contract.GetOp(_pc)
- //[zkevm] - SELFDESTRUCT removed and replaced by SENDALL
- if op == SELFDESTRUCT {
- op = SENDALL
- }
operation := in.jt[op]
cost = operation.constantGas // For tracing
// Validate stack
diff --git a/core/vm/interpreter_zkevm.go b/core/vm/interpreter_zkevm.go
new file mode 100644
index 00000000000..86cbe6813be
--- /dev/null
+++ b/core/vm/interpreter_zkevm.go
@@ -0,0 +1,35 @@
+package vm
+
+import "github.com/ledgerwatch/log/v3"
+
+// NewZKEVMInterpreter returns a new instance of the Interpreter.
+func NewZKEVMInterpreter(evm VMInterpreter, cfg Config) *EVMInterpreter {
+ var jt *JumpTable
+ switch {
+ // to add our own IsRohan chain rule, we would need to fork or code or chain.Config
+ // that is why we hard code it here for POC
+ // our fork extends berlin anyways and starts from block 1
+ case evm.ChainRules().IsMordor:
+ jt = &zkevmForkID5InstructionSet
+ case evm.ChainRules().IsBerlin:
+ jt = &zkevmForkID4InstructionSet
+ }
+ if len(cfg.ExtraEips) > 0 {
+ jt = copyJumpTable(jt)
+ for i, eip := range cfg.ExtraEips {
+ if err := EnableEIP(eip, jt); err != nil {
+ // Disable it, so caller can check if it's activated or not
+ cfg.ExtraEips = append(cfg.ExtraEips[:i], cfg.ExtraEips[i+1:]...)
+ log.Error("EIP activation failed", "eip", eip, "err", err)
+ }
+ }
+ }
+
+ return &EVMInterpreter{
+ VM: &VM{
+ evm: evm,
+ cfg: cfg,
+ },
+ jt: jt,
+ }
+}
diff --git a/core/vm/jump_table.go b/core/vm/jump_table.go
index 451d09726b2..c774e6f8d14 100644
--- a/core/vm/jump_table.go
+++ b/core/vm/jump_table.go
@@ -59,8 +59,10 @@ var (
constantinopleInstructionSet = newConstantinopleInstructionSet()
istanbulInstructionSet = newIstanbulInstructionSet()
berlinInstructionSet = newBerlinInstructionSet()
- rohanInstructionSet = newRohanInstructionSet()
- mordorInstructionSet = newMordorInstructionSet()
+ londonInstructionSet = newLondonInstructionSet()
+ shanghaiInstructionSet = newShanghaiInstructionSet()
+ cancunInstructionSet = newCancunInstructionSet()
+ pragueInstructionSet = newPragueInstructionSet()
)
// JumpTable contains the EVM opcodes supported at a given fork.
@@ -83,38 +85,41 @@ func validateAndFillMaxStack(jt *JumpTable) {
op.maxStack = maxStack(op.numPop, op.numPush)
}
}
-func newMordorInstructionSet() JumpTable {
- instructionSet := newRohanInstructionSet()
- instructionSet[CALLDATACOPY].execute = opCallDataCopyFixed
- instructionSet[CALLDATALOAD].execute = opCallDataLoadFixed
- instructionSet[STATICCALL].execute = opStaticCallForkId5
+// newPragueInstructionSet returns the frontier, homestead, byzantium,
+// constantinople, istanbul, petersburg, berlin, london, paris, shanghai,
+// cancun, and prague instructions.
+func newPragueInstructionSet() JumpTable {
+ instructionSet := newCancunInstructionSet()
+ validateAndFillMaxStack(&instructionSet)
+ return instructionSet
+}
- enable3855(&instructionSet) // EIP-3855: Enable PUSH0 opcode
+// newCancunInstructionSet returns the frontier, homestead, byzantium,
+// constantinople, istanbul, petersburg, berlin, london, paris, shanghai,
+// and cancun instructions.
+func newCancunInstructionSet() JumpTable {
+ instructionSet := newShanghaiInstructionSet()
+ validateAndFillMaxStack(&instructionSet)
+ return instructionSet
+}
+// newShanghaiInstructionSet returns the frontier, homestead, byzantium,
+// constantinople, istanbul, petersburg, berlin, london, paris, and shanghai instructions.
+func newShanghaiInstructionSet() JumpTable {
+ instructionSet := newLondonInstructionSet()
+ enable3855(&instructionSet) // PUSH0 instruction https://eips.ethereum.org/EIPS/eip-3855
+ enable3860(&instructionSet) // Limit and meter initcode https://eips.ethereum.org/EIPS/eip-3860
validateAndFillMaxStack(&instructionSet)
return instructionSet
}
-func newRohanInstructionSet() JumpTable {
+// newLondonInstructionSet returns the frontier, homestead, byzantium,
+// constantinople, istanbul, petersburg, berlin, and london instructions.
+func newLondonInstructionSet() JumpTable {
instructionSet := newBerlinInstructionSet()
-
- instructionSet[NUMBER].execute = opNumberV2
-
- instructionSet[DIFFICULTY].execute = opDifficultyV2
-
- instructionSet[BLOCKHASH].execute = opBlockhashV2
-
- instructionSet[EXTCODEHASH].execute = opExtCodeHashV2
- instructionSet[SELFDESTRUCT].execute = opSendAll
-
- instructionSet[SENDALL] = &operation{
- execute: opSendAll,
- dynamicGas: gasSelfdestruct,
- numPop: 1,
- numPush: 0,
- }
-
+ enable3529(&instructionSet) // Reduction in refunds https://eips.ethereum.org/EIPS/eip-3529
+ enable3198(&instructionSet) // Base fee opcode https://eips.ethereum.org/EIPS/eip-3198
validateAndFillMaxStack(&instructionSet)
return instructionSet
}
diff --git a/core/vm/jump_table_zkevm.go b/core/vm/jump_table_zkevm.go
new file mode 100644
index 00000000000..23168fc8e4d
--- /dev/null
+++ b/core/vm/jump_table_zkevm.go
@@ -0,0 +1,53 @@
+package vm
+
+var (
+ zkevmForkID4InstructionSet = newZkEVM_forkID4InstructionSet()
+ zkevmForkID5InstructionSet = newZkEVM_forkID5InstructionSet()
+)
+
+// newZkEVM_forkID4InstructionSet returns the instruction set for the forkID4
+func newZkEVM_forkID4InstructionSet() JumpTable {
+ instructionSet := newBerlinInstructionSet()
+
+ enable2929_zkevm(&instructionSet) // Access lists for trie accesses https://eips.ethereum.org/EIPS/eip-2929
+
+ instructionSet[CALLDATALOAD].execute = opCallDataLoad_zkevmIncompatible
+ instructionSet[CALLDATACOPY].execute = opCallDataCopy_zkevmIncompatible
+
+ instructionSet[STATICCALL].execute = opStaticCall_zkevm
+
+ instructionSet[NUMBER].execute = opNumber_zkevm
+
+ instructionSet[DIFFICULTY].execute = opDifficulty_zkevm
+
+ instructionSet[BLOCKHASH].execute = opBlockhash_zkevm
+
+ instructionSet[EXTCODEHASH].execute = opExtCodeHash_zkevm
+
+ instructionSet[SENDALL] = &operation{
+ execute: opSendAll_zkevm,
+ dynamicGas: gasSelfdestruct_zkevm,
+ numPop: 1,
+ numPush: 0,
+ }
+
+ // SELFDESTRUCT is replaces by SENDALL
+ instructionSet[SELFDESTRUCT] = instructionSet[SENDALL]
+
+ validateAndFillMaxStack(&instructionSet)
+ return instructionSet
+}
+
+// newZkEVM_forkID5InstructionSet returns the instruction set for the forkID5
+func newZkEVM_forkID5InstructionSet() JumpTable {
+ instructionSet := newZkEVM_forkID4InstructionSet()
+
+ instructionSet[CALLDATACOPY].execute = opCallDataCopy
+ instructionSet[CALLDATALOAD].execute = opCallDataLoad
+ instructionSet[STATICCALL].execute = opStaticCall
+
+ enable3855(&instructionSet) // EIP-3855: Enable PUSH0 opcode
+
+ validateAndFillMaxStack(&instructionSet)
+ return instructionSet
+}
diff --git a/core/vm/opcodes.go b/core/vm/opcodes.go
index c73af556292..7db520fd5e5 100644
--- a/core/vm/opcodes.go
+++ b/core/vm/opcodes.go
@@ -210,7 +210,6 @@ const (
DELEGATECALL
CREATE2
STATICCALL OpCode = 0xfa
- SENDALL OpCode = 0xfb
REVERT OpCode = 0xfd
INVALID OpCode = 0xfe
SELFDESTRUCT OpCode = 0xff
@@ -382,7 +381,6 @@ var opCodeToString = map[OpCode]string{
REVERT: "REVERT",
INVALID: "INVALID",
SELFDESTRUCT: "SELFDESTRUCT",
- SENDALL: "SENDALL",
}
func (op OpCode) String() string {
@@ -540,7 +538,6 @@ var stringToOp = map[string]OpCode{
"REVERT": REVERT,
"INVALID": INVALID,
"SELFDESTRUCT": SELFDESTRUCT,
- "SENDALL": SENDALL,
}
// StringToOp finds the opcode whose name is stored in `str`.
diff --git a/core/vm/opcodes_zkevm.go b/core/vm/opcodes_zkevm.go
new file mode 100644
index 00000000000..f1b22c90174
--- /dev/null
+++ b/core/vm/opcodes_zkevm.go
@@ -0,0 +1,12 @@
+package vm
+
+const (
+ SENDALL OpCode = 0xfb
+)
+
+// adding extra opcodes dynamically to keep separate from the main codebase
+// that simplifies rebasing new versions of Erigon
+func init() {
+ opCodeToString[SENDALL] = "SENDALL"
+ stringToOp["SENDALL"] = SENDALL
+}
diff --git a/core/vm/operations_acl.go b/core/vm/operations_acl.go
index 80ab431ce89..10ace257499 100644
--- a/core/vm/operations_acl.go
+++ b/core/vm/operations_acl.go
@@ -173,15 +173,12 @@ func makeCallVariantGasCallEIP2929(oldCalculator gasFunc) gasFunc {
// the cost to charge for cold access, if any, is Cold - Warm
coldCost := params.ColdAccountAccessCostEIP2929 - params.WarmStorageReadCostEIP2929
if !warmAccess {
- //[zkevm] - moved after err check, because zkevm reverts the address add
- // evm.IntraBlockState().AddAddressToAccessList(addr)
-
+ evm.IntraBlockState().AddAddressToAccessList(addr)
// Charge the remaining difference here already, to correctly calculate available
// gas for call
if !contract.UseGas(coldCost) {
return 0, ErrOutOfGas
}
- evm.IntraBlockState().AddAddressToAccessList(addr)
}
// Now call the old calculator, which takes into account
// - create new account
@@ -245,10 +242,9 @@ func makeSelfdestructGasFn(refundsEnabled bool) gasFunc {
if evm.IntraBlockState().Empty(address) && !evm.IntraBlockState().GetBalance(contract.Address()).IsZero() {
gas += params.CreateBySelfdestructGas
}
- //[zkevm] - according to eip-4758 this is removed
- // if refundsEnabled && !evm.IntraBlockState().HasSelfdestructed(contract.Address()) {
- // evm.IntraBlockState().AddRefund(params.SelfdestructRefundGas)
- // }
+ if refundsEnabled && !evm.IntraBlockState().HasSelfdestructed(contract.Address()) {
+ evm.IntraBlockState().AddRefund(params.SelfdestructRefundGas)
+ }
return gas, nil
}
return gasFunc
diff --git a/core/vm/operations_acl_zkevm.go b/core/vm/operations_acl_zkevm.go
new file mode 100644
index 00000000000..a6578cd4e3a
--- /dev/null
+++ b/core/vm/operations_acl_zkevm.go
@@ -0,0 +1,80 @@
+package vm
+
+import (
+ libcommon "github.com/ledgerwatch/erigon-lib/common"
+ "github.com/ledgerwatch/erigon/core/vm/stack"
+ "github.com/ledgerwatch/erigon/params"
+)
+
+var (
+ gasCallEIP2929_zkevm = makeCallVariantGasCallEIP2929_zkevm(gasCall)
+ gasDelegateCallEIP2929_zkevm = makeCallVariantGasCallEIP2929_zkevm(gasDelegateCall)
+ gasStaticCallEIP2929_zkevm = makeCallVariantGasCallEIP2929_zkevm(gasStaticCall)
+ gasCallCodeEIP2929_zkevm = makeCallVariantGasCallEIP2929_zkevm(gasCallCode)
+ gasSelfdestructEIP2929_zkevm = makeSelfdestructGasFn_zkevm(true)
+ // gasSelfdestructEIP3529_zkevm implements the changes in EIP-2539 (no refunds)
+ gasSelfdestructEIP3529_zkevm = makeSelfdestructGasFn_zkevm(false)
+)
+
+// makeCallGasFn_zkevm can create the call dynamic gas function for EIP-2929 and EIP-2539 for Polygon zkEVM
+func makeCallVariantGasCallEIP2929_zkevm(oldCalculator gasFunc) gasFunc {
+ return func(evm VMInterpreter, contract *Contract, stack *stack.Stack, mem *Memory, memorySize uint64) (uint64, error) {
+ addr := libcommon.Address(stack.Back(1).Bytes20())
+ // Check slot presence in the access list
+ warmAccess := evm.IntraBlockState().AddressInAccessList(addr)
+ // The WarmStorageReadCostEIP2929 (100) is already deducted in the form of a constant cost, so
+ // the cost to charge for cold access, if any, is Cold - Warm
+ coldCost := params.ColdAccountAccessCostEIP2929 - params.WarmStorageReadCostEIP2929
+ if !warmAccess {
+ //[zkevm] - moved after err check, because zkevm reverts the address add
+ // evm.IntraBlockState().AddAddressToAccessList(addr)
+
+ // Charge the remaining difference here already, to correctly calculate available
+ // gas for call
+ if !contract.UseGas(coldCost) {
+ return 0, ErrOutOfGas
+ }
+ evm.IntraBlockState().AddAddressToAccessList(addr)
+ }
+ // Now call the old calculator, which takes into account
+ // - create new account
+ // - transfer value
+ // - memory expansion
+ // - 63/64ths rule
+ gas, err := oldCalculator(evm, contract, stack, mem, memorySize)
+ if warmAccess || err != nil {
+ return gas, err
+ }
+ // In case of a cold access, we temporarily add the cold charge back, and also
+ // add it to the returned gas. By adding it to the return, it will be charged
+ // outside of this function, as part of the dynamic gas, and that will make it
+ // also become correctly reported to tracers.
+ contract.Gas += coldCost
+ return gas + coldCost, nil
+ }
+}
+
+// makeSelfdestructGasFn_zkevm can create the selfdestruct dynamic gas function for EIP-2929 and EIP-2539 for Polygon zkEVM
+func makeSelfdestructGasFn_zkevm(refundsEnabled bool) gasFunc {
+ gasFunc := func(evm VMInterpreter, contract *Contract, stack *stack.Stack, mem *Memory, memorySize uint64) (uint64, error) {
+ var (
+ gas uint64
+ address = libcommon.Address(stack.Peek().Bytes20())
+ )
+ if !evm.IntraBlockState().AddressInAccessList(address) {
+ // If the caller cannot afford the cost, this change will be rolled back
+ evm.IntraBlockState().AddAddressToAccessList(address)
+ gas = params.ColdAccountAccessCostEIP2929
+ }
+ // if empty and transfers value
+ if evm.IntraBlockState().Empty(address) && !evm.IntraBlockState().GetBalance(contract.Address()).IsZero() {
+ gas += params.CreateBySelfdestructGas
+ }
+ //[zkevm] - according to eip-4758 this is removed
+ // if refundsEnabled && !evm.IntraBlockState().HasSelfdestructed(contract.Address()) {
+ // evm.IntraBlockState().AddRefund(params.SelfdestructRefundGas)
+ // }
+ return gas, nil
+ }
+ return gasFunc
+}
diff --git a/core/vm/runtime/runtime.go b/core/vm/runtime/runtime.go
index ba15351212d..e380e0b8fb1 100644
--- a/core/vm/runtime/runtime.go
+++ b/core/vm/runtime/runtime.go
@@ -68,8 +68,12 @@ func setDefaults(cfg *Config) {
IstanbulBlock: new(big.Int),
MuirGlacierBlock: new(big.Int),
BerlinBlock: new(big.Int),
+ LondonBlock: new(big.Int),
ArrowGlacierBlock: new(big.Int),
GrayGlacierBlock: new(big.Int),
+ ShanghaiTime: new(big.Int),
+ CancunTime: new(big.Int),
+ PragueTime: new(big.Int),
}
}