func (pm *PaymentMgr) generatePayments(height uint32, source *PaymentSource, amt dcrutil.Amount, workCreatedOn int64) error {
	switch pm.cfg.PaymentMethod {
	case PPS:
		return pm.payPerShare(source, amt, height, workCreatedOn)
	case PPLNS:
		return pm.payPerLastNShares(source, amt, height)
	default:
		return fmt.Errorf("unknown payment method provided %v", pm.cfg.PaymentMethod)
	}
}
generatePayments creates payments for participating accounts. This should
only be called when a block is confirmed mined, in pool mining mode.
package pool
import (
	"context"
	"errors"
	"fmt"
	"math/big"
	"math/rand"
	"sync"
	"sync/atomic"
	"time"
	"decred.org/dcrwallet/rpc/walletrpc"
	txrules "decred.org/dcrwallet/wallet/txrules"
	"decred.org/dcrwallet/wallet/txsizes"
	"github.com/decred/dcrd/chaincfg/chainhash"
	"github.com/decred/dcrd/chaincfg/v3"
	"github.com/decred/dcrd/dcrutil/v3"
	chainjson "github.com/decred/dcrd/rpc/jsonrpc/types/v2"
	"github.com/decred/dcrd/wire"
	"google.golang.org/grpc"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
	errs "github.com/decred/dcrpool/errors"
)
const (
	PPS = "pps"
	PPLNS = "pplns"
	maxRoundingDiff = dcrutil.Amount(500)
	maxTxConfThreshold = uint32(3)
	paymentBufferSize = uint32(30)
)
type TxCreator interface {
	GetTxOut(context.Context, *chainhash.Hash, uint32, bool) (*chainjson.GetTxOutResult, error)
	CreateRawTransaction(context.Context, []chainjson.TransactionInput, map[dcrutil.Address]dcrutil.Amount, *int64, *int64) (*wire.MsgTx, error)
	GetBlock(ctx context.Context, blockHash *chainhash.Hash) (*wire.MsgBlock, error)
}
type TxBroadcaster interface {
	SignTransaction(context.Context, *walletrpc.SignTransactionRequest, ...grpc.CallOption) (*walletrpc.SignTransactionResponse, error)
	PublishTransaction(context.Context, *walletrpc.PublishTransactionRequest, ...grpc.CallOption) (*walletrpc.PublishTransactionResponse, error)
	GetTransaction(context.Context, *walletrpc.GetTransactionRequest, ...grpc.CallOption) (*walletrpc.GetTransactionResponse, error)
	Rescan(ctx context.Context, in *walletrpc.RescanRequest, opts ...grpc.CallOption) (walletrpc.WalletService_RescanClient, error)
}
type rescanMsg struct {
	resp *walletrpc.RescanResponse
	err  error
}
type PaymentMgrConfig struct {
	db Database
	ActiveNet *chaincfg.Params
	PoolFee float64
	LastNPeriod time.Duration
	SoloPool bool
	PaymentMethod string
	PoolFeeAddrs []dcrutil.Address
	WalletAccount uint32
	WalletPass string
	GetBlockConfirmations func(context.Context, *chainhash.Hash) (int64, error)
	FetchTxCreator func() TxCreator
	FetchTxBroadcaster func() TxBroadcaster
	CoinbaseConfTimeout time.Duration
	SignalCache func(event CacheUpdateEvent)
	HubWg *sync.WaitGroup
}
type paymentMsg struct {
	CurrentHeight  uint32
	TreasuryActive bool
	Done           chan bool
}
type PaymentMgr struct {
	failedTxConfs uint32  
	txConfHashes  map[chainhash.Hash]uint32
	processing bool
	paymentCh  chan *paymentMsg
	cfg        *PaymentMgrConfig
	mtx        sync.Mutex
}
func NewPaymentMgr(pCfg *PaymentMgrConfig) (*PaymentMgr, error) {
	pm := &PaymentMgr{
		cfg:          pCfg,
		txConfHashes: make(map[chainhash.Hash]uint32),
		paymentCh:    make(chan *paymentMsg, paymentBufferSize),
	}
	rand.Seed(time.Now().UnixNano())
	_, _, err := pm.cfg.db.loadLastPaymentInfo()
	if err != nil {
		if errors.Is(err, errs.ValueNotFound) {
			err = pm.cfg.db.persistLastPaymentInfo(0, 0)
			if err != nil {
				return nil, err
			}
		} else {
			return nil, err
		}
	}
	_, err = pm.cfg.db.loadLastPaymentCreatedOn()
	if err != nil {
		if errors.Is(err, errs.ValueNotFound) {
			err = pm.cfg.db.persistLastPaymentCreatedOn(0)
			if err != nil {
				return nil, err
			}
		} else {
			return nil, err
		}
	}
	return pm, nil
}
func (pm *PaymentMgr) isProcessing() bool {
	pm.mtx.Lock()
	defer pm.mtx.Unlock()
	return pm.processing
}
func (pm *PaymentMgr) setProcessing(status bool) {
	pm.mtx.Lock()
	pm.processing = status
	pm.mtx.Unlock()
}
func (pm *PaymentMgr) sharePercentages(shares []*Share) (map[string]*big.Rat, error) {
	totalShares := new(big.Rat)
	tally := make(map[string]*big.Rat)
	percentages := make(map[string]*big.Rat)
	for _, share := range shares {
		totalShares = totalShares.Add(totalShares, share.Weight)
		if _, ok := tally[share.Account]; ok {
			tally[share.Account] = tally[share.Account].
				Add(tally[share.Account], share.Weight)
			continue
		}
		tally[share.Account] = share.Weight
	}
	for account, shareCount := range tally {
		if tally[account].Cmp(ZeroRat) == 0 {
			return nil, errs.PoolError(errs.DivideByZero, "division by zero")
		}
		accPercent := new(big.Rat).Quo(shareCount, totalShares)
		percentages[account] = accPercent
	}
	return percentages, nil
}
func (pm *PaymentMgr) PPSSharePercentages(workCreatedOn int64) (map[string]*big.Rat, error) {
	shares, err := pm.cfg.db.ppsEligibleShares(workCreatedOn)
	if err != nil {
		return nil, err
	}
	if len(shares) == 0 {
		return make(map[string]*big.Rat), nil
	}
	percentages, err := pm.sharePercentages(shares)
	if err != nil {
		return nil, err
	}
	return percentages, nil
}
func (pm *PaymentMgr) PPLNSSharePercentages() (map[string]*big.Rat, error) {
	now := time.Now()
	min := now.Add(-pm.cfg.LastNPeriod)
	shares, err := pm.cfg.db.pplnsEligibleShares(min.UnixNano())
	if err != nil {
		return nil, err
	}
	if len(shares) == 0 {
		return make(map[string]*big.Rat), nil
	}
	percentages, err := pm.sharePercentages(shares)
	if err != nil {
		return nil, err
	}
	return percentages, nil
}
func (pm *PaymentMgr) calculatePayments(ratios map[string]*big.Rat, source *PaymentSource,
	total dcrutil.Amount, poolFee float64, height uint32, estMaturity uint32) ([]*Payment, int64, error) {
	funcName := "calculatePayments"
	if len(ratios) == 0 {
		desc := fmt.Sprintf("%s: valid share ratios required to "+
			"generate payments", funcName)
		return nil, 0, errs.PoolError(errs.ShareRatio, desc)
	}
	fee := total.MulF64(poolFee)
	amtSansFees := total - fee
	sansFees := new(big.Rat).SetInt64(int64(amtSansFees))
	paymentTotal := dcrutil.Amount(0)
	dustAmts := make([]dcrutil.Amount, 0)
	payments := make([]*Payment, 0)
	for account, ratio := range ratios {
		amtRat := new(big.Rat).Mul(sansFees, ratio)
		amtI, accuracy := new(big.Float).SetRat(amtRat).Int64()
		amt := dcrutil.Amount(amtI)
		if accuracy > 0 {
			amt -= dcrutil.Amount(1)
		}
		paymentTotal += amt
		if txrules.IsDustAmount(amt, txsizes.P2PKHOutputSize,
			txrules.DefaultRelayFeePerKb) {
			dustAmts = append(dustAmts, amt)
		} else {
			payments = append(payments, NewPayment(account, source, amt, height,
				estMaturity))
		}
	}
	if amtSansFees < paymentTotal {
		diff := paymentTotal - amtSansFees
		desc := fmt.Sprintf("%s: total payments (%s) is greater than "+
			"the remaining coinbase amount after fees (%s). Difference is %s",
			funcName, paymentTotal, amtSansFees, diff)
		return nil, 0, errs.PoolError(errs.PaymentSource, desc)
	}
	var dustTotal dcrutil.Amount
	for _, amt := range dustAmts {
		dustTotal += amt
	}
	feePayment := NewPayment(PoolFeesK, source, fee+dustTotal, height, estMaturity)
	payments = append(payments, feePayment)
	return payments, feePayment.CreatedOn, nil
}
func (pm *PaymentMgr) payPerShare(source *PaymentSource, amt dcrutil.Amount, height uint32, workCreatedOn int64) error {
	percentages, err := pm.PPSSharePercentages(workCreatedOn)
	if err != nil {
		return err
	}
	estMaturity := height + uint32(pm.cfg.ActiveNet.CoinbaseMaturity)
	payments, lastPmtCreatedOn, err := pm.calculatePayments(percentages,
		source, amt, pm.cfg.PoolFee, height, estMaturity)
	if err != nil {
		return err
	}
	for _, payment := range payments {
		err := pm.cfg.db.PersistPayment(payment)
		if err != nil {
			return err
		}
	}
	err = pm.cfg.db.persistLastPaymentCreatedOn(lastPmtCreatedOn)
	if err != nil {
		return err
	}
	return pm.cfg.db.pruneShares(workCreatedOn)
}
func (pm *PaymentMgr) payPerLastNShares(source *PaymentSource, amt dcrutil.Amount, height uint32) error {
	percentages, err := pm.PPLNSSharePercentages()
	if err != nil {
		return err
	}
	estMaturity := height + uint32(pm.cfg.ActiveNet.CoinbaseMaturity)
	payments, lastPmtCreatedOn, err := pm.calculatePayments(percentages,
		source, amt, pm.cfg.PoolFee, height, estMaturity)
	if err != nil {
		return err
	}
	for _, payment := range payments {
		err := pm.cfg.db.PersistPayment(payment)
		if err != nil {
			return err
		}
	}
	err = pm.cfg.db.persistLastPaymentCreatedOn(lastPmtCreatedOn)
	if err != nil {
		return err
	}
	minNano := time.Now().Add(-pm.cfg.LastNPeriod).UnixNano()
	return pm.cfg.db.pruneShares(minNano)
}