You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
331 lines
8.7 KiB
331 lines
8.7 KiB
package core
|
|
|
|
import (
|
|
"math/big"
|
|
"sync/atomic"
|
|
"time"
|
|
|
|
"github.com/ethereum/go-ethereum/common"
|
|
"github.com/ethereum/go-ethereum/ethdb"
|
|
"github.com/harmony-one/harmony/block"
|
|
"github.com/harmony-one/harmony/consensus/engine"
|
|
consensus_engine "github.com/harmony-one/harmony/consensus/engine"
|
|
"github.com/harmony-one/harmony/core/rawdb"
|
|
"github.com/harmony-one/harmony/core/types"
|
|
"github.com/harmony-one/harmony/core/vm"
|
|
"github.com/harmony-one/harmony/internal/chain"
|
|
"github.com/harmony-one/harmony/internal/params"
|
|
"github.com/harmony-one/harmony/internal/utils"
|
|
"github.com/harmony-one/harmony/shard"
|
|
lru "github.com/hashicorp/golang-lru"
|
|
"github.com/pkg/errors"
|
|
)
|
|
|
|
type SideEffect func()
|
|
|
|
type EpochChain struct {
|
|
chainConfig *params.ChainConfig
|
|
genesisBlock *types.Block
|
|
db ethdb.Database // Low level persistent database to store final content in.
|
|
mu chan struct{}
|
|
currentHeader atomic.Value // Current head of the blockchain.
|
|
engine consensus_engine.Engine
|
|
vmConfig *vm.Config
|
|
|
|
headerCache *lru.Cache // Cache for the most recent block headers
|
|
numberCache *lru.Cache // Cache for the most recent block numbers
|
|
canonicalCache *lru.Cache // number -> Hash
|
|
shardStateCache *lru.Cache // Cache for shard states where key is epoch number.
|
|
|
|
Stub
|
|
}
|
|
|
|
func cache(size int) *lru.Cache {
|
|
cache, _ := lru.New(size)
|
|
return cache
|
|
}
|
|
|
|
func NewEpochChain(db ethdb.Database, chainConfig *params.ChainConfig,
|
|
engine consensus_engine.Engine, vmConfig vm.Config) (*EpochChain, error) {
|
|
|
|
hash := rawdb.ReadCanonicalHash(db, 0)
|
|
genesisBlock := rawdb.ReadBlock(db, hash, 0)
|
|
if genesisBlock == nil {
|
|
return nil, ErrNoGenesis
|
|
}
|
|
|
|
bc := &EpochChain{
|
|
chainConfig: chainConfig,
|
|
genesisBlock: genesisBlock,
|
|
db: db,
|
|
mu: make(chan struct{}, 1),
|
|
currentHeader: atomic.Value{},
|
|
engine: engine,
|
|
vmConfig: &vmConfig,
|
|
|
|
headerCache: cache(headerCacheLimit),
|
|
numberCache: cache(numberCacheLimit),
|
|
canonicalCache: cache(canonicalCacheLimit),
|
|
shardStateCache: cache(shardCacheLimit),
|
|
|
|
Stub: Stub{Name: "EpochChain"},
|
|
}
|
|
|
|
head := rawdb.ReadHeadHeaderHash(db)
|
|
if head == (common.Hash{}) {
|
|
// Corrupt or empty database, init from scratch
|
|
utils.Logger().Warn().Msg("Empty database, resetting chain")
|
|
err := bc.resetWithGenesisBlock(bc.genesisBlock)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
} else {
|
|
header := bc.GetHeaderByHash(head)
|
|
if header == nil {
|
|
return nil, errors.New("failed to initialize: missing header")
|
|
}
|
|
|
|
bc.currentHeader.Store(header)
|
|
}
|
|
return bc, nil
|
|
}
|
|
|
|
func (bc *EpochChain) resetWithGenesisBlock(genesis *types.Block) error {
|
|
// Prepare the genesis block and reinitialise the chain
|
|
if err := rawdb.WriteHeader(bc.db, genesis.Header()); err != nil {
|
|
return err
|
|
}
|
|
bc.currentHeader.Store(bc.genesisBlock.Header())
|
|
return rawdb.WriteHeadBlockHash(bc.db, bc.genesisBlock.Hash())
|
|
}
|
|
|
|
func (bc *EpochChain) ShardID() uint32 {
|
|
return 0
|
|
}
|
|
|
|
func (bc *EpochChain) CurrentBlock() *types.Block {
|
|
return types.NewBlockWithHeader(bc.currentHeader.Load().(*block.Header))
|
|
}
|
|
|
|
func (bc *EpochChain) Stop() {
|
|
bc.mu <- struct{}{}
|
|
time.AfterFunc(1*time.Second, func() {
|
|
<-bc.mu
|
|
})
|
|
}
|
|
|
|
func (bc *EpochChain) InsertChain(blocks types.Blocks, _ bool) (int, error) {
|
|
if len(blocks) == 0 {
|
|
return 0, nil
|
|
}
|
|
bc.mu <- struct{}{}
|
|
defer func() {
|
|
<-bc.mu
|
|
}()
|
|
for i, block := range blocks {
|
|
if !block.IsLastBlockInEpoch() {
|
|
return i, errors.New("block is not last block in epoch")
|
|
}
|
|
sig, bitmap, err := chain.ParseCommitSigAndBitmap(block.GetCurrentCommitSig())
|
|
if err != nil {
|
|
return i, errors.Wrap(err, "parse commitSigAndBitmap")
|
|
}
|
|
|
|
// Signature validation.
|
|
err = bc.Engine().VerifyHeaderSignature(bc, block.Header(), sig, bitmap)
|
|
if err != nil {
|
|
return i, errors.Wrap(err, "failed signature validation")
|
|
}
|
|
|
|
ep := block.Header().Epoch()
|
|
next := ep.Add(ep, common.Big1)
|
|
batch := bc.db.NewBatch()
|
|
_, se1, err := bc.writeShardStateBytes(batch, next, block.Header().ShardState())
|
|
if err != nil {
|
|
return i, err
|
|
}
|
|
se2, err := bc.writeHeadBlock(batch, block)
|
|
if err != nil {
|
|
return i, err
|
|
}
|
|
err = rawdb.WriteHeadHeaderHash(batch, block.Hash())
|
|
if err != nil {
|
|
return i, err
|
|
}
|
|
err = rawdb.WriteHeaderNumber(batch, block.Hash(), block.NumberU64())
|
|
if err != nil {
|
|
return i, err
|
|
}
|
|
err = rawdb.WriteHeader(batch, block.Header())
|
|
if err != nil {
|
|
return i, err
|
|
}
|
|
if err := batch.Write(); err != nil {
|
|
return i, err
|
|
}
|
|
se1()
|
|
se2()
|
|
utils.Logger().Info().
|
|
Msgf("[EPOCHSYNC] Added block %d %s", block.NumberU64(), block.Hash().Hex())
|
|
}
|
|
return 0, nil
|
|
}
|
|
|
|
func (bc *EpochChain) CurrentHeader() *block.Header {
|
|
return bc.currentHeader.Load().(*block.Header)
|
|
}
|
|
|
|
// GetBlockNumber retrieves the block number belonging to the given hash
|
|
// from the cache or database
|
|
func (bc *EpochChain) GetBlockNumber(hash common.Hash) *uint64 {
|
|
if cached, ok := bc.numberCache.Get(hash); ok {
|
|
number := cached.(uint64)
|
|
return &number
|
|
}
|
|
number := rawdb.ReadHeaderNumber(bc.db, hash)
|
|
if number != nil {
|
|
bc.numberCache.Add(hash, *number)
|
|
}
|
|
return number
|
|
}
|
|
|
|
func (bc *EpochChain) GetHeaderByHash(hash common.Hash) *block.Header {
|
|
number := bc.GetBlockNumber(hash)
|
|
if number == nil {
|
|
return nil
|
|
}
|
|
return bc.GetHeader(hash, *number)
|
|
}
|
|
|
|
// GetHeader retrieves a block header from the database by hash and number,
|
|
// caching it if found.
|
|
func (bc *EpochChain) GetHeader(hash common.Hash, number uint64) *block.Header {
|
|
// Short circuit if the header's already in the cache, retrieve otherwise
|
|
if header, ok := bc.headerCache.Get(hash); ok {
|
|
return header.(*block.Header)
|
|
}
|
|
header := rawdb.ReadHeader(bc.db, hash, number)
|
|
if header == nil {
|
|
return nil
|
|
}
|
|
// Cache the found header for next time and return
|
|
bc.headerCache.Add(hash, header)
|
|
return header
|
|
}
|
|
|
|
func (bc *EpochChain) GetCanonicalHash(number uint64) common.Hash {
|
|
return rawdb.ReadCanonicalHash(bc.db, number)
|
|
}
|
|
|
|
func (bc *EpochChain) GetHeaderByNumber(number uint64) *block.Header {
|
|
hash := bc.getHashByNumber(number)
|
|
if hash == (common.Hash{}) {
|
|
return nil
|
|
}
|
|
return bc.GetHeader(hash, number)
|
|
}
|
|
|
|
func (bc *EpochChain) getHashByNumber(number uint64) common.Hash {
|
|
// Since canonical chain is immutable, it's safe to read header
|
|
// hash by number from cache.
|
|
if hash, ok := bc.canonicalCache.Get(number); ok {
|
|
return hash.(common.Hash)
|
|
}
|
|
hash := rawdb.ReadCanonicalHash(bc.db, number)
|
|
if hash != (common.Hash{}) {
|
|
bc.canonicalCache.Add(number, hash)
|
|
}
|
|
return hash
|
|
}
|
|
|
|
func (bc *EpochChain) Config() *params.ChainConfig {
|
|
return bc.chainConfig
|
|
}
|
|
|
|
func (bc *EpochChain) Engine() engine.Engine {
|
|
return bc.engine
|
|
}
|
|
|
|
func (bc *EpochChain) ReadShardState(epoch *big.Int) (*shard.State, error) {
|
|
cacheKey := string(epoch.Bytes())
|
|
if cached, ok := bc.shardStateCache.Get(cacheKey); ok {
|
|
shardState := cached.(*shard.State)
|
|
return shardState, nil
|
|
}
|
|
shardState, err := rawdb.ReadShardState(bc.db, epoch)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
bc.shardStateCache.Add(cacheKey, shardState)
|
|
return shardState, nil
|
|
}
|
|
|
|
// pure function
|
|
func (bc *EpochChain) writeShardStateBytes(db rawdb.DatabaseWriter,
|
|
epoch *big.Int, shardState []byte,
|
|
) (*shard.State, SideEffect, error) {
|
|
decodeShardState, err := shard.DecodeWrapper(shardState)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
err = rawdb.WriteShardStateBytes(db, epoch, shardState)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
cacheKey := string(epoch.Bytes())
|
|
se := func() {
|
|
bc.shardStateCache.Add(cacheKey, decodeShardState)
|
|
}
|
|
return decodeShardState, se, nil
|
|
}
|
|
|
|
// WriteHeadBlock writes a new head block.
|
|
func (bc *EpochChain) WriteHeadBlock(block *types.Block) error {
|
|
batch := bc.db.NewBatch()
|
|
se, err := bc.writeHeadBlock(batch, block)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if err := batch.Write(); err != nil {
|
|
return err
|
|
}
|
|
se()
|
|
return nil
|
|
}
|
|
|
|
// pure function
|
|
func (bc *EpochChain) writeHeadBlock(db rawdb.DatabaseWriter, block *types.Block) (SideEffect, error) {
|
|
// Add the block to the canonical chain number scheme and mark as the head
|
|
if err := rawdb.WriteCanonicalHash(db, block.Hash(), block.NumberU64()); err != nil {
|
|
return nil, err
|
|
}
|
|
if err := rawdb.WriteHeadBlockHash(db, block.Hash()); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return func() {
|
|
bc.currentHeader.Store(block.Header())
|
|
}, nil
|
|
}
|
|
|
|
func (bc *EpochChain) IsSameLeaderAsPreviousBlock(block *types.Block) bool {
|
|
if IsEpochBlock(block) {
|
|
return false
|
|
}
|
|
|
|
previousHeader := bc.GetHeaderByNumber(block.NumberU64() - 1)
|
|
if previousHeader == nil {
|
|
return false
|
|
}
|
|
return block.Coinbase() == previousHeader.Coinbase()
|
|
}
|
|
|
|
func (bc *EpochChain) GetVMConfig() *vm.Config {
|
|
return bc.vmConfig
|
|
}
|
|
|
|
func (bc *EpochChain) CommitPreimages() error {
|
|
// epoch chain just has last block, which does not have any txs
|
|
// so no pre-images here
|
|
return nil
|
|
}
|
|
|