Skip to content

Commit

Permalink
all: implement EIP-2929 (gas cost increases for state access opcodes)…
Browse files Browse the repository at this point in the history
… + 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
  • Loading branch information
holiman authored Oct 23, 2020
1 parent fb2c79d commit 6487c00
Show file tree
Hide file tree
Showing 28 changed files with 980 additions and 73 deletions.
10 changes: 10 additions & 0 deletions cmd/evm/internal/t8ntool/execution.go
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,16 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
vmContext.Origin = msg.From()

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()
// (ret []byte, usedGas uint64, failed bool, err error)
msgResult, err := core.ApplyMessage(evm, msg, gaspool)
Expand Down
4 changes: 2 additions & 2 deletions cmd/geth/chaincmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ The export-preimages command export hash preimages to an RLP encoded stream`,
utils.RinkebyFlag,
utils.TxLookupLimitFlag,
utils.GoerliFlag,
utils.YoloV1Flag,
utils.YoloV2Flag,
utils.LegacyTestnetFlag,
},
Category: "BLOCKCHAIN COMMANDS",
Expand Down Expand Up @@ -213,7 +213,7 @@ Use "ethereum dump 0" to dump the genesis block.`,
utils.RopstenFlag,
utils.RinkebyFlag,
utils.GoerliFlag,
utils.YoloV1Flag,
utils.YoloV2Flag,
utils.LegacyTestnetFlag,
utils.SyncModeFlag,
},
Expand Down
2 changes: 1 addition & 1 deletion cmd/geth/consolecmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ func remoteConsole(ctx *cli.Context) error {
path = filepath.Join(path, "rinkeby")
} else if ctx.GlobalBool(utils.GoerliFlag.Name) {
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")
}
}
Expand Down
2 changes: 1 addition & 1 deletion cmd/geth/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ var (
utils.RopstenFlag,
utils.RinkebyFlag,
utils.GoerliFlag,
utils.YoloV1Flag,
utils.YoloV2Flag,
utils.VMEnableDebugFlag,
utils.NetworkIdFlag,
utils.EthStatsURLFlag,
Expand Down
2 changes: 1 addition & 1 deletion cmd/geth/usage.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ var AppHelpFlagGroups = []flags.FlagGroup{
utils.NetworkIdFlag,
utils.GoerliFlag,
utils.RinkebyFlag,
utils.YoloV1Flag,
utils.YoloV2Flag,
utils.RopstenFlag,
utils.SyncModeFlag,
utils.ExitWhenSyncedFlag,
Expand Down
4 changes: 2 additions & 2 deletions cmd/puppeth/wizard_genesis.go
Original file line number Diff line number Diff line change
Expand Up @@ -236,8 +236,8 @@ func (w *wizard) manageGenesis() {
w.conf.Genesis.Config.IstanbulBlock = w.readDefaultBigInt(w.conf.Genesis.Config.IstanbulBlock)

fmt.Println()
fmt.Printf("Which block should YOLOv1 come into effect? (default = %v)\n", w.conf.Genesis.Config.YoloV1Block)
w.conf.Genesis.Config.YoloV1Block = w.readDefaultBigInt(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.YoloV2Block = w.readDefaultBigInt(w.conf.Genesis.Config.YoloV2Block)

out, _ := json.MarshalIndent(w.conf.Genesis.Config, "", " ")
fmt.Printf("Chain configuration updated:\n\n%s\n", out)
Expand Down
30 changes: 15 additions & 15 deletions cmd/utils/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -135,9 +135,9 @@ var (
Name: "goerli",
Usage: "Görli network: pre-configured proof-of-authority test network",
}
YoloV1Flag = cli.BoolFlag{
Name: "yolov1",
Usage: "YOLOv1 network: pre-configured proof-of-authority shortlived test network.",
YoloV2Flag = cli.BoolFlag{
Name: "yolov2",
Usage: "YOLOv2 network: pre-configured proof-of-authority shortlived test network.",
}
RinkebyFlag = cli.BoolFlag{
Name: "rinkeby",
Expand Down Expand Up @@ -744,8 +744,8 @@ func MakeDataDir(ctx *cli.Context) string {
if ctx.GlobalBool(GoerliFlag.Name) {
return filepath.Join(path, "goerli")
}
if ctx.GlobalBool(YoloV1Flag.Name) {
return filepath.Join(path, "yolo-v1")
if ctx.GlobalBool(YoloV2Flag.Name) {
return filepath.Join(path, "yolo-v2")
}
return path
}
Expand Down Expand Up @@ -803,7 +803,7 @@ func setBootstrapNodes(ctx *cli.Context, cfg *p2p.Config) {
urls = params.RinkebyBootnodes
case ctx.GlobalBool(GoerliFlag.Name):
urls = params.GoerliBootnodes
case ctx.GlobalBool(YoloV1Flag.Name):
case ctx.GlobalBool(YoloV2Flag.Name):
urls = params.YoloV1Bootnodes
case cfg.BootstrapNodes != nil:
return // already set, don't apply defaults.
Expand Down Expand Up @@ -839,7 +839,7 @@ func setBootstrapNodesV5(ctx *cli.Context, cfg *p2p.Config) {
urls = params.RinkebyBootnodes
case ctx.GlobalBool(GoerliFlag.Name):
urls = params.GoerliBootnodes
case ctx.GlobalBool(YoloV1Flag.Name):
case ctx.GlobalBool(YoloV2Flag.Name):
urls = params.YoloV1Bootnodes
case cfg.BootstrapNodesV5 != nil:
return // already set, don't apply defaults.
Expand Down Expand Up @@ -1269,8 +1269,8 @@ func setDataDir(ctx *cli.Context, cfg *node.Config) {
cfg.DataDir = filepath.Join(node.DefaultDataDir(), "rinkeby")
case ctx.GlobalBool(GoerliFlag.Name) && cfg.DataDir == node.DefaultDataDir():
cfg.DataDir = filepath.Join(node.DefaultDataDir(), "goerli")
case ctx.GlobalBool(YoloV1Flag.Name) && cfg.DataDir == node.DefaultDataDir():
cfg.DataDir = filepath.Join(node.DefaultDataDir(), "yolo-v1")
case ctx.GlobalBool(YoloV2Flag.Name) && cfg.DataDir == node.DefaultDataDir():
cfg.DataDir = filepath.Join(node.DefaultDataDir(), "yolo-v2")
}
}

Expand Down Expand Up @@ -1483,7 +1483,7 @@ func SetShhConfig(ctx *cli.Context, stack *node.Node) {
// SetEthConfig applies eth-related command line flags to the config.
func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *eth.Config) {
// 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, DeveloperFlag, ExternalSignerFlag) // Can't use both ephemeral unlocked and external signer
CheckExclusive(ctx, GCModeFlag, "archive", TxLookupLimitFlag)
Expand Down Expand Up @@ -1603,11 +1603,11 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *eth.Config) {
}
cfg.Genesis = core.DefaultGoerliGenesisBlock()
SetDNSDiscoveryDefaults(cfg, params.GoerliGenesisHash)
case ctx.GlobalBool(YoloV1Flag.Name):
case ctx.GlobalBool(YoloV2Flag.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):
if !ctx.GlobalIsSet(NetworkIdFlag.Name) {
cfg.NetworkId = 1337
Expand Down Expand Up @@ -1791,8 +1791,8 @@ func MakeGenesis(ctx *cli.Context) *core.Genesis {
genesis = core.DefaultRinkebyGenesisBlock()
case ctx.GlobalBool(GoerliFlag.Name):
genesis = core.DefaultGoerliGenesisBlock()
case ctx.GlobalBool(YoloV1Flag.Name):
genesis = core.DefaultYoloV1GenesisBlock()
case ctx.GlobalBool(YoloV2Flag.Name):
genesis = core.DefaultYoloV2GenesisBlock()
case ctx.GlobalBool(DeveloperFlag.Name):
Fatalf("Developer chains are ephemeral")
}
Expand Down
11 changes: 6 additions & 5 deletions core/genesis.go
Original file line number Diff line number Diff line change
Expand Up @@ -243,8 +243,8 @@ func (g *Genesis) configOrDefault(ghash common.Hash) *params.ChainConfig {
return params.RinkebyChainConfig
case ghash == params.GoerliGenesisHash:
return params.GoerliChainConfig
case ghash == params.YoloV1GenesisHash:
return params.YoloV1ChainConfig
case ghash == params.YoloV2GenesisHash:
return params.YoloV2ChainConfig
default:
return params.AllEthashProtocolChanges
}
Expand Down Expand Up @@ -380,10 +380,11 @@ func DefaultGoerliGenesisBlock() *Genesis {
}
}

func DefaultYoloV1GenesisBlock() *Genesis {
func DefaultYoloV2GenesisBlock() *Genesis {
// TODO: Update with yolov2 values + regenerate alloc data
return &Genesis{
Config: params.YoloV1ChainConfig,
Timestamp: 0x5ed754f1,
Config: params.YoloV2ChainConfig,
Timestamp: 0x5f91b932,
ExtraData: hexutil.MustDecode("0x00000000000000000000000000000000000000000000000000000000000000008a37866fd3627c9205a37c8685666f32ec07bb1b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"),
GasLimit: 0x47b760,
Difficulty: big.NewInt(1),
Expand Down
136 changes: 136 additions & 0 deletions core/state/access_list.go
Original file line number Diff line number Diff line change
@@ -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)
}
33 changes: 33 additions & 0 deletions core/state/journal.go
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,14 @@ type (
touchChange struct {
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) {
Expand Down Expand Up @@ -234,3 +242,28 @@ func (ch addPreimageChange) revert(s *StateDB) {
func (ch addPreimageChange) dirtied() *common.Address {
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
}
Loading

0 comments on commit 6487c00

Please sign in to comment.