OSDN Git Service

update master (#487)
[bytom/bytom.git] / protocol / protocol.go
old mode 100644 (file)
new mode 100755 (executable)
index 56a0b86..38f405d
@@ -1,43 +1,3 @@
-/*
-Package protocol provides the logic to tie together
-storage and validation for a Chain Protocol blockchain.
-
-This comprises all behavior that's common to every full
-node, as well as other functions that need to operate on the
-blockchain state.
-
-Here are a few examples of typical full node types.
-
-Generator
-
-A generator has two basic jobs: collecting transactions from
-other nodes and putting them into blocks.
-
-To add a new block to the blockchain, call GenerateBlock,
-sign the block (possibly collecting signatures from other
-parties), and call CommitBlock.
-
-Signer
-
-A signer validates blocks generated by the Generator and signs
-at most one block at each height.
-
-Participant
-
-A participant node in a network may select outputs for spending
-and compose transactions.
-
-To publish a new transaction, prepare your transaction
-(select outputs, and compose and sign the tx) and send the
-transaction to the network's generator. To wait for
-confirmation, call BlockWaiter on successive block heights
-and inspect the blockchain state until you find that the
-transaction has been either confirmed or rejected. Note
-that transactions may be malleable if there's no commitment
-to TXSIGHASH.
-
-To ingest a block, call ValidateBlock and CommitBlock.
-*/
 package protocol
 
 import (
@@ -45,13 +5,13 @@ import (
        "sync"
        "time"
 
-       "github.com/golang/groupcache/lru"
-
-       "github.com/blockchain/errors"
-       //"github.com/blockchain/log"
-       "github.com/blockchain/protocol/bc"
-       "github.com/blockchain/protocol/bc/legacy"
-       "github.com/blockchain/protocol/state"
+       "github.com/bytom/consensus"
+       "github.com/bytom/database"
+       "github.com/bytom/database/storage"
+       "github.com/bytom/errors"
+       "github.com/bytom/protocol/bc"
+       "github.com/bytom/protocol/bc/types"
+       "github.com/bytom/protocol/state"
 )
 
 // maxCachedValidatedTxs is the max number of validated txs to cache.
@@ -63,21 +23,74 @@ var (
        ErrTheDistantFuture = errors.New("block height too far in future")
 )
 
-// Store provides storage for blockchain data: blocks and state tree
-// snapshots.
-//
-// Note, this is different from a state snapshot. A state snapshot
-// provides access to the state at a given point in time -- outputs
-// and issuance memory. The Chain type uses Store to load state
-// from storage and persist validated data.
-type Store interface {
-       Height(context.Context) (uint64, error)
-       GetBlock(context.Context, uint64) (*legacy.Block, error)
-//     LatestSnapshot(context.Context) (*state.Snapshot, uint64, error)
+// OrphanManage is use to handle all the orphan block
+type OrphanManage struct {
+       //TODO: add orphan cached block limit
+       orphan     map[bc.Hash]*types.Block
+       preOrphans map[bc.Hash][]*bc.Hash
+       mtx        sync.RWMutex
+}
 
-//     SaveBlock(context.Context, *legacy.Block) error
-       FinalizeBlock(context.Context, uint64) error
-//     SaveSnapshot(context.Context, uint64, *state.Snapshot) error
+// NewOrphanManage return a new orphan block
+func NewOrphanManage() *OrphanManage {
+       return &OrphanManage{
+               orphan:     make(map[bc.Hash]*types.Block),
+               preOrphans: make(map[bc.Hash][]*bc.Hash),
+       }
+}
+
+// BlockExist check is the block in OrphanManage
+func (o *OrphanManage) BlockExist(hash *bc.Hash) bool {
+       o.mtx.RLock()
+       _, ok := o.orphan[*hash]
+       o.mtx.RUnlock()
+       return ok
+}
+
+// Add will add the block to OrphanManage
+func (o *OrphanManage) Add(block *types.Block) {
+       blockHash := block.Hash()
+       o.mtx.Lock()
+       defer o.mtx.Unlock()
+
+       if _, ok := o.orphan[blockHash]; ok {
+               return
+       }
+
+       o.orphan[blockHash] = block
+       o.preOrphans[block.PreviousBlockHash] = append(o.preOrphans[block.PreviousBlockHash], &blockHash)
+}
+
+// Delete will delelte the block from OrphanManage
+func (o *OrphanManage) Delete(hash *bc.Hash) {
+       o.mtx.Lock()
+       defer o.mtx.Unlock()
+       block, ok := o.orphan[*hash]
+       if !ok {
+               return
+       }
+       delete(o.orphan, *hash)
+
+       preOrphans, ok := o.preOrphans[block.PreviousBlockHash]
+       if !ok || len(preOrphans) == 1 {
+               delete(o.preOrphans, block.PreviousBlockHash)
+               return
+       }
+
+       for i, preOrphan := range preOrphans {
+               if preOrphan == hash {
+                       o.preOrphans[block.PreviousBlockHash] = append(preOrphans[:i], preOrphans[i+1:]...)
+                       return
+               }
+       }
+}
+
+// Get return the orphan block by hash
+func (o *OrphanManage) Get(hash *bc.Hash) (*types.Block, bool) {
+       o.mtx.RLock()
+       block, ok := o.orphan[*hash]
+       o.mtx.RUnlock()
+       return block, ok
 }
 
 // Chain provides a complete, minimal blockchain database. It
@@ -88,67 +101,42 @@ type Chain struct {
        InitialBlockHash  bc.Hash
        MaxIssuanceWindow time.Duration // only used by generators
 
+       orphanManage *OrphanManage
+       txPool       *TxPool
+
        state struct {
-               cond     sync.Cond // protects height, block, snapshot
-               height   uint64
-               block    *legacy.Block   // current only if leader
-               snapshot *state.Snapshot // current only if leader
+               cond      sync.Cond
+               block     *types.Block
+               hash      *bc.Hash
+               mainChain map[uint64]*bc.Hash
        }
-       store Store
-
-       lastQueuedSnapshot time.Time
-       pendingSnapshots   chan pendingSnapshot
-
-       prevalidated prevalidatedTxsCache
-}
-
-type pendingSnapshot struct {
-       height   uint64
-       snapshot *state.Snapshot
+       store database.Store
 }
 
 // NewChain returns a new Chain using store as the underlying storage.
-func NewChain(ctx context.Context, initialBlockHash bc.Hash, store Store, heights <-chan uint64) (*Chain, error) {
+func NewChain(initialBlockHash bc.Hash, store database.Store, txPool *TxPool) (*Chain, error) {
        c := &Chain{
                InitialBlockHash: initialBlockHash,
+               orphanManage:     NewOrphanManage(),
                store:            store,
-               pendingSnapshots: make(chan pendingSnapshot, 1),
-               prevalidated: prevalidatedTxsCache{
-                       lru: lru.New(maxCachedValidatedTxs),
-               },
+               txPool:           txPool,
        }
        c.state.cond.L = new(sync.Mutex)
+       storeStatus := store.GetStoreStatus()
 
-       var err error
-       c.state.height, err = store.Height(ctx)
-       if err != nil {
-               return nil, errors.Wrap(err, "looking up blockchain height")
+       if storeStatus.Hash == nil {
+               c.state.mainChain = make(map[uint64]*bc.Hash)
+               return c, nil
        }
 
-       // Note that c.height.n may still be zero here.
-       if heights != nil {
-               go func() {
-                       for h := range heights {
-                               c.setHeight(h)
-                       }
-               }()
+       c.state.hash = storeStatus.Hash
+       var err error
+       if c.state.block, err = store.GetBlock(storeStatus.Hash); err != nil {
+               return nil, err
+       }
+       if c.state.mainChain, err = store.GetMainchain(storeStatus.Hash); err != nil {
+               return nil, err
        }
-
-       go func() {
-               for {
-                       select {
-                       case <-ctx.Done():
-                               return
-                       //case ps := <-c.pendingSnapshots:
-                               /*err = store.SaveSnapshot(ctx, ps.height, ps.snapshot)
-                               if err != nil {
-                                       log.Error(ctx, err, "at", "saving snapshot")
-                               }
-                */
-                       }
-               }
-       }()
-
        return c, nil
 }
 
@@ -156,37 +144,96 @@ func NewChain(ctx context.Context, initialBlockHash bc.Hash, store Store, height
 func (c *Chain) Height() uint64 {
        c.state.cond.L.Lock()
        defer c.state.cond.L.Unlock()
-       return c.state.height
+       if c.state.block == nil {
+               return 0
+       }
+       return c.state.block.Height
 }
 
-// TimestampMS returns the latest known block timestamp.
-func (c *Chain) TimestampMS() uint64 {
+// BestBlockHash return the hash of the chain tail block
+func (c *Chain) BestBlockHash() *bc.Hash {
        c.state.cond.L.Lock()
        defer c.state.cond.L.Unlock()
-       if c.state.block == nil {
-               return 0
+       return c.state.hash
+}
+
+func (c *Chain) inMainchain(block *types.Block) bool {
+       hash, ok := c.state.mainChain[block.Height]
+       if !ok {
+               return false
        }
-       return c.state.block.TimestampMS
+       return *hash == block.Hash()
 }
 
-// State returns the most recent state available. It will not be current
-// unless the current process is the leader. Callers should examine the
-// returned block header's height if they need to verify the current state.
-func (c *Chain) State() (*legacy.Block, *state.Snapshot) {
+// InMainChain checks wheather a block is in the main chain
+func (c *Chain) InMainChain(height uint64, hash bc.Hash) bool {
+       c.state.cond.L.Lock()
+       h, ok := c.state.mainChain[height]
+       c.state.cond.L.Unlock()
+       if !ok {
+               return false
+       }
+
+       return *h == hash
+}
+
+// Timestamp returns the latest known block timestamp.
+func (c *Chain) Timestamp() uint64 {
        c.state.cond.L.Lock()
        defer c.state.cond.L.Unlock()
-       return c.state.block, c.state.snapshot
+       if c.state.block == nil {
+               return 0
+       }
+       return c.state.block.Timestamp
 }
 
-func (c *Chain) setState(b *legacy.Block, s *state.Snapshot) {
+// BestBlock returns the chain tail block
+func (c *Chain) BestBlock() *types.Block {
        c.state.cond.L.Lock()
        defer c.state.cond.L.Unlock()
-       c.state.block = b
-       c.state.snapshot = s
-       if b != nil && b.Height > c.state.height {
-               c.state.height = b.Height
-               c.state.cond.Broadcast()
+       return c.state.block
+}
+
+// GetUtxo try to find the utxo status in db
+func (c *Chain) GetUtxo(hash *bc.Hash) (*storage.UtxoEntry, error) {
+       return c.store.GetUtxo(hash)
+}
+
+// GetSeed return the seed for the given block
+func (c *Chain) GetSeed(height uint64, preBlock *bc.Hash) (*bc.Hash, error) {
+       if height == 0 {
+               return consensus.InitialSeed, nil
+       } else if height%consensus.SeedPerRetarget == 0 {
+               return preBlock, nil
+       }
+       return c.store.GetSeed(preBlock)
+}
+
+// GetTransactionStatus return the transaction status of give block
+func (c *Chain) GetTransactionStatus(hash *bc.Hash) (*bc.TransactionStatus, error) {
+       return c.store.GetTransactionStatus(hash)
+}
+
+// GetTransactionsUtxo return all the utxos that related to the txs' inputs
+func (c *Chain) GetTransactionsUtxo(view *state.UtxoViewpoint, txs []*bc.Tx) error {
+       return c.store.GetTransactionsUtxo(view, txs)
+}
+
+// This function must be called with mu lock in above level
+func (c *Chain) setState(block *types.Block, view *state.UtxoViewpoint, m map[uint64]*bc.Hash) error {
+       blockHash := block.Hash()
+       c.state.block = block
+       c.state.hash = &blockHash
+       for k, v := range m {
+               c.state.mainChain[k] = v
+       }
+
+       if err := c.store.SaveChainStatus(block, view, c.state.mainChain); err != nil {
+               return err
        }
+
+       c.state.cond.Broadcast()
+       return nil
 }
 
 // BlockSoonWaiter returns a channel that
@@ -222,7 +269,7 @@ func (c *Chain) BlockWaiter(height uint64) <-chan struct{} {
        go func() {
                c.state.cond.L.Lock()
                defer c.state.cond.L.Unlock()
-               for c.state.height < height {
+               for c.state.block.Height < height {
                        c.state.cond.Wait()
                }
                ch <- struct{}{}