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
|
|
|
|
2020-10-20 19:45:18 +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 (
|
2020-04-28 15:12:32 +08:00
|
|
|
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
|
2020-04-26 20:21:15 +08:00
|
|
|
ldb storage.Storage
|
2020-10-19 23:39:05 +08:00
|
|
|
bf *blockfile.BlockFile
|
2020-04-28 15:12:32 +08:00
|
|
|
minJnlHeight uint64
|
|
|
|
maxJnlHeight uint64
|
2020-09-01 16:52:50 +08:00
|
|
|
events sync.Map
|
2020-10-22 13:49:05 +08:00
|
|
|
accounts map[string]*Account
|
2020-05-07 14:11:02 +08:00
|
|
|
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
|
2020-05-07 14:11:02 +08:00
|
|
|
|
|
|
|
journalMutex sync.RWMutex
|
2020-09-01 16:52:50 +08:00
|
|
|
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
|
2020-05-07 14:11:02 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
type BlockData struct {
|
2020-06-08 15:21:39 +08:00
|
|
|
Block *pb.Block
|
|
|
|
Receipts []*pb.Receipt
|
2020-10-22 13:49:05 +08:00
|
|
|
Accounts map[string]*Account
|
2020-06-08 15:21:39 +08:00
|
|
|
Journal *BlockJournal
|
|
|
|
InterchainMeta *pb.InterchainMeta
|
2020-12-17 18:50:06 +08:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
|
2020-04-28 15:12:32 +08:00
|
|
|
minJnlHeight, maxJnlHeight := getJournalRange(ldb)
|
2020-03-29 21:32:01 +08:00
|
|
|
|
2020-10-22 13:49:05 +08:00
|
|
|
prevJnlHash := &types.Hash{}
|
2020-04-28 15:12:32 +08:00
|
|
|
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)
|
|
|
|
}
|
2020-04-28 15:12:32 +08:00
|
|
|
prevJnlHash = blockJournal.ChangedHash
|
|
|
|
}
|
|
|
|
|
2020-07-16 16:24:10 +08:00
|
|
|
if accountCache == nil {
|
2020-09-29 15:13:05 +08:00
|
|
|
accountCache, err = NewAccountCache()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2020-07-16 16:24:10 +08:00
|
|
|
}
|
|
|
|
|
2020-05-07 14:11:02 +08:00
|
|
|
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,
|
2020-04-28 15:12:32 +08:00
|
|
|
minJnlHeight: minJnlHeight,
|
|
|
|
maxJnlHeight: maxJnlHeight,
|
2020-10-22 13:49:05 +08:00
|
|
|
accounts: make(map[string]*Account),
|
2020-07-13 20:40:10 +08:00
|
|
|
accountCache: accountCache,
|
2020-04-28 15:12:32 +08:00
|
|
|
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
|
|
|
}
|
|
|
|
|
2020-05-07 14:11:02 +08:00
|
|
|
height := maxJnlHeight
|
|
|
|
if maxJnlHeight > chainMeta.Height {
|
|
|
|
height = chainMeta.Height
|
2020-04-28 15:12:32 +08:00
|
|
|
}
|
|
|
|
|
2020-07-16 16:24:10 +08:00
|
|
|
if err := ledger.Rollback(height); err != nil {
|
|
|
|
return nil, err
|
2020-03-29 21:32:01 +08:00
|
|
|
}
|
|
|
|
|
2020-05-07 14:11:02 +08:00
|
|
|
return ledger, nil
|
|
|
|
}
|
2020-04-28 15:12:32 +08:00
|
|
|
|
2020-07-16 16:24:10 +08:00
|
|
|
func (l *ChainLedger) AccountCache() *AccountCache {
|
|
|
|
return l.accountCache
|
|
|
|
}
|
2020-07-13 20:40:10 +08:00
|
|
|
|
2020-07-16 16:24:10 +08:00
|
|
|
// PersistBlockData persists block data
|
|
|
|
func (l *ChainLedger) PersistBlockData(blockData *BlockData) {
|
2020-05-21 19:08:53 +08:00
|
|
|
current := time.Now()
|
2020-05-07 14:11:02 +08:00
|
|
|
block := blockData.Block
|
|
|
|
receipts := blockData.Receipts
|
|
|
|
accounts := blockData.Accounts
|
|
|
|
journal := blockData.Journal
|
2020-06-08 15:21:39 +08:00
|
|
|
meta := blockData.InterchainMeta
|
2020-04-27 17:18:22 +08:00
|
|
|
|
2020-05-07 14:11:02 +08:00
|
|
|
if err := l.Commit(block.BlockHeader.Number, accounts, journal); err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
2020-04-27 17:18:22 +08:00
|
|
|
|
2020-06-08 15:21:39 +08:00
|
|
|
if err := l.PersistExecutionResult(block, receipts, meta); err != nil {
|
2020-05-07 14:11:02 +08:00
|
|
|
panic(err)
|
2020-04-28 15:12:32 +08:00
|
|
|
}
|
2020-04-27 17:18:22 +08:00
|
|
|
|
2020-05-21 19:08:53 +08:00
|
|
|
PersistBlockDuration.Observe(float64(time.Since(current)) / float64(time.Second))
|
2020-05-07 14:11:02 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Rollback rollback ledger to history version
|
|
|
|
func (l *ChainLedger) Rollback(height uint64) error {
|
|
|
|
if err := l.rollbackState(height); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2020-04-28 15:12:32 +08:00
|
|
|
|
2020-05-07 14:11:02 +08:00
|
|
|
if err := l.rollbackBlockChain(height); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2020-04-28 15:12:32 +08:00
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// RemoveJournalsBeforeBlock removes ledger journals whose block number < height
|
|
|
|
func (l *ChainLedger) RemoveJournalsBeforeBlock(height uint64) error {
|
2020-05-07 14:11:02 +08:00
|
|
|
l.journalMutex.Lock()
|
|
|
|
defer l.journalMutex.Unlock()
|
|
|
|
|
2020-04-28 15:12:32 +08:00
|
|
|
if height > l.maxJnlHeight {
|
|
|
|
return ErrorRemoveJournalOutOfRange
|
2020-03-29 21:32:01 +08:00
|
|
|
}
|
|
|
|
|
2020-04-28 15:12:32 +08:00
|
|
|
if height <= l.minJnlHeight {
|
|
|
|
return nil
|
2020-04-27 17:18:22 +08:00
|
|
|
}
|
2020-03-29 21:32:01 +08:00
|
|
|
|
2020-04-28 15:12:32 +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
|
|
|
|
2020-04-28 15:12:32 +08:00
|
|
|
l.minJnlHeight = height
|
2020-03-29 21:32:01 +08:00
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// AddEvent add ledger event
|
2020-07-16 16:24:10 +08:00
|
|
|
func (l *ChainLedger) AddEvent(event *pb.Event) {
|
2020-09-01 16:52:50 +08:00
|
|
|
var events []*pb.Event
|
2020-10-21 19:38:51 +08:00
|
|
|
hash := event.TxHash.String()
|
2020-09-01 16:52:50 +08:00
|
|
|
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 {
|
2020-09-01 16:52:50 +08:00
|
|
|
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
|
|
|
}
|