bitxhub/internal/ledger/ledger.go

212 lines
5.0 KiB
Go
Raw Normal View History

2020-03-29 21:32:01 +08:00
package ledger
import (
"fmt"
"sync"
2020-05-21 19:08:53 +08:00
"time"
2020-03-29 21:32:01 +08:00
"github.com/meshplus/bitxhub-kit/storage"
2020-11-27 02:07:18 +08:00
"github.com/meshplus/bitxhub-kit/storage/blockfile"
2020-03-29 21:32:01 +08:00
"github.com/meshplus/bitxhub-kit/types"
"github.com/meshplus/bitxhub-model/pb"
2020-09-22 11:35:55 +08:00
"github.com/meshplus/bitxhub/internal/repo"
2020-03-29 21:32:01 +08:00
"github.com/sirupsen/logrus"
)
var _ Ledger = (*ChainLedger)(nil)
var (
ErrorRollbackToHigherNumber = fmt.Errorf("rollback to higher blockchain height")
ErrorRollbackWithoutJournal = fmt.Errorf("rollback to blockchain height without journal")
ErrorRollbackTooMuch = fmt.Errorf("rollback too much block")
ErrorRemoveJournalOutOfRange = fmt.Errorf("remove journal out of range")
2020-03-29 21:32:01 +08:00
)
2021-04-24 14:47:00 +08:00
type revision struct {
id int
changerIndex int
}
2020-03-29 21:32:01 +08:00
type ChainLedger struct {
logger logrus.FieldLogger
blockchainStore storage.Storage
ldb storage.Storage
2020-10-19 23:39:05 +08:00
bf *blockfile.BlockFile
minJnlHeight uint64
maxJnlHeight uint64
events sync.Map
2020-10-22 13:49:05 +08:00
accounts map[string]*Account
accountCache *AccountCache
2020-10-22 13:49:05 +08:00
prevJnlHash *types.Hash
2020-08-26 16:22:10 +08:00
repo *repo.Repo
2020-03-29 21:32:01 +08:00
chainMutex sync.RWMutex
chainMeta *pb.ChainMeta
journalMutex sync.RWMutex
lock sync.RWMutex
2021-04-24 14:47:00 +08:00
validRevisions []revision
nextRevisionId int
changer *stateChanger
accessList *accessList
preimages map[types.Hash][]byte
refund uint64
logs *evmLogs
}
type BlockData struct {
Block *pb.Block
Receipts []*pb.Receipt
2020-10-22 13:49:05 +08:00
Accounts map[string]*Account
Journal *BlockJournal
InterchainMeta *pb.InterchainMeta
TxHashList []*types.Hash
2020-03-29 21:32:01 +08:00
}
// New create a new ledger instance
2020-10-19 23:39:05 +08:00
func New(repo *repo.Repo, blockchainStore storage.Storage, ldb storage.Storage, bf *blockfile.BlockFile, accountCache *AccountCache, logger logrus.FieldLogger) (*ChainLedger, error) {
2020-03-29 21:32:01 +08:00
chainMeta, err := loadChainMeta(blockchainStore)
if err != nil {
return nil, fmt.Errorf("load chain meta: %w", err)
}
minJnlHeight, maxJnlHeight := getJournalRange(ldb)
2020-03-29 21:32:01 +08:00
2020-10-22 13:49:05 +08:00
prevJnlHash := &types.Hash{}
if maxJnlHeight != 0 {
blockJournal := getBlockJournal(maxJnlHeight, ldb)
2020-09-29 19:29:14 +08:00
if blockJournal == nil {
return nil, fmt.Errorf("get empty block journal for block: %d", maxJnlHeight)
}
prevJnlHash = blockJournal.ChangedHash
}
if accountCache == nil {
2020-09-29 15:13:05 +08:00
accountCache, err = NewAccountCache()
if err != nil {
return nil, err
}
}
ledger := &ChainLedger{
2020-08-26 16:22:10 +08:00
repo: repo,
2020-03-29 21:32:01 +08:00
logger: logger,
chainMeta: chainMeta,
blockchainStore: blockchainStore,
ldb: ldb,
2020-10-19 23:39:05 +08:00
bf: bf,
minJnlHeight: minJnlHeight,
maxJnlHeight: maxJnlHeight,
2020-10-22 13:49:05 +08:00
accounts: make(map[string]*Account),
accountCache: accountCache,
prevJnlHash: prevJnlHash,
2021-04-24 14:47:00 +08:00
preimages: make(map[types.Hash][]byte),
changer: newChanger(),
accessList: newAccessList(),
logs: NewEvmLogs(),
2020-03-29 21:32:01 +08:00
}
height := maxJnlHeight
if maxJnlHeight > chainMeta.Height {
height = chainMeta.Height
}
if err := ledger.Rollback(height); err != nil {
return nil, err
2020-03-29 21:32:01 +08:00
}
return ledger, nil
}
func (l *ChainLedger) AccountCache() *AccountCache {
return l.accountCache
}
// PersistBlockData persists block data
func (l *ChainLedger) PersistBlockData(blockData *BlockData) {
2020-05-21 19:08:53 +08:00
current := time.Now()
block := blockData.Block
receipts := blockData.Receipts
accounts := blockData.Accounts
journal := blockData.Journal
meta := blockData.InterchainMeta
if err := l.Commit(block.BlockHeader.Number, accounts, journal); err != nil {
panic(err)
}
if err := l.PersistExecutionResult(block, receipts, meta); err != nil {
panic(err)
}
2020-05-21 19:08:53 +08:00
PersistBlockDuration.Observe(float64(time.Since(current)) / float64(time.Second))
}
// Rollback rollback ledger to history version
func (l *ChainLedger) Rollback(height uint64) error {
if err := l.rollbackState(height); err != nil {
return err
}
if err := l.rollbackBlockChain(height); err != nil {
return err
}
return nil
}
// RemoveJournalsBeforeBlock removes ledger journals whose block number < height
func (l *ChainLedger) RemoveJournalsBeforeBlock(height uint64) error {
l.journalMutex.Lock()
defer l.journalMutex.Unlock()
if height > l.maxJnlHeight {
return ErrorRemoveJournalOutOfRange
2020-03-29 21:32:01 +08:00
}
if height <= l.minJnlHeight {
return nil
}
2020-03-29 21:32:01 +08:00
batch := l.ldb.NewBatch()
for i := l.minJnlHeight; i < height; i++ {
batch.Delete(compositeKey(journalKey, i))
}
batch.Put(compositeKey(journalKey, minHeightStr), marshalHeight(height))
batch.Commit()
2020-03-29 21:32:01 +08:00
l.minJnlHeight = height
2020-03-29 21:32:01 +08:00
return nil
}
// AddEvent add ledger event
func (l *ChainLedger) AddEvent(event *pb.Event) {
var events []*pb.Event
hash := event.TxHash.String()
value, ok := l.events.Load(hash)
if ok {
events = value.([]*pb.Event)
}
events = append(events, event)
l.events.Store(hash, events)
2020-03-29 21:32:01 +08:00
}
// Events return ledger events
func (l *ChainLedger) Events(txHash string) []*pb.Event {
events, ok := l.events.Load(txHash)
if !ok {
return nil
}
return events.([]*pb.Event)
2020-03-29 21:32:01 +08:00
}
// Close close the ledger instance
func (l *ChainLedger) Close() {
l.ldb.Close()
2020-09-29 19:29:14 +08:00
l.blockchainStore.Close()
2020-10-19 23:39:05 +08:00
l.bf.Close()
2020-03-29 21:32:01 +08:00
}