forked from cerc-io/plugeth
all: implement EIP-2929 (gas cost increases for state access opcodes) + yolo-v2 (#21509)
* core/vm, core/state: implement EIP-2929 + YOLOv2 * core/state, core/vm: fix some review concerns * core/state, core/vm: address review concerns * core/vm: address review concerns * core/vm: better documentation * core/vm: unify sload cost as fully dynamic * core/vm: fix typo * core/vm/runtime: fix compilation flaw * core/vm/runtime: fix renaming-err leftovers * core/vm: renaming * params/config: use correct yolov2 chainid for config * core, params: use a proper new genesis for yolov2 * core/state/tests: golinter nitpicks
This commit is contained in:
parent
fb2c79df19
commit
6487c002f6
@ -147,6 +147,16 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
|
|||||||
vmContext.Origin = msg.From()
|
vmContext.Origin = msg.From()
|
||||||
|
|
||||||
evm := vm.NewEVM(vmContext, statedb, chainConfig, vmConfig)
|
evm := vm.NewEVM(vmContext, statedb, chainConfig, vmConfig)
|
||||||
|
if chainConfig.IsYoloV2(vmContext.BlockNumber) {
|
||||||
|
statedb.AddAddressToAccessList(msg.From())
|
||||||
|
if dst := msg.To(); dst != nil {
|
||||||
|
statedb.AddAddressToAccessList(*dst)
|
||||||
|
// If it's a create-tx, the destination will be added inside evm.create
|
||||||
|
}
|
||||||
|
for _, addr := range evm.ActivePrecompiles() {
|
||||||
|
statedb.AddAddressToAccessList(addr)
|
||||||
|
}
|
||||||
|
}
|
||||||
snapshot := statedb.Snapshot()
|
snapshot := statedb.Snapshot()
|
||||||
// (ret []byte, usedGas uint64, failed bool, err error)
|
// (ret []byte, usedGas uint64, failed bool, err error)
|
||||||
msgResult, err := core.ApplyMessage(evm, msg, gaspool)
|
msgResult, err := core.ApplyMessage(evm, msg, gaspool)
|
||||||
|
@ -163,7 +163,7 @@ The export-preimages command export hash preimages to an RLP encoded stream`,
|
|||||||
utils.RinkebyFlag,
|
utils.RinkebyFlag,
|
||||||
utils.TxLookupLimitFlag,
|
utils.TxLookupLimitFlag,
|
||||||
utils.GoerliFlag,
|
utils.GoerliFlag,
|
||||||
utils.YoloV1Flag,
|
utils.YoloV2Flag,
|
||||||
utils.LegacyTestnetFlag,
|
utils.LegacyTestnetFlag,
|
||||||
},
|
},
|
||||||
Category: "BLOCKCHAIN COMMANDS",
|
Category: "BLOCKCHAIN COMMANDS",
|
||||||
@ -213,7 +213,7 @@ Use "ethereum dump 0" to dump the genesis block.`,
|
|||||||
utils.RopstenFlag,
|
utils.RopstenFlag,
|
||||||
utils.RinkebyFlag,
|
utils.RinkebyFlag,
|
||||||
utils.GoerliFlag,
|
utils.GoerliFlag,
|
||||||
utils.YoloV1Flag,
|
utils.YoloV2Flag,
|
||||||
utils.LegacyTestnetFlag,
|
utils.LegacyTestnetFlag,
|
||||||
utils.SyncModeFlag,
|
utils.SyncModeFlag,
|
||||||
},
|
},
|
||||||
|
@ -136,7 +136,7 @@ func remoteConsole(ctx *cli.Context) error {
|
|||||||
path = filepath.Join(path, "rinkeby")
|
path = filepath.Join(path, "rinkeby")
|
||||||
} else if ctx.GlobalBool(utils.GoerliFlag.Name) {
|
} else if ctx.GlobalBool(utils.GoerliFlag.Name) {
|
||||||
path = filepath.Join(path, "goerli")
|
path = filepath.Join(path, "goerli")
|
||||||
} else if ctx.GlobalBool(utils.YoloV1Flag.Name) {
|
} else if ctx.GlobalBool(utils.YoloV2Flag.Name) {
|
||||||
path = filepath.Join(path, "yolo-v1")
|
path = filepath.Join(path, "yolo-v1")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -144,7 +144,7 @@ var (
|
|||||||
utils.RopstenFlag,
|
utils.RopstenFlag,
|
||||||
utils.RinkebyFlag,
|
utils.RinkebyFlag,
|
||||||
utils.GoerliFlag,
|
utils.GoerliFlag,
|
||||||
utils.YoloV1Flag,
|
utils.YoloV2Flag,
|
||||||
utils.VMEnableDebugFlag,
|
utils.VMEnableDebugFlag,
|
||||||
utils.NetworkIdFlag,
|
utils.NetworkIdFlag,
|
||||||
utils.EthStatsURLFlag,
|
utils.EthStatsURLFlag,
|
||||||
|
@ -42,7 +42,7 @@ var AppHelpFlagGroups = []flags.FlagGroup{
|
|||||||
utils.NetworkIdFlag,
|
utils.NetworkIdFlag,
|
||||||
utils.GoerliFlag,
|
utils.GoerliFlag,
|
||||||
utils.RinkebyFlag,
|
utils.RinkebyFlag,
|
||||||
utils.YoloV1Flag,
|
utils.YoloV2Flag,
|
||||||
utils.RopstenFlag,
|
utils.RopstenFlag,
|
||||||
utils.SyncModeFlag,
|
utils.SyncModeFlag,
|
||||||
utils.ExitWhenSyncedFlag,
|
utils.ExitWhenSyncedFlag,
|
||||||
|
@ -236,8 +236,8 @@ func (w *wizard) manageGenesis() {
|
|||||||
w.conf.Genesis.Config.IstanbulBlock = w.readDefaultBigInt(w.conf.Genesis.Config.IstanbulBlock)
|
w.conf.Genesis.Config.IstanbulBlock = w.readDefaultBigInt(w.conf.Genesis.Config.IstanbulBlock)
|
||||||
|
|
||||||
fmt.Println()
|
fmt.Println()
|
||||||
fmt.Printf("Which block should YOLOv1 come into effect? (default = %v)\n", w.conf.Genesis.Config.YoloV1Block)
|
fmt.Printf("Which block should YOLOv2 come into effect? (default = %v)\n", w.conf.Genesis.Config.YoloV2Block)
|
||||||
w.conf.Genesis.Config.YoloV1Block = w.readDefaultBigInt(w.conf.Genesis.Config.YoloV1Block)
|
w.conf.Genesis.Config.YoloV2Block = w.readDefaultBigInt(w.conf.Genesis.Config.YoloV2Block)
|
||||||
|
|
||||||
out, _ := json.MarshalIndent(w.conf.Genesis.Config, "", " ")
|
out, _ := json.MarshalIndent(w.conf.Genesis.Config, "", " ")
|
||||||
fmt.Printf("Chain configuration updated:\n\n%s\n", out)
|
fmt.Printf("Chain configuration updated:\n\n%s\n", out)
|
||||||
|
@ -135,9 +135,9 @@ var (
|
|||||||
Name: "goerli",
|
Name: "goerli",
|
||||||
Usage: "Görli network: pre-configured proof-of-authority test network",
|
Usage: "Görli network: pre-configured proof-of-authority test network",
|
||||||
}
|
}
|
||||||
YoloV1Flag = cli.BoolFlag{
|
YoloV2Flag = cli.BoolFlag{
|
||||||
Name: "yolov1",
|
Name: "yolov2",
|
||||||
Usage: "YOLOv1 network: pre-configured proof-of-authority shortlived test network.",
|
Usage: "YOLOv2 network: pre-configured proof-of-authority shortlived test network.",
|
||||||
}
|
}
|
||||||
RinkebyFlag = cli.BoolFlag{
|
RinkebyFlag = cli.BoolFlag{
|
||||||
Name: "rinkeby",
|
Name: "rinkeby",
|
||||||
@ -744,8 +744,8 @@ func MakeDataDir(ctx *cli.Context) string {
|
|||||||
if ctx.GlobalBool(GoerliFlag.Name) {
|
if ctx.GlobalBool(GoerliFlag.Name) {
|
||||||
return filepath.Join(path, "goerli")
|
return filepath.Join(path, "goerli")
|
||||||
}
|
}
|
||||||
if ctx.GlobalBool(YoloV1Flag.Name) {
|
if ctx.GlobalBool(YoloV2Flag.Name) {
|
||||||
return filepath.Join(path, "yolo-v1")
|
return filepath.Join(path, "yolo-v2")
|
||||||
}
|
}
|
||||||
return path
|
return path
|
||||||
}
|
}
|
||||||
@ -803,7 +803,7 @@ func setBootstrapNodes(ctx *cli.Context, cfg *p2p.Config) {
|
|||||||
urls = params.RinkebyBootnodes
|
urls = params.RinkebyBootnodes
|
||||||
case ctx.GlobalBool(GoerliFlag.Name):
|
case ctx.GlobalBool(GoerliFlag.Name):
|
||||||
urls = params.GoerliBootnodes
|
urls = params.GoerliBootnodes
|
||||||
case ctx.GlobalBool(YoloV1Flag.Name):
|
case ctx.GlobalBool(YoloV2Flag.Name):
|
||||||
urls = params.YoloV1Bootnodes
|
urls = params.YoloV1Bootnodes
|
||||||
case cfg.BootstrapNodes != nil:
|
case cfg.BootstrapNodes != nil:
|
||||||
return // already set, don't apply defaults.
|
return // already set, don't apply defaults.
|
||||||
@ -839,7 +839,7 @@ func setBootstrapNodesV5(ctx *cli.Context, cfg *p2p.Config) {
|
|||||||
urls = params.RinkebyBootnodes
|
urls = params.RinkebyBootnodes
|
||||||
case ctx.GlobalBool(GoerliFlag.Name):
|
case ctx.GlobalBool(GoerliFlag.Name):
|
||||||
urls = params.GoerliBootnodes
|
urls = params.GoerliBootnodes
|
||||||
case ctx.GlobalBool(YoloV1Flag.Name):
|
case ctx.GlobalBool(YoloV2Flag.Name):
|
||||||
urls = params.YoloV1Bootnodes
|
urls = params.YoloV1Bootnodes
|
||||||
case cfg.BootstrapNodesV5 != nil:
|
case cfg.BootstrapNodesV5 != nil:
|
||||||
return // already set, don't apply defaults.
|
return // already set, don't apply defaults.
|
||||||
@ -1269,8 +1269,8 @@ func setDataDir(ctx *cli.Context, cfg *node.Config) {
|
|||||||
cfg.DataDir = filepath.Join(node.DefaultDataDir(), "rinkeby")
|
cfg.DataDir = filepath.Join(node.DefaultDataDir(), "rinkeby")
|
||||||
case ctx.GlobalBool(GoerliFlag.Name) && cfg.DataDir == node.DefaultDataDir():
|
case ctx.GlobalBool(GoerliFlag.Name) && cfg.DataDir == node.DefaultDataDir():
|
||||||
cfg.DataDir = filepath.Join(node.DefaultDataDir(), "goerli")
|
cfg.DataDir = filepath.Join(node.DefaultDataDir(), "goerli")
|
||||||
case ctx.GlobalBool(YoloV1Flag.Name) && cfg.DataDir == node.DefaultDataDir():
|
case ctx.GlobalBool(YoloV2Flag.Name) && cfg.DataDir == node.DefaultDataDir():
|
||||||
cfg.DataDir = filepath.Join(node.DefaultDataDir(), "yolo-v1")
|
cfg.DataDir = filepath.Join(node.DefaultDataDir(), "yolo-v2")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1483,7 +1483,7 @@ func SetShhConfig(ctx *cli.Context, stack *node.Node) {
|
|||||||
// SetEthConfig applies eth-related command line flags to the config.
|
// SetEthConfig applies eth-related command line flags to the config.
|
||||||
func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *eth.Config) {
|
func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *eth.Config) {
|
||||||
// Avoid conflicting network flags
|
// Avoid conflicting network flags
|
||||||
CheckExclusive(ctx, DeveloperFlag, LegacyTestnetFlag, RopstenFlag, RinkebyFlag, GoerliFlag, YoloV1Flag)
|
CheckExclusive(ctx, DeveloperFlag, LegacyTestnetFlag, RopstenFlag, RinkebyFlag, GoerliFlag, YoloV2Flag)
|
||||||
CheckExclusive(ctx, LegacyLightServFlag, LightServeFlag, SyncModeFlag, "light")
|
CheckExclusive(ctx, LegacyLightServFlag, LightServeFlag, SyncModeFlag, "light")
|
||||||
CheckExclusive(ctx, DeveloperFlag, ExternalSignerFlag) // Can't use both ephemeral unlocked and external signer
|
CheckExclusive(ctx, DeveloperFlag, ExternalSignerFlag) // Can't use both ephemeral unlocked and external signer
|
||||||
CheckExclusive(ctx, GCModeFlag, "archive", TxLookupLimitFlag)
|
CheckExclusive(ctx, GCModeFlag, "archive", TxLookupLimitFlag)
|
||||||
@ -1603,11 +1603,11 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *eth.Config) {
|
|||||||
}
|
}
|
||||||
cfg.Genesis = core.DefaultGoerliGenesisBlock()
|
cfg.Genesis = core.DefaultGoerliGenesisBlock()
|
||||||
SetDNSDiscoveryDefaults(cfg, params.GoerliGenesisHash)
|
SetDNSDiscoveryDefaults(cfg, params.GoerliGenesisHash)
|
||||||
case ctx.GlobalBool(YoloV1Flag.Name):
|
case ctx.GlobalBool(YoloV2Flag.Name):
|
||||||
if !ctx.GlobalIsSet(NetworkIdFlag.Name) {
|
if !ctx.GlobalIsSet(NetworkIdFlag.Name) {
|
||||||
cfg.NetworkId = 133519467574833 // "yolov1"
|
cfg.NetworkId = 133519467574834 // "yolov2"
|
||||||
}
|
}
|
||||||
cfg.Genesis = core.DefaultYoloV1GenesisBlock()
|
cfg.Genesis = core.DefaultYoloV2GenesisBlock()
|
||||||
case ctx.GlobalBool(DeveloperFlag.Name):
|
case ctx.GlobalBool(DeveloperFlag.Name):
|
||||||
if !ctx.GlobalIsSet(NetworkIdFlag.Name) {
|
if !ctx.GlobalIsSet(NetworkIdFlag.Name) {
|
||||||
cfg.NetworkId = 1337
|
cfg.NetworkId = 1337
|
||||||
@ -1791,8 +1791,8 @@ func MakeGenesis(ctx *cli.Context) *core.Genesis {
|
|||||||
genesis = core.DefaultRinkebyGenesisBlock()
|
genesis = core.DefaultRinkebyGenesisBlock()
|
||||||
case ctx.GlobalBool(GoerliFlag.Name):
|
case ctx.GlobalBool(GoerliFlag.Name):
|
||||||
genesis = core.DefaultGoerliGenesisBlock()
|
genesis = core.DefaultGoerliGenesisBlock()
|
||||||
case ctx.GlobalBool(YoloV1Flag.Name):
|
case ctx.GlobalBool(YoloV2Flag.Name):
|
||||||
genesis = core.DefaultYoloV1GenesisBlock()
|
genesis = core.DefaultYoloV2GenesisBlock()
|
||||||
case ctx.GlobalBool(DeveloperFlag.Name):
|
case ctx.GlobalBool(DeveloperFlag.Name):
|
||||||
Fatalf("Developer chains are ephemeral")
|
Fatalf("Developer chains are ephemeral")
|
||||||
}
|
}
|
||||||
|
@ -243,8 +243,8 @@ func (g *Genesis) configOrDefault(ghash common.Hash) *params.ChainConfig {
|
|||||||
return params.RinkebyChainConfig
|
return params.RinkebyChainConfig
|
||||||
case ghash == params.GoerliGenesisHash:
|
case ghash == params.GoerliGenesisHash:
|
||||||
return params.GoerliChainConfig
|
return params.GoerliChainConfig
|
||||||
case ghash == params.YoloV1GenesisHash:
|
case ghash == params.YoloV2GenesisHash:
|
||||||
return params.YoloV1ChainConfig
|
return params.YoloV2ChainConfig
|
||||||
default:
|
default:
|
||||||
return params.AllEthashProtocolChanges
|
return params.AllEthashProtocolChanges
|
||||||
}
|
}
|
||||||
@ -380,10 +380,11 @@ func DefaultGoerliGenesisBlock() *Genesis {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func DefaultYoloV1GenesisBlock() *Genesis {
|
func DefaultYoloV2GenesisBlock() *Genesis {
|
||||||
|
// TODO: Update with yolov2 values + regenerate alloc data
|
||||||
return &Genesis{
|
return &Genesis{
|
||||||
Config: params.YoloV1ChainConfig,
|
Config: params.YoloV2ChainConfig,
|
||||||
Timestamp: 0x5ed754f1,
|
Timestamp: 0x5f91b932,
|
||||||
ExtraData: hexutil.MustDecode("0x00000000000000000000000000000000000000000000000000000000000000008a37866fd3627c9205a37c8685666f32ec07bb1b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"),
|
ExtraData: hexutil.MustDecode("0x00000000000000000000000000000000000000000000000000000000000000008a37866fd3627c9205a37c8685666f32ec07bb1b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"),
|
||||||
GasLimit: 0x47b760,
|
GasLimit: 0x47b760,
|
||||||
Difficulty: big.NewInt(1),
|
Difficulty: big.NewInt(1),
|
||||||
|
136
core/state/access_list.go
Normal file
136
core/state/access_list.go
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
// Copyright 2020 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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package state
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
)
|
||||||
|
|
||||||
|
type accessList struct {
|
||||||
|
addresses map[common.Address]int
|
||||||
|
slots []map[common.Hash]struct{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContainsAddress returns true if the address is in the access list.
|
||||||
|
func (al *accessList) ContainsAddress(address common.Address) bool {
|
||||||
|
_, ok := al.addresses[address]
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
// Contains checks if a slot within an account is present in the access list, returning
|
||||||
|
// separate flags for the presence of the account and the slot respectively.
|
||||||
|
func (al *accessList) Contains(address common.Address, slot common.Hash) (addressPresent bool, slotPresent bool) {
|
||||||
|
idx, ok := al.addresses[address]
|
||||||
|
if !ok {
|
||||||
|
// no such address (and hence zero slots)
|
||||||
|
return false, false
|
||||||
|
}
|
||||||
|
if idx == -1 {
|
||||||
|
// address yes, but no slots
|
||||||
|
return true, false
|
||||||
|
}
|
||||||
|
_, slotPresent = al.slots[idx][slot]
|
||||||
|
return true, slotPresent
|
||||||
|
}
|
||||||
|
|
||||||
|
// newAccessList creates a new accessList.
|
||||||
|
func newAccessList() *accessList {
|
||||||
|
return &accessList{
|
||||||
|
addresses: make(map[common.Address]int),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy creates an independent copy of an accessList.
|
||||||
|
func (a *accessList) Copy() *accessList {
|
||||||
|
cp := newAccessList()
|
||||||
|
for k, v := range a.addresses {
|
||||||
|
cp.addresses[k] = v
|
||||||
|
}
|
||||||
|
cp.slots = make([]map[common.Hash]struct{}, len(a.slots))
|
||||||
|
for i, slotMap := range a.slots {
|
||||||
|
newSlotmap := make(map[common.Hash]struct{}, len(slotMap))
|
||||||
|
for k := range slotMap {
|
||||||
|
newSlotmap[k] = struct{}{}
|
||||||
|
}
|
||||||
|
cp.slots[i] = newSlotmap
|
||||||
|
}
|
||||||
|
return cp
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddAddress adds an address to the access list, and returns 'true' if the operation
|
||||||
|
// caused a change (addr was not previously in the list).
|
||||||
|
func (al *accessList) AddAddress(address common.Address) bool {
|
||||||
|
if _, present := al.addresses[address]; present {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
al.addresses[address] = -1
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddSlot adds the specified (addr, slot) combo to the access list.
|
||||||
|
// Return values are:
|
||||||
|
// - address added
|
||||||
|
// - slot added
|
||||||
|
// For any 'true' value returned, a corresponding journal entry must be made.
|
||||||
|
func (al *accessList) AddSlot(address common.Address, slot common.Hash) (addrChange bool, slotChange bool) {
|
||||||
|
idx, addrPresent := al.addresses[address]
|
||||||
|
if !addrPresent || idx == -1 {
|
||||||
|
// Address not present, or addr present but no slots there
|
||||||
|
al.addresses[address] = len(al.slots)
|
||||||
|
slotmap := map[common.Hash]struct{}{slot: {}}
|
||||||
|
al.slots = append(al.slots, slotmap)
|
||||||
|
return !addrPresent, true
|
||||||
|
}
|
||||||
|
// There is already an (address,slot) mapping
|
||||||
|
slotmap := al.slots[idx]
|
||||||
|
if _, ok := slotmap[slot]; !ok {
|
||||||
|
slotmap[slot] = struct{}{}
|
||||||
|
// Journal add slot change
|
||||||
|
return false, true
|
||||||
|
}
|
||||||
|
// No changes required
|
||||||
|
return false, false
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteSlot removes an (address, slot)-tuple from the access list.
|
||||||
|
// This operation needs to be performed in the same order as the addition happened.
|
||||||
|
// This method is meant to be used by the journal, which maintains ordering of
|
||||||
|
// operations.
|
||||||
|
func (al *accessList) DeleteSlot(address common.Address, slot common.Hash) {
|
||||||
|
idx, addrOk := al.addresses[address]
|
||||||
|
// There are two ways this can fail
|
||||||
|
if !addrOk {
|
||||||
|
panic("reverting slot change, address not present in list")
|
||||||
|
}
|
||||||
|
slotmap := al.slots[idx]
|
||||||
|
delete(slotmap, slot)
|
||||||
|
// If that was the last (first) slot, remove it
|
||||||
|
// Since additions and rollbacks are always performed in order,
|
||||||
|
// we can delete the item without worrying about screwing up later indices
|
||||||
|
if len(slotmap) == 0 {
|
||||||
|
al.slots = al.slots[:idx]
|
||||||
|
al.addresses[address] = -1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteAddress removes an address from the access list. This operation
|
||||||
|
// needs to be performed in the same order as the addition happened.
|
||||||
|
// This method is meant to be used by the journal, which maintains ordering of
|
||||||
|
// operations.
|
||||||
|
func (al *accessList) DeleteAddress(address common.Address) {
|
||||||
|
delete(al.addresses, address)
|
||||||
|
}
|
@ -130,6 +130,14 @@ type (
|
|||||||
touchChange struct {
|
touchChange struct {
|
||||||
account *common.Address
|
account *common.Address
|
||||||
}
|
}
|
||||||
|
// Changes to the access list
|
||||||
|
accessListAddAccountChange struct {
|
||||||
|
address *common.Address
|
||||||
|
}
|
||||||
|
accessListAddSlotChange struct {
|
||||||
|
address *common.Address
|
||||||
|
slot *common.Hash
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
func (ch createObjectChange) revert(s *StateDB) {
|
func (ch createObjectChange) revert(s *StateDB) {
|
||||||
@ -234,3 +242,28 @@ func (ch addPreimageChange) revert(s *StateDB) {
|
|||||||
func (ch addPreimageChange) dirtied() *common.Address {
|
func (ch addPreimageChange) dirtied() *common.Address {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ch accessListAddAccountChange) revert(s *StateDB) {
|
||||||
|
/*
|
||||||
|
One important invariant here, is that whenever a (addr, slot) is added, if the
|
||||||
|
addr is not already present, the add causes two journal entries:
|
||||||
|
- one for the address,
|
||||||
|
- one for the (address,slot)
|
||||||
|
Therefore, when unrolling the change, we can always blindly delete the
|
||||||
|
(addr) at this point, since no storage adds can remain when come upon
|
||||||
|
a single (addr) change.
|
||||||
|
*/
|
||||||
|
s.accessList.DeleteAddress(*ch.address)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ch accessListAddAccountChange) dirtied() *common.Address {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ch accessListAddSlotChange) revert(s *StateDB) {
|
||||||
|
s.accessList.DeleteSlot(*ch.address, *ch.slot)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ch accessListAddSlotChange) dirtied() *common.Address {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
@ -93,6 +93,9 @@ type StateDB struct {
|
|||||||
|
|
||||||
preimages map[common.Hash][]byte
|
preimages map[common.Hash][]byte
|
||||||
|
|
||||||
|
// Per-transaction access list
|
||||||
|
accessList *accessList
|
||||||
|
|
||||||
// Journal of state modifications. This is the backbone of
|
// Journal of state modifications. This is the backbone of
|
||||||
// Snapshot and RevertToSnapshot.
|
// Snapshot and RevertToSnapshot.
|
||||||
journal *journal
|
journal *journal
|
||||||
@ -129,6 +132,7 @@ func New(root common.Hash, db Database, snaps *snapshot.Tree) (*StateDB, error)
|
|||||||
logs: make(map[common.Hash][]*types.Log),
|
logs: make(map[common.Hash][]*types.Log),
|
||||||
preimages: make(map[common.Hash][]byte),
|
preimages: make(map[common.Hash][]byte),
|
||||||
journal: newJournal(),
|
journal: newJournal(),
|
||||||
|
accessList: newAccessList(),
|
||||||
}
|
}
|
||||||
if sdb.snaps != nil {
|
if sdb.snaps != nil {
|
||||||
if sdb.snap = sdb.snaps.Snapshot(root); sdb.snap != nil {
|
if sdb.snap = sdb.snaps.Snapshot(root); sdb.snap != nil {
|
||||||
@ -178,6 +182,7 @@ func (s *StateDB) Reset(root common.Hash) error {
|
|||||||
s.snapStorage = make(map[common.Hash]map[common.Hash][]byte)
|
s.snapStorage = make(map[common.Hash]map[common.Hash][]byte)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
s.accessList = newAccessList()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -697,6 +702,12 @@ func (s *StateDB) Copy() *StateDB {
|
|||||||
for hash, preimage := range s.preimages {
|
for hash, preimage := range s.preimages {
|
||||||
state.preimages[hash] = preimage
|
state.preimages[hash] = preimage
|
||||||
}
|
}
|
||||||
|
// Do we need to copy the access list? In practice: No. At the start of a
|
||||||
|
// transaction, the access list is empty. In practice, we only ever copy state
|
||||||
|
// _between_ transactions/blocks, never in the middle of a transaction.
|
||||||
|
// However, it doesn't cost us much to copy an empty list, so we do it anyway
|
||||||
|
// to not blow up if we ever decide copy it in the middle of a transaction
|
||||||
|
state.accessList = s.accessList.Copy()
|
||||||
return state
|
return state
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -798,6 +809,7 @@ func (s *StateDB) Prepare(thash, bhash common.Hash, ti int) {
|
|||||||
s.thash = thash
|
s.thash = thash
|
||||||
s.bhash = bhash
|
s.bhash = bhash
|
||||||
s.txIndex = ti
|
s.txIndex = ti
|
||||||
|
s.accessList = newAccessList()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *StateDB) clearJournalAndRefund() {
|
func (s *StateDB) clearJournalAndRefund() {
|
||||||
@ -877,3 +889,38 @@ func (s *StateDB) Commit(deleteEmptyObjects bool) (common.Hash, error) {
|
|||||||
}
|
}
|
||||||
return root, err
|
return root, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AddAddressToAccessList adds the given address to the access list
|
||||||
|
func (s *StateDB) AddAddressToAccessList(addr common.Address) {
|
||||||
|
if s.accessList.AddAddress(addr) {
|
||||||
|
s.journal.append(accessListAddAccountChange{&addr})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddSlotToAccessList adds the given (address, slot)-tuple to the access list
|
||||||
|
func (s *StateDB) AddSlotToAccessList(addr common.Address, slot common.Hash) {
|
||||||
|
addrMod, slotMod := s.accessList.AddSlot(addr, slot)
|
||||||
|
if addrMod {
|
||||||
|
// In practice, this should not happen, since there is no way to enter the
|
||||||
|
// scope of 'address' without having the 'address' become already added
|
||||||
|
// to the access list (via call-variant, create, etc).
|
||||||
|
// Better safe than sorry, though
|
||||||
|
s.journal.append(accessListAddAccountChange{&addr})
|
||||||
|
}
|
||||||
|
if slotMod {
|
||||||
|
s.journal.append(accessListAddSlotChange{
|
||||||
|
address: &addr,
|
||||||
|
slot: &slot,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddressInAccessList returns true if the given address is in the access list.
|
||||||
|
func (s *StateDB) AddressInAccessList(addr common.Address) bool {
|
||||||
|
return s.accessList.ContainsAddress(addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SlotInAccessList returns true if the given (address, slot)-tuple is in the access list.
|
||||||
|
func (s *StateDB) SlotInAccessList(addr common.Address, slot common.Hash) (addressPresent bool, slotPresent bool) {
|
||||||
|
return s.accessList.Contains(addr, slot)
|
||||||
|
}
|
||||||
|
@ -328,6 +328,20 @@ func newTestAction(addr common.Address, r *rand.Rand) testAction {
|
|||||||
},
|
},
|
||||||
args: make([]int64, 1),
|
args: make([]int64, 1),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "AddAddressToAccessList",
|
||||||
|
fn: func(a testAction, s *StateDB) {
|
||||||
|
s.AddAddressToAccessList(addr)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "AddSlotToAccessList",
|
||||||
|
fn: func(a testAction, s *StateDB) {
|
||||||
|
s.AddSlotToAccessList(addr,
|
||||||
|
common.Hash{byte(a.args[0])})
|
||||||
|
},
|
||||||
|
args: make([]int64, 1),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
action := actions[r.Intn(len(actions))]
|
action := actions[r.Intn(len(actions))]
|
||||||
var nameargs []string
|
var nameargs []string
|
||||||
@ -727,3 +741,177 @@ func TestMissingTrieNodes(t *testing.T) {
|
|||||||
t.Fatalf("expected error, got root :%x", root)
|
t.Fatalf("expected error, got root :%x", root)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestStateDBAccessList(t *testing.T) {
|
||||||
|
// Some helpers
|
||||||
|
addr := func(a string) common.Address {
|
||||||
|
return common.HexToAddress(a)
|
||||||
|
}
|
||||||
|
slot := func(a string) common.Hash {
|
||||||
|
return common.HexToHash(a)
|
||||||
|
}
|
||||||
|
|
||||||
|
memDb := rawdb.NewMemoryDatabase()
|
||||||
|
db := NewDatabase(memDb)
|
||||||
|
state, _ := New(common.Hash{}, db, nil)
|
||||||
|
state.accessList = newAccessList()
|
||||||
|
|
||||||
|
verifyAddrs := func(astrings ...string) {
|
||||||
|
t.Helper()
|
||||||
|
// convert to common.Address form
|
||||||
|
var addresses []common.Address
|
||||||
|
var addressMap = make(map[common.Address]struct{})
|
||||||
|
for _, astring := range astrings {
|
||||||
|
address := addr(astring)
|
||||||
|
addresses = append(addresses, address)
|
||||||
|
addressMap[address] = struct{}{}
|
||||||
|
}
|
||||||
|
// Check that the given addresses are in the access list
|
||||||
|
for _, address := range addresses {
|
||||||
|
if !state.AddressInAccessList(address) {
|
||||||
|
t.Fatalf("expected %x to be in access list", address)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Check that only the expected addresses are present in the acesslist
|
||||||
|
for address := range state.accessList.addresses {
|
||||||
|
if _, exist := addressMap[address]; !exist {
|
||||||
|
t.Fatalf("extra address %x in access list", address)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
verifySlots := func(addrString string, slotStrings ...string) {
|
||||||
|
if !state.AddressInAccessList(addr(addrString)) {
|
||||||
|
t.Fatalf("scope missing address/slots %v", addrString)
|
||||||
|
}
|
||||||
|
var address = addr(addrString)
|
||||||
|
// convert to common.Hash form
|
||||||
|
var slots []common.Hash
|
||||||
|
var slotMap = make(map[common.Hash]struct{})
|
||||||
|
for _, slotString := range slotStrings {
|
||||||
|
s := slot(slotString)
|
||||||
|
slots = append(slots, s)
|
||||||
|
slotMap[s] = struct{}{}
|
||||||
|
}
|
||||||
|
// Check that the expected items are in the access list
|
||||||
|
for i, s := range slots {
|
||||||
|
if _, slotPresent := state.SlotInAccessList(address, s); !slotPresent {
|
||||||
|
t.Fatalf("input %d: scope missing slot %v (address %v)", i, s, addrString)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Check that no extra elements are in the access list
|
||||||
|
index := state.accessList.addresses[address]
|
||||||
|
if index >= 0 {
|
||||||
|
stateSlots := state.accessList.slots[index]
|
||||||
|
for s := range stateSlots {
|
||||||
|
if _, slotPresent := slotMap[s]; !slotPresent {
|
||||||
|
t.Fatalf("scope has extra slot %v (address %v)", s, addrString)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
state.AddAddressToAccessList(addr("aa")) // 1
|
||||||
|
state.AddSlotToAccessList(addr("bb"), slot("01")) // 2,3
|
||||||
|
state.AddSlotToAccessList(addr("bb"), slot("02")) // 4
|
||||||
|
verifyAddrs("aa", "bb")
|
||||||
|
verifySlots("bb", "01", "02")
|
||||||
|
|
||||||
|
// Make a copy
|
||||||
|
stateCopy1 := state.Copy()
|
||||||
|
if exp, got := 4, state.journal.length(); exp != got {
|
||||||
|
t.Fatalf("journal length mismatch: have %d, want %d", got, exp)
|
||||||
|
}
|
||||||
|
|
||||||
|
// same again, should cause no journal entries
|
||||||
|
state.AddSlotToAccessList(addr("bb"), slot("01"))
|
||||||
|
state.AddSlotToAccessList(addr("bb"), slot("02"))
|
||||||
|
state.AddAddressToAccessList(addr("aa"))
|
||||||
|
if exp, got := 4, state.journal.length(); exp != got {
|
||||||
|
t.Fatalf("journal length mismatch: have %d, want %d", got, exp)
|
||||||
|
}
|
||||||
|
// some new ones
|
||||||
|
state.AddSlotToAccessList(addr("bb"), slot("03")) // 5
|
||||||
|
state.AddSlotToAccessList(addr("aa"), slot("01")) // 6
|
||||||
|
state.AddSlotToAccessList(addr("cc"), slot("01")) // 7,8
|
||||||
|
state.AddAddressToAccessList(addr("cc"))
|
||||||
|
if exp, got := 8, state.journal.length(); exp != got {
|
||||||
|
t.Fatalf("journal length mismatch: have %d, want %d", got, exp)
|
||||||
|
}
|
||||||
|
|
||||||
|
verifyAddrs("aa", "bb", "cc")
|
||||||
|
verifySlots("aa", "01")
|
||||||
|
verifySlots("bb", "01", "02", "03")
|
||||||
|
verifySlots("cc", "01")
|
||||||
|
|
||||||
|
// now start rolling back changes
|
||||||
|
state.journal.revert(state, 7)
|
||||||
|
if _, ok := state.SlotInAccessList(addr("cc"), slot("01")); ok {
|
||||||
|
t.Fatalf("slot present, expected missing")
|
||||||
|
}
|
||||||
|
verifyAddrs("aa", "bb", "cc")
|
||||||
|
verifySlots("aa", "01")
|
||||||
|
verifySlots("bb", "01", "02", "03")
|
||||||
|
|
||||||
|
state.journal.revert(state, 6)
|
||||||
|
if state.AddressInAccessList(addr("cc")) {
|
||||||
|
t.Fatalf("addr present, expected missing")
|
||||||
|
}
|
||||||
|
verifyAddrs("aa", "bb")
|
||||||
|
verifySlots("aa", "01")
|
||||||
|
verifySlots("bb", "01", "02", "03")
|
||||||
|
|
||||||
|
state.journal.revert(state, 5)
|
||||||
|
if _, ok := state.SlotInAccessList(addr("aa"), slot("01")); ok {
|
||||||
|
t.Fatalf("slot present, expected missing")
|
||||||
|
}
|
||||||
|
verifyAddrs("aa", "bb")
|
||||||
|
verifySlots("bb", "01", "02", "03")
|
||||||
|
|
||||||
|
state.journal.revert(state, 4)
|
||||||
|
if _, ok := state.SlotInAccessList(addr("bb"), slot("03")); ok {
|
||||||
|
t.Fatalf("slot present, expected missing")
|
||||||
|
}
|
||||||
|
verifyAddrs("aa", "bb")
|
||||||
|
verifySlots("bb", "01", "02")
|
||||||
|
|
||||||
|
state.journal.revert(state, 3)
|
||||||
|
if _, ok := state.SlotInAccessList(addr("bb"), slot("02")); ok {
|
||||||
|
t.Fatalf("slot present, expected missing")
|
||||||
|
}
|
||||||
|
verifyAddrs("aa", "bb")
|
||||||
|
verifySlots("bb", "01")
|
||||||
|
|
||||||
|
state.journal.revert(state, 2)
|
||||||
|
if _, ok := state.SlotInAccessList(addr("bb"), slot("01")); ok {
|
||||||
|
t.Fatalf("slot present, expected missing")
|
||||||
|
}
|
||||||
|
verifyAddrs("aa", "bb")
|
||||||
|
|
||||||
|
state.journal.revert(state, 1)
|
||||||
|
if state.AddressInAccessList(addr("bb")) {
|
||||||
|
t.Fatalf("addr present, expected missing")
|
||||||
|
}
|
||||||
|
verifyAddrs("aa")
|
||||||
|
|
||||||
|
state.journal.revert(state, 0)
|
||||||
|
if state.AddressInAccessList(addr("aa")) {
|
||||||
|
t.Fatalf("addr present, expected missing")
|
||||||
|
}
|
||||||
|
if got, exp := len(state.accessList.addresses), 0; got != exp {
|
||||||
|
t.Fatalf("expected empty, got %d", got)
|
||||||
|
}
|
||||||
|
if got, exp := len(state.accessList.slots), 0; got != exp {
|
||||||
|
t.Fatalf("expected empty, got %d", got)
|
||||||
|
}
|
||||||
|
// Check the copy
|
||||||
|
// Make a copy
|
||||||
|
state = stateCopy1
|
||||||
|
verifyAddrs("aa", "bb")
|
||||||
|
verifySlots("bb", "01", "02")
|
||||||
|
if got, exp := len(state.accessList.addresses), 2; got != exp {
|
||||||
|
t.Fatalf("expected empty, got %d", got)
|
||||||
|
}
|
||||||
|
if got, exp := len(state.accessList.slots), 1; got != exp {
|
||||||
|
t.Fatalf("expected empty, got %d", got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -95,6 +95,18 @@ func ApplyTransaction(config *params.ChainConfig, bc ChainContext, author *commo
|
|||||||
// Create a new environment which holds all relevant information
|
// Create a new environment which holds all relevant information
|
||||||
// about the transaction and calling mechanisms.
|
// about the transaction and calling mechanisms.
|
||||||
vmenv := vm.NewEVM(context, statedb, config, cfg)
|
vmenv := vm.NewEVM(context, statedb, config, cfg)
|
||||||
|
|
||||||
|
if config.IsYoloV2(header.Number) {
|
||||||
|
statedb.AddAddressToAccessList(msg.From())
|
||||||
|
if dst := msg.To(); dst != nil {
|
||||||
|
statedb.AddAddressToAccessList(*dst)
|
||||||
|
// If it's a create-tx, the destination will be added inside evm.create
|
||||||
|
}
|
||||||
|
for _, addr := range vmenv.ActivePrecompiles() {
|
||||||
|
statedb.AddAddressToAccessList(addr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Apply the transaction to the current state (included in the env)
|
// Apply the transaction to the current state (included in the env)
|
||||||
result, err := ApplyMessage(vmenv, msg, gp)
|
result, err := ApplyMessage(vmenv, msg, gp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -78,9 +78,9 @@ var PrecompiledContractsIstanbul = map[common.Address]PrecompiledContract{
|
|||||||
common.BytesToAddress([]byte{9}): &blake2F{},
|
common.BytesToAddress([]byte{9}): &blake2F{},
|
||||||
}
|
}
|
||||||
|
|
||||||
// PrecompiledContractsYoloV1 contains the default set of pre-compiled Ethereum
|
// PrecompiledContractsYoloV2 contains the default set of pre-compiled Ethereum
|
||||||
// contracts used in the Yolo v1 test release.
|
// contracts used in the Yolo v2 test release.
|
||||||
var PrecompiledContractsYoloV1 = map[common.Address]PrecompiledContract{
|
var PrecompiledContractsYoloV2 = map[common.Address]PrecompiledContract{
|
||||||
common.BytesToAddress([]byte{1}): &ecrecover{},
|
common.BytesToAddress([]byte{1}): &ecrecover{},
|
||||||
common.BytesToAddress([]byte{2}): &sha256hash{},
|
common.BytesToAddress([]byte{2}): &sha256hash{},
|
||||||
common.BytesToAddress([]byte{3}): &ripemd160hash{},
|
common.BytesToAddress([]byte{3}): &ripemd160hash{},
|
||||||
@ -101,6 +101,28 @@ var PrecompiledContractsYoloV1 = map[common.Address]PrecompiledContract{
|
|||||||
common.BytesToAddress([]byte{18}): &bls12381MapG2{},
|
common.BytesToAddress([]byte{18}): &bls12381MapG2{},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
PrecompiledAddressesYoloV2 []common.Address
|
||||||
|
PrecompiledAddressesIstanbul []common.Address
|
||||||
|
PrecompiledAddressesByzantium []common.Address
|
||||||
|
PrecompiledAddressesHomestead []common.Address
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
for k := range PrecompiledContractsHomestead {
|
||||||
|
PrecompiledAddressesHomestead = append(PrecompiledAddressesHomestead, k)
|
||||||
|
}
|
||||||
|
for k := range PrecompiledContractsByzantium {
|
||||||
|
PrecompiledAddressesHomestead = append(PrecompiledAddressesByzantium, k)
|
||||||
|
}
|
||||||
|
for k := range PrecompiledContractsIstanbul {
|
||||||
|
PrecompiledAddressesIstanbul = append(PrecompiledAddressesIstanbul, k)
|
||||||
|
}
|
||||||
|
for k := range PrecompiledContractsYoloV2 {
|
||||||
|
PrecompiledAddressesYoloV2 = append(PrecompiledAddressesYoloV2, k)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// RunPrecompiledContract runs and evaluates the output of a precompiled contract.
|
// RunPrecompiledContract runs and evaluates the output of a precompiled contract.
|
||||||
// It returns
|
// It returns
|
||||||
// - the returned bytes,
|
// - the returned bytes,
|
||||||
|
@ -43,7 +43,7 @@ type precompiledFailureTest struct {
|
|||||||
Name string
|
Name string
|
||||||
}
|
}
|
||||||
|
|
||||||
var allPrecompiles = PrecompiledContractsYoloV1
|
var allPrecompiles = PrecompiledContractsYoloV2
|
||||||
|
|
||||||
// EIP-152 test vectors
|
// EIP-152 test vectors
|
||||||
var blake2FMalformedInputTests = []precompiledFailureTest{
|
var blake2FMalformedInputTests = []precompiledFailureTest{
|
||||||
|
@ -25,6 +25,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var activators = map[int]func(*JumpTable){
|
var activators = map[int]func(*JumpTable){
|
||||||
|
2929: enable2929,
|
||||||
2200: enable2200,
|
2200: enable2200,
|
||||||
1884: enable1884,
|
1884: enable1884,
|
||||||
1344: enable1344,
|
1344: enable1344,
|
||||||
@ -134,3 +135,41 @@ func enable2315(jt *JumpTable) {
|
|||||||
jumps: true,
|
jumps: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// enable2929 enables "EIP-2929: Gas cost increases for state access opcodes"
|
||||||
|
// https://eips.ethereum.org/EIPS/eip-2929
|
||||||
|
func enable2929(jt *JumpTable) {
|
||||||
|
jt[SSTORE].dynamicGas = gasSStoreEIP2929
|
||||||
|
|
||||||
|
jt[SLOAD].constantGas = 0
|
||||||
|
jt[SLOAD].dynamicGas = gasSLoadEIP2929
|
||||||
|
|
||||||
|
jt[EXTCODECOPY].constantGas = WarmStorageReadCostEIP2929
|
||||||
|
jt[EXTCODECOPY].dynamicGas = gasExtCodeCopyEIP2929
|
||||||
|
|
||||||
|
jt[EXTCODESIZE].constantGas = WarmStorageReadCostEIP2929
|
||||||
|
jt[EXTCODESIZE].dynamicGas = gasEip2929AccountCheck
|
||||||
|
|
||||||
|
jt[EXTCODEHASH].constantGas = WarmStorageReadCostEIP2929
|
||||||
|
jt[EXTCODEHASH].dynamicGas = gasEip2929AccountCheck
|
||||||
|
|
||||||
|
jt[BALANCE].constantGas = WarmStorageReadCostEIP2929
|
||||||
|
jt[BALANCE].dynamicGas = gasEip2929AccountCheck
|
||||||
|
|
||||||
|
jt[CALL].constantGas = WarmStorageReadCostEIP2929
|
||||||
|
jt[CALL].dynamicGas = gasCallEIP2929
|
||||||
|
|
||||||
|
jt[CALLCODE].constantGas = WarmStorageReadCostEIP2929
|
||||||
|
jt[CALLCODE].dynamicGas = gasCallCodeEIP2929
|
||||||
|
|
||||||
|
jt[STATICCALL].constantGas = WarmStorageReadCostEIP2929
|
||||||
|
jt[STATICCALL].dynamicGas = gasStaticCallEIP2929
|
||||||
|
|
||||||
|
jt[DELEGATECALL].constantGas = WarmStorageReadCostEIP2929
|
||||||
|
jt[DELEGATECALL].dynamicGas = gasDelegateCallEIP2929
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
@ -42,11 +42,26 @@ type (
|
|||||||
GetHashFunc func(uint64) common.Hash
|
GetHashFunc func(uint64) common.Hash
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// ActivePrecompiles returns the addresses of the precompiles enabled with the current
|
||||||
|
// configuration
|
||||||
|
func (evm *EVM) ActivePrecompiles() []common.Address {
|
||||||
|
switch {
|
||||||
|
case evm.chainRules.IsYoloV2:
|
||||||
|
return PrecompiledAddressesYoloV2
|
||||||
|
case evm.chainRules.IsIstanbul:
|
||||||
|
return PrecompiledAddressesIstanbul
|
||||||
|
case evm.chainRules.IsByzantium:
|
||||||
|
return PrecompiledAddressesByzantium
|
||||||
|
default:
|
||||||
|
return PrecompiledAddressesHomestead
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (evm *EVM) precompile(addr common.Address) (PrecompiledContract, bool) {
|
func (evm *EVM) precompile(addr common.Address) (PrecompiledContract, bool) {
|
||||||
var precompiles map[common.Address]PrecompiledContract
|
var precompiles map[common.Address]PrecompiledContract
|
||||||
switch {
|
switch {
|
||||||
case evm.chainRules.IsYoloV1:
|
case evm.chainRules.IsYoloV2:
|
||||||
precompiles = PrecompiledContractsYoloV1
|
precompiles = PrecompiledContractsYoloV2
|
||||||
case evm.chainRules.IsIstanbul:
|
case evm.chainRules.IsIstanbul:
|
||||||
precompiles = PrecompiledContractsIstanbul
|
precompiles = PrecompiledContractsIstanbul
|
||||||
case evm.chainRules.IsByzantium:
|
case evm.chainRules.IsByzantium:
|
||||||
@ -416,7 +431,11 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
|
|||||||
}
|
}
|
||||||
nonce := evm.StateDB.GetNonce(caller.Address())
|
nonce := evm.StateDB.GetNonce(caller.Address())
|
||||||
evm.StateDB.SetNonce(caller.Address(), nonce+1)
|
evm.StateDB.SetNonce(caller.Address(), nonce+1)
|
||||||
|
// 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.IsYoloV2 {
|
||||||
|
evm.StateDB.AddAddressToAccessList(address)
|
||||||
|
}
|
||||||
// Ensure there's no existing contract already at the designated address
|
// Ensure there's no existing contract already at the designated address
|
||||||
contractHash := evm.StateDB.GetCodeHash(address)
|
contractHash := evm.StateDB.GetCodeHash(address)
|
||||||
if evm.StateDB.GetNonce(address) != 0 || (contractHash != (common.Hash{}) && contractHash != emptyCodeHash) {
|
if evm.StateDB.GetNonce(address) != 0 || (contractHash != (common.Hash{}) && contractHash != emptyCodeHash) {
|
||||||
|
@ -57,6 +57,15 @@ type StateDB interface {
|
|||||||
// is defined according to EIP161 (balance = nonce = code = 0).
|
// is defined according to EIP161 (balance = nonce = code = 0).
|
||||||
Empty(common.Address) bool
|
Empty(common.Address) bool
|
||||||
|
|
||||||
|
AddressInAccessList(addr common.Address) bool
|
||||||
|
SlotInAccessList(addr common.Address, slot common.Hash) (addressOk bool, slotOk bool)
|
||||||
|
// AddAddressToAccessList adds the given address to the access list. This operation is safe to perform
|
||||||
|
// even if the feature/fork is not active yet
|
||||||
|
AddAddressToAccessList(addr common.Address)
|
||||||
|
// AddSlotToAccessList adds the given (address,slot) to the access list. This operation is safe to perform
|
||||||
|
// even if the feature/fork is not active yet
|
||||||
|
AddSlotToAccessList(addr common.Address, slot common.Hash)
|
||||||
|
|
||||||
RevertToSnapshot(int)
|
RevertToSnapshot(int)
|
||||||
Snapshot() int
|
Snapshot() int
|
||||||
|
|
||||||
|
@ -99,8 +99,8 @@ func NewEVMInterpreter(evm *EVM, cfg Config) *EVMInterpreter {
|
|||||||
if cfg.JumpTable[STOP] == nil {
|
if cfg.JumpTable[STOP] == nil {
|
||||||
var jt JumpTable
|
var jt JumpTable
|
||||||
switch {
|
switch {
|
||||||
case evm.chainRules.IsYoloV1:
|
case evm.chainRules.IsYoloV2:
|
||||||
jt = yoloV1InstructionSet
|
jt = yoloV2InstructionSet
|
||||||
case evm.chainRules.IsIstanbul:
|
case evm.chainRules.IsIstanbul:
|
||||||
jt = istanbulInstructionSet
|
jt = istanbulInstructionSet
|
||||||
case evm.chainRules.IsConstantinople:
|
case evm.chainRules.IsConstantinople:
|
||||||
|
@ -56,17 +56,19 @@ var (
|
|||||||
byzantiumInstructionSet = newByzantiumInstructionSet()
|
byzantiumInstructionSet = newByzantiumInstructionSet()
|
||||||
constantinopleInstructionSet = newConstantinopleInstructionSet()
|
constantinopleInstructionSet = newConstantinopleInstructionSet()
|
||||||
istanbulInstructionSet = newIstanbulInstructionSet()
|
istanbulInstructionSet = newIstanbulInstructionSet()
|
||||||
yoloV1InstructionSet = newYoloV1InstructionSet()
|
yoloV2InstructionSet = newYoloV2InstructionSet()
|
||||||
)
|
)
|
||||||
|
|
||||||
// JumpTable contains the EVM opcodes supported at a given fork.
|
// JumpTable contains the EVM opcodes supported at a given fork.
|
||||||
type JumpTable [256]*operation
|
type JumpTable [256]*operation
|
||||||
|
|
||||||
func newYoloV1InstructionSet() JumpTable {
|
// newYoloV2InstructionSet creates an instructionset containing
|
||||||
|
// - "EIP-2315: Simple Subroutines"
|
||||||
|
// - "EIP-2929: Gas cost increases for state access opcodes"
|
||||||
|
func newYoloV2InstructionSet() JumpTable {
|
||||||
instructionSet := newIstanbulInstructionSet()
|
instructionSet := newIstanbulInstructionSet()
|
||||||
|
|
||||||
enable2315(&instructionSet) // Subroutines - https://eips.ethereum.org/EIPS/eip-2315
|
enable2315(&instructionSet) // Subroutines - https://eips.ethereum.org/EIPS/eip-2315
|
||||||
|
enable2929(&instructionSet) // Access lists for trie accesses https://eips.ethereum.org/EIPS/eip-2929
|
||||||
return instructionSet
|
return instructionSet
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,6 +29,7 @@ import (
|
|||||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
"github.com/ethereum/go-ethereum/common/math"
|
"github.com/ethereum/go-ethereum/common/math"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
"github.com/ethereum/go-ethereum/params"
|
||||||
)
|
)
|
||||||
|
|
||||||
var errTraceLimitReached = errors.New("the number of logs reached the specified limit")
|
var errTraceLimitReached = errors.New("the number of logs reached the specified limit")
|
||||||
@ -53,6 +54,8 @@ type LogConfig struct {
|
|||||||
DisableReturnData bool // disable return data capture
|
DisableReturnData bool // disable return data capture
|
||||||
Debug bool // print output during capture end
|
Debug bool // print output during capture end
|
||||||
Limit int // maximum length of output, but zero means unlimited
|
Limit int // maximum length of output, but zero means unlimited
|
||||||
|
// Chain overrides, can be used to execute a trace using future fork rules
|
||||||
|
Overrides *params.ChainConfig `json:"overrides,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
//go:generate gencodec -type StructLog -field-override structLogMarshaling -out gen_structlog.go
|
//go:generate gencodec -type StructLog -field-override structLogMarshaling -out gen_structlog.go
|
||||||
@ -314,8 +317,8 @@ func (t *mdLogger) CaptureStart(from common.Address, to common.Address, create b
|
|||||||
}
|
}
|
||||||
|
|
||||||
fmt.Fprintf(t.out, `
|
fmt.Fprintf(t.out, `
|
||||||
| Pc | Op | Cost | Stack | RStack |
|
| Pc | Op | Cost | Stack | RStack | Refund |
|
||||||
|-------|-------------|------|-----------|-----------|
|
|-------|-------------|------|-----------|-----------|---------|
|
||||||
`)
|
`)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -327,7 +330,7 @@ func (t *mdLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64
|
|||||||
// format stack
|
// format stack
|
||||||
var a []string
|
var a []string
|
||||||
for _, elem := range stack.data {
|
for _, elem := range stack.data {
|
||||||
a = append(a, fmt.Sprintf("%d", elem))
|
a = append(a, fmt.Sprintf("%v", elem.String()))
|
||||||
}
|
}
|
||||||
b := fmt.Sprintf("[%v]", strings.Join(a, ","))
|
b := fmt.Sprintf("[%v]", strings.Join(a, ","))
|
||||||
fmt.Fprintf(t.out, "%10v |", b)
|
fmt.Fprintf(t.out, "%10v |", b)
|
||||||
@ -340,6 +343,7 @@ func (t *mdLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64
|
|||||||
b = fmt.Sprintf("[%v]", strings.Join(a, ","))
|
b = fmt.Sprintf("[%v]", strings.Join(a, ","))
|
||||||
fmt.Fprintf(t.out, "%10v |", b)
|
fmt.Fprintf(t.out, "%10v |", b)
|
||||||
}
|
}
|
||||||
|
fmt.Fprintf(t.out, "%10v |", env.StateDB.GetRefund())
|
||||||
fmt.Fprintln(t.out, "")
|
fmt.Fprintln(t.out, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(t.out, "Error: %v\n", err)
|
fmt.Fprintf(t.out, "Error: %v\n", err)
|
||||||
@ -355,11 +359,7 @@ func (t *mdLogger) CaptureFault(env *EVM, pc uint64, op OpCode, gas, cost uint64
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (t *mdLogger) CaptureEnd(output []byte, gasUsed uint64, tm time.Duration, err error) error {
|
func (t *mdLogger) CaptureEnd(output []byte, gasUsed uint64, tm time.Duration, err error) error {
|
||||||
fmt.Fprintf(t.out, `
|
fmt.Fprintf(t.out, "\nOutput: `0x%x`\nConsumed gas: `%d`\nError: `%v`\n",
|
||||||
Output: 0x%x
|
|
||||||
Consumed gas: %d
|
|
||||||
Error: %v
|
|
||||||
`,
|
|
||||||
output, gasUsed, err)
|
output, gasUsed, err)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
222
core/vm/operations_acl.go
Normal file
222
core/vm/operations_acl.go
Normal file
@ -0,0 +1,222 @@
|
|||||||
|
// Copyright 2020 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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package vm
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/common/math"
|
||||||
|
"github.com/ethereum/go-ethereum/params"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
ColdAccountAccessCostEIP2929 = uint64(2600) // COLD_ACCOUNT_ACCESS_COST
|
||||||
|
ColdSloadCostEIP2929 = uint64(2100) // COLD_SLOAD_COST
|
||||||
|
WarmStorageReadCostEIP2929 = uint64(100) // WARM_STORAGE_READ_COST
|
||||||
|
)
|
||||||
|
|
||||||
|
// gasSStoreEIP2929 implements gas cost for SSTORE according to EIP-2929"
|
||||||
|
//
|
||||||
|
// When calling SSTORE, check if the (address, storage_key) pair is in accessed_storage_keys.
|
||||||
|
// If it is not, charge an additional COLD_SLOAD_COST gas, and add the pair to accessed_storage_keys.
|
||||||
|
// Additionally, modify the parameters defined in EIP 2200 as follows:
|
||||||
|
//
|
||||||
|
// Parameter Old value New value
|
||||||
|
// SLOAD_GAS 800 = WARM_STORAGE_READ_COST
|
||||||
|
// SSTORE_RESET_GAS 5000 5000 - COLD_SLOAD_COST
|
||||||
|
//
|
||||||
|
//The other parameters defined in EIP 2200 are unchanged.
|
||||||
|
// see gasSStoreEIP2200(...) in core/vm/gas_table.go for more info about how EIP 2200 is specified
|
||||||
|
func gasSStoreEIP2929(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||||
|
// If we fail the minimum gas availability invariant, fail (0)
|
||||||
|
if contract.Gas <= params.SstoreSentryGasEIP2200 {
|
||||||
|
return 0, errors.New("not enough gas for reentrancy sentry")
|
||||||
|
}
|
||||||
|
// Gas sentry honoured, do the actual gas calculation based on the stored value
|
||||||
|
var (
|
||||||
|
y, x = stack.Back(1), stack.peek()
|
||||||
|
slot = common.Hash(x.Bytes32())
|
||||||
|
current = evm.StateDB.GetState(contract.Address(), slot)
|
||||||
|
cost = uint64(0)
|
||||||
|
)
|
||||||
|
// Check slot presence in the access list
|
||||||
|
if addrPresent, slotPresent := evm.StateDB.SlotInAccessList(contract.Address(), slot); !slotPresent {
|
||||||
|
cost = ColdSloadCostEIP2929
|
||||||
|
// If the caller cannot afford the cost, this change will be rolled back
|
||||||
|
evm.StateDB.AddSlotToAccessList(contract.Address(), slot)
|
||||||
|
if !addrPresent {
|
||||||
|
// Once we're done with YOLOv2 and schedule this for mainnet, might
|
||||||
|
// be good to remove this panic here, which is just really a
|
||||||
|
// canary to have during testing
|
||||||
|
panic("impossible case: address was not present in access list during sstore op")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
value := common.Hash(y.Bytes32())
|
||||||
|
|
||||||
|
if current == value { // noop (1)
|
||||||
|
// EIP 2200 original clause:
|
||||||
|
// return params.SloadGasEIP2200, nil
|
||||||
|
return cost + WarmStorageReadCostEIP2929, nil // SLOAD_GAS
|
||||||
|
}
|
||||||
|
original := evm.StateDB.GetCommittedState(contract.Address(), common.Hash(x.Bytes32()))
|
||||||
|
if original == current {
|
||||||
|
if original == (common.Hash{}) { // create slot (2.1.1)
|
||||||
|
return cost + params.SstoreSetGasEIP2200, nil
|
||||||
|
}
|
||||||
|
if value == (common.Hash{}) { // delete slot (2.1.2b)
|
||||||
|
evm.StateDB.AddRefund(params.SstoreClearsScheduleRefundEIP2200)
|
||||||
|
}
|
||||||
|
// EIP-2200 original clause:
|
||||||
|
// return params.SstoreResetGasEIP2200, nil // write existing slot (2.1.2)
|
||||||
|
return cost + (params.SstoreResetGasEIP2200 - ColdSloadCostEIP2929), nil // write existing slot (2.1.2)
|
||||||
|
}
|
||||||
|
if original != (common.Hash{}) {
|
||||||
|
if current == (common.Hash{}) { // recreate slot (2.2.1.1)
|
||||||
|
evm.StateDB.SubRefund(params.SstoreClearsScheduleRefundEIP2200)
|
||||||
|
} else if value == (common.Hash{}) { // delete slot (2.2.1.2)
|
||||||
|
evm.StateDB.AddRefund(params.SstoreClearsScheduleRefundEIP2200)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if original == value {
|
||||||
|
if original == (common.Hash{}) { // reset to original inexistent slot (2.2.2.1)
|
||||||
|
// EIP 2200 Original clause:
|
||||||
|
//evm.StateDB.AddRefund(params.SstoreSetGasEIP2200 - params.SloadGasEIP2200)
|
||||||
|
evm.StateDB.AddRefund(params.SstoreSetGasEIP2200 - WarmStorageReadCostEIP2929)
|
||||||
|
} else { // reset to original existing slot (2.2.2.2)
|
||||||
|
// EIP 2200 Original clause:
|
||||||
|
// evm.StateDB.AddRefund(params.SstoreResetGasEIP2200 - params.SloadGasEIP2200)
|
||||||
|
// - SSTORE_RESET_GAS redefined as (5000 - COLD_SLOAD_COST)
|
||||||
|
// - SLOAD_GAS redefined as WARM_STORAGE_READ_COST
|
||||||
|
// Final: (5000 - COLD_SLOAD_COST) - WARM_STORAGE_READ_COST
|
||||||
|
evm.StateDB.AddRefund((params.SstoreResetGasEIP2200 - ColdSloadCostEIP2929) - WarmStorageReadCostEIP2929)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// EIP-2200 original clause:
|
||||||
|
//return params.SloadGasEIP2200, nil // dirty update (2.2)
|
||||||
|
return cost + WarmStorageReadCostEIP2929, nil // dirty update (2.2)
|
||||||
|
}
|
||||||
|
|
||||||
|
// gasSLoadEIP2929 calculates dynamic gas for SLOAD according to EIP-2929
|
||||||
|
// For SLOAD, if the (address, storage_key) pair (where address is the address of the contract
|
||||||
|
// whose storage is being read) is not yet in accessed_storage_keys,
|
||||||
|
// charge 2100 gas and add the pair to accessed_storage_keys.
|
||||||
|
// If the pair is already in accessed_storage_keys, charge 100 gas.
|
||||||
|
func gasSLoadEIP2929(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||||
|
loc := stack.peek()
|
||||||
|
slot := common.Hash(loc.Bytes32())
|
||||||
|
// Check slot presence in the access list
|
||||||
|
if _, slotPresent := evm.StateDB.SlotInAccessList(contract.Address(), slot); !slotPresent {
|
||||||
|
// If the caller cannot afford the cost, this change will be rolled back
|
||||||
|
// If he does afford it, we can skip checking the same thing later on, during execution
|
||||||
|
evm.StateDB.AddSlotToAccessList(contract.Address(), slot)
|
||||||
|
return ColdSloadCostEIP2929, nil
|
||||||
|
}
|
||||||
|
return WarmStorageReadCostEIP2929, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// gasExtCodeCopyEIP2929 implements extcodecopy according to EIP-2929
|
||||||
|
// EIP spec:
|
||||||
|
// > If the target is not in accessed_addresses,
|
||||||
|
// > charge COLD_ACCOUNT_ACCESS_COST gas, and add the address to accessed_addresses.
|
||||||
|
// > Otherwise, charge WARM_STORAGE_READ_COST gas.
|
||||||
|
func gasExtCodeCopyEIP2929(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||||
|
// memory expansion first (dynamic part of pre-2929 implementation)
|
||||||
|
gas, err := gasExtCodeCopy(evm, contract, stack, mem, memorySize)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
addr := common.Address(stack.peek().Bytes20())
|
||||||
|
// Check slot presence in the access list
|
||||||
|
if !evm.StateDB.AddressInAccessList(addr) {
|
||||||
|
evm.StateDB.AddAddressToAccessList(addr)
|
||||||
|
var overflow bool
|
||||||
|
// We charge (cold-warm), since 'warm' is already charged as constantGas
|
||||||
|
if gas, overflow = math.SafeAdd(gas, ColdAccountAccessCostEIP2929-WarmStorageReadCostEIP2929); overflow {
|
||||||
|
return 0, ErrGasUintOverflow
|
||||||
|
}
|
||||||
|
return gas, nil
|
||||||
|
}
|
||||||
|
return gas, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// gasEip2929AccountCheck checks whether the first stack item (as address) is present in the access list.
|
||||||
|
// If it is, this method returns '0', otherwise 'cold-warm' gas, presuming that the opcode using it
|
||||||
|
// is also using 'warm' as constant factor.
|
||||||
|
// This method is used by:
|
||||||
|
// - extcodehash,
|
||||||
|
// - extcodesize,
|
||||||
|
// - (ext) balance
|
||||||
|
func gasEip2929AccountCheck(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||||
|
addr := common.Address(stack.peek().Bytes20())
|
||||||
|
// Check slot presence in the access list
|
||||||
|
if !evm.StateDB.AddressInAccessList(addr) {
|
||||||
|
// If the caller cannot afford the cost, this change will be rolled back
|
||||||
|
evm.StateDB.AddAddressToAccessList(addr)
|
||||||
|
// The warm storage read cost is already charged as constantGas
|
||||||
|
return ColdAccountAccessCostEIP2929 - WarmStorageReadCostEIP2929, nil
|
||||||
|
}
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeCallVariantGasCallEIP2929(oldCalculator gasFunc) gasFunc {
|
||||||
|
return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||||
|
addr := common.Address(stack.Back(1).Bytes20())
|
||||||
|
// Check slot presence in the access list
|
||||||
|
if !evm.StateDB.AddressInAccessList(addr) {
|
||||||
|
evm.StateDB.AddAddressToAccessList(addr)
|
||||||
|
// The WarmStorageReadCostEIP2929 (100) is already deducted in the form of a constant cost
|
||||||
|
if !contract.UseGas(ColdAccountAccessCostEIP2929 - WarmStorageReadCostEIP2929) {
|
||||||
|
return 0, ErrOutOfGas
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Now call the old calculator, which takes into account
|
||||||
|
// - create new account
|
||||||
|
// - transfer value
|
||||||
|
// - memory expansion
|
||||||
|
// - 63/64ths rule
|
||||||
|
return oldCalculator(evm, contract, stack, mem, memorySize)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
gasCallEIP2929 = makeCallVariantGasCallEIP2929(gasCall)
|
||||||
|
gasDelegateCallEIP2929 = makeCallVariantGasCallEIP2929(gasDelegateCall)
|
||||||
|
gasStaticCallEIP2929 = makeCallVariantGasCallEIP2929(gasStaticCall)
|
||||||
|
gasCallCodeEIP2929 = makeCallVariantGasCallEIP2929(gasCallCode)
|
||||||
|
)
|
||||||
|
|
||||||
|
func gasSelfdestructEIP2929(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||||
|
var (
|
||||||
|
gas uint64
|
||||||
|
address = common.Address(stack.peek().Bytes20())
|
||||||
|
)
|
||||||
|
if !evm.StateDB.AddressInAccessList(address) {
|
||||||
|
// If the caller cannot afford the cost, this change will be rolled back
|
||||||
|
evm.StateDB.AddAddressToAccessList(address)
|
||||||
|
gas = ColdAccountAccessCostEIP2929
|
||||||
|
}
|
||||||
|
// if empty and transfers value
|
||||||
|
if evm.StateDB.Empty(address) && evm.StateDB.GetBalance(contract.Address()).Sign() != 0 {
|
||||||
|
gas += params.CreateBySelfdestructGas
|
||||||
|
}
|
||||||
|
if !evm.StateDB.HasSuicided(contract.Address()) {
|
||||||
|
evm.StateDB.AddRefund(params.SelfdestructRefundGas)
|
||||||
|
}
|
||||||
|
return gas, nil
|
||||||
|
|
||||||
|
}
|
@ -65,7 +65,7 @@ func setDefaults(cfg *Config) {
|
|||||||
PetersburgBlock: new(big.Int),
|
PetersburgBlock: new(big.Int),
|
||||||
IstanbulBlock: new(big.Int),
|
IstanbulBlock: new(big.Int),
|
||||||
MuirGlacierBlock: new(big.Int),
|
MuirGlacierBlock: new(big.Int),
|
||||||
YoloV1Block: nil,
|
YoloV2Block: nil,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -113,6 +113,14 @@ func Execute(code, input []byte, cfg *Config) ([]byte, *state.StateDB, error) {
|
|||||||
vmenv = NewEnv(cfg)
|
vmenv = NewEnv(cfg)
|
||||||
sender = vm.AccountRef(cfg.Origin)
|
sender = vm.AccountRef(cfg.Origin)
|
||||||
)
|
)
|
||||||
|
if cfg.ChainConfig.IsYoloV2(vmenv.BlockNumber) {
|
||||||
|
cfg.State.AddAddressToAccessList(cfg.Origin)
|
||||||
|
cfg.State.AddAddressToAccessList(address)
|
||||||
|
for _, addr := range vmenv.ActivePrecompiles() {
|
||||||
|
cfg.State.AddAddressToAccessList(addr)
|
||||||
|
cfg.State.AddAddressToAccessList(addr)
|
||||||
|
}
|
||||||
|
}
|
||||||
cfg.State.CreateAccount(address)
|
cfg.State.CreateAccount(address)
|
||||||
// set the receiver's (the executing contract) code for execution.
|
// set the receiver's (the executing contract) code for execution.
|
||||||
cfg.State.SetCode(address, code)
|
cfg.State.SetCode(address, code)
|
||||||
@ -142,6 +150,12 @@ func Create(input []byte, cfg *Config) ([]byte, common.Address, uint64, error) {
|
|||||||
vmenv = NewEnv(cfg)
|
vmenv = NewEnv(cfg)
|
||||||
sender = vm.AccountRef(cfg.Origin)
|
sender = vm.AccountRef(cfg.Origin)
|
||||||
)
|
)
|
||||||
|
if cfg.ChainConfig.IsYoloV2(vmenv.BlockNumber) {
|
||||||
|
cfg.State.AddAddressToAccessList(cfg.Origin)
|
||||||
|
for _, addr := range vmenv.ActivePrecompiles() {
|
||||||
|
cfg.State.AddAddressToAccessList(addr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Call the code with the given configuration.
|
// Call the code with the given configuration.
|
||||||
code, address, leftOverGas, err := vmenv.Create(
|
code, address, leftOverGas, err := vmenv.Create(
|
||||||
@ -164,6 +178,14 @@ func Call(address common.Address, input []byte, cfg *Config) ([]byte, uint64, er
|
|||||||
vmenv := NewEnv(cfg)
|
vmenv := NewEnv(cfg)
|
||||||
|
|
||||||
sender := cfg.State.GetOrNewStateObject(cfg.Origin)
|
sender := cfg.State.GetOrNewStateObject(cfg.Origin)
|
||||||
|
if cfg.ChainConfig.IsYoloV2(vmenv.BlockNumber) {
|
||||||
|
cfg.State.AddAddressToAccessList(cfg.Origin)
|
||||||
|
cfg.State.AddAddressToAccessList(address)
|
||||||
|
for _, addr := range vmenv.ActivePrecompiles() {
|
||||||
|
cfg.State.AddAddressToAccessList(addr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Call the code with the given configuration.
|
// Call the code with the given configuration.
|
||||||
ret, leftOverGas, err := vmenv.Call(
|
ret, leftOverGas, err := vmenv.Call(
|
||||||
sender,
|
sender,
|
||||||
|
@ -722,3 +722,115 @@ func BenchmarkSimpleLoop(b *testing.B) {
|
|||||||
//benchmarkNonModifyingCode(10000000, staticCallIdentity, "staticcall-identity-10M", b)
|
//benchmarkNonModifyingCode(10000000, staticCallIdentity, "staticcall-identity-10M", b)
|
||||||
//benchmarkNonModifyingCode(10000000, loopingCode, "loop-10M", b)
|
//benchmarkNonModifyingCode(10000000, loopingCode, "loop-10M", b)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestEip2929Cases contains various testcases that are used for
|
||||||
|
// EIP-2929 about gas repricings
|
||||||
|
func TestEip2929Cases(t *testing.T) {
|
||||||
|
|
||||||
|
id := 1
|
||||||
|
prettyPrint := func(comment string, code []byte) {
|
||||||
|
|
||||||
|
instrs := make([]string, 0)
|
||||||
|
it := asm.NewInstructionIterator(code)
|
||||||
|
for it.Next() {
|
||||||
|
if it.Arg() != nil && 0 < len(it.Arg()) {
|
||||||
|
instrs = append(instrs, fmt.Sprintf("%v 0x%x", it.Op(), it.Arg()))
|
||||||
|
} else {
|
||||||
|
instrs = append(instrs, fmt.Sprintf("%v", it.Op()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ops := strings.Join(instrs, ", ")
|
||||||
|
fmt.Printf("### Case %d\n\n", id)
|
||||||
|
id++
|
||||||
|
fmt.Printf("%v\n\nBytecode: \n```\n0x%x\n```\nOperations: \n```\n%v\n```\n\n",
|
||||||
|
comment,
|
||||||
|
code, ops)
|
||||||
|
Execute(code, nil, &Config{
|
||||||
|
EVMConfig: vm.Config{
|
||||||
|
Debug: true,
|
||||||
|
Tracer: vm.NewMarkdownLogger(nil, os.Stdout),
|
||||||
|
ExtraEips: []int{2929},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
{ // First eip testcase
|
||||||
|
code := []byte{
|
||||||
|
// Three checks against a precompile
|
||||||
|
byte(vm.PUSH1), 1, byte(vm.EXTCODEHASH), byte(vm.POP),
|
||||||
|
byte(vm.PUSH1), 2, byte(vm.EXTCODESIZE), byte(vm.POP),
|
||||||
|
byte(vm.PUSH1), 3, byte(vm.BALANCE), byte(vm.POP),
|
||||||
|
// Three checks against a non-precompile
|
||||||
|
byte(vm.PUSH1), 0xf1, byte(vm.EXTCODEHASH), byte(vm.POP),
|
||||||
|
byte(vm.PUSH1), 0xf2, byte(vm.EXTCODESIZE), byte(vm.POP),
|
||||||
|
byte(vm.PUSH1), 0xf3, byte(vm.BALANCE), byte(vm.POP),
|
||||||
|
// Same three checks (should be cheaper)
|
||||||
|
byte(vm.PUSH1), 0xf2, byte(vm.EXTCODEHASH), byte(vm.POP),
|
||||||
|
byte(vm.PUSH1), 0xf3, byte(vm.EXTCODESIZE), byte(vm.POP),
|
||||||
|
byte(vm.PUSH1), 0xf1, byte(vm.BALANCE), byte(vm.POP),
|
||||||
|
// Check the origin, and the 'this'
|
||||||
|
byte(vm.ORIGIN), byte(vm.BALANCE), byte(vm.POP),
|
||||||
|
byte(vm.ADDRESS), byte(vm.BALANCE), byte(vm.POP),
|
||||||
|
|
||||||
|
byte(vm.STOP),
|
||||||
|
}
|
||||||
|
prettyPrint("This checks `EXT`(codehash,codesize,balance) of precompiles, which should be `100`, "+
|
||||||
|
"and later checks the same operations twice against some non-precompiles. "+
|
||||||
|
"Those are cheaper second time they are accessed. Lastly, it checks the `BALANCE` of `origin` and `this`.", code)
|
||||||
|
}
|
||||||
|
|
||||||
|
{ // EXTCODECOPY
|
||||||
|
code := []byte{
|
||||||
|
// extcodecopy( 0xff,0,0,0,0)
|
||||||
|
byte(vm.PUSH1), 0x00, byte(vm.PUSH1), 0x00, byte(vm.PUSH1), 0x00, //length, codeoffset, memoffset
|
||||||
|
byte(vm.PUSH1), 0xff, byte(vm.EXTCODECOPY),
|
||||||
|
// extcodecopy( 0xff,0,0,0,0)
|
||||||
|
byte(vm.PUSH1), 0x00, byte(vm.PUSH1), 0x00, byte(vm.PUSH1), 0x00, //length, codeoffset, memoffset
|
||||||
|
byte(vm.PUSH1), 0xff, byte(vm.EXTCODECOPY),
|
||||||
|
// extcodecopy( this,0,0,0,0)
|
||||||
|
byte(vm.PUSH1), 0x00, byte(vm.PUSH1), 0x00, byte(vm.PUSH1), 0x00, //length, codeoffset, memoffset
|
||||||
|
byte(vm.ADDRESS), byte(vm.EXTCODECOPY),
|
||||||
|
|
||||||
|
byte(vm.STOP),
|
||||||
|
}
|
||||||
|
prettyPrint("This checks `extcodecopy( 0xff,0,0,0,0)` twice, (should be expensive first time), "+
|
||||||
|
"and then does `extcodecopy( this,0,0,0,0)`.", code)
|
||||||
|
}
|
||||||
|
|
||||||
|
{ // SLOAD + SSTORE
|
||||||
|
code := []byte{
|
||||||
|
|
||||||
|
// Add slot `0x1` to access list
|
||||||
|
byte(vm.PUSH1), 0x01, byte(vm.SLOAD), byte(vm.POP), // SLOAD( 0x1) (add to access list)
|
||||||
|
// Write to `0x1` which is already in access list
|
||||||
|
byte(vm.PUSH1), 0x11, byte(vm.PUSH1), 0x01, byte(vm.SSTORE), // SSTORE( loc: 0x01, val: 0x11)
|
||||||
|
// Write to `0x2` which is not in access list
|
||||||
|
byte(vm.PUSH1), 0x11, byte(vm.PUSH1), 0x02, byte(vm.SSTORE), // SSTORE( loc: 0x02, val: 0x11)
|
||||||
|
// Write again to `0x2`
|
||||||
|
byte(vm.PUSH1), 0x11, byte(vm.PUSH1), 0x02, byte(vm.SSTORE), // SSTORE( loc: 0x02, val: 0x11)
|
||||||
|
// Read slot in access list (0x2)
|
||||||
|
byte(vm.PUSH1), 0x02, byte(vm.SLOAD), // SLOAD( 0x2)
|
||||||
|
// Read slot in access list (0x1)
|
||||||
|
byte(vm.PUSH1), 0x01, byte(vm.SLOAD), // SLOAD( 0x1)
|
||||||
|
}
|
||||||
|
prettyPrint("This checks `sload( 0x1)` followed by `sstore(loc: 0x01, val:0x11)`, then 'naked' sstore:"+
|
||||||
|
"`sstore(loc: 0x02, val:0x11)` twice, and `sload(0x2)`, `sload(0x1)`. ", code)
|
||||||
|
}
|
||||||
|
{ // Call variants
|
||||||
|
code := []byte{
|
||||||
|
// identity precompile
|
||||||
|
byte(vm.PUSH1), 0x0, byte(vm.DUP1), byte(vm.DUP1), byte(vm.DUP1), byte(vm.DUP1),
|
||||||
|
byte(vm.PUSH1), 0x04, byte(vm.PUSH1), 0x0, byte(vm.CALL), byte(vm.POP),
|
||||||
|
|
||||||
|
// random account - call 1
|
||||||
|
byte(vm.PUSH1), 0x0, byte(vm.DUP1), byte(vm.DUP1), byte(vm.DUP1), byte(vm.DUP1),
|
||||||
|
byte(vm.PUSH1), 0xff, byte(vm.PUSH1), 0x0, byte(vm.CALL), byte(vm.POP),
|
||||||
|
|
||||||
|
// random account - call 2
|
||||||
|
byte(vm.PUSH1), 0x0, byte(vm.DUP1), byte(vm.DUP1), byte(vm.DUP1), byte(vm.DUP1),
|
||||||
|
byte(vm.PUSH1), 0xff, byte(vm.PUSH1), 0x0, byte(vm.STATICCALL), byte(vm.POP),
|
||||||
|
}
|
||||||
|
prettyPrint("This calls the `identity`-precompile (cheap), then calls an account (expensive) and `staticcall`s the same"+
|
||||||
|
"account (cheap)", code)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -38,6 +38,7 @@ import (
|
|||||||
"github.com/ethereum/go-ethereum/eth/tracers"
|
"github.com/ethereum/go-ethereum/eth/tracers"
|
||||||
"github.com/ethereum/go-ethereum/internal/ethapi"
|
"github.com/ethereum/go-ethereum/internal/ethapi"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
"github.com/ethereum/go-ethereum/rpc"
|
"github.com/ethereum/go-ethereum/rpc"
|
||||||
"github.com/ethereum/go-ethereum/trie"
|
"github.com/ethereum/go-ethereum/trie"
|
||||||
@ -563,7 +564,26 @@ func (api *PrivateDebugAPI) standardTraceBlockToFile(ctx context.Context, block
|
|||||||
var (
|
var (
|
||||||
signer = types.MakeSigner(api.eth.blockchain.Config(), block.Number())
|
signer = types.MakeSigner(api.eth.blockchain.Config(), block.Number())
|
||||||
dumps []string
|
dumps []string
|
||||||
|
chainConfig = api.eth.blockchain.Config()
|
||||||
|
canon = true
|
||||||
)
|
)
|
||||||
|
// Check if there are any overrides: the caller may wish to enable a future
|
||||||
|
// fork when executing this block. Note, such overrides are only applicable to the
|
||||||
|
// actual specified block, not any preceding blocks that we have to go through
|
||||||
|
// in order to obtain the state.
|
||||||
|
// Therefore, it's perfectly valid to specify `"futureForkBlock": 0`, to enable `futureFork`
|
||||||
|
|
||||||
|
if config != nil && config.Overrides != nil {
|
||||||
|
// Copy the config, to not screw up the main config
|
||||||
|
// Note: the Clique-part is _not_ deep copied
|
||||||
|
chainConfigCopy := new(params.ChainConfig)
|
||||||
|
*chainConfigCopy = *chainConfig
|
||||||
|
chainConfig = chainConfigCopy
|
||||||
|
if yolov2 := config.Overrides.YoloV2Block; yolov2 != nil {
|
||||||
|
chainConfig.YoloV2Block = yolov2
|
||||||
|
canon = false
|
||||||
|
}
|
||||||
|
}
|
||||||
for i, tx := range block.Transactions() {
|
for i, tx := range block.Transactions() {
|
||||||
// Prepare the trasaction for un-traced execution
|
// Prepare the trasaction for un-traced execution
|
||||||
var (
|
var (
|
||||||
@ -579,7 +599,9 @@ func (api *PrivateDebugAPI) standardTraceBlockToFile(ctx context.Context, block
|
|||||||
if tx.Hash() == txHash || txHash == (common.Hash{}) {
|
if tx.Hash() == txHash || txHash == (common.Hash{}) {
|
||||||
// Generate a unique temporary file to dump it into
|
// Generate a unique temporary file to dump it into
|
||||||
prefix := fmt.Sprintf("block_%#x-%d-%#x-", block.Hash().Bytes()[:4], i, tx.Hash().Bytes()[:4])
|
prefix := fmt.Sprintf("block_%#x-%d-%#x-", block.Hash().Bytes()[:4], i, tx.Hash().Bytes()[:4])
|
||||||
|
if !canon {
|
||||||
|
prefix = fmt.Sprintf("%valt-", prefix)
|
||||||
|
}
|
||||||
dump, err = ioutil.TempFile(os.TempDir(), prefix)
|
dump, err = ioutil.TempFile(os.TempDir(), prefix)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -595,7 +617,7 @@ func (api *PrivateDebugAPI) standardTraceBlockToFile(ctx context.Context, block
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Execute the transaction and flush any traces to disk
|
// Execute the transaction and flush any traces to disk
|
||||||
vmenv := vm.NewEVM(vmctx, statedb, api.eth.blockchain.Config(), vmConf)
|
vmenv := vm.NewEVM(vmctx, statedb, chainConfig, vmConf)
|
||||||
_, err = core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas()))
|
_, err = core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas()))
|
||||||
if writer != nil {
|
if writer != nil {
|
||||||
writer.Flush()
|
writer.Flush()
|
||||||
|
@ -31,7 +31,8 @@ var (
|
|||||||
RopstenGenesisHash = common.HexToHash("0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d")
|
RopstenGenesisHash = common.HexToHash("0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d")
|
||||||
RinkebyGenesisHash = common.HexToHash("0x6341fd3daf94b748c72ced5a5b26028f2474f5f00d824504e4fa37a75767e177")
|
RinkebyGenesisHash = common.HexToHash("0x6341fd3daf94b748c72ced5a5b26028f2474f5f00d824504e4fa37a75767e177")
|
||||||
GoerliGenesisHash = common.HexToHash("0xbf7e331f7f7c1dd2e05159666b3bf8bc7a8a3a9eb1d518969eab529dd9b88c1a")
|
GoerliGenesisHash = common.HexToHash("0xbf7e331f7f7c1dd2e05159666b3bf8bc7a8a3a9eb1d518969eab529dd9b88c1a")
|
||||||
YoloV1GenesisHash = common.HexToHash("0xc3fd235071f24f93865b0850bd2a2119b30f7224d18a0e34c7bbf549ad7e3d36")
|
// TODO: update with yolov2 values
|
||||||
|
YoloV2GenesisHash = common.HexToHash("0x498a7239036dd2cd09e2bb8a80922b78632017958c332b42044c250d603a8a3e")
|
||||||
)
|
)
|
||||||
|
|
||||||
// TrustedCheckpoints associates each known checkpoint with the genesis hash of
|
// TrustedCheckpoints associates each known checkpoint with the genesis hash of
|
||||||
@ -213,9 +214,9 @@ var (
|
|||||||
Threshold: 2,
|
Threshold: 2,
|
||||||
}
|
}
|
||||||
|
|
||||||
// YoloV1ChainConfig contains the chain parameters to run a node on the YOLOv1 test network.
|
// YoloV2ChainConfig contains the chain parameters to run a node on the YOLOv2 test network.
|
||||||
YoloV1ChainConfig = &ChainConfig{
|
YoloV2ChainConfig = &ChainConfig{
|
||||||
ChainID: big.NewInt(133519467574833),
|
ChainID: big.NewInt(133519467574834),
|
||||||
HomesteadBlock: big.NewInt(0),
|
HomesteadBlock: big.NewInt(0),
|
||||||
DAOForkBlock: nil,
|
DAOForkBlock: nil,
|
||||||
DAOForkSupport: true,
|
DAOForkSupport: true,
|
||||||
@ -227,7 +228,7 @@ var (
|
|||||||
PetersburgBlock: big.NewInt(0),
|
PetersburgBlock: big.NewInt(0),
|
||||||
IstanbulBlock: big.NewInt(0),
|
IstanbulBlock: big.NewInt(0),
|
||||||
MuirGlacierBlock: nil,
|
MuirGlacierBlock: nil,
|
||||||
YoloV1Block: big.NewInt(0),
|
YoloV2Block: big.NewInt(0),
|
||||||
Clique: &CliqueConfig{
|
Clique: &CliqueConfig{
|
||||||
Period: 15,
|
Period: 15,
|
||||||
Epoch: 30000,
|
Epoch: 30000,
|
||||||
@ -320,7 +321,7 @@ type ChainConfig struct {
|
|||||||
IstanbulBlock *big.Int `json:"istanbulBlock,omitempty"` // Istanbul switch block (nil = no fork, 0 = already on istanbul)
|
IstanbulBlock *big.Int `json:"istanbulBlock,omitempty"` // Istanbul switch block (nil = no fork, 0 = already on istanbul)
|
||||||
MuirGlacierBlock *big.Int `json:"muirGlacierBlock,omitempty"` // Eip-2384 (bomb delay) switch block (nil = no fork, 0 = already activated)
|
MuirGlacierBlock *big.Int `json:"muirGlacierBlock,omitempty"` // Eip-2384 (bomb delay) switch block (nil = no fork, 0 = already activated)
|
||||||
|
|
||||||
YoloV1Block *big.Int `json:"yoloV1Block,omitempty"` // YOLO v1: https://github.com/ethereum/EIPs/pull/2657 (Ephemeral testnet)
|
YoloV2Block *big.Int `json:"yoloV2Block,omitempty"` // YOLO v2: Gas repricings TODO @holiman add EIP references
|
||||||
EWASMBlock *big.Int `json:"ewasmBlock,omitempty"` // EWASM switch block (nil = no fork, 0 = already activated)
|
EWASMBlock *big.Int `json:"ewasmBlock,omitempty"` // EWASM switch block (nil = no fork, 0 = already activated)
|
||||||
|
|
||||||
// Various consensus engines
|
// Various consensus engines
|
||||||
@ -358,7 +359,7 @@ func (c *ChainConfig) String() string {
|
|||||||
default:
|
default:
|
||||||
engine = "unknown"
|
engine = "unknown"
|
||||||
}
|
}
|
||||||
return fmt.Sprintf("{ChainID: %v Homestead: %v DAO: %v DAOSupport: %v EIP150: %v EIP155: %v EIP158: %v Byzantium: %v Constantinople: %v Petersburg: %v Istanbul: %v, Muir Glacier: %v, YOLO v1: %v, Engine: %v}",
|
return fmt.Sprintf("{ChainID: %v Homestead: %v DAO: %v DAOSupport: %v EIP150: %v EIP155: %v EIP158: %v Byzantium: %v Constantinople: %v Petersburg: %v Istanbul: %v, Muir Glacier: %v, YOLO v2: %v, Engine: %v}",
|
||||||
c.ChainID,
|
c.ChainID,
|
||||||
c.HomesteadBlock,
|
c.HomesteadBlock,
|
||||||
c.DAOForkBlock,
|
c.DAOForkBlock,
|
||||||
@ -371,7 +372,7 @@ func (c *ChainConfig) String() string {
|
|||||||
c.PetersburgBlock,
|
c.PetersburgBlock,
|
||||||
c.IstanbulBlock,
|
c.IstanbulBlock,
|
||||||
c.MuirGlacierBlock,
|
c.MuirGlacierBlock,
|
||||||
c.YoloV1Block,
|
c.YoloV2Block,
|
||||||
engine,
|
engine,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -428,9 +429,9 @@ func (c *ChainConfig) IsIstanbul(num *big.Int) bool {
|
|||||||
return isForked(c.IstanbulBlock, num)
|
return isForked(c.IstanbulBlock, num)
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsYoloV1 returns whether num is either equal to the YoloV1 fork block or greater.
|
// IsYoloV2 returns whether num is either equal to the YoloV1 fork block or greater.
|
||||||
func (c *ChainConfig) IsYoloV1(num *big.Int) bool {
|
func (c *ChainConfig) IsYoloV2(num *big.Int) bool {
|
||||||
return isForked(c.YoloV1Block, num)
|
return isForked(c.YoloV2Block, num)
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsEWASM returns whether num represents a block number after the EWASM fork
|
// IsEWASM returns whether num represents a block number after the EWASM fork
|
||||||
@ -476,7 +477,7 @@ func (c *ChainConfig) CheckConfigForkOrder() error {
|
|||||||
{name: "petersburgBlock", block: c.PetersburgBlock},
|
{name: "petersburgBlock", block: c.PetersburgBlock},
|
||||||
{name: "istanbulBlock", block: c.IstanbulBlock},
|
{name: "istanbulBlock", block: c.IstanbulBlock},
|
||||||
{name: "muirGlacierBlock", block: c.MuirGlacierBlock, optional: true},
|
{name: "muirGlacierBlock", block: c.MuirGlacierBlock, optional: true},
|
||||||
{name: "yoloV1Block", block: c.YoloV1Block},
|
{name: "yoloV2Block", block: c.YoloV2Block},
|
||||||
} {
|
} {
|
||||||
if lastFork.name != "" {
|
if lastFork.name != "" {
|
||||||
// Next one must be higher number
|
// Next one must be higher number
|
||||||
@ -540,8 +541,8 @@ func (c *ChainConfig) checkCompatible(newcfg *ChainConfig, head *big.Int) *Confi
|
|||||||
if isForkIncompatible(c.MuirGlacierBlock, newcfg.MuirGlacierBlock, head) {
|
if isForkIncompatible(c.MuirGlacierBlock, newcfg.MuirGlacierBlock, head) {
|
||||||
return newCompatError("Muir Glacier fork block", c.MuirGlacierBlock, newcfg.MuirGlacierBlock)
|
return newCompatError("Muir Glacier fork block", c.MuirGlacierBlock, newcfg.MuirGlacierBlock)
|
||||||
}
|
}
|
||||||
if isForkIncompatible(c.YoloV1Block, newcfg.YoloV1Block, head) {
|
if isForkIncompatible(c.YoloV2Block, newcfg.YoloV2Block, head) {
|
||||||
return newCompatError("YOLOv1 fork block", c.YoloV1Block, newcfg.YoloV1Block)
|
return newCompatError("YOLOv2 fork block", c.YoloV2Block, newcfg.YoloV2Block)
|
||||||
}
|
}
|
||||||
if isForkIncompatible(c.EWASMBlock, newcfg.EWASMBlock, head) {
|
if isForkIncompatible(c.EWASMBlock, newcfg.EWASMBlock, head) {
|
||||||
return newCompatError("ewasm fork block", c.EWASMBlock, newcfg.EWASMBlock)
|
return newCompatError("ewasm fork block", c.EWASMBlock, newcfg.EWASMBlock)
|
||||||
@ -613,7 +614,7 @@ type Rules struct {
|
|||||||
ChainID *big.Int
|
ChainID *big.Int
|
||||||
IsHomestead, IsEIP150, IsEIP155, IsEIP158 bool
|
IsHomestead, IsEIP150, IsEIP155, IsEIP158 bool
|
||||||
IsByzantium, IsConstantinople, IsPetersburg, IsIstanbul bool
|
IsByzantium, IsConstantinople, IsPetersburg, IsIstanbul bool
|
||||||
IsYoloV1 bool
|
IsYoloV2 bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rules ensures c's ChainID is not nil.
|
// Rules ensures c's ChainID is not nil.
|
||||||
@ -632,6 +633,6 @@ func (c *ChainConfig) Rules(num *big.Int) Rules {
|
|||||||
IsConstantinople: c.IsConstantinople(num),
|
IsConstantinople: c.IsConstantinople(num),
|
||||||
IsPetersburg: c.IsPetersburg(num),
|
IsPetersburg: c.IsPetersburg(num),
|
||||||
IsIstanbul: c.IsIstanbul(num),
|
IsIstanbul: c.IsIstanbul(num),
|
||||||
IsYoloV1: c.IsYoloV1(num),
|
IsYoloV2: c.IsYoloV2(num),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -141,7 +141,7 @@ var Forks = map[string]*params.ChainConfig{
|
|||||||
PetersburgBlock: big.NewInt(0),
|
PetersburgBlock: big.NewInt(0),
|
||||||
IstanbulBlock: big.NewInt(5),
|
IstanbulBlock: big.NewInt(5),
|
||||||
},
|
},
|
||||||
"YOLOv1": {
|
"YOLOv2": {
|
||||||
ChainID: big.NewInt(1),
|
ChainID: big.NewInt(1),
|
||||||
HomesteadBlock: big.NewInt(0),
|
HomesteadBlock: big.NewInt(0),
|
||||||
EIP150Block: big.NewInt(0),
|
EIP150Block: big.NewInt(0),
|
||||||
@ -151,9 +151,9 @@ var Forks = map[string]*params.ChainConfig{
|
|||||||
ConstantinopleBlock: big.NewInt(0),
|
ConstantinopleBlock: big.NewInt(0),
|
||||||
PetersburgBlock: big.NewInt(0),
|
PetersburgBlock: big.NewInt(0),
|
||||||
IstanbulBlock: big.NewInt(0),
|
IstanbulBlock: big.NewInt(0),
|
||||||
YoloV1Block: big.NewInt(0),
|
YoloV2Block: big.NewInt(0),
|
||||||
},
|
},
|
||||||
// This specification is subject to change, but is for now identical to YOLOv1
|
// This specification is subject to change, but is for now identical to YOLOv2
|
||||||
// for cross-client testing purposes
|
// for cross-client testing purposes
|
||||||
"Berlin": {
|
"Berlin": {
|
||||||
ChainID: big.NewInt(1),
|
ChainID: big.NewInt(1),
|
||||||
@ -165,7 +165,7 @@ var Forks = map[string]*params.ChainConfig{
|
|||||||
ConstantinopleBlock: big.NewInt(0),
|
ConstantinopleBlock: big.NewInt(0),
|
||||||
PetersburgBlock: big.NewInt(0),
|
PetersburgBlock: big.NewInt(0),
|
||||||
IstanbulBlock: big.NewInt(0),
|
IstanbulBlock: big.NewInt(0),
|
||||||
YoloV1Block: big.NewInt(0),
|
YoloV2Block: big.NewInt(0),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -186,6 +186,16 @@ func (t *StateTest) RunNoVerify(subtest StateSubtest, vmconfig vm.Config, snapsh
|
|||||||
context.GetHash = vmTestBlockHash
|
context.GetHash = vmTestBlockHash
|
||||||
evm := vm.NewEVM(context, statedb, config, vmconfig)
|
evm := vm.NewEVM(context, statedb, config, vmconfig)
|
||||||
|
|
||||||
|
if config.IsYoloV2(context.BlockNumber) {
|
||||||
|
statedb.AddAddressToAccessList(msg.From())
|
||||||
|
if dst := msg.To(); dst != nil {
|
||||||
|
statedb.AddAddressToAccessList(*dst)
|
||||||
|
// If it's a create-tx, the destination will be added inside evm.create
|
||||||
|
}
|
||||||
|
for _, addr := range evm.ActivePrecompiles() {
|
||||||
|
statedb.AddAddressToAccessList(addr)
|
||||||
|
}
|
||||||
|
}
|
||||||
gaspool := new(core.GasPool)
|
gaspool := new(core.GasPool)
|
||||||
gaspool.AddGas(block.GasLimit())
|
gaspool.AddGas(block.GasLimit())
|
||||||
snapshot := statedb.Snapshot()
|
snapshot := statedb.Snapshot()
|
||||||
|
Loading…
Reference in New Issue
Block a user