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
  • Loading branch information
gzliudan committed Apr 22, 2024
1 parent 3b9fc51 commit 8568af0
Show file tree
Hide file tree
Showing 20 changed files with 948 additions and 47 deletions.
1 change: 1 addition & 0 deletions common/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ var BerlinBlock = big.NewInt(9999999999)
var LondonBlock = big.NewInt(9999999999)
var MergeBlock = big.NewInt(9999999999)
var ShanghaiBlock = big.NewInt(9999999999)
var Eip1559Block = big.NewInt(9999999999)

var TIPXDCXTestnet = big.NewInt(38383838)
var IsTestnet bool = false
Expand Down
7 changes: 4 additions & 3 deletions common/constants/constants.go.devnet
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,11 @@ var TIPXDCXCancellationFee = big.NewInt(225000)
var TIPXDCXCancellationFeeTestnet = big.NewInt(225000)
var TIPXDCXMinerDisable = big.NewInt(15894900)
var TIPXDCXReceiverDisable = big.NewInt(18018000)
var BerlinBlock = big.NewInt(9999999999)
var LondonBlock = big.NewInt(9999999999)
var MergeBlock = big.NewInt(9999999999)
var BerlinBlock = big.NewInt(16832700)
var LondonBlock = big.NewInt(16832700)
var MergeBlock = big.NewInt(16832700)
var ShanghaiBlock = big.NewInt(16832700)
var Eip1559Block = big.NewInt(9999999999)

var TIPXDCXTestnet = big.NewInt(0)
var IsTestnet bool = false
Expand Down
7 changes: 4 additions & 3 deletions common/constants/constants.go.testnet
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,11 @@ var TIPXDCXCancellationFee = big.NewInt(23779191)
var TIPXDCXCancellationFeeTestnet = big.NewInt(23779191)
var TIPXDCXMinerDisable = big.NewInt(61290000) // Target 31st March 2024
var TIPXDCXReceiverDisable = big.NewInt(9999999999)
var BerlinBlock = big.NewInt(9999999999)
var LondonBlock = big.NewInt(9999999999)
var MergeBlock = big.NewInt(9999999999)
var BerlinBlock = big.NewInt(61290000)
var LondonBlock = big.NewInt(61290000)
var MergeBlock = big.NewInt(61290000)
var ShanghaiBlock = big.NewInt(61290000) // Target 31st March 2024
var Eip1559Block = big.NewInt(9999999999)

var TIPXDCXTestnet = big.NewInt(23779191)
var IsTestnet bool = false
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/XinFinOrg/XDPoSChain/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)
}
30 changes: 30 additions & 0 deletions core/state/journal.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@ type journalEntry interface {

type journal []journalEntry

// length returns the current number of entries in the journal.
func (j *journal) length() int {
return len(*j)
}

type (
// Changes to the account trie.
createObjectChange struct {
Expand Down Expand Up @@ -75,6 +80,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) undo(s *StateDB) {
Expand Down Expand Up @@ -138,3 +151,20 @@ func (ch addLogChange) undo(s *StateDB) {
func (ch addPreimageChange) undo(s *StateDB) {
delete(s.preimages, ch.hash)
}

func (ch accessListAddAccountChange) undo(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 accessListAddSlotChange) undo(s *StateDB) {
s.accessList.DeleteSlot(*ch.address, *ch.slot)
}
47 changes: 47 additions & 0 deletions core/state/statedb.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,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 @@ -121,6 +124,7 @@ func New(root common.Hash, db Database) (*StateDB, error) {
stateObjectsDirty: make(map[common.Address]struct{}),
logs: make(map[common.Hash][]*types.Log),
preimages: make(map[common.Hash][]byte),
accessList: newAccessList(),
}, nil
}

Expand Down Expand Up @@ -152,6 +156,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 @@ -568,6 +573,12 @@ func (self *StateDB) Copy() *StateDB {
for hash, preimage := range self.preimages {
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()
return state
}

Expand Down Expand Up @@ -635,6 +646,7 @@ func (self *StateDB) Prepare(thash, bhash common.Hash, ti int) {
self.thash = thash
self.bhash = bhash
self.txIndex = ti
self.accessList = newAccessList()
}

// DeleteSuicides flags the suicided objects for deletion so that it
Expand Down Expand Up @@ -709,6 +721,41 @@ func (s *StateDB) Commit(deleteEmptyObjects bool) (root common.Hash, err error)
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(s.journal, 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(s.journal, accessListAddAccountChange{&addr})
}
if slotMod {
s.journal = append(s.journal, 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)
}

func (s *StateDB) GetOwner(candidate common.Address) common.Address {
slot := slotValidatorMapping["validatorsState"]
// validatorsState[_candidate].owner;
Expand Down
Loading

0 comments on commit 8568af0

Please sign in to comment.