OSDN Git Service

fix casper rollback (#1951)
authorPoseidon <shenao.78@163.com>
Tue, 8 Jun 2021 07:15:42 +0000 (15:15 +0800)
committerGitHub <noreply@github.com>
Tue, 8 Jun 2021 07:15:42 +0000 (15:15 +0800)
* fix casper rollback

* opt code

Co-authored-by: Paladz <yzhu101@uottawa.ca>
19 files changed:
database/store.go
database/store_test.go
netsync/chainmgr/block_keeper.go
netsync/chainmgr/block_keeper_test.go
netsync/chainmgr/fast_sync_test.go
netsync/chainmgr/handle.go
netsync/messages/chain_msg.go
netsync/peers/peer.go
netsync/peers/peer_test.go
protocol/apply_block.go
protocol/auth_verification.go
protocol/block.go
protocol/casper.go
protocol/protocol.go
protocol/store.go
protocol/tree_node.go
protocol/txpool_test.go
test/mock/chain.go
test/utxo_view/utxo_view_test.go

index b46f4cb..1a4594b 100644 (file)
@@ -231,7 +231,7 @@ func (s *Store) LoadBlockIndex(stateBestHeight uint64) (*state.BlockIndex, error
 }
 
 // SaveChainStatus save the core's newest status && delete old status
-func (s *Store) SaveChainStatus(node *state.BlockNode, view *state.UtxoViewpoint, contractView *state.ContractViewpoint, checkpoints []*state.Checkpoint, finalizedHeight uint64, finalizedHash *bc.Hash) error {
+func (s *Store) SaveChainStatus(node *state.BlockNode, view *state.UtxoViewpoint, contractView *state.ContractViewpoint, finalizedHeight uint64, finalizedHash *bc.Hash) error {
        batch := s.db.NewBatch()
        if err := saveUtxoView(batch, view); err != nil {
                return err
@@ -245,10 +245,6 @@ func (s *Store) SaveChainStatus(node *state.BlockNode, view *state.UtxoViewpoint
                return err
        }
 
-       if err := s.saveCheckpoints(batch, checkpoints); err != nil {
-               return err
-       }
-
        bytes, err := json.Marshal(protocol.BlockStoreState{Height: node.Height, Hash: &node.Hash, FinalizedHeight: finalizedHeight, FinalizedHash: finalizedHash})
        if err != nil {
                return err
@@ -346,12 +342,13 @@ func (s *Store) SaveCheckpoints(checkpoints []*state.Checkpoint) error {
 
 func (s *Store) saveCheckpoints(batch dbm.Batch, checkpoints []*state.Checkpoint) error {
        for _, checkpoint := range checkpoints {
+               startTime := time.Now()
                data, err := json.Marshal(checkpoint)
                if err != nil {
                        return err
                }
 
-               if checkpoint.Height % state.BlocksOfEpoch != 1 {
+               if checkpoint.Height%state.BlocksOfEpoch != 1 {
                        header, err := s.GetBlockHeader(&checkpoint.Hash)
                        if err != nil {
                                return err
@@ -361,6 +358,13 @@ func (s *Store) saveCheckpoints(batch dbm.Batch, checkpoints []*state.Checkpoint
                }
 
                batch.Set(calcCheckpointKey(checkpoint.Height, &checkpoint.Hash), data)
+               log.WithFields(log.Fields{
+                       "module": logModule,
+                       "height": checkpoint.Height,
+                       "hash":   checkpoint.Hash.String(),
+                       "status": checkpoint.Status,
+                       "duration": time.Since(startTime),
+               }).Info("checkpoint saved on disk")
        }
        return nil
 }
index 10cf0d1..ba91edc 100644 (file)
@@ -151,7 +151,7 @@ func TestSaveChainStatus(t *testing.T) {
        }
 
        contractView := state.NewContractViewpoint()
-       if err := store.SaveChainStatus(node, view, contractView, nil,0, &bc.Hash{}); err != nil {
+       if err := store.SaveChainStatus(node, view, contractView,0, &bc.Hash{}); err != nil {
                t.Fatal(err)
        }
 
index be27436..199c891 100644 (file)
@@ -197,9 +197,9 @@ func (bk *blockKeeper) start() {
 
 func (bk *blockKeeper) checkSyncType() int {
        bestHeight := bk.chain.BestBlockHeight()
-       peer := bk.peers.BestIrreversiblePeer(consensus.SFFullNode | consensus.SFFastSync)
+       peer := bk.peers.BestPeer(consensus.SFFullNode | consensus.SFFastSync)
        if peer != nil {
-               if peerIrreversibleHeight := peer.IrreversibleHeight(); peerIrreversibleHeight >= bestHeight+minGapStartFastSync {
+               if peerJustifiedHeight := peer.JustifiedHeight(); peerJustifiedHeight >= bestHeight+minGapStartFastSync {
                        bk.fastSync.setSyncPeer(peer)
                        return fastSyncType
                }
@@ -253,7 +253,7 @@ func (bk *blockKeeper) syncWorker() {
                                continue
                        }
 
-                       if err := bk.peers.BroadcastNewStatus(bk.chain.BestBlockHeader(), bk.chain.LastIrreversibleHeader()); err != nil {
+                       if err := bk.peers.BroadcastNewStatus(bk.chain.BestBlockHeader(), bk.chain.LastJustifiedHeader()); err != nil {
                                log.WithFields(log.Fields{"module": logModule, "err": err}).Error("fail on syncWorker broadcast new status")
                        }
                case <-bk.quit:
index 192db69..ccbd560 100644 (file)
@@ -98,7 +98,7 @@ func TestCheckSyncType(t *testing.T) {
                for _, syncPeer := range c.peers {
                        blockKeeper.peers.AddPeer(syncPeer.peer)
                        blockKeeper.peers.SetStatus(syncPeer.peer.id, syncPeer.bestHeight, nil)
-                       blockKeeper.peers.SetIrreversibleStatus(syncPeer.peer.id, syncPeer.irreversibleHeight, nil)
+                       blockKeeper.peers.SetJustifiedStatus(syncPeer.peer.id, syncPeer.irreversibleHeight, nil)
                }
                gotType := blockKeeper.checkSyncType()
                if c.syncType != gotType {
index 1deb7bb..3f5bc6a 100644 (file)
@@ -356,7 +356,7 @@ func TestCreateFetchBlocksTasks(t *testing.T) {
                for _, syncPeer := range c.peers {
                        peers.AddPeer(syncPeer.peer)
                        peers.SetStatus(syncPeer.peer.id, syncPeer.bestHeight, nil)
-                       peers.SetIrreversibleStatus(syncPeer.peer.id, syncPeer.irreversibleHeight, nil)
+                       peers.SetJustifiedStatus(syncPeer.peer.id, syncPeer.irreversibleHeight, nil)
                }
                mockChain := mock.NewChain()
                fs := newFastSync(mockChain, &mockFetcher{baseChain: baseChain, peerStatus: peerStatus, testType: c.testType}, nil, peers)
index 464471a..c857acc 100644 (file)
@@ -27,7 +27,7 @@ const (
 // Chain is the interface for Bytom core
 type Chain interface {
        BestBlockHeader() *types.BlockHeader
-       LastIrreversibleHeader() *types.BlockHeader
+       LastJustifiedHeader() *types.BlockHeader
        BestBlockHeight() uint64
        GetBlockByHash(*bc.Hash) (*types.Block, error)
        GetBlockByHeight(uint64) (*types.Block, error)
@@ -249,7 +249,7 @@ func (m *Manager) handleHeadersMsg(peer *peers.Peer, msg *msgs.HeadersMessage) {
 func (m *Manager) handleStatusMsg(basePeer peers.BasePeer, msg *msgs.StatusMessage) {
        if peer := m.peers.GetPeer(basePeer.ID()); peer != nil {
                peer.SetBestStatus(msg.BestHeight, msg.GetBestHash())
-               peer.SetIrreversibleStatus(msg.IrreversibleHeight, msg.GetIrreversibleHash())
+               peer.SetJustifiedStatus(msg.JustifiedHeight, msg.GetIrreversibleHash())
        }
 }
 
@@ -371,7 +371,7 @@ func (m *Manager) SendStatus(peer peers.BasePeer) error {
                return errors.New("invalid peer")
        }
 
-       if err := p.SendStatus(m.chain.BestBlockHeader(), m.chain.LastIrreversibleHeader()); err != nil {
+       if err := p.SendStatus(m.chain.BestBlockHeader(), m.chain.LastJustifiedHeader()); err != nil {
                m.peers.RemovePeer(p.ID())
                return err
        }
index 10b0f19..167449f 100644 (file)
@@ -270,19 +270,19 @@ func (m *BlocksMessage) String() string {
 
 //StatusResponseMessage get status response msg
 type StatusMessage struct {
-       BestHeight         uint64
-       BestHash           [32]byte
-       IrreversibleHeight uint64
-       IrreversibleHash   [32]byte
+       BestHeight      uint64
+       BestHash        [32]byte
+       JustifiedHeight uint64
+       JustifiedHash   [32]byte
 }
 
 //NewStatusResponseMessage construct get status response msg
-func NewStatusMessage(bestHeader, irreversibleHeader *types.BlockHeader) *StatusMessage {
+func NewStatusMessage(bestHeader, justifiedHeader *types.BlockHeader) *StatusMessage {
        return &StatusMessage{
-               BestHeight:         bestHeader.Height,
-               BestHash:           bestHeader.Hash().Byte32(),
-               IrreversibleHeight: irreversibleHeader.Height,
-               IrreversibleHash:   irreversibleHeader.Hash().Byte32(),
+               BestHeight:      bestHeader.Height,
+               BestHash:        bestHeader.Hash().Byte32(),
+               JustifiedHeight: justifiedHeader.Height,
+               JustifiedHash:   justifiedHeader.Hash().Byte32(),
        }
 }
 
@@ -293,12 +293,12 @@ func (m *StatusMessage) GetBestHash() *bc.Hash {
 }
 
 func (m *StatusMessage) GetIrreversibleHash() *bc.Hash {
-       hash := bc.NewHash(m.IrreversibleHash)
+       hash := bc.NewHash(m.JustifiedHash)
        return &hash
 }
 
 func (m *StatusMessage) String() string {
-       return fmt.Sprintf("{best hash: %s, irreversible hash: %s}", hex.EncodeToString(m.BestHash[:]), hex.EncodeToString(m.IrreversibleHash[:]))
+       return fmt.Sprintf("{best hash: %s, irreversible hash: %s}", hex.EncodeToString(m.BestHash[:]), hex.EncodeToString(m.JustifiedHash[:]))
 }
 
 //TransactionMessage notify new tx msg
index 15fdcca..c343538 100644 (file)
@@ -77,17 +77,17 @@ type PeerInfo struct {
 
 type Peer struct {
        BasePeer
-       mtx                sync.RWMutex
-       services           consensus.ServiceFlag
-       bestHeight         uint64
-       bestHash           *bc.Hash
-       irreversibleHeight uint64
-       irreversibleHash   *bc.Hash
-       knownTxs           *set.Set // Set of transaction hashes known to be known by this peer
-       knownBlocks        *set.Set // Set of block hashes known to be known by this peer
-       knownSignatures    *set.Set // Set of block signatures known to be known by this peer
-       knownStatus        uint64   // Set of chain status known to be known by this peer
-       filterAdds         *set.Set // Set of addresses that the spv node cares about.
+       mtx             sync.RWMutex
+       services        consensus.ServiceFlag
+       bestHeight      uint64
+       bestHash        *bc.Hash
+       justifiedHeight uint64
+       justifiedHash   *bc.Hash
+       knownTxs        *set.Set // Set of transaction hashes known to be known by this peer
+       knownBlocks     *set.Set // Set of block hashes known to be known by this peer
+       knownSignatures *set.Set // Set of block signatures known to be known by this peer
+       knownStatus     uint64   // Set of chain status known to be known by this peer
+       filterAdds      *set.Set // Set of addresses that the spv node cares about.
 }
 
 func newPeer(basePeer BasePeer) *Peer {
@@ -108,11 +108,11 @@ func (p *Peer) Height() uint64 {
        return p.bestHeight
 }
 
-func (p *Peer) IrreversibleHeight() uint64 {
+func (p *Peer) JustifiedHeight() uint64 {
        p.mtx.RLock()
        defer p.mtx.RUnlock()
 
-       return p.irreversibleHeight
+       return p.justifiedHeight
 }
 
 func (p *Peer) AddFilterAddress(address []byte) {
@@ -342,8 +342,8 @@ func (p *Peer) SendTransactions(txs []*types.Tx) error {
        return nil
 }
 
-func (p *Peer) SendStatus(bestHeader, irreversibleHeader *types.BlockHeader) error {
-       msg := msgs.NewStatusMessage(bestHeader, irreversibleHeader)
+func (p *Peer) SendStatus(bestHeader, justifiedHeader *types.BlockHeader) error {
+       msg := msgs.NewStatusMessage(bestHeader, justifiedHeader)
        if ok := p.TrySend(msgs.BlockchainChannel, struct{ msgs.BlockchainMessage }{msg}); !ok {
                return errSendStatusMsg
        }
@@ -359,12 +359,12 @@ func (p *Peer) SetBestStatus(bestHeight uint64, bestHash *bc.Hash) {
        p.bestHash = bestHash
 }
 
-func (p *Peer) SetIrreversibleStatus(irreversibleHeight uint64, irreversibleHash *bc.Hash) {
+func (p *Peer) SetJustifiedStatus(justifiedHeight uint64, justifiedHash *bc.Hash) {
        p.mtx.Lock()
        defer p.mtx.Unlock()
 
-       p.irreversibleHeight = irreversibleHeight
-       p.irreversibleHash = irreversibleHash
+       p.justifiedHeight = justifiedHeight
+       p.justifiedHash = justifiedHash
 }
 
 type PeerSet struct {
@@ -416,23 +416,9 @@ func (ps *PeerSet) BestPeer(flag consensus.ServiceFlag) *Peer {
                if !p.services.IsEnable(flag) {
                        continue
                }
-               if bestPeer == nil || p.bestHeight > bestPeer.bestHeight || (p.bestHeight == bestPeer.bestHeight && p.IsLAN()) {
-                       bestPeer = p
-               }
-       }
-       return bestPeer
-}
-
-func (ps *PeerSet) BestIrreversiblePeer(flag consensus.ServiceFlag) *Peer {
-       ps.mtx.RLock()
-       defer ps.mtx.RUnlock()
-
-       var bestPeer *Peer
-       for _, p := range ps.peers {
-               if !p.services.IsEnable(flag) {
-                       continue
-               }
-               if bestPeer == nil || p.irreversibleHeight > bestPeer.irreversibleHeight || (p.irreversibleHeight == bestPeer.irreversibleHeight && p.IsLAN()) {
+               if bestPeer == nil || p.JustifiedHeight() > bestPeer.JustifiedHeight() ||
+                       (p.JustifiedHeight() == bestPeer.JustifiedHeight() && p.bestHeight > bestPeer.bestHeight) ||
+                       (p.JustifiedHeight() == bestPeer.JustifiedHeight() && p.bestHeight == bestPeer.bestHeight && p.IsLAN()) {
                        bestPeer = p
                }
        }
@@ -474,8 +460,8 @@ func (ps *PeerSet) BroadcastMsg(bm BroadcastMsg) error {
        return nil
 }
 
-func (ps *PeerSet) BroadcastNewStatus(bestHeader, irreversibleHeader *types.BlockHeader) error {
-       msg := msgs.NewStatusMessage(bestHeader, irreversibleHeader)
+func (ps *PeerSet) BroadcastNewStatus(bestHeader, justifiedHeader *types.BlockHeader) error {
+       msg := msgs.NewStatusMessage(bestHeader, justifiedHeader)
        peers := ps.peersWithoutNewStatus(bestHeader.Height)
        for _, peer := range peers {
                if ok := peer.TrySend(msgs.BlockchainChannel, struct{ msgs.BlockchainMessage }{msg}); !ok {
@@ -648,11 +634,11 @@ func (ps *PeerSet) SetStatus(peerID string, height uint64, hash *bc.Hash) {
        peer.SetBestStatus(height, hash)
 }
 
-func (ps *PeerSet) SetIrreversibleStatus(peerID string, height uint64, hash *bc.Hash) {
+func (ps *PeerSet) SetJustifiedStatus(peerID string, height uint64, hash *bc.Hash) {
        peer := ps.GetPeer(peerID)
        if peer == nil {
                return
        }
 
-       peer.SetIrreversibleStatus(height, hash)
+       peer.SetJustifiedStatus(height, hash)
 }
index b49cdbc..41a903f 100644 (file)
@@ -86,8 +86,8 @@ func TestSetIrreversibleStatus(t *testing.T) {
        peer := newPeer(&basePeer{})
        height := uint64(100)
        hash := bc.NewHash([32]byte{0x1, 0x2})
-       peer.SetIrreversibleStatus(height, &hash)
-       if peer.IrreversibleHeight() != height {
+       peer.SetJustifiedStatus(height, &hash)
+       if peer.JustifiedHeight() != height {
                t.Fatalf("test set Irreversible status err. got %d want %d", peer.Height(), height)
        }
 }
@@ -341,12 +341,12 @@ func TestIrreversibleStatus(t *testing.T) {
        ps.AddPeer(&basePeer{id: peer2ID, serviceFlag: consensus.SFFullNode})
        ps.AddPeer(&basePeer{id: peer3ID, serviceFlag: consensus.SFFastSync})
        ps.AddPeer(&basePeer{id: peer4ID, serviceFlag: consensus.SFFastSync, isLan: true})
-       ps.SetIrreversibleStatus(peer1ID, 1000, &block1000Hash)
-       ps.SetIrreversibleStatus(peer2ID, 2000, &block2000Hash)
-       ps.SetIrreversibleStatus(peer3ID, 3000, &block3000Hash)
-       ps.SetIrreversibleStatus(peer4ID, 3000, &block3000Hash)
+       ps.SetJustifiedStatus(peer1ID, 1000, &block1000Hash)
+       ps.SetJustifiedStatus(peer2ID, 2000, &block2000Hash)
+       ps.SetJustifiedStatus(peer3ID, 3000, &block3000Hash)
+       ps.SetJustifiedStatus(peer4ID, 3000, &block3000Hash)
        targetPeer := peer4ID
-       peer := ps.BestIrreversiblePeer(consensus.SFFastSync)
+       peer := ps.BestPeer(consensus.SFFastSync)
 
        if peer.ID() != targetPeer {
                t.Fatalf("test set status err. Name of target peer %s got %s", peer4ID, peer.ID())
index 7b49466..acb0e4d 100644 (file)
@@ -3,6 +3,8 @@ package protocol
 import (
        "encoding/hex"
 
+       "github.com/sirupsen/logrus"
+
        "github.com/bytom/bytom/config"
        "github.com/bytom/bytom/errors"
        "github.com/bytom/bytom/math/checked"
@@ -11,13 +13,19 @@ import (
        "github.com/bytom/bytom/protocol/state"
 )
 
+type applyBlockReply struct {
+       verification *Verification
+       isRollback   bool
+       newBestHash  bc.Hash
+}
+
 // ApplyBlock used to receive a new block from upper layer, it provides idempotence
 // and parse the vote and mortgage from the transactions, then save to the checkpoint
 // the tree of checkpoint will grow with the arrival of new blocks
 // it will return verification when an epoch is reached and the current node is the validator, otherwise return nil
 // the chain module must broadcast the verification
-func (c *Casper) ApplyBlock(block *types.Block) (*Verification, *state.Checkpoint, error) {
-       if block.Height % state.BlocksOfEpoch == 1 {
+func (c *Casper) ApplyBlock(block *types.Block) (*applyBlockReply, error) {
+       if block.Height%state.BlocksOfEpoch == 1 {
                c.newEpochCh <- block.PreviousBlockHash
        }
 
@@ -25,39 +33,44 @@ func (c *Casper) ApplyBlock(block *types.Block) (*Verification, *state.Checkpoin
        defer c.mu.Unlock()
 
        if _, err := c.tree.nodeByHash(block.Hash()); err == nil {
-               // already processed
-               return nil, nil, nil
+               return nil, errAlreadyProcessedBlock
        }
 
+       _, oldBestHash := c.bestChain()
        target, err := c.applyBlockToCheckpoint(block)
        if err != nil {
-               return nil, nil, errors.Wrap(err, "apply block to checkpoint")
+               return nil, errors.Wrap(err, "apply block to checkpoint")
        }
 
-       if err := c.applyTransactions(target, block.Transactions); err != nil {
-               return nil, nil, err
+       if err := applyTransactions(target, block.Transactions); err != nil {
+               return nil, err
        }
 
        validators, err := c.Validators(&target.Hash)
        if err != nil {
-               return nil, nil, err
+               return nil, err
        }
 
        verification, err := c.applyMyVerification(target, block, validators)
        if err != nil {
-               return nil, nil, err
+               return nil, err
        }
 
        affectedCheckpoints, err := c.applySupLinks(target, block.SupLinks, validators)
        if err != nil {
-               return nil, nil, err
+               return nil, err
        }
 
-       return verification, target, c.saveCheckpoints(affectedCheckpoints, target)
+       reply := &applyBlockReply{verification: verification}
+       if c.isRollback(oldBestHash, block) {
+               reply.isRollback = true
+               reply.newBestHash = block.Hash()
+       }
+       return reply, c.saveCheckpoints(affectedCheckpoints)
 }
 
 func (c *Casper) applyBlockToCheckpoint(block *types.Block) (*state.Checkpoint, error) {
-       node, err := c.tree.nodeByHash(block.PreviousBlockHash)
+       node, err := c.checkpointByHash(block.PreviousBlockHash)
        if err != nil {
                return nil, err
        }
@@ -79,7 +92,7 @@ func (c *Casper) applyBlockToCheckpoint(block *types.Block) (*state.Checkpoint,
                for pubKey, num := range parent.Guaranties {
                        checkpoint.Guaranties[pubKey] = num
                }
-               node.children = append(node.children, &treeNode{checkpoint: checkpoint})
+               node.addChild(&treeNode{checkpoint: checkpoint})
        } else if mod == 0 {
                checkpoint.Status = state.Unjustified
        }
@@ -90,7 +103,68 @@ func (c *Casper) applyBlockToCheckpoint(block *types.Block) (*state.Checkpoint,
        return checkpoint, nil
 }
 
-func (c *Casper) applyTransactions(target *state.Checkpoint, transactions []*types.Tx) error {
+func (c *Casper) checkpointByHash(blockHash bc.Hash) (*treeNode, error) {
+       node, err := c.tree.nodeByHash(blockHash)
+       if err != nil {
+               logrus.WithField("err", err).Error("fail find checkpoint, start to reorganize checkpoint")
+
+               return c.reorganizeCheckpoint(blockHash)
+       }
+
+       return node, nil
+}
+
+func (c *Casper) isRollback(oldBestHash bc.Hash, block *types.Block) bool {
+       _, newBestHash := c.bestChain()
+       return block.Hash() == newBestHash && block.PreviousBlockHash != oldBestHash
+}
+
+func (c *Casper) reorganizeCheckpoint(hash bc.Hash) (*treeNode, error) {
+       prevHash := hash
+       var attachBlocks []*types.Block
+       for {
+               prevBlock, err := c.store.GetBlock(&prevHash)
+               if err != nil {
+                       return nil, err
+               }
+
+               if prevBlock.Height%state.BlocksOfEpoch == 0 {
+                       break
+               }
+
+               attachBlocks = append([]*types.Block{prevBlock}, attachBlocks...)
+               prevHash = prevBlock.PreviousBlockHash
+       }
+
+       parent, err := c.tree.nodeByHash(prevHash)
+       if err != nil {
+               return nil, err
+       }
+
+       node := &treeNode{
+               checkpoint: &state.Checkpoint{
+                       ParentHash: parent.checkpoint.Hash,
+                       Parent:     parent.checkpoint,
+                       Status:     state.Growing,
+                       Votes:      make(map[string]uint64),
+                       Guaranties: make(map[string]uint64),
+               },
+       }
+
+       parent.addChild(node)
+       for _, attachBlock := range attachBlocks {
+               if err := applyTransactions(node.checkpoint, attachBlock.Transactions); err != nil {
+                       return nil, err
+               }
+
+               node.checkpoint.Hash = attachBlock.Hash()
+               node.checkpoint.Height = attachBlock.Height
+               node.checkpoint.Timestamp = attachBlock.Timestamp
+       }
+       return node, nil
+}
+
+func applyTransactions(target *state.Checkpoint, transactions []*types.Tx) error {
        for _, tx := range transactions {
                for _, input := range tx.Inputs {
                        if vetoInput, ok := input.TypedInput.(*types.VetoInput); ok {
@@ -124,12 +198,12 @@ func (c *Casper) applyTransactions(target *state.Checkpoint, transactions []*typ
 }
 
 // applySupLinks copy the block's supLink to the checkpoint
-func (c *Casper) applySupLinks(target *state.Checkpoint, supLinks []*types.SupLink, validators map[string]*state.Validator) (map[bc.Hash]*state.Checkpoint, error) {
+func (c *Casper) applySupLinks(target *state.Checkpoint, supLinks []*types.SupLink, validators map[string]*state.Validator) ([]*state.Checkpoint, error) {
+       affectedCheckpoints := []*state.Checkpoint{target}
        if target.Height%state.BlocksOfEpoch != 0 {
                return nil, nil
        }
 
-       affectedCheckpoints := make(map[bc.Hash]*state.Checkpoint)
        for _, supLink := range supLinks {
                var validVerifications []*Verification
                for _, v := range supLinkToVerifications(supLink, validators, target.Hash, target.Height) {
@@ -143,11 +217,8 @@ func (c *Casper) applySupLinks(target *state.Checkpoint, supLinks []*types.SupLi
                        return nil, err
                }
 
-               for _, c := range checkpoints {
-                       affectedCheckpoints[c.Hash] = c
-               }
+               affectedCheckpoints = append(affectedCheckpoints, checkpoints...)
        }
-
        return affectedCheckpoints, nil
 }
 
@@ -171,7 +242,7 @@ func (c *Casper) applyMyVerification(target *state.Checkpoint, block *types.Bloc
 }
 
 func (c *Casper) myVerification(target *state.Checkpoint, validators map[string]*state.Validator) (*Verification, error) {
-       if target.Height % state.BlocksOfEpoch != 0 {
+       if target.Height%state.BlocksOfEpoch != 0 {
                return nil, nil
        }
 
@@ -199,7 +270,7 @@ func (c *Casper) myVerification(target *state.Checkpoint, validators map[string]
                        return nil, err
                }
 
-               if err := c.verifyVerification(v, validatorOrder,false); err != nil {
+               if err := c.verifyVerification(v, validatorOrder, false); err != nil {
                        return nil, nil
                }
 
@@ -208,18 +279,6 @@ func (c *Casper) myVerification(target *state.Checkpoint, validators map[string]
        return nil, nil
 }
 
-func (c *Casper) saveCheckpoints(affectedCheckpoints map[bc.Hash]*state.Checkpoint, target *state.Checkpoint) error {
-       // the target checkpoint must eventually be saved in the chain state
-       delete(affectedCheckpoints, target.Hash)
-
-       var checkpoints []*state.Checkpoint
-       for _, c := range affectedCheckpoints {
-               checkpoints = append(checkpoints, c)
-       }
-       return c.store.SaveCheckpoints(checkpoints)
-}
-
-
 type guarantyArgs struct {
        Amount uint64
        PubKey []byte
@@ -305,6 +364,20 @@ func (c *Casper) lastJustifiedCheckpointOfBranch(branch *state.Checkpoint) *stat
        return nil
 }
 
+func (c *Casper) saveCheckpoints(checkpoints []*state.Checkpoint) error {
+       checkpointSet := make(map[bc.Hash]*state.Checkpoint)
+       for _, c := range checkpoints {
+               checkpointSet[c.Hash] = c
+       }
+
+       var result []*state.Checkpoint
+       for _, c := range checkpointSet {
+               result = append(result, c)
+       }
+
+       return c.store.SaveCheckpoints(result)
+}
+
 func supLinkToVerifications(supLink *types.SupLink, validators map[string]*state.Validator, targetHash bc.Hash, targetHeight uint64) []*Verification {
        validatorList := make([]*state.Validator, len(validators))
        for _, validator := range validators {
index a381646..25d87f6 100644 (file)
@@ -48,7 +48,12 @@ func (c *Casper) AuthVerification(v *Verification) error {
                return nil
        }
 
-       return c.authVerification(v, targetNode.checkpoint, validators)
+       _, oldBestHash := c.bestChain()
+       if err := c.authVerification(v, targetNode.checkpoint, validators); err != nil {
+               return err
+       }
+
+       return c.tryRollback(oldBestHash)
 }
 
 func (c *Casper) authVerification(v *Verification, target *state.Checkpoint, validators map[string]*state.Validator) error {
@@ -70,17 +75,14 @@ func (c *Casper) authVerification(v *Verification, target *state.Checkpoint, val
 }
 
 func (c *Casper) addVerificationToCheckpoint(target *state.Checkpoint, validators map[string]*state.Validator, verifications ...*Verification) ([]*state.Checkpoint, error) {
-       _, oldBestHash := c.bestChain()
-       var affectedCheckpoints []*state.Checkpoint
+       affectedCheckpoints := []*state.Checkpoint{target}
        for _, v := range verifications {
-               source, err := c.store. GetCheckpoint(&v.SourceHash)
+               source, err := c.store.GetCheckpoint(&v.SourceHash)
                if err != nil {
                        return nil, err
                }
 
                supLink := target.AddVerification(v.SourceHash, v.SourceHeight, validators[v.PubKey].Order, v.Signature)
-               affectedCheckpoints = append(affectedCheckpoints, target)
-
                if target.Status != state.Unjustified || !supLink.IsMajority(len(validators)) || source.Status == state.Finalized {
                        continue
                }
@@ -88,12 +90,6 @@ func (c *Casper) addVerificationToCheckpoint(target *state.Checkpoint, validator
                c.setJustified(source, target)
                affectedCheckpoints = append(affectedCheckpoints, source)
        }
-
-       _, newBestHash := c.bestChain()
-       if oldBestHash != newBestHash {
-               c.rollbackNotifyCh <- newBestHash
-       }
-
        return affectedCheckpoints, nil
 }
 
@@ -134,6 +130,15 @@ func (c *Casper) setFinalized(checkpoint *state.Checkpoint) {
        c.tree = newRoot
 }
 
+func (c *Casper) tryRollback(oldBestHash bc.Hash) error {
+       if _, newBestHash := c.bestChain(); oldBestHash != newBestHash {
+               msg := &rollbackMsg{bestHash: newBestHash}
+               c.rollbackCh <- msg
+               return <-msg.reply
+       }
+       return nil
+}
+
 func (c *Casper) authVerificationLoop() {
        for blockHash := range c.newEpochCh {
                validators, err := c.Validators(&blockHash)
index f2dc01d..9d9941d 100644 (file)
@@ -86,13 +86,13 @@ func (c *Chain) connectBlock(block *types.Block) (err error) {
                return err
        }
 
-       verification, checkpoint, err := c.casper.ApplyBlock(block)
+       reply, err := c.casper.ApplyBlock(block)
        if err != nil {
                return err
        }
 
-       if verification != nil {
-               if err := c.broadcastVerification(verification); err != nil {
+       if reply.verification != nil {
+               if err := c.broadcastVerification(reply.verification); err != nil {
                        return err
                }
        }
@@ -103,7 +103,7 @@ func (c *Chain) connectBlock(block *types.Block) (err error) {
        }
 
        node := c.index.GetNode(&bcBlock.ID)
-       if err := c.setState(node, utxoView, contractView, checkpoint); err != nil {
+       if err := c.setState(node, utxoView, contractView); err != nil {
                return err
        }
 
@@ -145,7 +145,6 @@ func (c *Chain) reorganizeChain(node *state.BlockNode) error {
        }
 
        txsToRemove := map[bc.Hash]*types.Tx{}
-       var affectedCheckpoints []*state.Checkpoint
        for _, attachNode := range attachNodes {
                b, err := c.store.GetBlock(&attachNode.Hash)
                if err != nil {
@@ -161,17 +160,6 @@ func (c *Chain) reorganizeChain(node *state.BlockNode) error {
                        return err
                }
 
-               verification, checkpoint, err := c.casper.ApplyBlock(b)
-               if err != nil {
-                       return err
-               }
-
-               affectedCheckpoints = append(affectedCheckpoints, checkpoint)
-
-               if err := c.broadcastVerification(verification); err != nil {
-                       return err
-               }
-
                if err := contractView.ApplyBlock(b); err != nil {
                        return err
                }
@@ -187,7 +175,7 @@ func (c *Chain) reorganizeChain(node *state.BlockNode) error {
                log.WithFields(log.Fields{"module": logModule, "height": node.Height, "hash": node.Hash.String()}).Debug("attach from mainchain")
        }
 
-       if err := c.setState(node, utxoView, contractView, affectedCheckpoints...); err != nil {
+       if err := c.setState(node, utxoView, contractView); err != nil {
                return err
        }
 
@@ -299,16 +287,20 @@ func (c *Chain) ProcessBlock(block *types.Block) (bool, error) {
        return response.isOrphan, response.err
 }
 
+type rollbackMsg struct {
+       bestHash bc.Hash
+       reply    chan error
+}
+
 func (c *Chain) blockProcessor() {
        for {
                select {
                case msg := <-c.processBlockCh:
                        isOrphan, err := c.processBlock(msg.block)
                        msg.reply <- processBlockResponse{isOrphan: isOrphan, err: err}
-               case newBestHash := <-c.rollbackNotifyCh:
-                       if err := c.rollback(newBestHash); err != nil {
-                               log.WithFields(log.Fields{"module": logModule, "err": err}).Warning("fail on rollback block")
-                       }
+               case msg := <-c.processRollbackCh:
+                       err := c.rollback(msg.bestHash)
+                       msg.reply <- err
                }
        }
 }
@@ -338,15 +330,45 @@ func (c *Chain) processBlock(block *types.Block) (bool, error) {
                log.WithFields(log.Fields{"module": logModule}).Debug("append block to the end of mainchain")
                return false, c.connectBlock(bestBlock)
        }
-       return false, nil
+
+       log.WithFields(log.Fields{"module": logModule}).Debug("apply fork chain to casper")
+       return false, c.applyForkChainToCasper(bestNode)
+}
+
+func (c *Chain) applyForkChainToCasper(bestNode *state.BlockNode) error {
+       attachNodes, _ := c.calcReorganizeNodes(bestNode)
+       for _, node := range attachNodes {
+               block, err := c.store.GetBlock(&node.Hash)
+               if err != nil {
+                       return err
+               }
+
+               reply, err := c.casper.ApplyBlock(block)
+               if err != nil {
+                       return err
+               }
+
+               if reply.verification != nil {
+                       if err := c.broadcastVerification(reply.verification); err != nil {
+                               return err
+                       }
+               }
+
+               if reply.isRollback {
+                       if err := c.rollback(reply.newBestHash); err != nil {
+                               return err
+                       }
+               }
+       }
+       return nil
 }
 
-func (c *Chain) rollback(newBestHash bc.Hash) error {
-       if c.bestNode.Hash == newBestHash {
+func (c *Chain) rollback(bestHash bc.Hash) error {
+       if c.bestNode.Hash == bestHash {
                return nil
        }
 
-       node := c.index.GetNode(&newBestHash)
+       node := c.index.GetNode(&bestHash)
        log.WithFields(log.Fields{"module": logModule}).Debug("start to reorganize chain")
        return c.reorganizeChain(node)
 }
index 77bec89..52d05f5 100644 (file)
@@ -20,6 +20,7 @@ var (
        errVoteToNonValidator       = errors.New("pubKey of vote is not validator")
        errGuarantyLessThanMinimum  = errors.New("guaranty less than minimum")
        errOverflow                 = errors.New("arithmetic overflow/underflow")
+       errAlreadyProcessedBlock    = errors.New("block already processed in casper")
 )
 
 const minGuaranty = 1E14
@@ -27,11 +28,11 @@ const minGuaranty = 1E14
 // Casper is BFT based proof of stack consensus algorithm, it provides safety and liveness in theory,
 // it's design mainly refers to https://github.com/ethereum/research/blob/master/papers/casper-basics/casper_basics.pdf
 type Casper struct {
-       mu               sync.RWMutex
-       tree             *treeNode
-       rollbackNotifyCh chan bc.Hash
-       newEpochCh       chan bc.Hash
-       store            Store
+       mu         sync.RWMutex
+       tree       *treeNode
+       rollbackCh chan *rollbackMsg
+       newEpochCh chan bc.Hash
+       store      Store
        // pubKey -> conflicting verifications
        evilValidators map[string][]*Verification
        // block hash -> previous checkpoint hash
@@ -44,52 +45,45 @@ type Casper struct {
 // argument checkpoints load the checkpoints from leveldb
 // the first element of checkpoints must genesis checkpoint or the last finalized checkpoint in order to reduce memory space
 // the others must be successors of first one
-func NewCasper(store Store, checkpoints []*state.Checkpoint, rollbackNotifyCh chan bc.Hash) *Casper {
+func NewCasper(store Store, checkpoints []*state.Checkpoint, rollbackCh chan *rollbackMsg) *Casper {
        if checkpoints[0].Height != 0 && checkpoints[0].Status != state.Finalized {
                log.Panic("first element of checkpoints must genesis or in finalized status")
        }
 
        casper := &Casper{
-               tree:                  makeTree(checkpoints[0], checkpoints[1:]),
-               rollbackNotifyCh:      rollbackNotifyCh,
-               newEpochCh:            make(chan bc.Hash),
-               store:                 store,
-               evilValidators:        make(map[string][]*Verification),
-               prevCheckpointCache:   common.NewCache(1024),
-               verificationCache:     common.NewCache(1024),
+               tree:                makeTree(checkpoints[0], checkpoints[1:]),
+               rollbackCh:          rollbackCh,
+               newEpochCh:          make(chan bc.Hash),
+               store:               store,
+               evilValidators:      make(map[string][]*Verification),
+               prevCheckpointCache: common.NewCache(1024),
+               verificationCache:   common.NewCache(1024),
        }
        go casper.authVerificationLoop()
        return casper
 }
 
-// Best chain return the chain containing the justified checkpoint of the largest height
-func (c *Casper) BestChain() (uint64, bc.Hash) {
+// LastFinalized return the block height and block hash which is finalized at last
+func (c *Casper) LastFinalized() (uint64, bc.Hash) {
        c.mu.RLock()
        defer c.mu.RUnlock()
 
-       return c.bestChain()
-}
-
-func (c *Casper) bestChain() (uint64, bc.Hash) {
-       // root is init justified
        root := c.tree.checkpoint
-       bestHeight, bestHash, _ := chainOfMaxJustifiedHeight(c.tree, root.Height)
-       return bestHeight, bestHash
+       return root.Height, root.Hash
 }
 
-// LastFinalized return the block height and block hash which is finalized ast last
-func (c *Casper) LastFinalized() (uint64, bc.Hash) {
+// LastJustified return the block height and block hash which is justified at last
+func (c *Casper) LastJustified() (uint64, bc.Hash) {
        c.mu.RLock()
        defer c.mu.RUnlock()
 
-       root := c.tree.checkpoint
-       return root.Height, root.Hash
+       return lastJustified(c.tree)
 }
 
 // Validators return the validators by specified block hash
 // e.g. if the block num of epoch is 100, and the block height corresponding to the block hash is 130, then will return the voting results of height in 0~100
 func (c *Casper) Validators(blockHash *bc.Hash) (map[string]*state.Validator, error) {
-       checkpoint, err := c.prevCheckpoint(blockHash)
+       checkpoint, err := c.parentCheckpoint(blockHash)
        if err != nil {
                return nil, err
        }
@@ -97,7 +91,7 @@ func (c *Casper) Validators(blockHash *bc.Hash) (map[string]*state.Validator, er
        return checkpoint.Validators(), nil
 }
 
-func (c *Casper) prevCheckpoint(blockHash *bc.Hash) (*state.Checkpoint, error) {
+func (c *Casper) parentCheckpoint(blockHash *bc.Hash) (*state.Checkpoint, error) {
        hash, err := c.prevCheckpointHash(blockHash)
        if err != nil {
                return nil, err
@@ -106,7 +100,7 @@ func (c *Casper) prevCheckpoint(blockHash *bc.Hash) (*state.Checkpoint, error) {
        return c.store.GetCheckpoint(hash)
 }
 
-func (c *Casper) prevCheckpointByPrevHash(prevBlockHash *bc.Hash) (*state.Checkpoint, error) {
+func (c *Casper) parentCheckpointByPrevHash(prevBlockHash *bc.Hash) (*state.Checkpoint, error) {
        hash, err := c.prevCheckpointHashByPrevHash(prevBlockHash)
        if err != nil {
                return nil, err
@@ -138,6 +132,27 @@ func (c *Casper) EvilValidators() []*EvilValidator {
        return validators
 }
 
+func (c *Casper) bestChain() (uint64, bc.Hash) {
+       // root is init justified
+       root := c.tree.checkpoint
+       bestHeight, bestHash, _ := chainOfMaxJustifiedHeight(c.tree, root.Height)
+       return bestHeight, bestHash
+}
+
+func lastJustified(node *treeNode) (uint64, bc.Hash) {
+       lastJustifiedHeight, lastJustifiedHash := uint64(0), bc.Hash{}
+       if node.checkpoint.Status == state.Justified {
+               lastJustifiedHeight, lastJustifiedHash = node.checkpoint.Height, node.checkpoint.Hash
+       }
+
+       for _, child := range node.children {
+               if justifiedHeight, justifiedHash := lastJustified(child); justifiedHeight > lastJustifiedHeight {
+                       lastJustifiedHeight, lastJustifiedHash = justifiedHeight, justifiedHash
+               }
+       }
+       return lastJustifiedHeight, lastJustifiedHash
+}
+
 // justifiedHeight is the max justified height of checkpoint from node to root
 func chainOfMaxJustifiedHeight(node *treeNode, justifiedHeight uint64) (uint64, bc.Hash, uint64) {
        checkpoint := node.checkpoint
@@ -147,7 +162,7 @@ func chainOfMaxJustifiedHeight(node *treeNode, justifiedHeight uint64) (uint64,
 
        bestHeight, bestHash, maxJustifiedHeight := checkpoint.Height, checkpoint.Hash, justifiedHeight
        for _, child := range node.children {
-               if height, hash, justified := chainOfMaxJustifiedHeight(child, justifiedHeight); justified >= maxJustifiedHeight {
+               if height, hash, justified := chainOfMaxJustifiedHeight(child, justifiedHeight); justified > maxJustifiedHeight || height > bestHeight {
                        bestHeight, bestHash, maxJustifiedHeight = height, hash, justified
                }
        }
index d9fafe2..44ca69f 100644 (file)
@@ -15,18 +15,21 @@ import (
        "github.com/bytom/bytom/protocol/state"
 )
 
-const maxProcessBlockChSize = 1024
+const (
+       maxProcessBlockChSize  = 1024
+       maxProcessRollbackSize = 1024
+)
 
 // Chain provides functions for working with the Bytom block chain.
 type Chain struct {
-       index            *state.BlockIndex
-       orphanManage     *OrphanManage
-       txPool           *TxPool
-       store            Store
-       casper           *Casper
-       processBlockCh   chan *processBlockMsg
-       rollbackNotifyCh chan bc.Hash
-       eventDispatcher  *event.Dispatcher
+       index             *state.BlockIndex
+       orphanManage      *OrphanManage
+       txPool            *TxPool
+       store             Store
+       casper            *Casper
+       processBlockCh    chan *processBlockMsg
+       processRollbackCh chan *rollbackMsg
+       eventDispatcher   *event.Dispatcher
 
        cond     sync.Cond
        bestNode *state.BlockNode
@@ -39,12 +42,12 @@ func NewChain(store Store, txPool *TxPool, eventDispatcher *event.Dispatcher) (*
 
 func NewChainWithOrphanManage(store Store, txPool *TxPool, manage *OrphanManage, eventDispatcher *event.Dispatcher) (*Chain, error) {
        c := &Chain{
-               orphanManage:     manage,
-               eventDispatcher:  eventDispatcher,
-               txPool:           txPool,
-               store:            store,
-               rollbackNotifyCh: make(chan bc.Hash),
-               processBlockCh:   make(chan *processBlockMsg, maxProcessBlockChSize),
+               orphanManage:      manage,
+               eventDispatcher:   eventDispatcher,
+               txPool:            txPool,
+               store:             store,
+               processRollbackCh: make(chan *rollbackMsg, maxProcessRollbackSize),
+               processBlockCh:    make(chan *processBlockMsg, maxProcessBlockChSize),
        }
        c.cond.L = new(sync.Mutex)
 
@@ -64,7 +67,7 @@ func NewChainWithOrphanManage(store Store, txPool *TxPool, manage *OrphanManage,
        c.bestNode = c.index.GetNode(storeStatus.Hash)
        c.index.SetMainChain(c.bestNode)
 
-       casper, err := newCasper(store, storeStatus, c.rollbackNotifyCh)
+       casper, err := newCasper(store, storeStatus, c.processRollbackCh)
        if err != nil {
                return nil, err
        }
@@ -87,6 +90,10 @@ func (c *Chain) initChainStatus() error {
                Status:    state.Justified,
        }
 
+       if err := c.store.SaveCheckpoints([]*state.Checkpoint{checkpoint}); err != nil {
+               return err
+       }
+
        utxoView := state.NewUtxoViewpoint()
        bcBlock := types.MapBlock(genesisBlock)
        if err := utxoView.ApplyBlock(bcBlock); err != nil {
@@ -99,21 +106,21 @@ func (c *Chain) initChainStatus() error {
        }
 
        contractView := state.NewContractViewpoint()
-       return c.store.SaveChainStatus(node, utxoView, contractView, []*state.Checkpoint{checkpoint}, 0, &checkpoint.Hash)
+       return c.store.SaveChainStatus(node, utxoView, contractView, 0, &checkpoint.Hash)
 }
 
-func newCasper(store Store, storeStatus *BlockStoreState, rollbackNotifyCh chan bc.Hash) (*Casper, error) {
+func newCasper(store Store, storeStatus *BlockStoreState, rollbackCh chan *rollbackMsg) (*Casper, error) {
        checkpoints, err := store.CheckpointsFromNode(storeStatus.FinalizedHeight, storeStatus.FinalizedHash)
        if err != nil {
                return nil, err
        }
 
-       return NewCasper(store, checkpoints, rollbackNotifyCh), nil
+       return NewCasper(store, checkpoints, rollbackCh), nil
 }
 
-// BestBlockHeight returns the last irreversible block header of the blockchain
-func (c *Chain) LastIrreversibleHeader() *types.BlockHeader {
-       _, hash := c.casper.LastFinalized()
+// LastFinalizedHeader returns the last finalized block header of the block chain
+func (c *Chain) LastJustifiedHeader() *types.BlockHeader {
+       _, hash := c.casper.LastJustified()
        node := c.index.GetNode(&hash)
        return node.BlockHeader()
 }
@@ -152,7 +159,7 @@ func (c *Chain) BestBlockHash() *bc.Hash {
 
 // GetValidator return validator by specified blockHash and timestamp
 func (c *Chain) GetValidator(prevHash *bc.Hash, timeStamp uint64) (*state.Validator, error) {
-       prevCheckpoint, err := c.casper.prevCheckpointByPrevHash(prevHash)
+       prevCheckpoint, err := c.casper.parentCheckpointByPrevHash(prevHash)
        if err != nil {
                return nil, err
        }
@@ -199,9 +206,9 @@ func (c *Chain) SignBlockHeader(blockHeader *types.BlockHeader) {
 }
 
 // This function must be called with mu lock in above level
-func (c *Chain) setState(node *state.BlockNode, view *state.UtxoViewpoint, contractView *state.ContractViewpoint, checkpoints ...*state.Checkpoint) error {
+func (c *Chain) setState(node *state.BlockNode, view *state.UtxoViewpoint, contractView *state.ContractViewpoint) error {
        finalizedHeight, finalizedHash := c.casper.LastFinalized()
-       if err := c.store.SaveChainStatus(node, view, contractView, checkpoints, finalizedHeight, &finalizedHash); err != nil {
+       if err := c.store.SaveChainStatus(node, view, contractView, finalizedHeight, &finalizedHash); err != nil {
                return err
        }
 
index bfb9bbd..ce5f472 100644 (file)
@@ -26,7 +26,7 @@ type Store interface {
        LoadBlockIndex(uint64) (*state.BlockIndex, error)
        SaveBlock(*types.Block) error
        SaveBlockHeader(*types.BlockHeader) error
-       SaveChainStatus(*state.BlockNode, *state.UtxoViewpoint, *state.ContractViewpoint, []*state.Checkpoint, uint64, *bc.Hash) error
+       SaveChainStatus(*state.BlockNode, *state.UtxoViewpoint, *state.ContractViewpoint, uint64, *bc.Hash) error
 }
 
 // BlockStoreState represents the core's db status
index 3d0dd83..ac0b7e4 100644 (file)
@@ -34,6 +34,16 @@ func makeTree(root *state.Checkpoint, successors []*state.Checkpoint) *treeNode
        return rootNode
 }
 
+func (t *treeNode) addChild(child *treeNode) {
+       for i, n := range t.children {
+               if n.checkpoint.Hash == child.checkpoint.Hash {
+                       t.children[i] = child
+                       return
+               }
+       }
+       t.children = append(t.children, child)
+}
+
 func (t *treeNode) nodeByHash(blockHash bc.Hash) (*treeNode, error) {
        if c := t.findOnlyOne(func(c *state.Checkpoint) bool {
                return c.Hash == blockHash
index 3924202..189697a 100644 (file)
@@ -111,7 +111,7 @@ func (s *mockStore) GetContract(hash [32]byte) ([]byte, error)
 func (s *mockStore) LoadBlockIndex(uint64) (*state.BlockIndex, error)             { return nil, nil }
 func (s *mockStore) SaveBlock(*types.Block) error                                 { return nil }
 func (s *mockStore) SaveBlockHeader(*types.BlockHeader) error                     { return nil }
-func (s *mockStore) SaveChainStatus(*state.BlockNode, *state.UtxoViewpoint, *state.ContractViewpoint, []*state.Checkpoint, uint64, *bc.Hash) error {
+func (s *mockStore) SaveChainStatus(*state.BlockNode, *state.UtxoViewpoint, *state.ContractViewpoint, uint64, *bc.Hash) error {
        return nil
 }
 
@@ -614,7 +614,7 @@ func (s *mockStore1) GetContract(hash [32]byte) ([]byte, error)           { retu
 func (s *mockStore1) LoadBlockIndex(uint64) (*state.BlockIndex, error)    { return nil, nil }
 func (s *mockStore1) SaveBlock(*types.Block) error                        { return nil }
 func (s *mockStore1) SaveBlockHeader(*types.BlockHeader) error            { return nil }
-func (s *mockStore1) SaveChainStatus(*state.BlockNode, *state.UtxoViewpoint, *state.ContractViewpoint, []*state.Checkpoint, uint64, *bc.Hash) error { return nil}
+func (s *mockStore1) SaveChainStatus(*state.BlockNode, *state.UtxoViewpoint, *state.ContractViewpoint, uint64, *bc.Hash) error { return nil}
 
 func TestProcessTransaction(t *testing.T) {
        txPool := &TxPool{
index 8e41a3a..692efe1 100644 (file)
@@ -27,7 +27,7 @@ func NewChain() *Chain {
        }
 }
 
-func (c *Chain) LastIrreversibleHeader() *types.BlockHeader {
+func (c *Chain) LastJustifiedHeader() *types.BlockHeader {
        return nil
 }
 
index 53db6ec..df7744c 100644 (file)
@@ -293,7 +293,7 @@ func TestAttachOrDetachBlocks(t *testing.T) {
                        utxoViewpoint0.Entries[k] = v
                }
                contractView := state.NewContractViewpoint()
-               if err := store.SaveChainStatus(node, utxoViewpoint0, contractView, nil, 0, &bc.Hash{}); err != nil {
+               if err := store.SaveChainStatus(node, utxoViewpoint0, contractView, 0, &bc.Hash{}); err != nil {
                        t.Error(err)
                }
 
@@ -315,7 +315,7 @@ func TestAttachOrDetachBlocks(t *testing.T) {
                                t.Error(err)
                        }
                }
-               if err := store.SaveChainStatus(node, utxoViewpoint, contractView, nil,0, &bc.Hash{}); err != nil {
+               if err := store.SaveChainStatus(node, utxoViewpoint, contractView,0, &bc.Hash{}); err != nil {
                        t.Error(err)
                }