func (s *Server) handleBlockConnected(ctx context.Context, header *wire.BlockHeader) error {
	chainHeight := int64(header.Height)
	chainHash := header.BlockHash()
	svrLog.Debugf("Received connected block %s at height %d", chainHash, chainHeight)
	_, tipHeight, err := s.lastProcessedBlock(ctx)
	if err != nil {
		return err
	}
	if chainHeight < tipHeight {
		svrLog.Debugf("Ignoring block connected %s at height lower than "+
			"current tip (%d < %d)", chainHash, chainHeight, tipHeight)
		return nil
	}
	b, err := s.getBlock(ctx, &chainHash)
	if err != nil {
		return fmt.Errorf("unable to fetch dcrd connected block %s: %v",
			chainHash, err)
	}
	return s.switchChainTo(ctx, &chainHash, b, nil)
}
handleBlockConnected is called when new blocks are connected to the
blockchain.

This is called from the main Run goroutine.
package backend
import (
	"context"
	"fmt"
	"decred.org/dcrros/backend/backenddb"
	"decred.org/dcrros/types"
	"github.com/decred/dcrd/chaincfg/chainhash"
	"github.com/decred/dcrd/wire"
)
func (s *Server) rollbackTip(dbtx backenddb.WriteTx, tipHash *chainhash.Hash,
	tipHeight int64) error {
	b, err := s.getBlock(dbtx.Context(), tipHash)
	if err != nil {
		return err
	}
	var prev *wire.MsgBlock
	if !types.VoteBitsApprovesParent(b.Header.VoteBits) {
		prev, err = s.getBlock(dbtx.Context(), &b.Header.PrevBlock)
		if err != nil {
			return err
		}
	}
	applyOp := func(op *types.Op) error {
		var err error
		switch {
		case op.Type == types.OpTypeDebit && op.Status == types.OpStatusSuccess:
			err = s.db.AddUtxo(dbtx, op.Account,
				&op.In.PreviousOutPoint, -op.Amount)
		case op.Type == types.OpTypeDebit && op.Status == types.OpStatusReversed:
			err = s.db.DelUtxo(dbtx, op.Account, &op.In.PreviousOutPoint)
		case op.Type == types.OpTypeCredit && op.Status == types.OpStatusSuccess:
			outp := &wire.OutPoint{
				Hash:  op.TxHash,
				Index: uint32(op.IOIndex),
				Tree:  op.Tree,
			}
			err = s.db.DelUtxo(dbtx, op.Account, outp)
		case op.Type == types.OpTypeCredit && op.Status == types.OpStatusReversed:
			outp := &wire.OutPoint{
				Hash:  op.TxHash,
				Index: uint32(op.IOIndex),
				Tree:  op.Tree,
			}
			err = s.db.AddUtxo(dbtx, op.Account, outp, -op.Amount)
		default:
			err = fmt.Errorf("unknown op type: %v and "+
				"status: %v", op.Type, op.Status)
		}
		return err
	}
	fetchPrevOuts := s.makePrevOutsFetcher(dbtx.Context(), nil)
	err = types.IterateBlockOps(b, prev, fetchPrevOuts, applyOp, s.chainParams)
	if err != nil {
		return err
	}
	return s.db.RollbackTip(dbtx, *tipHash, tipHeight)
}
func (s *Server) rollbackDbChain(dbtx backenddb.WriteTx,
	targetHash *chainhash.Hash, targetHeight int64) (
	*chainhash.Hash, int64, []*chainhash.Hash, error) {
	tipHash, tipHeight, err := s.db.LastProcessedBlock(dbtx)
	if err != nil {
		return nil, 0, nil, err
	}
	if targetHeight == tipHeight && targetHash.IsEqual(&tipHash) {
		return &tipHash, tipHeight, nil, nil
	}
	for tipHeight > 0 && tipHeight > targetHeight {
		var err error
		svrLog.Debugf("Rolling back rewinded tip %d %s", tipHeight, tipHash)
		if err = s.rollbackTip(dbtx, &tipHash, tipHeight); err != nil {
			return nil, 0, nil, err
		}
		if tipHash, tipHeight, err = s.db.LastProcessedBlock(dbtx); err != nil {
			return nil, 0, nil, err
		}
	}
	var missingBlocks []*chainhash.Hash
	for targetHeight > 0 && targetHeight > tipHeight {
		missingBlocks = append(missingBlocks, targetHash)
		b, err := s.getBlock(dbtx.Context(), targetHash)
		if err != nil {
			return nil, 0, nil, err
		}
		targetHash = &b.Header.PrevBlock
		targetHeight--
	}
	for tipHeight > 0 && !targetHash.IsEqual(&tipHash) {
		missingBlocks = append(missingBlocks, targetHash)
		var err error
		svrLog.Debugf("Rolling back reorged tip %d %s", tipHeight, tipHash)
		if err = s.rollbackTip(dbtx, &tipHash, tipHeight); err != nil {
			return nil, 0, nil, err
		}
		if tipHash, tipHeight, err = s.db.LastProcessedBlock(dbtx); err != nil {
			return nil, 0, nil, err
		}
		b, err := s.getBlock(dbtx.Context(), targetHash)
		if err != nil {
			return nil, 0, nil, err
		}
		targetHash = &b.Header.PrevBlock
	}
	if len(missingBlocks) > 0 {
		svrLog.Infof("Rolled back db chain to block %d %s", tipHeight, tipHash)
	}
	return &tipHash, tipHeight, missingBlocks, nil
}
func (s *Server) reorgToParentOf(ctx context.Context, bh *chainhash.Hash,
	block *wire.MsgBlock) (*wire.MsgBlock, error) {
	var tipHeight int64
	var tipHash *chainhash.Hash
	var missingBlocks []*chainhash.Hash
	err := s.db.Update(ctx, func(dbtx backenddb.WriteTx) error {
		var err error
		targetHash := &block.Header.PrevBlock
		targetHeight := int64(block.Header.Height - 1)
		tipHash, tipHeight, missingBlocks, err = s.rollbackDbChain(dbtx, targetHash, targetHeight)
		return err
	})
	if err != nil {
		return nil, err
	}
	var prev *wire.MsgBlock
	if tipHeight > 0 {
		prev, err = s.getBlock(ctx, tipHash)
		if err != nil {
			return nil, fmt.Errorf("unable to fetch tip block %s at height "+
				"%d", tipHash, tipHeight)
		}
	}
	for i := len(missingBlocks) - 1; i >= 0; i-- {
		nextTipHash := missingBlocks[i]
		b, err := s.getBlock(ctx, nextTipHash)
		if err != nil {
			return nil, fmt.Errorf("unable to fetch missing block %s: %v",
				nextTipHash, err)
		}
		err = s.preProcessAccountBlock(ctx, nextTipHash, b, prev, nil)
		if err != nil {
			return nil, fmt.Errorf("unable to process accounts of connected block "+
				"%s: %v", nextTipHash, err)
		}
		prev = b
		tipHeight++
		svrLog.Infof("Connected missing block %s at height %d", nextTipHash, tipHeight)
	}
	return prev, nil
}
func (s *Server) switchChainTo(ctx context.Context, bh *chainhash.Hash,
	b *wire.MsgBlock, utxoSet map[wire.OutPoint]*types.PrevOutput) error {
	prev, err := s.reorgToParentOf(ctx, bh, b)
	if err != nil {
		return fmt.Errorf("unable to reorg to parent of %s (height %d): "+
			"%v", bh, b.Header.Height, err)
	}
	err = s.preProcessAccountBlock(ctx, bh, b, prev, utxoSet)
	if err != nil {
		return fmt.Errorf("unable to process accounts of connected block "+
			"%s (height %d): %v", bh, b.Header.Height, err)
	}
	svrLog.Debugf("Connected block %s at height %d", bh, b.Header.Height)
	return nil
}