OSDN Git Service

Mov (#518)
[bytom/vapor.git] / protocol / protocol.go
index 80cb53a..a1cbb70 100644 (file)
@@ -5,15 +5,31 @@ import (
 
        log "github.com/sirupsen/logrus"
 
-       "github.com/vapor/common"
-       "github.com/vapor/config"
-       "github.com/vapor/event"
-       "github.com/vapor/protocol/bc"
-       "github.com/vapor/protocol/bc/types"
-       "github.com/vapor/protocol/state"
+       "github.com/bytom/vapor/common"
+       "github.com/bytom/vapor/config"
+       "github.com/bytom/vapor/errors"
+       "github.com/bytom/vapor/event"
+       "github.com/bytom/vapor/protocol/bc"
+       "github.com/bytom/vapor/protocol/bc/types"
+       "github.com/bytom/vapor/protocol/state"
 )
 
-const maxProcessBlockChSize = 1024
+const (
+       maxProcessBlockChSize = 1024
+       maxKnownTxs           = 32768 // Maximum transactions hashes to keep in the known list (prevent DOS)
+)
+
+// Protocoler is interface for layer 2 consensus protocol
+type Protocoler interface {
+       Name() string
+       StartHeight() uint64
+       BeforeProposalBlock(txs []*types.Tx, blockHeight uint64, gasLeft int64, isTimeout func() bool) ([]*types.Tx, error)
+       ChainStatus() (uint64, *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
+       DetachBlock(block *types.Block) error
+}
 
 // Chain provides functions for working with the Bytom block chain.
 type Chain struct {
@@ -21,6 +37,7 @@ type Chain struct {
        txPool         *TxPool
        store          Store
        processBlockCh chan *processBlockMsg
+       subProtocols   []Protocoler
 
        signatureCache  *common.Cache
        eventDispatcher *event.Dispatcher
@@ -28,17 +45,22 @@ type Chain struct {
        cond               sync.Cond
        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)
 
@@ -60,6 +82,13 @@ func NewChain(store Store, txPool *TxPool, eventDispatcher *event.Dispatcher) (*
        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
 }
@@ -83,7 +112,13 @@ func (c *Chain) initChainStatus() error {
                return err
        }
 
-       consensusResults := []*state.ConsensusResult{&state.ConsensusResult{
+       for _, subProtocol := range c.subProtocols {
+               if err := subProtocol.ApplyBlock(genesisBlock); err != nil {
+                       return err
+               }
+       }
+
+       consensusResults := []*state.ConsensusResult{{
                Seq:            0,
                NumOfVote:      make(map[string]uint64),
                CoinbaseReward: make(map[string]uint64),
@@ -139,14 +174,18 @@ func (c *Chain) InMainChain(hash bc.Hash) bool {
        return *blockHash == hash
 }
 
+// SubProtocols return list of layer 2 consensus protocol
+func (c *Chain) SubProtocols() []Protocoler {
+       return c.subProtocols
+}
+
 // 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 {
+       for ; len(workQueue) > 0; workQueue = workQueue[1:] {
                currentHeader := workQueue[0]
                currentHash := currentHeader.Hash()
-               workQueue = workQueue[1:]
                hashes, err := c.store.GetBlockHashesByHeight(currentHeader.Height + 1)
                if err != nil {
                        return nil, err
@@ -166,6 +205,60 @@ func (c *Chain) traceLongestChainTail(blockHeader *types.BlockHeader) (*types.Bl
        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 {
+       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 *protocolHash == c.bestBlockHeader.Hash() {
+               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 = block.Height-1, &block.PreviousBlockHash
+       }
+
+       for height := protocolHeight + 1; height <= c.bestBlockHeader.Height; 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")
+               }
+
+               blockHash := block.Hash()
+               protocolHeight, protocolHash = block.Height, &blockHash
+       }
+
+       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 {