Skip to content

Commit

Permalink
Merge branch 'ramtin/evm-offchain-part2' of github.com:onflow/flow-go…
Browse files Browse the repository at this point in the history
… into ramtin/evm-offchain-part2
  • Loading branch information
ramtinms committed Oct 21, 2024
2 parents cf13be4 + deeba2e commit 76758aa
Show file tree
Hide file tree
Showing 9 changed files with 605 additions and 28 deletions.
73 changes: 47 additions & 26 deletions fvm/evm/emulator/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,36 +48,50 @@ func (c *Config) ChainRules() gethParams.Rules {
c.BlockContext.Time)
}

// DefaultChainConfig is the default chain config used by the emulator
// considers majority of EVM upgrades (e.g. Cancun update) are already applied
// PreviewNetChainConfig is the chain config used by the previewnet
var PreviewNetChainConfig = MakeChainConfig(types.FlowEVMPreviewNetChainID)

// PreviewNetChainConfig is the chain config used by the testnet
var TestNetChainConfig = MakeChainConfig(types.FlowEVMTestNetChainID)

// MainNetChainConfig is the chain config used by the mainnet
var MainNetChainConfig = MakeChainConfig(types.FlowEVMMainNetChainID)

// DefaultChainConfig is the chain config used by the emulator
var DefaultChainConfig = PreviewNetChainConfig

// MakeChainConfig constructs a chain config
// it considers majority of EVM upgrades (e.g. Cancun update) are already applied
// This has been done through setting the height of these changes
// to zero nad setting the time for some other changes to zero
// For the future changes of EVM, we need to update the EVM go mod version
// and set a proper height for the specific release based on the Flow EVM heights
// so it could gets activated at a desired time.
var DefaultChainConfig = &gethParams.ChainConfig{
ChainID: types.FlowEVMPreviewNetChainID,

// Fork scheduling based on block heights
HomesteadBlock: bigZero,
DAOForkBlock: bigZero,
DAOForkSupport: false,
EIP150Block: bigZero,
EIP155Block: bigZero,
EIP158Block: bigZero,
ByzantiumBlock: bigZero, // already on Byzantium
ConstantinopleBlock: bigZero, // already on Constantinople
PetersburgBlock: bigZero, // already on Petersburg
IstanbulBlock: bigZero, // already on Istanbul
BerlinBlock: bigZero, // already on Berlin
LondonBlock: bigZero, // already on London
MuirGlacierBlock: bigZero, // already on MuirGlacier

// Fork scheduling based on timestamps
ShanghaiTime: &zero, // already on Shanghai
CancunTime: &zero, // already on Cancun
PragueTime: nil, // not on Prague
VerkleTime: nil, // not on Verkle
func MakeChainConfig(chainID *big.Int) *gethParams.ChainConfig {
return &gethParams.ChainConfig{
ChainID: chainID,

// Fork scheduling based on block heights
HomesteadBlock: bigZero,
DAOForkBlock: bigZero,
DAOForkSupport: false,
EIP150Block: bigZero,
EIP155Block: bigZero,
EIP158Block: bigZero,
ByzantiumBlock: bigZero, // already on Byzantium
ConstantinopleBlock: bigZero, // already on Constantinople
PetersburgBlock: bigZero, // already on Petersburg
IstanbulBlock: bigZero, // already on Istanbul
BerlinBlock: bigZero, // already on Berlin
LondonBlock: bigZero, // already on London
MuirGlacierBlock: bigZero, // already on MuirGlacier

// Fork scheduling based on timestamps
ShanghaiTime: &zero, // already on Shanghai
CancunTime: &zero, // already on Cancun
PragueTime: nil, // not on Prague
VerkleTime: nil, // not on Verkle
}
}

// Default config supports the dynamic fee structure (EIP-1559)
Expand Down Expand Up @@ -123,7 +137,14 @@ type Option func(*Config) *Config
// WithChainID sets the evm chain ID
func WithChainID(chainID *big.Int) Option {
return func(c *Config) *Config {
c.ChainConfig.ChainID = chainID
switch chainID.Uint64() {
case types.FlowEVMPreviewNetChainIDInUInt64:
c.ChainConfig = PreviewNetChainConfig
case types.FlowEVMTestNetChainIDInUInt64:
c.ChainConfig = TestNetChainConfig
case types.FlowEVMMainNetChainIDInUInt64:
c.ChainConfig = MainNetChainConfig
}
return c
}
}
Expand Down
6 changes: 6 additions & 0 deletions fvm/evm/emulator/state/account.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,16 @@ func NewAccount(
}
}

// HasCode returns true if account has code
func (a *Account) HasCode() bool {
return a.CodeHash != gethTypes.EmptyCodeHash
}

// HasStoredValues returns true if account has stored values
func (a *Account) HasStoredValues() bool {
return len(a.CollectionID) != 0
}

// Encode encodes the account
func (a *Account) Encode() ([]byte, error) {
return rlp.EncodeToBytes(a)
Expand Down
144 changes: 142 additions & 2 deletions fvm/evm/emulator/state/base.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ const (
CodesStorageIDKey = "CodesStorageIDKey"
)

var EmptyHash = gethCommon.Hash{}

// BaseView implements a types.BaseView
// it acts as the base layer of state queries for the stateDB
// it stores accounts, codes and storage slots.
Expand Down Expand Up @@ -389,6 +391,65 @@ func (v *BaseView) NumberOfAccounts() uint64 {
return v.accounts.Size()
}

// AccountIterator returns an account iterator
//
// Warning! this is an expensive operation and should only be used
// for testing and exporting state operations, while no changes
// are applied to accounts. Note that the iteration order is not guaranteed.
func (v *BaseView) AccountIterator() (*AccountIterator, error) {
itr, err := v.accounts.ReadOnlyIterator()
if err != nil {
return nil, err
}
return &AccountIterator{colIterator: itr}, nil
}

// CodeIterator returns a code iterator
//
// Warning! this is an expensive operation and should only be used
// for testing and exporting state operations, while no changes
// are applied to codes. Note that the iteration order is not guaranteed.
func (v *BaseView) CodeIterator() (*CodeIterator, error) {
itr, err := v.codes.ReadOnlyIterator()
if err != nil {
return nil, err
}
return &CodeIterator{colIterator: itr}, nil
}

// AccountStorageIterator returns an account storage iterator
// for the given address
//
// Warning! this is an expensive operation and should only be used
// for testing and exporting state operations, while no changes
// are applied to accounts. Note that the iteration order is not guaranteed.
func (v *BaseView) AccountStorageIterator(
addr gethCommon.Address,
) (*AccountStorageIterator, error) {
acc, err := v.getAccount(addr)
if err != nil {
return nil, err
}
if acc == nil || !acc.HasStoredValues() {
return nil, fmt.Errorf("account %s has no stored value", addr.String())
}
col, found := v.slots[addr]
if !found {
col, err = v.collectionProvider.CollectionByID(acc.CollectionID)
if err != nil {
return nil, fmt.Errorf("failed to load storage collection for account %s: %w", addr.String(), err)
}
}
itr, err := col.ReadOnlyIterator()
if err != nil {
return nil, err
}
return &AccountStorageIterator{
address: addr,
colIterator: itr,
}, nil
}

func (v *BaseView) fetchOrCreateCollection(path string) (collection *Collection, created bool, error error) {
collectionID, err := v.ledger.GetValue(v.rootAddress[:], []byte(path))
if err != nil {
Expand Down Expand Up @@ -592,8 +653,7 @@ func (v *BaseView) storeSlot(sk types.SlotAddress, data gethCommon.Hash) error {
return err
}

emptyValue := gethCommon.Hash{}
if data == emptyValue {
if data == EmptyHash {
delete(v.cachedSlots, sk)
return col.Remove(sk.Key.Bytes())
}
Expand Down Expand Up @@ -631,3 +691,83 @@ func (v *BaseView) getSlotCollection(acc *Account) (*Collection, error) {
}
return col, nil
}

// AccountIterator iterates over accounts
type AccountIterator struct {
colIterator *CollectionIterator
}

// Next returns the next account
// if no more accounts next would return nil (no error)
func (ai *AccountIterator) Next() (*Account, error) {
_, value, err := ai.colIterator.Next()
if err != nil {
return nil, fmt.Errorf("account iteration failed: %w", err)
}
return DecodeAccount(value)
}

// CodeIterator iterates over codes stored in EVM
// code storage only stores unique codes
type CodeIterator struct {
colIterator *CollectionIterator
}

// Next returns the next code
// if no more codes, it return nil (no error)
func (ci *CodeIterator) Next() (
*CodeInContext,
error,
) {
ch, encodedCC, err := ci.colIterator.Next()
if err != nil {
return nil, fmt.Errorf("code iteration failed: %w", err)
}
// no more keys
if ch == nil {
return nil, nil
}
if len(encodedCC) == 0 {
return nil,
fmt.Errorf("encoded code container is empty (code hash: %x)", ch)
}

codeCont, err := CodeContainerFromEncoded(encodedCC)
if err != nil {
return nil, fmt.Errorf("code container decoding failed (code hash: %x)", ch)

}
return &CodeInContext{
Hash: gethCommon.BytesToHash(ch),
Code: codeCont.Code(),
RefCounts: codeCont.RefCount(),
}, nil
}

// AccountStorageIterator iterates over slots of an account
type AccountStorageIterator struct {
address gethCommon.Address
colIterator *CollectionIterator
}

// Next returns the next slot in the storage
// if no more keys, it returns nil (no error)
func (asi *AccountStorageIterator) Next() (
*types.SlotEntry,
error,
) {
k, v, err := asi.colIterator.Next()
if err != nil {
return nil, fmt.Errorf("account storage iteration failed: %w", err)
}
// no more keys
if k == nil {
return nil, nil
}
return &types.SlotEntry{
Address: asi.address,
Key: gethCommon.BytesToHash(k),
Value: gethCommon.BytesToHash(v),
}, nil

}
Loading

0 comments on commit 76758aa

Please sign in to comment.