evm: use TransientStore for AccessList (#75)

* evm: use transient store for access list

* evm: remove address and slot access list mappings

* update tests

* update

* changelog

* update types
This commit is contained in:
Federico Kunze
2021-06-07 06:05:49 -04:00
committed by GitHub
parent c78d720532
commit e639cb4a82
10 changed files with 146 additions and 457 deletions
+1 -8
View File
@@ -45,12 +45,6 @@ type Keeper struct {
// Ethermint concrete implementation on the EVM StateDB interface
CommitStateDB *types.CommitStateDB
// Per-transaction access list
// See EIP-2930 for more info: https://eips.ethereum.org/EIPS/eip-2930
// TODO: (@fedekunze) for how long should we persist the entries in the access list?
// same block (i.e Transient Store)? 2 or more (KVStore with module Parameter which resets the state after that window)?
accessList *types.AccessListMappings
// hash header for the current height. Reset during abci.RequestBeginBlock
headerHash common.Hash
}
@@ -73,8 +67,7 @@ func NewKeeper(
bankKeeper: bankKeeper,
storeKey: storeKey,
transientKey: transientKey,
CommitStateDB: types.NewCommitStateDB(sdk.Context{}, storeKey, paramSpace, ak, bankKeeper),
accessList: types.NewAccessListMappings(),
CommitStateDB: types.NewCommitStateDB(sdk.Context{}, storeKey, transientKey, paramSpace, ak, bankKeeper),
}
}
+26 -15
View File
@@ -493,37 +493,48 @@ func (k *Keeper) PrepareAccessList(sender common.Address, dest *common.Address,
}
}
// AddressInAccessList returns true if the address is registered on the access list map.
// AddressInAccessList returns true if the address is registered on the transient store.
func (k *Keeper) AddressInAccessList(addr common.Address) bool {
return k.accessList.ContainsAddress(addr)
ts := prefix.NewStore(k.ctx.TransientStore(k.transientKey), types.KeyPrefixTransientAccessListAddress)
return ts.Has(addr.Bytes())
}
// SlotInAccessList checks if the address and the slots are registered in the transient store
func (k *Keeper) SlotInAccessList(addr common.Address, slot common.Hash) (addressOk bool, slotOk bool) {
return k.accessList.Contains(addr, slot)
addressOk = k.AddressInAccessList(addr)
slotOk = k.addressSlotInAccessList(addr, slot)
return addressOk, slotOk
}
// AddAddressToAccessList adds the given address to the access list. This operation is safe to perform
// even if the feature/fork is not active yet
// addressSlotInAccessList returns true if the address's slot is registered on the transient store.
func (k *Keeper) addressSlotInAccessList(addr common.Address, slot common.Hash) bool {
ts := prefix.NewStore(k.ctx.TransientStore(k.transientKey), types.KeyPrefixTransientAccessListSlot)
key := append(addr.Bytes(), slot.Bytes()...)
return ts.Has(key)
}
// AddAddressToAccessList adds the given address to the access list. If the address is already
// in the access list, this function performs a no-op.
func (k *Keeper) AddAddressToAccessList(addr common.Address) {
// NOTE: only update the access list during DeliverTx
if k.ctx.IsCheckTx() || k.ctx.IsReCheckTx() {
if k.AddressInAccessList(addr) {
return
}
// NOTE: ignore change return bool because we don't have to keep a journal for state changes
_ = k.accessList.AddAddress(addr)
ts := prefix.NewStore(k.ctx.TransientStore(k.transientKey), types.KeyPrefixTransientAccessListAddress)
ts.Set(addr.Bytes(), []byte{0x1})
}
// 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 adds the given (address, slot) to the access list. If the address and slot are
// already in the access list, this function performs a no-op.
func (k *Keeper) AddSlotToAccessList(addr common.Address, slot common.Hash) {
// NOTE: only update the access list during DeliverTx
if k.ctx.IsCheckTx() || k.ctx.IsReCheckTx() {
k.AddAddressToAccessList(addr)
if k.addressSlotInAccessList(addr, slot) {
return
}
// NOTE: ignore change return booleans because we don't have to keep a journal for state changes
_, _ = k.accessList.AddSlot(addr, slot)
ts := prefix.NewStore(k.ctx.TransientStore(k.transientKey), types.KeyPrefixTransientAccessListSlot)
key := append(addr.Bytes(), slot.Bytes()...)
ts.Set(key, []byte{0x1})
}
// ----------------------------------------------------------------------------
+43 -3
View File
@@ -506,7 +506,7 @@ func (suite *KeeperTestSuite) TestAddLog() {
}
}
func (suite *KeeperTestSuite) TestAccessList() {
func (suite *KeeperTestSuite) TestPrepareAccessList() {
dest := tests.GenerateAddress()
precompiles := []common.Address{tests.GenerateAddress(), tests.GenerateAddress()}
accesses := ethtypes.AccessList{
@@ -526,12 +526,52 @@ func (suite *KeeperTestSuite) TestAccessList() {
for _, access := range accesses {
for _, key := range access.StorageKeys {
addrOK, slotOK := suite.app.EvmKeeper.SlotInAccessList(access.Address, key)
suite.Require().True(addrOK)
suite.Require().True(slotOK)
suite.Require().True(addrOK, access.Address.Hex())
suite.Require().True(slotOK, key.Hex())
}
}
}
func (suite *KeeperTestSuite) TestAddAddressToAccessList() {
testCases := []struct {
name string
addr common.Address
}{
{"new address", suite.address},
{"existing address", suite.address},
}
for _, tc := range testCases {
suite.Run(tc.name, func() {
suite.app.EvmKeeper.AddAddressToAccessList(tc.addr)
addrOk := suite.app.EvmKeeper.AddressInAccessList(tc.addr)
suite.Require().True(addrOk, tc.addr.Hex())
})
}
}
func (suite *KeeperTestSuite) AddSlotToAccessList() {
testCases := []struct {
name string
addr common.Address
slot common.Hash
}{
{"new address and slot (1)", tests.GenerateAddress(), common.BytesToHash([]byte("hash"))},
{"new address and slot (2)", suite.address, common.Hash{}},
{"existing address and slot", suite.address, common.Hash{}},
{"existing address, new slot", suite.address, common.BytesToHash([]byte("hash"))},
}
for _, tc := range testCases {
suite.Run(tc.name, func() {
suite.app.EvmKeeper.AddSlotToAccessList(tc.addr, tc.slot)
addrOk, slotOk := suite.app.EvmKeeper.SlotInAccessList(tc.addr, tc.slot)
suite.Require().True(addrOk, tc.addr.Hex())
suite.Require().True(slotOk, tc.slot.Hex())
})
}
}
func (suite *KeeperTestSuite) TestForEachStorage() {
var storage types.Storage