github.com/decred/dcrlnd@v0.7.6/chainntnfs/dcrdnotify/dcrd_dev.go (about)

     1  //go:build dev
     2  // +build dev
     3  
     4  package dcrdnotify
     5  
     6  import (
     7  	"context"
     8  	"fmt"
     9  	"time"
    10  
    11  	"github.com/decred/dcrd/chaincfg/chainhash"
    12  	"github.com/decred/dcrlnd/chainntnfs"
    13  )
    14  
    15  // TODO(decred): Determine if the generateBlocks is needed.
    16  //
    17  // UnsafeStart starts the notifier with a specified best height and optional
    18  // best hash. Its bestBlock and txNotifier are initialized with bestHeight and
    19  // optionally bestHash. The parameter generateBlocks is necessary for the
    20  // dcrd notifier to ensure we drain all notifications up to syncHeight,
    21  // since if they are generated ahead of UnsafeStart the chainConn may start up
    22  // with an outdated best block and miss sending ntfns. Used for testing.
    23  func (b *DcrdNotifier) UnsafeStart(bestHeight int64, bestHash *chainhash.Hash,
    24  	syncHeight int64, generateBlocks func() error) error {
    25  
    26  	// TODO(decred): Handle 20 retries...
    27  	//
    28  	// Connect to dcrd, and register for notifications on connected, and
    29  	// disconnected blocks.
    30  	if err := b.chainConn.Connect(context.Background(), true); err != nil {
    31  		return err
    32  	}
    33  	if err := b.chainConn.NotifyBlocks(context.TODO()); err != nil {
    34  		return err
    35  	}
    36  
    37  	b.txNotifier = chainntnfs.NewTxNotifier(
    38  		uint32(bestHeight), chainntnfs.ReorgSafetyLimit,
    39  		b.confirmHintCache, b.spendHintCache, b.chainParams,
    40  	)
    41  
    42  	b.chainUpdates.Start()
    43  
    44  	if generateBlocks != nil {
    45  		// Ensure no block notifications are pending when we start the
    46  		// notification dispatcher goroutine.
    47  
    48  		// First generate the blocks, then drain the notifications
    49  		// for the generated blocks.
    50  		if err := generateBlocks(); err != nil {
    51  			return err
    52  		}
    53  
    54  		timeout := time.After(60 * time.Second)
    55  	loop:
    56  		for {
    57  			select {
    58  			case ntfn := <-b.chainUpdates.ChanOut():
    59  				lastReceivedNtfn := ntfn.(*filteredBlock)
    60  				if lastReceivedNtfn.header.Height >= uint32(syncHeight) {
    61  					break loop
    62  				}
    63  			case <-timeout:
    64  				return fmt.Errorf("unable to catch up to height %d",
    65  					syncHeight)
    66  			}
    67  		}
    68  	}
    69  
    70  	// Run notificationDispatcher after setting the notifier's best block
    71  	// to avoid a race condition.
    72  	b.bestBlock = chainntnfs.BlockEpoch{
    73  		Height: int32(bestHeight), Hash: bestHash,
    74  	}
    75  	if bestHash == nil {
    76  		hash, err := b.chainConn.GetBlockHash(context.TODO(), int64(bestHeight))
    77  		if err != nil {
    78  			return err
    79  		}
    80  		b.bestBlock.Hash = hash
    81  	}
    82  
    83  	b.wg.Add(1)
    84  	go b.notificationDispatcher()
    85  
    86  	return nil
    87  }