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