"github.com/vapor/common"
"github.com/vapor/config"
+ "github.com/vapor/errors"
"github.com/vapor/event"
"github.com/vapor/protocol/bc"
"github.com/vapor/protocol/bc/types"
"github.com/vapor/protocol/state"
)
-const maxProcessBlockChSize = 1024
+const (
+ maxProcessBlockChSize = 1024
+ maxKnownTxs = 32768 // Maximum transactions hashes to keep in the known list (prevent DOS)
+)
+
+type Protocoler interface {
+ Name() string
+ SyncStatus() error
+ Status() (uint64, *bc.Hash)
+ ValidateBlock(block *bc.Block) error
+ ValidateTxs(txs []*bc.Tx) error
+ ApplyBlock(block *types.Block) error
+ DetachBlock(block *types.Block) error
+}
// Chain provides functions for working with the Bytom block chain.
type Chain struct {
txPool *TxPool
store Store
processBlockCh chan *processBlockMsg
+ subProtocols []Protocoler
signatureCache *common.Cache
eventDispatcher *event.Dispatcher
cond sync.Cond
- bestBlockHeader *types.BlockHeader
- bestIrrBlockHeader *types.BlockHeader
+ bestBlockHeader *types.BlockHeader // the last block on current main chain
+ lastIrrBlockHeader *types.BlockHeader // the last irreversible block
+
+ knownTxs *common.OrderedSet
}
// NewChain returns a new Chain using store as the underlying storage.
-func NewChain(store Store, txPool *TxPool, eventDispatcher *event.Dispatcher) (*Chain, error) {
+func NewChain(store Store, txPool *TxPool, subProtocols []Protocoler, eventDispatcher *event.Dispatcher) (*Chain, error) {
+ knownTxs, _ := common.NewOrderedSet(maxKnownTxs)
c := &Chain{
orphanManage: NewOrphanManage(),
txPool: txPool,
store: store,
+ subProtocols: subProtocols,
signatureCache: common.NewCache(maxSignatureCacheSize),
eventDispatcher: eventDispatcher,
processBlockCh: make(chan *processBlockMsg, maxProcessBlockChSize),
+ knownTxs: knownTxs,
}
c.cond.L = new(sync.Mutex)
return nil, err
}
- c.bestIrrBlockHeader, err = c.store.GetBlockHeader(storeStatus.IrreversibleHash)
+ c.lastIrrBlockHeader, err = c.store.GetBlockHeader(storeStatus.IrreversibleHash)
if err != nil {
return nil, err
}
+
+ for _, p := range c.subProtocols {
+ if err := c.syncProtocolStatus(p); err != nil {
+ return nil, errors.Wrap(err, p.Name(), "sync sub protocol status")
+ }
+ }
+
go c.blockProcesser()
return c, nil
}
return c.bestBlockHeader.Height
}
-// BestBlockHash return the hash of the chain tail block
+// BestBlockHash return the hash of the main chain tail block
func (c *Chain) BestBlockHash() *bc.Hash {
c.cond.L.Lock()
defer c.cond.L.Unlock()
return &bestHash
}
-// BestIrreversibleHeader returns the chain best irreversible block header
-func (c *Chain) BestIrreversibleHeader() *types.BlockHeader {
+// LastIrreversibleHeader returns the chain last irreversible block header
+func (c *Chain) LastIrreversibleHeader() *types.BlockHeader {
c.cond.L.Lock()
defer c.cond.L.Unlock()
- return c.bestIrrBlockHeader
+ return c.lastIrrBlockHeader
}
// BestBlockHeader returns the chain best block header
return *blockHash == hash
}
+// trace back to the tail of the chain from the given block header
+func (c *Chain) traceLongestChainTail(blockHeader *types.BlockHeader) (*types.BlockHeader, error) {
+ longestTail, workQueue := blockHeader, []*types.BlockHeader{blockHeader}
+
+ for ; len(workQueue) > 0; workQueue = workQueue[1:] {
+ currentHeader := workQueue[0]
+ currentHash := currentHeader.Hash()
+ hashes, err := c.store.GetBlockHashesByHeight(currentHeader.Height + 1)
+ if err != nil {
+ return nil, err
+ }
+
+ for _, h := range hashes {
+ if header, err := c.store.GetBlockHeader(h); err != nil {
+ return nil, err
+ } else if header.PreviousBlockHash == currentHash {
+ if longestTail.Height < header.Height {
+ longestTail = header
+ }
+ workQueue = append(workQueue, header)
+ }
+ }
+ }
+ return longestTail, nil
+}
+
+func (c *Chain) hasSeenTx(tx *types.Tx) bool {
+ return c.knownTxs.Has(tx.ID.String())
+}
+
+func (c *Chain) markTransactions(txs ...*types.Tx) {
+ for _, tx := range txs {
+ c.knownTxs.Add(tx.ID.String())
+ }
+}
+
+func (c *Chain) syncProtocolStatus(subProtocol Protocoler) error {
+ protocolHeight, protocolHash := subProtocol.Status()
+ if protocolHeight == c.BestBlockHeight() && protocolHash == c.BestBlockHash() {
+ return nil
+ }
+
+ for !c.InMainChain(*protocolHash) {
+ block, err := c.GetBlockByHash(protocolHash)
+ if err != nil {
+ return errors.Wrap(err, subProtocol.Name(), "can't get block by hash in chain")
+ }
+
+ if err := subProtocol.DetachBlock(block); err != nil {
+ return errors.Wrap(err, subProtocol.Name(), "sub protocol detach block err")
+ }
+
+ protocolHeight, protocolHash = subProtocol.Status()
+ }
+
+ for height := protocolHeight + 1; height <= c.BestBlockHeight(); height++ {
+ block, err := c.GetBlockByHeight(height)
+ if err != nil {
+ return errors.Wrap(err, subProtocol.Name(), "can't get block by height in chain")
+ }
+
+ if err := subProtocol.ApplyBlock(block); err != nil {
+ return errors.Wrap(err, subProtocol.Name(), "sub protocol apply block err")
+ }
+
+ protocolHeight, protocolHash = subProtocol.Status()
+ if *protocolHash != block.Hash() {
+ return errors.Wrap(errors.New("sub protocol status sync err"), subProtocol.Name())
+ }
+ }
+
+ return nil
+}
+
// This function must be called with mu lock in above level
func (c *Chain) setState(blockHeader, irrBlockHeader *types.BlockHeader, mainBlockHeaders []*types.BlockHeader, view *state.UtxoViewpoint, consensusResults []*state.ConsensusResult) error {
if err := c.store.SaveChainStatus(blockHeader, irrBlockHeader, mainBlockHeaders, view, consensusResults); err != nil {
}
c.bestBlockHeader = blockHeader
- c.bestIrrBlockHeader = irrBlockHeader
+ c.lastIrrBlockHeader = irrBlockHeader
blockHash := blockHeader.Hash()
log.WithFields(log.Fields{"module": logModule, "height": blockHeader.Height, "hash": blockHash.String()}).Debug("chain best status has been update")