8000 vm: bringing gascost change eips by yoomee1313 · Pull Request #1705 · klaytn/klaytn · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content
This repository was archived by the owner on Aug 19, 2024. It is now read-only.

vm: bringing gascost change eips #1705

Merged
merged 8 commits into from
Nov 22, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
140 changes: 140 additions & 0 deletions blockchain/state/access_list.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
// Modifications Copyright 2022 The klaytn Authors
// 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/>.
//
// This file is derived from core/state/access_list.go (2022/11/15).
// Modified and improved for the klaytn development.

package state

import (
"github.com/klaytn/klaytn/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 blockchain/state/journal.go
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,14 @@ type (
prev bool
prevDirty bool
}
// 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 @@ -240,3 +248,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
}
84 changes: 82 additions & 2 deletions blockchain/state/statedb.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,6 @@ import (
"sync/atomic"
"time"

"github.com/klaytn/klaytn/snapshot"

"github.com/klaytn/klaytn/blockchain/types"
"github.com/klaytn/klaytn/blockchain/types/account"
"github.com/klaytn/klaytn/blockchain/types/accountkey"
Expand All @@ -37,6 +35,7 @@ import (
"github.com/klaytn/klaytn/log"
"github.com/klaytn/klaytn/params"
"github.com/klaytn/klaytn/rlp"
"github.com/klaytn/klaytn/snapshot"
"github.com/klaytn/klaytn/storage/statedb"
)

Expand Down Expand Up @@ -92,6 +91,9 @@ type StateDB struct {

preimages map[common.Hash][]byte

// Per-transaction access list
accessList *accessList

// Journal of state modifications. This is the backbone of
// Snapshot and RevertToSnapshot.
journal *journal
Expand Down Expand Up @@ -130,6 +132,7 @@ func New(root common.Hash, db Database, snaps *snapshot.Tree) (*StateDB, error)
logs: make(map[common.Hash][]*types.Log),
preimages: make(map[common.Hash][]byte),
journal: newJournal(),
accessList: newAccessList(),
}
if sdb.snaps != nil {
if sdb.snap = sdb.snaps.Snapshot(root); sdb.snap != nil {
Expand Down Expand Up @@ -207,6 +210,7 @@ func (self *StateDB) Reset(root common.Hash) error {
self.logSize = 0
self.preimages = make(map[common.Hash][]byte)
self.clearJournalAndRefund()
self.accessList = newAccessList()
return nil
}

Expand Down Expand Up @@ -836,6 +840,13 @@ func (self *StateDB) Copy() *StateDB {
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 = self.accessList.Copy()

if self.snaps != nil {
// In order for the miner to be able to use and make additions
// to the snapshot tree, we need to copy that aswell.
Expand Down Expand Up @@ -1092,3 +1103,72 @@ func (s *StateDB) GetContractStorageRoot(contractAddr common.Address) (common.Ha
}
return contract.GetStorageRoot(), nil
}

// PrepareAccessList handles the preparatory steps for executing a state transition with
// regards to EIP-2929:
//
// - Add sender to access list (2929)
// - Add feepayer to access list (only for klaytn)
// - Add destination to access list (2929)
// - Add precompiles to access list (2929)
//
// This method should only be called if Yolov3/Berlin/2929+2930 is applicable at the current number.
func (s *StateDB) PrepareAccessList(sender common.Address, feepayer common.Address, dst *common.Address, precompiles []common.Address) {
// Clear out any leftover from previous executions
s.accessList = newAccessList()

s.AddAddressToAccessList(sender)
if !common.EmptyAddress(feepayer) && sender != feepayer {
s.AddAddressToAccessList(feepayer)
}
if dst != nil {
s.AddAddressToAccessList(*dst)
// If it's a create-tx, the destination will be added inside evm.create
}
for _, addr := range precompiles {
s.AddAddressToAccessList(addr)
}
// Uncommented below code because we do not support optional accessList yet (written at eip-2930).
// Optional accessList is the accessList mentioned through tx args.
//for _, el := range list {
// s.AddAddressToAccessList(el.Address)
// for _, key := range el.StorageKeys {
// s.AddSlotToAccessList(el.Address, key)
// }
//}
}

// 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)
}
Loading
0