X-Git-Url: http://git.osdn.net/view?a=blobdiff_plain;f=protocol%2Fblock.go;h=72a2dd7b73587b6c54224483c254d9532cc29b0b;hb=1e9eb32359e203543f50b70fbc5668fc0e347ff0;hp=aafb60c89754bedf804c159057bd1530b1bf16e6;hpb=faf2a71fa583fcbc06ae9de3363f891c888abde4;p=bytom%2Fbytom.git diff --git a/protocol/block.go b/protocol/block.go index aafb60c8..72a2dd7b 100644 --- a/protocol/block.go +++ b/protocol/block.go @@ -1,9 +1,11 @@ package protocol import ( + log "github.com/sirupsen/logrus" + "github.com/bytom/errors" "github.com/bytom/protocol/bc" - "github.com/bytom/protocol/bc/legacy" + "github.com/bytom/protocol/bc/types" "github.com/bytom/protocol/state" "github.com/bytom/protocol/validation" ) @@ -11,150 +13,242 @@ import ( var ( // ErrBadBlock is returned when a block is invalid. ErrBadBlock = errors.New("invalid block") - - // ErrStaleState is returned when the Chain does not have a current - // blockchain state. - ErrStaleState = errors.New("stale blockchain state") - // ErrBadStateRoot is returned when the computed assets merkle root // disagrees with the one declared in a block header. ErrBadStateRoot = errors.New("invalid state merkle root") ) +// BlockExist check is a block in chain or orphan func (c *Chain) BlockExist(hash *bc.Hash) bool { - return c.orphanManage.BlockExist(hash) || c.store.BlockExist(hash) + return c.index.BlockExist(hash) || c.orphanManage.BlockExist(hash) } -func (c *Chain) GetBlockByHash(hash *bc.Hash) (*legacy.Block, error) { +// GetBlockByHash return a block by given hash +func (c *Chain) GetBlockByHash(hash *bc.Hash) (*types.Block, error) { return c.store.GetBlock(hash) } -func (c *Chain) GetBlockByHeight(height uint64) (*legacy.Block, error) { - c.state.cond.L.Lock() - hash, ok := c.state.mainChain[height] - c.state.cond.L.Unlock() - if !ok { - return nil, errors.New("can't find block in given hight") +// GetBlockByHeight return a block header by given height +func (c *Chain) GetBlockByHeight(height uint64) (*types.Block, error) { + node := c.index.NodeByHeight(height) + if node == nil { + return nil, errors.New("can't find block in given height") } - return c.GetBlockByHash(hash) + return c.store.GetBlock(&node.Hash) } -// ValidateBlock validates an incoming block in advance of applying it -// to a snapshot (with ApplyValidBlock) and committing it to the -// blockchain (with CommitAppliedBlock). -func (c *Chain) ValidateBlock(block, prev *legacy.Block) error { - blockEnts := legacy.MapBlock(block) - prevEnts := legacy.MapBlock(prev) - if err := validation.ValidateBlock(blockEnts, prevEnts); err != nil { - return errors.Sub(ErrBadBlock, err) +// GetHeaderByHash return a block header by given hash +func (c *Chain) GetHeaderByHash(hash *bc.Hash) (*types.BlockHeader, error) { + node := c.index.GetNode(hash) + if node == nil { + return nil, errors.New("can't find block header in given hash") } - return nil + return node.BlockHeader(), nil } -// ApplyValidBlock creates an updated snapshot without validating the -// block. -func (c *Chain) ConnectBlock(block *legacy.Block) error { - newSnapshot := state.Copy(c.state.snapshot) - if err := newSnapshot.ApplyBlock(legacy.MapBlock(block)); err != nil { - return err +// GetHeaderByHeight return a block header by given height +func (c *Chain) GetHeaderByHeight(height uint64) (*types.BlockHeader, error) { + node := c.index.NodeByHeight(height) + if node == nil { + return nil, errors.New("can't find block header in given height") } + return node.BlockHeader(), nil +} - blockHash := block.Hash() - if err := c.setState(block, newSnapshot, map[uint64]*bc.Hash{block.Height: &blockHash}); err != nil { - return err +func (c *Chain) calcReorganizeNodes(node *state.BlockNode) ([]*state.BlockNode, []*state.BlockNode) { + var attachNodes []*state.BlockNode + var detachNodes []*state.BlockNode + + attachNode := node + for c.index.NodeByHeight(attachNode.Height) != attachNode { + attachNodes = append([]*state.BlockNode{attachNode}, attachNodes...) + attachNode = attachNode.Parent } - for _, tx := range block.Transactions { - c.txPool.RemoveTransaction(&tx.Tx.ID) + detachNode := c.bestNode + for detachNode != attachNode { + detachNodes = append(detachNodes, detachNode) + detachNode = detachNode.Parent } - return nil + return attachNodes, detachNodes } -func (c *Chain) getReorganizeBlocks(block *legacy.Block) ([]*legacy.Block, []*legacy.Block) { - attachBlocks := []*legacy.Block{} - detachBlocks := []*legacy.Block{} - ancestor := block +func (c *Chain) connectBlock(block *types.Block) (err error) { + bcBlock := types.MapBlock(block) + if bcBlock.TransactionStatus, err = c.store.GetTransactionStatus(&bcBlock.ID); err != nil { + return err + } - for !c.InMainchain(ancestor) { - attachBlocks = append([]*legacy.Block{ancestor}, attachBlocks...) - ancestor, _ = c.GetBlockByHash(&ancestor.PreviousBlockHash) + utxoView := state.NewUtxoViewpoint() + if err := c.store.GetTransactionsUtxo(utxoView, bcBlock.Transactions); err != nil { + return err + } + if err := utxoView.ApplyBlock(bcBlock, bcBlock.TransactionStatus); err != nil { + return err } - for d := c.state.block; d.Hash() != ancestor.Hash(); d, _ = c.GetBlockByHash(&d.PreviousBlockHash) { - detachBlocks = append(detachBlocks, d) + node := c.index.GetNode(&bcBlock.ID) + if err := c.setState(node, utxoView); err != nil { + return err } - return attachBlocks, detachBlocks + for _, tx := range block.Transactions { + c.txPool.RemoveTransaction(&tx.Tx.ID) + } + return nil } -func (c *Chain) reorganizeChain(block *legacy.Block) error { - attachBlocks, detachBlocks := c.getReorganizeBlocks(block) - newSnapshot := state.Copy(c.state.snapshot) - chainChanges := map[uint64]*bc.Hash{} +func (c *Chain) reorganizeChain(node *state.BlockNode) error { + attachNodes, detachNodes := c.calcReorganizeNodes(node) + utxoView := state.NewUtxoViewpoint() - for _, d := range detachBlocks { - if err := newSnapshot.DetachBlock(legacy.MapBlock(d)); err != nil { + for _, detachNode := range detachNodes { + b, err := c.store.GetBlock(&detachNode.Hash) + if err != nil { return err } + + detachBlock := types.MapBlock(b) + if err := c.store.GetTransactionsUtxo(utxoView, detachBlock.Transactions); err != nil { + return err + } + txStatus, err := c.GetTransactionStatus(&detachBlock.ID) + if err != nil { + return err + } + if err := utxoView.DetachBlock(detachBlock, txStatus); err != nil { + return err + } + + log.WithFields(log.Fields{"height": node.Height, "hash": node.Hash.String()}).Debug("detach from mainchain") } - for _, a := range attachBlocks { - if err := newSnapshot.ApplyBlock(legacy.MapBlock(a)); err != nil { + for _, attachNode := range attachNodes { + b, err := c.store.GetBlock(&attachNode.Hash) + if err != nil { return err } - aHash := a.Hash() - chainChanges[a.Height] = &aHash + + attachBlock := types.MapBlock(b) + if err := c.store.GetTransactionsUtxo(utxoView, attachBlock.Transactions); err != nil { + return err + } + txStatus, err := c.GetTransactionStatus(&attachBlock.ID) + if err != nil { + return err + } + if err := utxoView.ApplyBlock(attachBlock, txStatus); err != nil { + return err + } + + log.WithFields(log.Fields{"height": node.Height, "hash": node.Hash.String()}).Debug("attach from mainchain") } - return c.setState(block, newSnapshot, chainChanges) + return c.setState(node, utxoView) } -func (c *Chain) SaveBlock(block *legacy.Block) error { - preBlock, _ := c.GetBlockByHash(&block.PreviousBlockHash) - if err := c.ValidateBlock(block, preBlock); err != nil { +// SaveBlock will validate and save block into storage +func (c *Chain) saveBlock(block *types.Block) error { + bcBlock := types.MapBlock(block) + parent := c.index.GetNode(&block.PreviousBlockHash) + + if err := validation.ValidateBlock(bcBlock, parent); err != nil { + return errors.Sub(ErrBadBlock, err) + } + if err := c.store.SaveBlock(block, bcBlock.TransactionStatus); err != nil { return err } - if err := c.store.SaveBlock(block); err != nil { + + c.orphanManage.Delete(&bcBlock.ID) + node, err := state.NewBlockNode(&block.BlockHeader, parent) + if err != nil { return err } - preorphans, ok := c.orphanManage.preOrphans[block.Hash()] + c.index.AddNode(node) + return nil +} + +func (c *Chain) saveSubBlock(block *types.Block) *types.Block { + blockHash := block.Hash() + prevOrphans, ok := c.orphanManage.GetPrevOrphans(&blockHash) if !ok { - return nil + return block } - for _, preorphan := range preorphans { - orphanBlock, ok := c.orphanManage.Get(preorphan) + + bestBlock := block + for _, prevOrphan := range prevOrphans { + orphanBlock, ok := c.orphanManage.Get(prevOrphan) if !ok { + log.WithFields(log.Fields{"hash": prevOrphan.String()}).Warning("saveSubBlock fail to get block from orphanManage") + continue + } + if err := c.saveBlock(orphanBlock); err != nil { + log.WithFields(log.Fields{"hash": prevOrphan.String(), "height": orphanBlock.Height}).Warning("saveSubBlock fail to save block") continue } - c.SaveBlock(orphanBlock) - c.orphanManage.Delete(preorphan) + + if subBestBlock := c.saveSubBlock(orphanBlock); subBestBlock.Height > bestBlock.Height { + bestBlock = subBestBlock + } + } + return bestBlock +} + +type processBlockResponse struct { + isOrphan bool + err error +} + +type processBlockMsg struct { + block *types.Block + reply chan processBlockResponse +} + +// ProcessBlock is the entry for chain update +func (c *Chain) ProcessBlock(block *types.Block) (bool, error) { + reply := make(chan processBlockResponse, 1) + c.processBlockCh <- &processBlockMsg{block: block, reply: reply} + response := <-reply + return response.isOrphan, response.err +} + +func (c *Chain) blockProcesser() { + for msg := range c.processBlockCh { + isOrphan, err := c.processBlock(msg.block) + msg.reply <- processBlockResponse{isOrphan: isOrphan, err: err} } - return nil } -func (c *Chain) ProcessBlock(block *legacy.Block) (bool, error) { - if blockHash := block.Hash(); c.BlockExist(&blockHash) { - return false, nil +// ProcessBlock is the entry for handle block insert +func (c *Chain) processBlock(block *types.Block) (bool, error) { + blockHash := block.Hash() + if c.BlockExist(&blockHash) { + log.WithFields(log.Fields{"hash": blockHash.String(), "height": block.Height}).Info("block has been processed") + return c.orphanManage.BlockExist(&blockHash), nil } - if !c.BlockExist(&block.PreviousBlockHash) { + + if parent := c.index.GetNode(&block.PreviousBlockHash); parent == nil { c.orphanManage.Add(block) return true, nil } - if err := c.SaveBlock(block); err != nil { + + if err := c.saveBlock(block); err != nil { return false, err } - c.state.cond.L.Lock() - if c.state.block.Hash() == block.PreviousBlockHash { - defer c.state.cond.L.Unlock() - return false, c.ConnectBlock(block) + bestBlock := c.saveSubBlock(block) + bestBlockHash := bestBlock.Hash() + bestNode := c.index.GetNode(&bestBlockHash) + + if bestNode.Parent == c.bestNode { + log.Debug("append block to the end of mainchain") + return false, c.connectBlock(bestBlock) } - if block.Height > c.state.height && block.Bits >= c.state.block.Bits { - defer c.state.cond.L.Unlock() - return false, c.reorganizeChain(block) + if bestNode.Height > c.bestNode.Height && bestNode.WorkSum.Cmp(c.bestNode.WorkSum) >= 0 { + log.Debug("start to reorganize chain") + return false, c.reorganizeChain(bestNode) } - c.state.cond.L.Unlock() return false, nil }