// Copyright (c) 2020-2023 The Decred developers // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. package vspd import ( "context" "errors" "time" "github.com/decred/dcrd/wire" "github.com/decred/slog" "github.com/decred/vspd/database" "github.com/decred/vspd/internal/config" "github.com/decred/vspd/rpc" ) const ( // requiredConfs is the number of confirmations required to consider a // ticket purchase or a fee transaction to be final. requiredConfs = 6 // consistencyInterval is the time period between wallet consistency checks. consistencyInterval = 30 * time.Minute // dcrdInterval is the time period between dcrd connection checks. dcrdInterval = time.Second * 15 ) type Vspd struct { network *config.Network log slog.Logger db *database.VspDatabase dcrd rpc.DcrdConnect wallets rpc.WalletConnect blockNotifChan chan *wire.BlockHeader // lastScannedBlock is the height of the most recent block which has been // scanned for spent tickets. lastScannedBlock int64 } func New(network *config.Network, log slog.Logger, db *database.VspDatabase, dcrd rpc.DcrdConnect, wallets rpc.WalletConnect, blockNotifChan chan *wire.BlockHeader) *Vspd { v := &Vspd{ network: network, log: log, db: db, dcrd: dcrd, wallets: wallets, blockNotifChan: blockNotifChan, } return v } func (v *Vspd) Run(ctx context.Context) { // Run database integrity checks to ensure all data in database is present // and up-to-date. err := v.checkDatabaseIntegrity(ctx) if err != nil { // Don't log error if shutdown was requested, just return. if errors.Is(err, context.Canceled) { return } // vspd should still start if this fails, so just log an error. v.log.Errorf("Database integrity check failed: %v", err) } // Stop if shutdown requested. if ctx.Err() != nil { return } // Run the update function now to catch up with any blocks mined while vspd // was shut down. v.update(ctx) // Stop if shutdown requested. if ctx.Err() != nil { return } // Run voting wallet consistency check now to ensure all wallets are up to // date. v.checkWalletConsistency(ctx) // Stop if shutdown requested. if ctx.Err() != nil { return } // Start all background tasks and notification handlers. consistencyTicker := time.NewTicker(consistencyInterval) defer consistencyTicker.Stop() dcrdTicker := time.NewTicker(dcrdInterval) defer dcrdTicker.Stop() for { select { // Run voting wallet consistency check periodically. case <-consistencyTicker.C: v.checkWalletConsistency(ctx) // Ensure dcrd client is connected so notifications are received. case <-dcrdTicker.C: _, _, err := v.dcrd.Client() if err != nil { v.log.Error(err) } // Run the update function every time a block connected notification is // received from dcrd. case header := <-v.blockNotifChan: v.log.Debugf("Block notification %d (%s)", header.Height, header.BlockHash().String()) v.update(ctx) // Handle shutdown request. case <-ctx.Done(): return } } }