Skip to content
This repository has been archived by the owner on Jun 17, 2022. It is now read-only.

Separate app store #547

Open
wants to merge 5 commits into
base: develop2
Choose a base branch
from
Open
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
90 changes: 88 additions & 2 deletions app/apply_genesis.go
Original file line number Diff line number Diff line change
@@ -1,20 +1,75 @@
package app

import (
"fmt"
"math/big"
"reflect"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/log"
lru "github.com/hashicorp/golang-lru"

"github.com/Fantom-foundation/go-lachesis/evmcore"
"github.com/Fantom-foundation/go-lachesis/inter"
"github.com/Fantom-foundation/go-lachesis/inter/sfctype"
"github.com/Fantom-foundation/go-lachesis/lachesis"
)

// ApplyGenesis writes initial state.
func (s *Store) ApplyGenesis(net *lachesis.Config) (evmBlock *evmcore.EvmBlock, err error) {
evmBlock, err = evmcore.ApplyGenesis(s.table.Evm, net)
func (s *Store) ApplyGenesis(net *lachesis.Config) (stateRoot common.Hash, isNew bool, err error) {
s.migrate()

var stored *inter.Block
// TODO: enable when implemented .GetBlock()
/*
stored = s.GetBlock(0)
*/
if stored != nil {
isNew = false
stateRoot, err = calcGenesis(net)
if err != nil {
return
}

if stateRoot != stored.Root {
err = fmt.Errorf("database contains incompatible state hash (have %s, new %s)", stored.Root, stateRoot)
}

return
}

// if we'here, then it's first time genesis is applied
isNew = true
stateRoot, err = s.applyGenesis(net)
if err != nil {
return
}

// init stats
s.SetEpochStats(0, &sfctype.EpochStats{
Start: net.Genesis.Time,
End: net.Genesis.Time,
TotalFee: new(big.Int),
})
s.SetDirtyEpochStats(&sfctype.EpochStats{
Start: net.Genesis.Time,
TotalFee: new(big.Int),
})

// TODO: enable when implemented .GetBlock()
/*
block := evmcore.GenesisBlock(net, stateRoot)
info := blockInfo(&block.EvmHeader)
s.SetBlock(info)


s.SetCheckpoint(Checkpoint{
BlockN: 0,
EpochN: 1,
EpochBlock: 0,
EpochStart: net.Genesis.Time,
})
*/
// calc total pre-minted supply
totalSupply := big.NewInt(0)
for _, account := range net.Genesis.Alloc.Accounts {
Expand All @@ -39,5 +94,36 @@ func (s *Store) ApplyGenesis(net *lachesis.Config) (evmBlock *evmcore.EvmBlock,
}
s.SetEpochValidators(1, validatorsArr)

// clear caches because tables can be filled direct by migrations
s.purgeCaches()
s.FlushState()

return
}

// calcGenesis calcs hash of genesis state.
func calcGenesis(net *lachesis.Config) (common.Hash, error) {
s := NewMemStore()
defer s.Close()

s.Log.SetHandler(log.DiscardHandler())

return s.applyGenesis(net)
}

func (s *Store) applyGenesis(net *lachesis.Config) (stateRoot common.Hash, err error) {
stateRoot, err = evmcore.ApplyGenesis(s.table.Evm, net)
if err != nil {
return
}

return
}

func (s *Store) purgeCaches() {
caches := reflect.ValueOf(s.cache)
for i := caches.NumField() - 1; i >= 0; i-- {
c := caches.Field(i).Interface().(*lru.Cache)
c.Purge()
}
}
8 changes: 6 additions & 2 deletions app/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,23 +9,27 @@ type (
StakersCacheSize int
// Cache size for Delegations.
DelegationsCacheSize int
// Cache size for EpochStats.
EpochStatsCacheSize int
}
)

// DefaultStoreConfig for product.
func DefaultStoreConfig() StoreConfig {
return StoreConfig{
ReceiptsCacheSize: 100,
DelegationsCacheSize: 4000,
StakersCacheSize: 4000,
DelegationsCacheSize: 4000,
EpochStatsCacheSize: 100,
}
}

// LiteStoreConfig is for tests or inmemory.
func LiteStoreConfig() StoreConfig {
return StoreConfig{
ReceiptsCacheSize: 100,
DelegationsCacheSize: 400,
StakersCacheSize: 400,
DelegationsCacheSize: 400,
EpochStatsCacheSize: 100,
}
}
51 changes: 42 additions & 9 deletions app/store.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import (
lru "github.com/hashicorp/golang-lru"

"github.com/Fantom-foundation/go-lachesis/kvdb"
"github.com/Fantom-foundation/go-lachesis/kvdb/flushable"
"github.com/Fantom-foundation/go-lachesis/kvdb/memorydb"
"github.com/Fantom-foundation/go-lachesis/kvdb/nokeyiserr"
"github.com/Fantom-foundation/go-lachesis/kvdb/table"
"github.com/Fantom-foundation/go-lachesis/logger"
Expand All @@ -20,10 +22,16 @@ import (

// Store is a node persistent storage working over physical key-value database.
type Store struct {
dbs *flushable.SyncedPool
cfg StoreConfig

mainDb kvdb.KeyValueStore
table struct {
Version kvdb.KeyValueStore `table:"_"`

// general economy tables
EpochStats kvdb.KeyValueStore `table:"E"`

// score economy tables
ActiveValidationScore kvdb.KeyValueStore `table:"V"`
DirtyValidationScore kvdb.KeyValueStore `table:"v"`
Expand All @@ -39,9 +47,6 @@ type Store struct {
AddressLastTxTime kvdb.KeyValueStore `table:"X"`
TotalPoiFee kvdb.KeyValueStore `table:"U"`

// gas power economy tables
GasPowerRefund kvdb.KeyValueStore `table:"R"`

// SFC-related economy tables
Validators kvdb.KeyValueStore `table:"1"`
Stakers kvdb.KeyValueStore `table:"2"`
Expand All @@ -55,12 +60,17 @@ type Store struct {
StakerOldRewards kvdb.KeyValueStore `table:"7"`
StakerDelegationsOldRewards kvdb.KeyValueStore `table:"8"`

// internal tables
ForEvmTable kvdb.KeyValueStore `table:"M"`
ForEvmLogsTable kvdb.KeyValueStore `table:"L"`

Evm ethdb.Database
EvmState state.Database
EvmLogs *topicsdb.Index
}

cache struct {
EpochStats *lru.Cache `cache:"-"` // store by value
Receipts *lru.Cache `cache:"-"` // store by value
Validators *lru.Cache `cache:"-"` // store by pointer
Stakers *lru.Cache `cache:"-"` // store by pointer
Expand All @@ -75,36 +85,59 @@ type Store struct {
logger.Instance
}

// NewMemStore creates store over memory map.
func NewMemStore() *Store {
mems := memorydb.NewProducer("")
dbs := flushable.NewSyncedPool(mems)
cfg := LiteStoreConfig()

return NewStore(dbs, cfg)
}

// NewStore creates store over key-value db.
func NewStore(mainDb kvdb.KeyValueStore, cfg StoreConfig) *Store {
func NewStore(dbs *flushable.SyncedPool, cfg StoreConfig) *Store {
s := &Store{
dbs: dbs,
cfg: cfg,
mainDb: mainDb,
mainDb: dbs.GetDb("app-main"),
Instance: logger.MakeInstance(),
}

table.MigrateTables(&s.table, s.mainDb)

evmTable := nokeyiserr.Wrap(table.New(s.mainDb, []byte("M"))) // ETH expects that "not found" is an error
evmTable := nokeyiserr.Wrap(s.table.ForEvmTable) // ETH expects that "not found" is an error
s.table.Evm = rawdb.NewDatabase(evmTable)
s.table.EvmState = state.NewDatabaseWithCache(s.table.Evm, 16, "")
s.table.EvmLogs = topicsdb.New(table.New(s.mainDb, []byte("L")))
s.table.EvmLogs = topicsdb.New(s.table.ForEvmLogsTable)

s.initCache()

return s
}

func (s *Store) initCache() {
s.cache.EpochStats = s.makeCache(s.cfg.EpochStatsCacheSize)
s.cache.Receipts = s.makeCache(s.cfg.ReceiptsCacheSize)
s.cache.Validators = s.makeCache(2)
s.cache.Stakers = s.makeCache(s.cfg.StakersCacheSize)
s.cache.Delegations = s.makeCache(s.cfg.DelegationsCacheSize)
s.cache.BlockDowntime = s.makeCache(256)
}

// Commit changes.
func (s *Store) Commit() error {
// Close leaves underlying database.
func (s *Store) Close() {
setnil := func() interface{} {
return nil
}

table.MigrateTables(&s.table, nil)
table.MigrateCaches(&s.cache, setnil)

s.mainDb.Close()
}

// FlushState changes.
func (s *Store) FlushState() error {
// Flush trie on the DB
err := s.table.EvmState.TrieDB().Cap(0)
if err != nil {
Expand Down
12 changes: 3 additions & 9 deletions gossip/store_epoch_stats.go → app/store_epoch_stats.go
Original file line number Diff line number Diff line change
@@ -1,19 +1,13 @@
package gossip
package app

import (
"math"

"github.com/Fantom-foundation/go-lachesis/inter/idx"
"github.com/Fantom-foundation/go-lachesis/inter/sfctype"
)

const (
pendingEpoch = idx.Epoch(math.MaxUint32 - 2)
)

// GetDirtyEpochStats returns EpochStats for current (not sealed) epoch
func (s *Store) GetDirtyEpochStats() *sfctype.EpochStats {
return s.GetEpochStats(pendingEpoch)
return s.GetEpochStats(idx.PendingEpoch)
}

// GetEpochStats returns EpochStats for an already sealed epoch
Expand Down Expand Up @@ -41,7 +35,7 @@ func (s *Store) GetEpochStats(epoch idx.Epoch) *sfctype.EpochStats {

// SetDirtyEpochStats set EpochStats for current (not sealed) epoch
func (s *Store) SetDirtyEpochStats(value *sfctype.EpochStats) {
s.SetEpochStats(pendingEpoch, value)
s.SetEpochStats(idx.PendingEpoch, value)
}

// SetEpochStats set EpochStats for an already sealed epoch
Expand Down
Loading