|
|
|
package shardchain
|
|
|
|
|
|
|
|
import (
|
|
|
|
"math/big"
|
|
|
|
"sync"
|
|
|
|
|
|
|
|
"github.com/harmony-one/harmony/shard"
|
|
|
|
|
|
|
|
"github.com/ethereum/go-ethereum/common"
|
|
|
|
"github.com/ethereum/go-ethereum/ethdb"
|
|
|
|
"github.com/harmony-one/harmony/consensus/engine"
|
|
|
|
"github.com/harmony-one/harmony/core"
|
|
|
|
"github.com/harmony-one/harmony/core/rawdb"
|
|
|
|
"github.com/harmony-one/harmony/core/vm"
|
|
|
|
"github.com/harmony-one/harmony/internal/params"
|
|
|
|
"github.com/harmony-one/harmony/internal/utils"
|
|
|
|
"github.com/pkg/errors"
|
|
|
|
)
|
|
|
|
|
|
|
|
// Collection is a collection of per-shard blockchains.
|
|
|
|
type Collection interface {
|
|
|
|
// ShardChain returns the blockchain for the given shard,
|
|
|
|
// opening one as necessary.
|
|
|
|
ShardChain(shardID uint32) (*core.BlockChain, error)
|
|
|
|
|
|
|
|
// CloseShardChain closes the given shard chain.
|
|
|
|
CloseShardChain(shardID uint32) error
|
|
|
|
|
|
|
|
// Close closes all shard chains.
|
|
|
|
Close() error
|
|
|
|
}
|
|
|
|
|
|
|
|
// CollectionImpl is the main implementation of the shard chain collection.
|
|
|
|
// See the Collection interface for details.
|
|
|
|
type CollectionImpl struct {
|
|
|
|
dbFactory DBFactory
|
|
|
|
dbInit DBInitializer
|
|
|
|
engine engine.Engine
|
|
|
|
mtx sync.Mutex
|
|
|
|
pool map[uint32]*core.BlockChain
|
|
|
|
disableCache map[uint32]bool
|
|
|
|
chainConfig *params.ChainConfig
|
|
|
|
traceDB string
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewCollection creates and returns a new shard chain collection.
|
|
|
|
//
|
|
|
|
// dbFactory is the shard chain database factory to use.
|
|
|
|
//
|
|
|
|
// dbInit is the shard chain initializer to use when the database returned by
|
|
|
|
// the factory is brand new (empty).
|
|
|
|
func NewCollection(
|
|
|
|
dbFactory DBFactory, dbInit DBInitializer, engine engine.Engine, traceDB string,
|
|
|
|
chainConfig *params.ChainConfig,
|
|
|
|
) *CollectionImpl {
|
|
|
|
return &CollectionImpl{
|
|
|
|
dbFactory: dbFactory,
|
|
|
|
dbInit: dbInit,
|
|
|
|
engine: engine,
|
|
|
|
pool: make(map[uint32]*core.BlockChain),
|
|
|
|
disableCache: make(map[uint32]bool),
|
|
|
|
chainConfig: chainConfig,
|
|
|
|
traceDB: traceDB,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// ShardChain returns the blockchain for the given shard,
|
|
|
|
// opening one as necessary.
|
|
|
|
func (sc *CollectionImpl) ShardChain(shardID uint32) (*core.BlockChain, error) {
|
|
|
|
sc.mtx.Lock()
|
|
|
|
defer sc.mtx.Unlock()
|
|
|
|
if bc, ok := sc.pool[shardID]; ok {
|
|
|
|
return bc, nil
|
|
|
|
}
|
|
|
|
var db ethdb.Database
|
|
|
|
defer func() {
|
|
|
|
if db != nil {
|
|
|
|
db.Close()
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
var err error
|
|
|
|
if db, err = sc.dbFactory.NewChainDB(shardID); err != nil {
|
|
|
|
// NewChainDB may return incompletely initialized DB;
|
|
|
|
// avoid closing it.
|
|
|
|
db = nil
|
|
|
|
return nil, errors.Wrap(err, "cannot open chain database")
|
|
|
|
}
|
|
|
|
if rawdb.ReadCanonicalHash(db, 0) == (common.Hash{}) {
|
|
|
|
utils.Logger().Info().
|
|
|
|
Uint32("shardID", shardID).
|
|
|
|
Msg("initializing a new chain database")
|
|
|
|
if err := sc.dbInit.InitChainDB(db, shardID); err != nil {
|
|
|
|
return nil, errors.Wrapf(err, "cannot initialize a new chain database")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
var cacheConfig *core.CacheConfig
|
|
|
|
if sc.disableCache[shardID] {
|
|
|
|
cacheConfig = &core.CacheConfig{Disabled: true}
|
|
|
|
utils.Logger().Info().
|
|
|
|
Uint32("shardID", shardID).
|
|
|
|
Msg("disable cache, running in archival mode")
|
|
|
|
}
|
|
|
|
|
|
|
|
chainConfig := *sc.chainConfig
|
|
|
|
|
|
|
|
if shardID == shard.BeaconChainShardID {
|
|
|
|
// For beacon chain inside a shard chain, need to reset the eth chainID to shard 0's eth chainID in the config
|
|
|
|
chainConfig.EthCompatibleChainID = big.NewInt(chainConfig.EthCompatibleShard0ChainID.Int64())
|
|
|
|
}
|
|
|
|
bc, err := core.NewBlockChain(
|
|
|
|
db, cacheConfig, &chainConfig, sc.engine, vm.Config{}, sc.traceDB, nil,
|
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
return nil, errors.Wrapf(err, "cannot create blockchain")
|
|
|
|
}
|
|
|
|
db = nil // don't close
|
|
|
|
sc.pool[shardID] = bc
|
|
|
|
return bc, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// DisableCache disables caching mode for newly opened chains.
|
|
|
|
// It does not affect already open chains. For best effect,
|
|
|
|
// use this immediately after creating collection.
|
|
|
|
func (sc *CollectionImpl) DisableCache(shardID uint32) {
|
|
|
|
if sc.disableCache == nil {
|
|
|
|
sc.disableCache = make(map[uint32]bool)
|
|
|
|
}
|
|
|
|
sc.disableCache[shardID] = true
|
|
|
|
}
|
|
|
|
|
|
|
|
// CloseShardChain closes the given shard chain.
|
|
|
|
func (sc *CollectionImpl) CloseShardChain(shardID uint32) error {
|
|
|
|
sc.mtx.Lock()
|
|
|
|
defer sc.mtx.Unlock()
|
|
|
|
bc, ok := sc.pool[shardID]
|
|
|
|
if !ok {
|
|
|
|
return errors.Errorf("shard chain not found %d", shardID)
|
|
|
|
}
|
|
|
|
utils.Logger().Info().
|
|
|
|
Uint32("shardID", shardID).
|
|
|
|
Msg("closing shard chain")
|
|
|
|
delete(sc.pool, shardID)
|
|
|
|
bc.Stop()
|
|
|
|
bc.ChainDb().Close()
|
|
|
|
utils.Logger().Info().
|
|
|
|
Uint32("shardID", shardID).
|
|
|
|
Msg("closed shard chain")
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Close closes all shard chains.
|
|
|
|
func (sc *CollectionImpl) Close() error {
|
|
|
|
newPool := make(map[uint32]*core.BlockChain)
|
|
|
|
sc.mtx.Lock()
|
|
|
|
oldPool := sc.pool
|
|
|
|
sc.pool = newPool
|
|
|
|
sc.mtx.Unlock()
|
|
|
|
for shardID, bc := range oldPool {
|
|
|
|
utils.Logger().Info().
|
|
|
|
Uint32("shardID", shardID).
|
|
|
|
Msg("closing shard chain")
|
|
|
|
bc.Stop()
|
|
|
|
bc.ChainDb().Close()
|
|
|
|
utils.Logger().Info().
|
|
|
|
Uint32("shardID", shardID).
|
|
|
|
Msg("closed shard chain")
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|