"github.com/bytom/vapor/protocol/bc/types"
)
+// ErrNotInitDBState represent the database state of mov store is not initialized
+var ErrNotInitDBState = errors.New("database state of mov store is not initialized")
+
// MovStore is the interface for mov's persistent storage
type MovStore interface {
+ Clear()
GetMovDatabaseState() (*common.MovDatabaseState, error)
InitDBState(height uint64, hash *bc.Hash) error
ListOrders(orderAfter *common.Order) ([]*common.Order, error)
return &LevelDBMovStore{db: db}
}
+// Clear will clear all the data of store
+func (m *LevelDBMovStore) Clear() {
+ batch := m.db.NewBatch()
+
+ iter := m.db.Iterator()
+ defer iter.Release()
+
+ for iter.Next() {
+ batch.Delete(iter.Key())
+ }
+ batch.Write()
+}
+
// GetMovDatabaseState return the current DB's image status
func (m *LevelDBMovStore) GetMovDatabaseState() (*common.MovDatabaseState, error) {
if value := m.db.Get(bestMatchStore); value != nil {
return state, json.Unmarshal(value, state)
}
- return nil, errors.New("don't find state of mov-database")
+ return nil, ErrNotInitDBState
}
// InitDBState set the DB's image status
"github.com/bytom/vapor/consensus/segwit"
dbm "github.com/bytom/vapor/database/leveldb"
"github.com/bytom/vapor/errors"
+ "github.com/bytom/vapor/protocol"
"github.com/bytom/vapor/protocol/bc"
"github.com/bytom/vapor/protocol/bc/types"
)
var (
+ errChainStatusHasAlreadyInit = errors.New("mov chain status has already initialized")
errInvalidTradePairs = errors.New("The trade pairs in the tx input is invalid")
errStatusFailMustFalse = errors.New("status fail of transaction does not allow to be true")
errInputProgramMustP2WMCScript = errors.New("input program of trade tx must p2wmc script")
if block.Height == m.startBlockHeight {
blockHash := block.Hash()
- return m.movStore.InitDBState(block.Height, &blockHash)
+ return m.InitChainStatus(&blockHash)
}
if err := m.validateMatchedTxSequence(block.Transactions); err != nil {
// ChainStatus return the current block height and block hash in dex core
func (m *Core) ChainStatus() (uint64, *bc.Hash, error) {
state, err := m.movStore.GetMovDatabaseState()
+ if err == database.ErrNotInitDBState {
+ return 0, nil, protocol.ErrNotInitSubProtocolChainStatus
+ }
+
if err != nil {
return 0, nil, err
}
return nil
}
+ if block.Height == m.startBlockHeight {
+ m.movStore.Clear()
+ return nil
+ }
+
deleteOrders, addOrders, err := decodeTxsOrders(block.Transactions)
if err != nil {
return err
return m.movStore.ProcessOrders(addOrders, deleteOrders, &block.BlockHeader)
}
+// InitChainStatus used to init the start block height and start block hash to store
+func (m *Core) InitChainStatus(startHash *bc.Hash) error {
+ if _, err := m.movStore.GetMovDatabaseState(); err == nil {
+ return errChainStatusHasAlreadyInit
+ }
+
+ return m.movStore.InitDBState(m.startBlockHeight, startHash)
+}
+
// IsDust block the transaction that are not generated by the match engine
func (m *Core) IsDust(tx *types.Tx) bool {
for _, input := range tx.Inputs {
movCore := mov.NewCore(config.DBBackend, config.DBDir(), consensus.ActiveNetParams.MovStartHeight)
assetFilter := protocol.NewAssetFilter(config.CrossChain.AssetWhitelist)
txPool := protocol.NewTxPool(store, []protocol.DustFilterer{movCore, assetFilter}, dispatcher)
- chain, err := protocol.NewChain(store, txPool, []protocol.Protocoler{movCore}, dispatcher)
+ chain, err := protocol.NewChain(store, txPool, []protocol.SubProtocol{movCore}, dispatcher)
if err != nil {
cmn.Exit(cmn.Fmt("Failed to create chain structure: %v", err))
}
dispatcher := event.NewDispatcher()
movCore := mov.NewCore(config.DBBackend, config.DBDir(), consensus.ActiveNetParams.MovStartHeight)
txPool := protocol.NewTxPool(store, []protocol.DustFilterer{movCore}, dispatcher)
- chain, err := protocol.NewChain(store, txPool, []protocol.Protocoler{movCore}, dispatcher)
+ chain, err := protocol.NewChain(store, txPool, []protocol.SubProtocol{movCore}, dispatcher)
if err != nil {
return err
}
return results, gasLeft
}
-func (b *blockBuilder) validateBySubProtocols(tx *types.Tx, statusFail bool, subProtocols []protocol.Protocoler) error {
+func (b *blockBuilder) validateBySubProtocols(tx *types.Tx, statusFail bool, subProtocols []protocol.SubProtocol) error {
for _, subProtocol := range subProtocols {
verifyResult := &bc.TxVerifyResult{StatusFail: statusFail}
if err := subProtocol.ValidateTx(tx, verifyResult, b.block.Height); err != nil {
maxKnownTxs = 32768 // Maximum transactions hashes to keep in the known list (prevent DOS)
)
-// Protocoler is interface for layer 2 consensus protocol
-type Protocoler interface {
+// ErrNotInitSubProtocolChainStatus represent the node state of sub protocol has not been initialized
+var ErrNotInitSubProtocolChainStatus = errors.New("node state of sub protocol has not been initialized")
+
+// SubProtocol is interface for layer 2 consensus protocol
+type SubProtocol interface {
Name() string
StartHeight() uint64
BeforeProposalBlock(txs []*types.Tx, blockHeight uint64, gasLeft int64, isTimeout func() bool) ([]*types.Tx, error)
+
+ // ChainStatus return the the current block height and block hash of sub protocol.
+ // it will return ErrNotInitSubProtocolChainStatus if not initialized.
ChainStatus() (uint64, *bc.Hash, error)
+ InitChainStatus(*bc.Hash) error
ValidateBlock(block *types.Block, verifyResults []*bc.TxVerifyResult) error
ValidateTx(tx *types.Tx, verifyResult *bc.TxVerifyResult, blockHeight uint64) error
ApplyBlock(block *types.Block) error
txPool *TxPool
store Store
processBlockCh chan *processBlockMsg
- subProtocols []Protocoler
+ subProtocols []SubProtocol
signatureCache *common.Cache
eventDispatcher *event.Dispatcher
}
// NewChain returns a new Chain using store as the underlying storage.
-func NewChain(store Store, txPool *TxPool, subProtocols []Protocoler, eventDispatcher *event.Dispatcher) (*Chain, error) {
+func NewChain(store Store, txPool *TxPool, subProtocols []SubProtocol, eventDispatcher *event.Dispatcher) (*Chain, error) {
knownTxs, _ := common.NewOrderedSet(maxKnownTxs)
c := &Chain{
orphanManage: NewOrphanManage(),
}
// SubProtocols return list of layer 2 consensus protocol
-func (c *Chain) SubProtocols() []Protocoler {
+func (c *Chain) SubProtocols() []SubProtocol {
return c.subProtocols
}
}
}
-func (c *Chain) syncProtocolStatus(subProtocol Protocoler) error {
+func (c *Chain) syncProtocolStatus(subProtocol SubProtocol) error {
if c.bestBlockHeader.Height < subProtocol.StartHeight() {
return nil
}
protocolHeight, protocolHash, err := subProtocol.ChainStatus()
- if err != nil {
- return errors.Wrap(err, "failed on get sub protocol status")
+ if err == ErrNotInitSubProtocolChainStatus {
+ startHash, err := c.store.GetMainChainHash(subProtocol.StartHeight())
+ if err != nil {
+ return errors.Wrap(err, subProtocol.Name(), "can't get block hash by height")
+ }
+
+ if err := subProtocol.InitChainStatus(startHash); err != nil {
+ return errors.Wrap(err, subProtocol.Name(), "fail init chain status")
+ }
+
+ protocolHeight, protocolHash = subProtocol.StartHeight(), startHash
+ } else if err != nil {
+ return errors.Wrap(err, subProtocol.Name(), "can't get chain status")
}
if *protocolHash == c.bestBlockHeader.Hash() {
t.Fatal(err)
}
- chain, err := protocol.NewChain(store, nil, []protocol.Protocoler{movCore}, nil)
+ chain, err := protocol.NewChain(store, nil, []protocol.SubProtocol{movCore}, nil)
if err != nil {
t.Fatal(err)
}
--- /dev/null
+package test
+
+import (
+ "os"
+ "testing"
+
+ "github.com/bytom/vapor/application/mov"
+ movDatabase "github.com/bytom/vapor/application/mov/database"
+ "github.com/bytom/vapor/database"
+ dbm "github.com/bytom/vapor/database/leveldb"
+ "github.com/bytom/vapor/protocol"
+ "github.com/bytom/vapor/protocol/bc"
+ "github.com/bytom/vapor/protocol/bc/types"
+ "github.com/bytom/vapor/protocol/state"
+ "github.com/bytom/vapor/testutil"
+)
+
+type chainStatus struct {
+ blockHeight uint64
+ blockHash bc.Hash
+}
+
+type chainBlock struct {
+ block *types.Block
+ inMainChain bool
+}
+
+var blocks = map[uint64][]*types.Block{
+ 0: {
+ {
+ BlockHeader: types.BlockHeader{
+ Height: 0,
+ Timestamp: 1585814309,
+ PreviousBlockHash: bc.Hash{},
+ },
+ },
+ },
+ 1: {
+ // prev block is [0][0]
+ {
+ BlockHeader: types.BlockHeader{
+ Height: 1,
+ Timestamp: 1585814310,
+ PreviousBlockHash: testutil.MustDecodeHash("2e5406c82fe34f6ee44fe694b05ffc8fb5918a026415b086df03fb03760b42a9"),
+ },
+ },
+ // prev block is [0][0]
+ {
+ BlockHeader: types.BlockHeader{
+ Height: 1,
+ Timestamp: 1585814311,
+ PreviousBlockHash: testutil.MustDecodeHash("2e5406c82fe34f6ee44fe694b05ffc8fb5918a026415b086df03fb03760b42a9"),
+ },
+ },
+ },
+ 2: {
+ // prev block is [1][0]
+ {
+ BlockHeader: types.BlockHeader{
+ Height: 2,
+ Timestamp: 1585814320,
+ PreviousBlockHash: testutil.MustDecodeHash("5bc198f4c0198e7e8b52173a82836cfd3f124d88bf052f53390948d845bf6fe0"),
+ },
+ },
+ },
+}
+
+func TestSyncProtocolStatus(t *testing.T) {
+ cases := []struct {
+ desc string
+ savedBlocks []*chainBlock
+ startHeight uint64
+ startHash *bc.Hash
+ wantChainStatus *chainStatus
+ }{
+ {
+ desc: "start height from 0, mov is not init",
+ savedBlocks: []*chainBlock{
+ {
+ block: blocks[0][0],
+ inMainChain: true,
+ },
+ {
+ block: blocks[1][0],
+ inMainChain: true,
+ },
+ {
+ block: blocks[2][0],
+ inMainChain: true,
+ },
+ },
+ startHeight: 0,
+ wantChainStatus: &chainStatus{
+ blockHeight: 2,
+ blockHash: blocks[2][0].Hash(),
+ },
+ },
+ {
+ desc: "start height from 1, mov is not init",
+ savedBlocks: []*chainBlock{
+ {
+ block: blocks[0][0],
+ inMainChain: true,
+ },
+ {
+ block: blocks[1][0],
+ inMainChain: true,
+ },
+ {
+ block: blocks[2][0],
+ inMainChain: true,
+ },
+ },
+ startHeight: 1,
+ wantChainStatus: &chainStatus{
+ blockHeight: 2,
+ blockHash: blocks[2][0].Hash(),
+ },
+ },
+ {
+ desc: "start height from 1, state of mov is not sync completed",
+ savedBlocks: []*chainBlock{
+ {
+ block: blocks[0][0],
+ inMainChain: true,
+ },
+ {
+ block: blocks[1][0],
+ inMainChain: true,
+ },
+ {
+ block: blocks[2][0],
+ inMainChain: true,
+ },
+ },
+ startHeight: 1,
+ startHash: hashPtr(blocks[1][0].Hash()),
+ wantChainStatus: &chainStatus{
+ blockHeight: 2,
+ blockHash: blocks[2][0].Hash(),
+ },
+ },
+ {
+ desc: "chain status of mov is forked",
+ savedBlocks: []*chainBlock{
+ {
+ block: blocks[0][0],
+ inMainChain: true,
+ },
+ {
+ block: blocks[1][0],
+ inMainChain: true,
+ },
+ {
+ block: blocks[1][1],
+ inMainChain: false,
+ },
+ {
+ block: blocks[2][0],
+ inMainChain: true,
+ },
+ },
+ startHeight: 1,
+ startHash: hashPtr(blocks[1][1].Hash()),
+ wantChainStatus: &chainStatus{
+ blockHeight: 2,
+ blockHash: blocks[2][0].Hash(),
+ },
+ },
+ }
+
+ defer os.RemoveAll("temp")
+
+ for i, c := range cases {
+ chainDB := dbm.NewDB("core", "leveldb", "temp")
+ store := database.NewStore(chainDB)
+ if err := initStore(store, c.savedBlocks); err != nil {
+ t.Fatal(err)
+ }
+
+ movDB := dbm.NewDB("mov", "leveldb", "temp")
+ movCore := mov.NewCoreWithDB(movDatabase.NewLevelDBMovStore(movDB), c.startHeight)
+ if c.startHash != nil {
+ if err := movCore.InitChainStatus(c.startHash); err != nil {
+ t.Fatal(err)
+ }
+ }
+
+ _, err := protocol.NewChain(store, nil, []protocol.SubProtocol{movCore}, nil)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ gotHeight, gotHash, err := movCore.ChainStatus()
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if gotHeight != c.wantChainStatus.blockHeight || *gotHash != c.wantChainStatus.blockHash {
+ t.Logf("#%d(%s): got chain status of sub protocol is not equals want chain status", i, c.desc)
+ }
+
+ movDB.Close()
+ chainDB.Close()
+ os.RemoveAll("temp")
+ }
+}
+
+func initStore(store *database.Store, savedBlocks []*chainBlock) error {
+ var mainBlockHeaders []*types.BlockHeader
+ for _, block := range savedBlocks {
+ if err := store.SaveBlock(block.block, bc.NewTransactionStatus()); err != nil {
+ return err
+ }
+
+ if block.inMainChain {
+ mainBlockHeaders = append(mainBlockHeaders, &block.block.BlockHeader)
+ }
+
+ last := len(mainBlockHeaders) - 1
+ if err := store.SaveChainStatus(mainBlockHeaders[last], mainBlockHeaders[last], mainBlockHeaders, state.NewUtxoViewpoint(), nil); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+func hashPtr(hash bc.Hash) *bc.Hash {
+ return &hash
+}
+
+func TestBlockHash(t *testing.T) {
+ blockHash := blocks[1][0].Hash()
+ t.Log(blockHash.String())
+}