deps: update go-ethereum to v1.9.24 (#594)

* update to go-ethereum v1.9.24

* go mod tidy

* add YoloV2 to chain config

* add accessList and implement csdb accessList funcs

* cleanup

* access list tests

* changelog

* add stateDB test

* test Copy

Co-authored-by: Federico Kunze <federico.kunze94@gmail.com>
This commit is contained in:
noot
2020-11-16 17:11:15 +01:00
committed by GitHub
co-authored by Federico Kunze
parent 73e5eb5e98
commit 2796f55b02
13 changed files with 501 additions and 17 deletions
+1 -1
View File
@@ -62,7 +62,7 @@ func (suite *KeeperTestSuite) TestBloomFilter() {
} else {
// get logs bloom from the log
bloomInt := ethtypes.LogsBloom(logs)
bloomFilter := ethtypes.BytesToBloom(bloomInt.Bytes())
bloomFilter := ethtypes.BytesToBloom(bloomInt)
suite.Require().True(ethtypes.BloomLookup(bloomFilter, contractAddress), tc.name)
suite.Require().False(ethtypes.BloomLookup(bloomFilter, ethcmn.BigToAddress(big.NewInt(2))), tc.name)
}
+134
View File
@@ -0,0 +1,134 @@
package types
import (
"github.com/ethereum/go-ethereum/common"
)
// accessList is copied from go-ethereum
// https://github.com/ethereum/go-ethereum/blob/cf856ea1ad96ac39ea477087822479b63417036a/core/state/access_list.go#L23
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
}
if idx >= len(al.slots) {
// return in case of out-of-range
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 (al *accessList) Copy() *accessList {
cp := newAccessList()
for k, v := range al.addresses {
cp.addresses[k] = v
}
cp.slots = make([]map[common.Hash]struct{}, len(al.slots))
for i, slotMap := range al.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
}
if idx >= len(al.slots) {
// return in case of out-of-range
return false, false
}
// 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)
}
+242
View File
@@ -0,0 +1,242 @@
package types
import (
"fmt"
"testing"
"github.com/stretchr/testify/suite"
ethcmn "github.com/ethereum/go-ethereum/common"
"github.com/cosmos/ethermint/crypto/ethsecp256k1"
)
type AccessListTestSuite struct {
suite.Suite
address ethcmn.Address
accessList *accessList
}
func (suite *AccessListTestSuite) SetupTest() {
privkey, err := ethsecp256k1.GenerateKey()
suite.Require().NoError(err)
suite.address = ethcmn.BytesToAddress(privkey.PubKey().Address().Bytes())
suite.accessList = newAccessList()
suite.accessList.addresses[suite.address] = 1
}
func TestAccessListTestSuite(t *testing.T) {
suite.Run(t, new(AccessListTestSuite))
}
func (suite *AccessListTestSuite) TestContainsAddress() {
found := suite.accessList.ContainsAddress(suite.address)
suite.Require().True(found)
}
func (suite *AccessListTestSuite) TestContains() {
testCases := []struct {
name string
malleate func()
expAddrPresent bool
expSlotPresent bool
}{
{"out of range", func() {}, true, false},
{
"address, no slots",
func() {
suite.accessList.addresses[suite.address] = -1
}, true, false,
},
{
"no address, no slots",
func() {
delete(suite.accessList.addresses, suite.address)
}, false, false,
},
{
"address, slot not present",
func() {
suite.accessList.addresses[suite.address] = 0
suite.accessList.slots = make([]map[ethcmn.Hash]struct{}, 1)
}, true, false,
},
{
"address, slots",
func() {
suite.accessList.addresses[suite.address] = 0
suite.accessList.slots = make([]map[ethcmn.Hash]struct{}, 1)
suite.accessList.slots[0] = make(map[ethcmn.Hash]struct{})
suite.accessList.slots[0][ethcmn.Hash{}] = struct{}{}
}, true, true,
},
}
for _, tc := range testCases {
tc.malleate()
addrPresent, slotPresent := suite.accessList.Contains(suite.address, ethcmn.Hash{})
suite.Require().Equal(tc.expAddrPresent, addrPresent, tc.name)
suite.Require().Equal(tc.expSlotPresent, slotPresent, tc.name)
}
}
func (suite *AccessListTestSuite) TestCopy() {
expAccessList := newAccessList()
testCases := []struct {
name string
malleate func()
}{
{"empty", func() {
expAccessList.slots = make([]map[ethcmn.Hash]struct{}, 0)
}},
{
"single address", func() {
expAccessList = newAccessList()
expAccessList.slots = make([]map[ethcmn.Hash]struct{}, 0)
expAccessList.addresses[suite.address] = -1
},
},
{
"single address, single slot",
func() {
expAccessList = newAccessList()
expAccessList.addresses[suite.address] = 0
expAccessList.slots = make([]map[ethcmn.Hash]struct{}, 1)
expAccessList.slots[0] = make(map[ethcmn.Hash]struct{})
expAccessList.slots[0][ethcmn.Hash{}] = struct{}{}
},
},
{
"multiple addresses, single slot each",
func() {
expAccessList = newAccessList()
expAccessList.slots = make([]map[ethcmn.Hash]struct{}, 10)
for i := 0; i < 10; i++ {
expAccessList.addresses[ethcmn.BytesToAddress([]byte(fmt.Sprintf("%d", i)))] = i
expAccessList.slots[i] = make(map[ethcmn.Hash]struct{})
expAccessList.slots[i][ethcmn.BytesToHash([]byte(fmt.Sprintf("%d", i)))] = struct{}{}
}
},
},
{
"multiple addresses, multiple slots each",
func() {
expAccessList = newAccessList()
expAccessList.slots = make([]map[ethcmn.Hash]struct{}, 10)
for i := 0; i < 10; i++ {
expAccessList.addresses[ethcmn.BytesToAddress([]byte(fmt.Sprintf("%d", i)))] = i
expAccessList.slots[i] = make(map[ethcmn.Hash]struct{})
for j := 0; j < 10; j++ {
expAccessList.slots[i][ethcmn.BytesToHash([]byte(fmt.Sprintf("%d-%d", i, j)))] = struct{}{}
}
}
},
},
}
for _, tc := range testCases {
tc.malleate()
accessList := expAccessList.Copy()
suite.Require().EqualValues(expAccessList, accessList, tc.name)
}
}
func (suite *AccessListTestSuite) TestAddAddress() {
testCases := []struct {
name string
address ethcmn.Address
ok bool
}{
{"already present", suite.address, false},
{"ok", ethcmn.Address{}, true},
}
for _, tc := range testCases {
ok := suite.accessList.AddAddress(tc.address)
suite.Require().Equal(tc.ok, ok, tc.name)
}
}
func (suite *AccessListTestSuite) TestAddSlot() {
testCases := []struct {
name string
malleate func()
expAddrChange bool
expSlotChange bool
}{
{"out of range", func() {}, false, false},
{
"address not present added, slot added",
func() {
delete(suite.accessList.addresses, suite.address)
}, true, true,
},
{
"address present, slot not present added",
func() {
suite.accessList.addresses[suite.address] = 0
suite.accessList.slots = make([]map[ethcmn.Hash]struct{}, 1)
suite.accessList.slots[0] = make(map[ethcmn.Hash]struct{})
}, false, true,
},
{
"address present, slot present",
func() {
suite.accessList.addresses[suite.address] = 0
suite.accessList.slots = make([]map[ethcmn.Hash]struct{}, 1)
suite.accessList.slots[0] = make(map[ethcmn.Hash]struct{})
suite.accessList.slots[0][ethcmn.Hash{}] = struct{}{}
}, false, false,
},
}
for _, tc := range testCases {
tc.malleate()
addrChange, slotChange := suite.accessList.AddSlot(suite.address, ethcmn.Hash{})
suite.Require().Equal(tc.expAddrChange, addrChange, tc.name)
suite.Require().Equal(tc.expSlotChange, slotChange, tc.name)
}
}
func (suite *AccessListTestSuite) TestDeleteSlot() {
testCases := []struct {
name string
malleate func()
expPanic bool
}{
{"panics, out of range", func() {}, true},
{"panics, address not found", func() {
delete(suite.accessList.addresses, suite.address)
}, true},
{
"single slot present",
func() {
suite.accessList.addresses[suite.address] = 0
suite.accessList.slots = make([]map[ethcmn.Hash]struct{}, 1)
suite.accessList.slots[0] = make(map[ethcmn.Hash]struct{})
suite.accessList.slots[0][ethcmn.Hash{}] = struct{}{}
}, false,
},
}
for _, tc := range testCases {
tc.malleate()
if tc.expPanic {
suite.Require().Panics(func() {
suite.accessList.DeleteSlot(suite.address, ethcmn.Hash{})
}, tc.name)
} else {
suite.Require().NotPanics(func() {
suite.accessList.DeleteSlot(suite.address, ethcmn.Hash{})
}, tc.name)
}
}
}
+5 -5
View File
@@ -40,7 +40,7 @@ type ChainConfig struct {
IstanbulBlock sdk.Int `json:"istanbul_block" yaml:"istanbul_block"` // Istanbul switch block (< 0 no fork, 0 = already on istanbul)
MuirGlacierBlock sdk.Int `json:"muir_glacier_block" yaml:"muir_glacier_block"` // Eip-2384 (bomb delay) switch block (< 0 no fork, 0 = already activated)
YoloV1Block sdk.Int `json:"yoloV1_block" yaml:"yoloV1_block"` // YOLO v1: https://github.com/ethereum/EIPs/pull/2657 (Ephemeral testnet)
YoloV2Block sdk.Int `json:"yoloV2_block" yaml:"yoloV2_block"` // YOLO v1: https://github.com/ethereum/EIPs/pull/2657 (Ephemeral testnet)
EWASMBlock sdk.Int `json:"ewasm_block" yaml:"ewasm_block"` // EWASM switch block (< 0 no fork, 0 = already activated)
}
@@ -61,7 +61,7 @@ func (cc ChainConfig) EthereumConfig(chainID *big.Int) *params.ChainConfig {
PetersburgBlock: getBlockValue(cc.PetersburgBlock),
IstanbulBlock: getBlockValue(cc.IstanbulBlock),
MuirGlacierBlock: getBlockValue(cc.MuirGlacierBlock),
YoloV1Block: getBlockValue(cc.YoloV1Block),
YoloV2Block: getBlockValue(cc.YoloV2Block),
EWASMBlock: getBlockValue(cc.EWASMBlock),
}
}
@@ -87,7 +87,7 @@ func DefaultChainConfig() ChainConfig {
PetersburgBlock: sdk.ZeroInt(),
IstanbulBlock: sdk.NewInt(-1),
MuirGlacierBlock: sdk.NewInt(-1),
YoloV1Block: sdk.NewInt(-1),
YoloV2Block: sdk.NewInt(-1),
EWASMBlock: sdk.NewInt(-1),
}
}
@@ -136,8 +136,8 @@ func (cc ChainConfig) Validate() error {
if err := validateBlock(cc.MuirGlacierBlock); err != nil {
return sdkerrors.Wrap(err, "muirGlacierBlock")
}
if err := validateBlock(cc.YoloV1Block); err != nil {
return sdkerrors.Wrap(err, "yoloV1Block")
if err := validateBlock(cc.YoloV2Block); err != nil {
return sdkerrors.Wrap(err, "yoloV2Block")
}
if err := validateBlock(cc.EWASMBlock); err != nil {
return sdkerrors.Wrap(err, "eWASMBlock")
+5 -5
View File
@@ -33,7 +33,7 @@ func TestChainConfigValidate(t *testing.T) {
PetersburgBlock: sdk.OneInt(),
IstanbulBlock: sdk.OneInt(),
MuirGlacierBlock: sdk.OneInt(),
YoloV1Block: sdk.OneInt(),
YoloV2Block: sdk.OneInt(),
EWASMBlock: sdk.OneInt(),
},
false,
@@ -176,7 +176,7 @@ func TestChainConfigValidate(t *testing.T) {
true,
},
{
"invalid YoloV1Block",
"invalid YoloV2Block",
ChainConfig{
HomesteadBlock: sdk.OneInt(),
DAOForkBlock: sdk.OneInt(),
@@ -189,7 +189,7 @@ func TestChainConfigValidate(t *testing.T) {
PetersburgBlock: sdk.OneInt(),
IstanbulBlock: sdk.OneInt(),
MuirGlacierBlock: sdk.OneInt(),
YoloV1Block: sdk.Int{},
YoloV2Block: sdk.Int{},
},
true,
},
@@ -207,7 +207,7 @@ func TestChainConfigValidate(t *testing.T) {
PetersburgBlock: sdk.OneInt(),
IstanbulBlock: sdk.OneInt(),
MuirGlacierBlock: sdk.OneInt(),
YoloV1Block: sdk.OneInt(),
YoloV2Block: sdk.OneInt(),
EWASMBlock: sdk.Int{},
},
true,
@@ -238,7 +238,7 @@ constantinople_block: "0"
petersburg_block: "0"
istanbul_block: "-1"
muir_glacier_block: "-1"
yoloV1_block: "-1"
yoloV2_block: "-1"
ewasm_block: "-1"
`
require.Equal(t, configStr, DefaultChainConfig().String())
+33
View File
@@ -186,10 +186,18 @@ type (
// prev bool
// prevDirty bool
}
accessListAddAccountChange struct {
address *ethcmn.Address
}
accessListAddSlotChange struct {
address *ethcmn.Address
slot *ethcmn.Hash
}
)
func (ch createObjectChange) revert(s *CommitStateDB) {
delete(s.stateObjectsDirty, *ch.account)
idx, exists := s.addressToObjectIndex[*ch.account]
if !exists {
// perform no-op
@@ -342,3 +350,28 @@ func (ch addPreimageChange) revert(s *CommitStateDB) {
func (ch addPreimageChange) dirtied() *ethcmn.Address {
return nil
}
func (ch accessListAddAccountChange) revert(s *CommitStateDB) {
/*
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() *ethcmn.Address {
return nil
}
func (ch accessListAddSlotChange) revert(s *CommitStateDB) {
s.accessList.DeleteSlot(*ch.address, *ch.slot)
}
func (ch accessListAddSlotChange) dirtied() *ethcmn.Address {
return nil
}
+6
View File
@@ -220,6 +220,12 @@ func (suite *JournalTestSuite) TestJournal_append_revert() {
txhash: ethcmn.BytesToHash([]byte("txhash")),
},
},
{
"accessListAddAccountChange",
accessListAddAccountChange{
address: &suite.address,
},
},
}
var dirtyCount int
for i, tc := range testCases {
+1 -1
View File
@@ -159,7 +159,7 @@ func (st StateTransition) TransitionDb(ctx sdk.Context, config ChainConfig) (*Ex
return nil, err
}
bloomInt = ethtypes.LogsBloom(logs)
bloomInt = big.NewInt(0).SetBytes(ethtypes.LogsBloom(logs))
bloomFilter = ethtypes.BytesToBloom(bloomInt.Bytes())
}
+39
View File
@@ -77,6 +77,9 @@ type CommitStateDB struct {
validRevisions []revision
nextRevisionID int
// Per-transaction access list
accessList *accessList
// mutex for state deep copying
lock sync.Mutex
}
@@ -100,6 +103,7 @@ func NewCommitStateDB(
preimages: []preimageEntry{},
hashToPreimageIndex: make(map[ethcmn.Hash]int),
journal: newJournal(),
accessList: newAccessList(),
}
}
@@ -243,6 +247,41 @@ func (csdb *CommitStateDB) SubRefund(gas uint64) {
csdb.refund -= gas
}
// AddAddressToAccessList adds the given address to the access list
func (csdb *CommitStateDB) AddAddressToAccessList(addr ethcmn.Address) {
if csdb.accessList.AddAddress(addr) {
csdb.journal.append(accessListAddAccountChange{&addr})
}
}
// AddSlotToAccessList adds the given (address, slot)-tuple to the access list
func (csdb *CommitStateDB) AddSlotToAccessList(addr ethcmn.Address, slot ethcmn.Hash) {
addrMod, slotMod := csdb.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
csdb.journal.append(accessListAddAccountChange{&addr})
}
if slotMod {
csdb.journal.append(accessListAddSlotChange{
address: &addr,
slot: &slot,
})
}
}
// AddressInAccessList returns true if the given address is in the access list.
func (csdb *CommitStateDB) AddressInAccessList(addr ethcmn.Address) bool {
return csdb.accessList.ContainsAddress(addr)
}
// SlotInAccessList returns true if the given (address, slot)-tuple is in the access list.
func (csdb *CommitStateDB) SlotInAccessList(addr ethcmn.Address, slot ethcmn.Hash) (bool, bool) {
return csdb.accessList.Contains(addr, slot)
}
// ----------------------------------------------------------------------------
// Getters
// ----------------------------------------------------------------------------
+20 -2
View File
@@ -113,8 +113,8 @@ func (suite *StateDBTestSuite) TestBloomFilter() {
}
} else {
// get logs bloom from the log
bloomInt := ethtypes.LogsBloom(logs)
bloomFilter := ethtypes.BytesToBloom(bloomInt.Bytes())
bloomBytes := ethtypes.LogsBloom(logs)
bloomFilter := ethtypes.BytesToBloom(bloomBytes)
suite.Require().True(ethtypes.BloomLookup(bloomFilter, contractAddress), tc.name)
suite.Require().False(ethtypes.BloomLookup(bloomFilter, ethcmn.BigToAddress(big.NewInt(2))), tc.name)
}
@@ -695,3 +695,21 @@ func (suite *StateDBTestSuite) TestCommitStateDB_ForEachStorage() {
storage = types.Storage{}
}
}
func (suite *StateDBTestSuite) TestCommitStateDB_AccessList() {
addr := ethcmn.Address([20]byte{77})
hash := ethcmn.Hash([32]byte{99})
suite.Require().False(suite.stateDB.AddressInAccessList(addr))
suite.stateDB.AddAddressToAccessList(addr)
suite.Require().True(suite.stateDB.AddressInAccessList(addr))
addrIn, slotIn := suite.stateDB.SlotInAccessList(addr, hash)
suite.Require().True(addrIn)
suite.Require().False(slotIn)
suite.stateDB.AddSlotToAccessList(addr, hash)
addrIn, slotIn = suite.stateDB.SlotInAccessList(addr, hash)
suite.Require().True(addrIn)
suite.Require().True(slotIn)
}