Merge pull request #3405 from wpaulino/chainrpc-sane-defaults

chainntnfs: validate conf/spend ntfn registration parameters
This commit is contained in:
Olaoluwa Osuntokun authored and GitHub committed 2019-08-26 16:25:06 -07:00
commit 3868bdc490
16 files changed
+634 -684

No files matched your search

+29 -59
View File
@@ -9,6 +9,7 @@ import (
"github.com/btcsuite/btcd/btcjson"
"github.com/btcsuite/btcd/chaincfg"
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/txscript"
"github.com/btcsuite/btcd/wire"
"github.com/btcsuite/btcutil"
"github.com/btcsuite/btcwallet/chain"
@@ -39,8 +40,6 @@ type chainUpdate struct {
// chain client. Multiple concurrent clients are supported. All notifications
// are achieved via non-blocking sends on client channels.
type BitcoindNotifier struct {
confClientCounter uint64 // To be used atomically.
spendClientCounter uint64 // To be used atomically.
epochClientCounter uint64 // To be used atomically.
started int32 // To be used atomically.
@@ -616,23 +615,11 @@ func (b *BitcoindNotifier) notifyBlockEpochClient(epochClient *blockEpochRegistr
func (b *BitcoindNotifier) RegisterSpendNtfn(outpoint *wire.OutPoint,
pkScript []byte, heightHint uint32) (*chainntnfs.SpendEvent, error) {
// First, we'll construct a spend notification request and hand it off
// to the txNotifier.
spendID := atomic.AddUint64(&b.spendClientCounter, 1)
spendRequest, err := chainntnfs.NewSpendRequest(outpoint, pkScript)
if err != nil {
return nil, err
}
ntfn := &chainntnfs.SpendNtfn{
SpendID: spendID,
SpendRequest: spendRequest,
Event: chainntnfs.NewSpendEvent(func() {
b.txNotifier.CancelSpend(spendRequest, spendID)
}),
HeightHint: heightHint,
}
historicalDispatch, _, err := b.txNotifier.RegisterSpend(ntfn)
// Register the conf notification with the TxNotifier. A non-nil value
// for `dispatch` will be returned if we are required to perform a
// manual scan for the confirmation. Otherwise the notifier will begin
// watching at tip for the transaction to confirm.
ntfn, err := b.txNotifier.RegisterSpend(outpoint, pkScript, heightHint)
if err != nil {
return nil, err
}
@@ -641,17 +628,18 @@ func (b *BitcoindNotifier) RegisterSpendNtfn(outpoint *wire.OutPoint,
// outpoint/output script as spent.
//
// TODO(wilmer): use LoadFilter API instead.
if spendRequest.OutPoint == chainntnfs.ZeroOutPoint {
addr, err := spendRequest.PkScript.Address(b.chainParams)
if outpoint == nil || *outpoint == chainntnfs.ZeroOutPoint {
_, addrs, _, err := txscript.ExtractPkScriptAddrs(
pkScript, b.chainParams,
)
if err != nil {
return nil, err
return nil, fmt.Errorf("unable to parse script: %v", err)
}
addrs := []btcutil.Address{addr}
if err := b.chainConn.NotifyReceived(addrs); err != nil {
return nil, err
}
} else {
ops := []*wire.OutPoint{&spendRequest.OutPoint}
ops := []*wire.OutPoint{outpoint}
if err := b.chainConn.NotifySpent(ops); err != nil {
return nil, err
}
@@ -660,7 +648,7 @@ func (b *BitcoindNotifier) RegisterSpendNtfn(outpoint *wire.OutPoint,
// If the txNotifier didn't return any details to perform a historical
// scan of the chain, then we can return early as there's nothing left
// for us to do.
if historicalDispatch == nil {
if ntfn.HistoricalDispatch == nil {
return ntfn.Event, nil
}
@@ -670,9 +658,9 @@ func (b *BitcoindNotifier) RegisterSpendNtfn(outpoint *wire.OutPoint,
// We'll short-circuit the path when dispatching the spend of a script,
// rather than an outpoint, as there aren't any additional checks we can
// make for scripts.
if spendRequest.OutPoint == chainntnfs.ZeroOutPoint {
if ntfn.HistoricalDispatch.OutPoint == chainntnfs.ZeroOutPoint {
select {
case b.notificationRegistry <- historicalDispatch:
case b.notificationRegistry <- ntfn.HistoricalDispatch:
case <-b.quit:
return nil, chainntnfs.ErrChainNotifierShuttingDown
}
@@ -687,16 +675,16 @@ func (b *BitcoindNotifier) RegisterSpendNtfn(outpoint *wire.OutPoint,
// We'll start by checking the backend's UTXO set to determine whether
// the outpoint has been spent. If it hasn't, we can return to the
// caller as well.
txOut, err := b.chainConn.GetTxOut(
&spendRequest.OutPoint.Hash, spendRequest.OutPoint.Index, true,
)
txOut, err := b.chainConn.GetTxOut(&outpoint.Hash, outpoint.Index, true)
if err != nil {
return nil, err
}
if txOut != nil {
// We'll let the txNotifier know the outpoint is still unspent
// in order to begin updating its spend hint.
err := b.txNotifier.UpdateSpendDetails(spendRequest, nil)
err := b.txNotifier.UpdateSpendDetails(
ntfn.HistoricalDispatch.SpendRequest, nil,
)
if err != nil {
return nil, err
}
@@ -711,14 +699,14 @@ func (b *BitcoindNotifier) RegisterSpendNtfn(outpoint *wire.OutPoint,
// index (if enabled) to determine if we have a better rescan starting
// height. We can do this as the GetRawTransaction call will return the
// hash of the block it was included in within the chain.
tx, err := b.chainConn.GetRawTransactionVerbose(&spendRequest.OutPoint.Hash)
tx, err := b.chainConn.GetRawTransactionVerbose(&outpoint.Hash)
if err != nil {
// Avoid returning an error if the transaction was not found to
// proceed with fallback methods.
jsonErr, ok := err.(*btcjson.RPCError)
if !ok || jsonErr.Code != btcjson.ErrRPCNoTxInfo {
return nil, fmt.Errorf("unable to query for txid %v: %v",
spendRequest.OutPoint.Hash, err)
outpoint.Hash, err)
}
}
@@ -741,15 +729,15 @@ func (b *BitcoindNotifier) RegisterSpendNtfn(outpoint *wire.OutPoint,
return nil, err
}
if uint32(blockHeight) > historicalDispatch.StartHeight {
historicalDispatch.StartHeight = uint32(blockHeight)
if uint32(blockHeight) > ntfn.HistoricalDispatch.StartHeight {
ntfn.HistoricalDispatch.StartHeight = uint32(blockHeight)
}
}
// Now that we've determined the starting point of our rescan, we can
// dispatch it and return.
select {
case b.notificationRegistry <- historicalDispatch:
case b.notificationRegistry <- ntfn.HistoricalDispatch:
case <-b.quit:
return nil, chainntnfs.ErrChainNotifierShuttingDown
}
@@ -827,41 +815,23 @@ func (b *BitcoindNotifier) RegisterConfirmationsNtfn(txid *chainhash.Hash,
pkScript []byte,
numConfs, heightHint uint32) (*chainntnfs.ConfirmationEvent, error) {
// Construct a notification request for the transaction and send it to
// the main event loop.
confID := atomic.AddUint64(&b.confClientCounter, 1)
confRequest, err := chainntnfs.NewConfRequest(txid, pkScript)
if err != nil {
return nil, err
}
ntfn := &chainntnfs.ConfNtfn{
ConfID: confID,
ConfRequest: confRequest,
NumConfirmations: numConfs,
Event: chainntnfs.NewConfirmationEvent(numConfs, func() {
b.txNotifier.CancelConf(confRequest, confID)
}),
HeightHint: heightHint,
}
chainntnfs.Log.Infof("New confirmation subscription: %v, num_confs=%v",
confRequest, numConfs)
// Register the conf notification with the TxNotifier. A non-nil value
// for `dispatch` will be returned if we are required to perform a
// manual scan for the confirmation. Otherwise the notifier will begin
// watching at tip for the transaction to confirm.
dispatch, _, err := b.txNotifier.RegisterConf(ntfn)
ntfn, err := b.txNotifier.RegisterConf(
txid, pkScript, numConfs, heightHint,
)
if err != nil {
return nil, err
}
if dispatch == nil {
if ntfn.HistoricalDispatch == nil {
return ntfn.Event, nil
}
select {
case b.notificationRegistry <- dispatch:
case b.notificationRegistry <- ntfn.HistoricalDispatch:
return ntfn.Event, nil
case <-b.quit:
return nil, chainntnfs.ErrChainNotifierShuttingDown
+38 -66
View File
@@ -11,6 +11,7 @@ import (
"github.com/btcsuite/btcd/chaincfg"
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/rpcclient"
"github.com/btcsuite/btcd/txscript"
"github.com/btcsuite/btcd/wire"
"github.com/btcsuite/btcutil"
"github.com/lightningnetwork/lnd/chainntnfs"
@@ -50,8 +51,6 @@ type txUpdate struct {
// notifications. Multiple concurrent clients are supported. All notifications
// are achieved via non-blocking sends on client channels.
type BtcdNotifier struct {
confClientCounter uint64 // To be used aotmically.
spendClientCounter uint64 // To be used atomically.
epochClientCounter uint64 // To be used atomically.
started int32 // To be used atomically.
@@ -652,23 +651,11 @@ func (b *BtcdNotifier) notifyBlockEpochClient(epochClient *blockEpochRegistratio
func (b *BtcdNotifier) RegisterSpendNtfn(outpoint *wire.OutPoint,
pkScript []byte, heightHint uint32) (*chainntnfs.SpendEvent, error) {
// First, we'll construct a spend notification request and hand it off
// to the txNotifier.
spendID := atomic.AddUint64(&b.spendClientCounter, 1)
spendRequest, err := chainntnfs.NewSpendRequest(outpoint, pkScript)
if err != nil {
return nil, err
}
ntfn := &chainntnfs.SpendNtfn{
SpendID: spendID,
SpendRequest: spendRequest,
Event: chainntnfs.NewSpendEvent(func() {
b.txNotifier.CancelSpend(spendRequest, spendID)
}),
HeightHint: heightHint,
}
historicalDispatch, _, err := b.txNotifier.RegisterSpend(ntfn)
// Register the conf notification with the TxNotifier. A non-nil value
// for `dispatch` will be returned if we are required to perform a
// manual scan for the confirmation. Otherwise the notifier will begin
// watching at tip for the transaction to confirm.
ntfn, err := b.txNotifier.RegisterSpend(outpoint, pkScript, heightHint)
if err != nil {
return nil, err
}
@@ -677,17 +664,18 @@ func (b *BtcdNotifier) RegisterSpendNtfn(outpoint *wire.OutPoint,
// outpoint/output script as spent.
//
// TODO(wilmer): use LoadFilter API instead.
if spendRequest.OutPoint == chainntnfs.ZeroOutPoint {
addr, err := spendRequest.PkScript.Address(b.chainParams)
if outpoint == nil || *outpoint == chainntnfs.ZeroOutPoint {
_, addrs, _, err := txscript.ExtractPkScriptAddrs(
pkScript, b.chainParams,
)
if err != nil {
return nil, err
return nil, fmt.Errorf("unable to parse script: %v", err)
}
addrs := []btcutil.Address{addr}
if err := b.chainConn.NotifyReceived(addrs); err != nil {
return nil, err
}
} else {
ops := []*wire.OutPoint{&spendRequest.OutPoint}
ops := []*wire.OutPoint{outpoint}
if err := b.chainConn.NotifySpent(ops); err != nil {
return nil, err
}
@@ -696,7 +684,7 @@ func (b *BtcdNotifier) RegisterSpendNtfn(outpoint *wire.OutPoint,
// If the txNotifier didn't return any details to perform a historical
// scan of the chain, then we can return early as there's nothing left
// for us to do.
if historicalDispatch == nil {
if ntfn.HistoricalDispatch == nil {
return ntfn.Event, nil
}
@@ -706,26 +694,29 @@ func (b *BtcdNotifier) RegisterSpendNtfn(outpoint *wire.OutPoint,
// We'll short-circuit the path when dispatching the spend of a script,
// rather than an outpoint, as there aren't any additional checks we can
// make for scripts.
if spendRequest.OutPoint == chainntnfs.ZeroOutPoint {
if outpoint == nil || *outpoint == chainntnfs.ZeroOutPoint {
startHash, err := b.chainConn.GetBlockHash(
int64(historicalDispatch.StartHeight),
int64(ntfn.HistoricalDispatch.StartHeight),
)
if err != nil {
return nil, err
}
// TODO(wilmer): add retry logic if rescan fails?
addr, err := spendRequest.PkScript.Address(b.chainParams)
_, addrs, _, err := txscript.ExtractPkScriptAddrs(
pkScript, b.chainParams,
)
if err != nil {
return nil, err
return nil, fmt.Errorf("unable to parse address: %v", err)
}
addrs := []btcutil.Address{addr}
asyncResult := b.chainConn.RescanAsync(startHash, addrs, nil)
go func() {
if rescanErr := asyncResult.Receive(); rescanErr != nil {
chainntnfs.Log.Errorf("Rescan to determine "+
"the spend details of %v failed: %v",
spendRequest, rescanErr)
ntfn.HistoricalDispatch.SpendRequest,
rescanErr)
}
}()
@@ -739,16 +730,16 @@ func (b *BtcdNotifier) RegisterSpendNtfn(outpoint *wire.OutPoint,
// We'll start by checking the backend's UTXO set to determine whether
// the outpoint has been spent. If it hasn't, we can return to the
// caller as well.
txOut, err := b.chainConn.GetTxOut(
&spendRequest.OutPoint.Hash, spendRequest.OutPoint.Index, true,
)
txOut, err := b.chainConn.GetTxOut(&outpoint.Hash, outpoint.Index, true)
if err != nil {
return nil, err
}
if txOut != nil {
// We'll let the txNotifier know the outpoint is still unspent
// in order to begin updating its spend hint.
err := b.txNotifier.UpdateSpendDetails(spendRequest, nil)
err := b.txNotifier.UpdateSpendDetails(
ntfn.HistoricalDispatch.SpendRequest, nil,
)
if err != nil {
return nil, err
}
@@ -760,25 +751,25 @@ func (b *BtcdNotifier) RegisterSpendNtfn(outpoint *wire.OutPoint,
// set, we'll determine when it happened by scanning the chain. We'll
// begin by fetching the block hash of our starting height.
startHash, err := b.chainConn.GetBlockHash(
int64(historicalDispatch.StartHeight),
int64(ntfn.HistoricalDispatch.StartHeight),
)
if err != nil {
return nil, fmt.Errorf("unable to get block hash for height "+
"%d: %v", historicalDispatch.StartHeight, err)
"%d: %v", ntfn.HistoricalDispatch.StartHeight, err)
}
// As a minimal optimization, we'll query the backend's transaction
// index (if enabled) to determine if we have a better rescan starting
// height. We can do this as the GetRawTransaction call will return the
// hash of the block it was included in within the chain.
tx, err := b.chainConn.GetRawTransactionVerbose(&spendRequest.OutPoint.Hash)
tx, err := b.chainConn.GetRawTransactionVerbose(&outpoint.Hash)
if err != nil {
// Avoid returning an error if the transaction was not found to
// proceed with fallback methods.
jsonErr, ok := err.(*btcjson.RPCError)
if !ok || jsonErr.Code != btcjson.ErrRPCNoTxInfo {
return nil, fmt.Errorf("unable to query for txid %v: %v",
spendRequest.OutPoint.Hash, err)
outpoint.Hash, err)
}
}
@@ -802,7 +793,7 @@ func (b *BtcdNotifier) RegisterSpendNtfn(outpoint *wire.OutPoint,
"block %v: %v", blockHash, err)
}
if uint32(blockHeader.Height) > historicalDispatch.StartHeight {
if uint32(blockHeader.Height) > ntfn.HistoricalDispatch.StartHeight {
startHash, err = b.chainConn.GetBlockHash(
int64(blockHeader.Height),
)
@@ -825,13 +816,12 @@ func (b *BtcdNotifier) RegisterSpendNtfn(outpoint *wire.OutPoint,
//
// TODO(wilmer): add retry logic if rescan fails?
asyncResult := b.chainConn.RescanAsync(
startHash, nil, []*wire.OutPoint{&spendRequest.OutPoint},
startHash, nil, []*wire.OutPoint{outpoint},
)
go func() {
if rescanErr := asyncResult.Receive(); rescanErr != nil {
chainntnfs.Log.Errorf("Rescan to determine the spend "+
"details of %v failed: %v", spendRequest,
rescanErr)
"details of %v failed: %v", outpoint, rescanErr)
}
}()
@@ -851,41 +841,23 @@ func (b *BtcdNotifier) RegisterConfirmationsNtfn(txid *chainhash.Hash,
pkScript []byte,
numConfs, heightHint uint32) (*chainntnfs.ConfirmationEvent, error) {
// Construct a notification request for the transaction and send it to
// the main event loop.
confID := atomic.AddUint64(&b.confClientCounter, 1)
confRequest, err := chainntnfs.NewConfRequest(txid, pkScript)
if err != nil {
return nil, err
}
ntfn := &chainntnfs.ConfNtfn{
ConfID: confID,
ConfRequest: confRequest,
NumConfirmations: numConfs,
Event: chainntnfs.NewConfirmationEvent(numConfs, func() {
b.txNotifier.CancelConf(confRequest, confID)
}),
HeightHint: heightHint,
}
chainntnfs.Log.Infof("New confirmation subscription: %v, num_confs=%v ",
confRequest, numConfs)
// Register the conf notification with the TxNotifier. A non-nil value
// for `dispatch` will be returned if we are required to perform a
// manual scan for the confirmation. Otherwise the notifier will begin
// watching at tip for the transaction to confirm.
dispatch, _, err := b.txNotifier.RegisterConf(ntfn)
ntfn, err := b.txNotifier.RegisterConf(
txid, pkScript, numConfs, heightHint,
)
if err != nil {
return nil, err
}
if dispatch == nil {
if ntfn.HistoricalDispatch == nil {
return ntfn.Event, nil
}
select {
case b.notificationRegistry <- dispatch:
case b.notificationRegistry <- ntfn.HistoricalDispatch:
return ntfn.Event, nil
case <-b.quit:
return nil, chainntnfs.ErrChainNotifierShuttingDown
+28 -59
View File
@@ -37,8 +37,6 @@ const (
// TODO(roasbeef): heavily consolidate with NeutrinoNotifier code
// * maybe combine into single package?
type NeutrinoNotifier struct {
confClientCounter uint64 // To be used atomically.
spendClientCounter uint64 // To be used atomically.
epochClientCounter uint64 // To be used atomically.
started int32 // To be used atomically.
@@ -663,23 +661,11 @@ func (n *NeutrinoNotifier) notifyBlockEpochClient(epochClient *blockEpochRegistr
func (n *NeutrinoNotifier) RegisterSpendNtfn(outpoint *wire.OutPoint,
pkScript []byte, heightHint uint32) (*chainntnfs.SpendEvent, error) {
// First, we'll construct a spend notification request and hand it off
// to the txNotifier.
spendID := atomic.AddUint64(&n.spendClientCounter, 1)
spendRequest, err := chainntnfs.NewSpendRequest(outpoint, pkScript)
if err != nil {
return nil, err
}
ntfn := &chainntnfs.SpendNtfn{
SpendID: spendID,
SpendRequest: spendRequest,
Event: chainntnfs.NewSpendEvent(func() {
n.txNotifier.CancelSpend(spendRequest, spendID)
}),
HeightHint: heightHint,
}
historicalDispatch, txNotifierTip, err := n.txNotifier.RegisterSpend(ntfn)
// Register the conf notification with the TxNotifier. A non-nil value
// for `dispatch` will be returned if we are required to perform a
// manual scan for the confirmation. Otherwise the notifier will begin
// watching at tip for the transaction to confirm.
ntfn, err := n.txNotifier.RegisterSpend(outpoint, pkScript, heightHint)
if err != nil {
return nil, err
}
@@ -691,9 +677,12 @@ func (n *NeutrinoNotifier) RegisterSpendNtfn(outpoint *wire.OutPoint,
//
// We'll update our filter first to ensure we can immediately detect the
// spend at tip.
if outpoint == nil {
outpoint = &chainntnfs.ZeroOutPoint
}
inputToWatch := neutrino.InputWithScript{
OutPoint: spendRequest.OutPoint,
PkScript: spendRequest.PkScript.Script(),
OutPoint: *outpoint,
PkScript: pkScript,
}
updateOptions := []neutrino.UpdateOption{
neutrino.AddInputs(inputToWatch),
@@ -704,10 +693,9 @@ func (n *NeutrinoNotifier) RegisterSpendNtfn(outpoint *wire.OutPoint,
// update. In the case of an output script spend request, we'll check if
// we should perform a historical rescan and start from there, as we
// cannot do so with GetUtxo since it matches outpoints.
rewindHeight := txNotifierTip
if historicalDispatch != nil &&
spendRequest.OutPoint == chainntnfs.ZeroOutPoint {
rewindHeight = historicalDispatch.StartHeight
rewindHeight := ntfn.Height
if ntfn.HistoricalDispatch != nil && *outpoint == chainntnfs.ZeroOutPoint {
rewindHeight = ntfn.HistoricalDispatch.StartHeight
}
updateOptions = append(updateOptions, neutrino.Rewind(rewindHeight))
@@ -734,8 +722,7 @@ func (n *NeutrinoNotifier) RegisterSpendNtfn(outpoint *wire.OutPoint,
// scan of the chain, or if we already performed one like in the case of
// output script spend requests, then we can return early as there's
// nothing left for us to do.
if historicalDispatch == nil ||
spendRequest.OutPoint == chainntnfs.ZeroOutPoint {
if ntfn.HistoricalDispatch == nil || *outpoint == chainntnfs.ZeroOutPoint {
return ntfn.Event, nil
}
@@ -753,7 +740,7 @@ func (n *NeutrinoNotifier) RegisterSpendNtfn(outpoint *wire.OutPoint,
currentHeight := uint32(n.bestBlock.Height)
n.bestBlockMtx.RUnlock()
if currentHeight >= historicalDispatch.StartHeight {
if currentHeight >= ntfn.HistoricalDispatch.StartHeight {
break
}
@@ -767,10 +754,10 @@ func (n *NeutrinoNotifier) RegisterSpendNtfn(outpoint *wire.OutPoint,
spendReport, err := n.p2pNode.GetUtxo(
neutrino.WatchInputs(inputToWatch),
neutrino.StartBlock(&waddrmgr.BlockStamp{
Height: int32(historicalDispatch.StartHeight),
Height: int32(ntfn.HistoricalDispatch.StartHeight),
}),
neutrino.EndBlock(&waddrmgr.BlockStamp{
Height: int32(historicalDispatch.EndHeight),
Height: int32(ntfn.HistoricalDispatch.EndHeight),
}),
neutrino.QuitChan(n.quit),
)
@@ -785,7 +772,7 @@ func (n *NeutrinoNotifier) RegisterSpendNtfn(outpoint *wire.OutPoint,
if spendReport != nil && spendReport.SpendingTx != nil {
spendingTxHash := spendReport.SpendingTx.TxHash()
spendDetails = &chainntnfs.SpendDetail{
SpentOutPoint: &spendRequest.OutPoint,
SpentOutPoint: outpoint,
SpenderTxHash: &spendingTxHash,
SpendingTx: spendReport.SpendingTx,
SpenderInputIndex: spendReport.SpendingInputIndex,
@@ -797,7 +784,9 @@ func (n *NeutrinoNotifier) RegisterSpendNtfn(outpoint *wire.OutPoint,
// not, we'll mark our historical rescan as complete to ensure the
// outpoint's spend hint gets updated upon connected/disconnected
// blocks.
err = n.txNotifier.UpdateSpendDetails(spendRequest, spendDetails)
err = n.txNotifier.UpdateSpendDetails(
ntfn.HistoricalDispatch.SpendRequest, spendDetails,
)
if err != nil {
chainntnfs.Log.Errorf("Failed to update spend details: %v", err)
return
@@ -820,31 +809,13 @@ func (n *NeutrinoNotifier) RegisterConfirmationsNtfn(txid *chainhash.Hash,
pkScript []byte,
numConfs, heightHint uint32) (*chainntnfs.ConfirmationEvent, error) {
// Construct a notification request for the transaction and send it to
// the main event loop.
confID := atomic.AddUint64(&n.confClientCounter, 1)
confRequest, err := chainntnfs.NewConfRequest(txid, pkScript)
if err != nil {
return nil, err
}
ntfn := &chainntnfs.ConfNtfn{
ConfID: confID,
ConfRequest: confRequest,
NumConfirmations: numConfs,
Event: chainntnfs.NewConfirmationEvent(numConfs, func() {
n.txNotifier.CancelConf(confRequest, confID)
}),
HeightHint: heightHint,
}
chainntnfs.Log.Infof("New confirmation subscription: %v, num_confs=%v",
confRequest, numConfs)
// Register the conf notification with the TxNotifier. A non-nil value
// for `dispatch` will be returned if we are required to perform a
// manual scan for the confirmation. Otherwise the notifier will begin
// watching at tip for the transaction to confirm.
dispatch, txNotifierTip, err := n.txNotifier.RegisterConf(ntfn)
ntfn, err := n.txNotifier.RegisterConf(
txid, pkScript, numConfs, heightHint,
)
if err != nil {
return nil, err
}
@@ -859,9 +830,7 @@ func (n *NeutrinoNotifier) RegisterConfirmationsNtfn(txid *chainhash.Hash,
// type so we can instruct neutrino to match if the transaction
// containing the script is found in a block.
params := n.p2pNode.ChainParams()
_, addrs, _, err := txscript.ExtractPkScriptAddrs(
confRequest.PkScript.Script(), &params,
)
_, addrs, _, err := txscript.ExtractPkScriptAddrs(pkScript, &params)
if err != nil {
return nil, fmt.Errorf("unable to extract script: %v", err)
}
@@ -873,7 +842,7 @@ func (n *NeutrinoNotifier) RegisterConfirmationsNtfn(txid *chainhash.Hash,
case n.notificationRegistry <- &rescanFilterUpdate{
updateOptions: []neutrino.UpdateOption{
neutrino.AddAddrs(addrs...),
neutrino.Rewind(txNotifierTip),
neutrino.Rewind(ntfn.Height),
neutrino.DisableDisconnectedNtfns(true),
},
errChan: errChan,
@@ -893,14 +862,14 @@ func (n *NeutrinoNotifier) RegisterConfirmationsNtfn(txid *chainhash.Hash,
// If a historical rescan was not requested by the txNotifier, then we
// can return to the caller.
if dispatch == nil {
if ntfn.HistoricalDispatch == nil {
return ntfn.Event, nil
}
// Finally, with the filter updated, we can dispatch the historical
// rescan to ensure we can detect if the event happened in the past.
select {
case n.notificationRegistry <- dispatch:
case n.notificationRegistry <- ntfn.HistoricalDispatch:
case <-n.quit:
return nil, chainntnfs.ErrChainNotifierShuttingDown
}
+207 -44
View File
@@ -5,6 +5,7 @@ import (
"errors"
"fmt"
"sync"
"sync/atomic"
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/txscript"
@@ -45,10 +46,22 @@ var (
// with the TxNotifier but it been shut down.
ErrTxNotifierExiting = errors.New("TxNotifier is exiting")
// ErrTxMaxConfs signals that the user requested a number of
// confirmations beyond the reorg safety limit.
ErrTxMaxConfs = fmt.Errorf("too many confirmations requested, max is %d",
MaxNumConfs)
// ErrNoScript is an error returned when a confirmation/spend
// registration is attempted without providing an accompanying output
// script.
ErrNoScript = errors.New("an output script must be provided")
// ErrNoHeightHint is an error returned when a confirmation/spend
// registration is attempted without providing an accompanying height
// hint.
ErrNoHeightHint = errors.New("a height hint greater than 0 must be " +
"provided")
// ErrNumConfsOutOfRange is an error returned when a confirmation/spend
// registration is attempted and the number of confirmations provided is
// out of range.
ErrNumConfsOutOfRange = fmt.Errorf("number of confirmations must be "+
"between %d and %d", 1, MaxNumConfs)
)
// rescanState indicates the progression of a registration before the notifier
@@ -255,6 +268,25 @@ type HistoricalConfDispatch struct {
EndHeight uint32
}
// ConfRegistration encompasses all of the information required for callers to
// retrieve details about a confirmation event.
type ConfRegistration struct {
// Event contains references to the channels that the notifications are
// to be sent over.
Event *ConfirmationEvent
// HistoricalDispatch, if non-nil, signals to the client who registered
// the notification that they are responsible for attempting to manually
// rescan blocks for the txid/output script between the start and end
// heights.
HistoricalDispatch *HistoricalConfDispatch
// Height is the height of the TxNotifier at the time the confirmation
// notification was registered. This can be used so that backends can
// request to be notified of confirmations from this point forwards.
Height uint32
}
// SpendRequest encapsulates a request for a spend notification of either an
// outpoint or output script.
type SpendRequest struct {
@@ -395,12 +427,34 @@ type HistoricalSpendDispatch struct {
EndHeight uint32
}
// SpendRegistration encompasses all of the information required for callers to
// retrieve details about a spend event.
type SpendRegistration struct {
// Event contains references to the channels that the notifications are
// to be sent over.
Event *SpendEvent
// HistoricalDispatch, if non-nil, signals to the client who registered
// the notification that they are responsible for attempting to manually
// rescan blocks for the txid/output script between the start and end
// heights.
HistoricalDispatch *HistoricalSpendDispatch
// Height is the height of the TxNotifier at the time the spend
// notification was registered. This can be used so that backends can
// request to be notified of spends from this point forwards.
Height uint32
}
// TxNotifier is a struct responsible for delivering transaction notifications
// to subscribers. These notifications can be of two different types:
// transaction/output script confirmations and/or outpoint/output script spends.
// The TxNotifier will watch the blockchain as new blocks come in, in order to
// satisfy its client requests.
type TxNotifier struct {
confClientCounter uint64 // To be used atomically.
spendClientCounter uint64 // To be used atomically.
// currentHeight is the height of the tracked blockchain. It is used to
// determine the number of confirmations a tx has and ensure blocks are
// connected and disconnected in order.
@@ -484,34 +538,72 @@ func NewTxNotifier(startHeight uint32, reorgSafetyLimit uint32,
}
}
// newConfNtfn validates all of the parameters required to successfully create
// and register a confirmation notification.
func (n *TxNotifier) newConfNtfn(txid *chainhash.Hash,
pkScript []byte, numConfs, heightHint uint32) (*ConfNtfn, error) {
// An accompanying output script must always be provided.
if len(pkScript) == 0 {
return nil, ErrNoScript
}
// Enforce that we will not dispatch confirmations beyond the reorg
// safety limit.
if numConfs == 0 || numConfs > n.reorgSafetyLimit {
return nil, ErrNumConfsOutOfRange
}
// A height hint must be provided to prevent scanning from the genesis
// block.
if heightHint == 0 {
return nil, ErrNoHeightHint
}
// Ensure the output script is of a supported type.
confRequest, err := NewConfRequest(txid, pkScript)
if err != nil {
return nil, err
}
confID := atomic.AddUint64(&n.confClientCounter, 1)
return &ConfNtfn{
ConfID: confID,
ConfRequest: confRequest,
NumConfirmations: numConfs,
Event: NewConfirmationEvent(numConfs, func() {
n.CancelConf(confRequest, confID)
}),
HeightHint: heightHint,
}, nil
}
// RegisterConf handles a new confirmation notification request. The client will
// be notified when the transaction/output script gets a sufficient number of
// confirmations in the blockchain. The registration succeeds if no error is
// returned. If the returned HistoricalConfDispatch is non-nil, the caller is
// responsible for attempting to manually rescan blocks for the txid/output
// script between the start and end heights. The notifier's current height is
// also returned so that backends can request to be notified of confirmations
// from this point forwards.
// confirmations in the blockchain.
//
// NOTE: If the transaction/output script has already been included in a block
// on the chain, the confirmation details must be provided with the
// UpdateConfDetails method, otherwise we will wait for the transaction/output
// script to confirm even though it already has.
func (n *TxNotifier) RegisterConf(ntfn *ConfNtfn) (*HistoricalConfDispatch,
uint32, error) {
func (n *TxNotifier) RegisterConf(txid *chainhash.Hash, pkScript []byte,
numConfs, heightHint uint32) (*ConfRegistration, error) {
select {
case <-n.quit:
return nil, 0, ErrTxNotifierExiting
return nil, ErrTxNotifierExiting
default:
}
// Enforce that we will not dispatch confirmations beyond the reorg
// safety limit.
if ntfn.NumConfirmations > n.reorgSafetyLimit {
return nil, 0, ErrTxMaxConfs
// We'll start by performing a series of validation checks.
ntfn, err := n.newConfNtfn(txid, pkScript, numConfs, heightHint)
if err != nil {
return nil, err
}
Log.Infof("New confirmation subscription: %v, num_confs=%v ",
ntfn.ConfRequest, numConfs)
// Before proceeding to register the notification, we'll query our
// height hint cache to determine whether a better one exists.
//
@@ -557,9 +649,16 @@ func (n *TxNotifier) RegisterConf(ntfn *ConfNtfn) (*HistoricalConfDispatch,
"registration since rescan has finished",
ntfn.ConfRequest)
return nil, n.currentHeight, n.dispatchConfDetails(
ntfn, confSet.details,
)
err := n.dispatchConfDetails(ntfn, confSet.details)
if err != nil {
return nil, err
}
return &ConfRegistration{
Event: ntfn.Event,
HistoricalDispatch: nil,
Height: n.currentHeight,
}, nil
// A rescan is already in progress, return here to prevent dispatching
// another. When the rescan returns, this notification's details will be
@@ -568,7 +667,11 @@ func (n *TxNotifier) RegisterConf(ntfn *ConfNtfn) (*HistoricalConfDispatch,
Log.Debugf("Waiting for pending rescan to finish before "+
"notifying %v at tip", ntfn.ConfRequest)
return nil, n.currentHeight, nil
return &ConfRegistration{
Event: ntfn.Event,
HistoricalDispatch: nil,
Height: n.currentHeight,
}, nil
// If no rescan has been dispatched, attempt to do so now.
case rescanNotStarted:
@@ -587,7 +690,11 @@ func (n *TxNotifier) RegisterConf(ntfn *ConfNtfn) (*HistoricalConfDispatch,
// notifier to start delivering messages for this set
// immediately.
confSet.rescanStatus = rescanComplete
return nil, n.currentHeight, nil
return &ConfRegistration{
Event: ntfn.Event,
HistoricalDispatch: nil,
Height: n.currentHeight,
}, nil
}
Log.Debugf("Dispatching historical confirmation rescan for %v",
@@ -607,7 +714,11 @@ func (n *TxNotifier) RegisterConf(ntfn *ConfNtfn) (*HistoricalConfDispatch,
// registrations don't also attempt a dispatch.
confSet.rescanStatus = rescanPending
return dispatch, n.currentHeight, nil
return &ConfRegistration{
Event: ntfn.Event,
HistoricalDispatch: dispatch,
Height: n.currentHeight,
}, nil
}
// CancelConf cancels an existing request for a spend notification of an
@@ -818,29 +929,62 @@ func (n *TxNotifier) dispatchConfDetails(
return nil
}
// newSpendNtfn validates all of the parameters required to successfully create
// and register a spend notification.
func (n *TxNotifier) newSpendNtfn(outpoint *wire.OutPoint,
pkScript []byte, heightHint uint32) (*SpendNtfn, error) {
// An accompanying output script must always be provided.
if len(pkScript) == 0 {
return nil, ErrNoScript
}
// A height hint must be provided to prevent scanning from the genesis
// block.
if heightHint == 0 {
return nil, ErrNoHeightHint
}
// Ensure the output script is of a supported type.
spendRequest, err := NewSpendRequest(outpoint, pkScript)
if err != nil {
return nil, err
}
spendID := atomic.AddUint64(&n.spendClientCounter, 1)
return &SpendNtfn{
SpendID: spendID,
SpendRequest: spendRequest,
Event: NewSpendEvent(func() {
n.CancelSpend(spendRequest, spendID)
}),
HeightHint: heightHint,
}, nil
}
// RegisterSpend handles a new spend notification request. The client will be
// notified once the outpoint/output script is detected as spent within the
// chain.
//
// The registration succeeds if no error is returned. If the returned
// HistoricalSpendDisaptch is non-nil, the caller is responsible for attempting
// to determine whether the outpoint/output script has been spent between the
// start and end heights. The notifier's current height is also returned so that
// backends can request to be notified of spends from this point forwards.
//
// NOTE: If the outpoint/output script has already been spent within the chain
// before the notifier's current tip, the spend details must be provided with
// the UpdateSpendDetails method, otherwise we will wait for the outpoint/output
// script to be spent at tip, even though it already has.
func (n *TxNotifier) RegisterSpend(ntfn *SpendNtfn) (*HistoricalSpendDispatch,
uint32, error) {
func (n *TxNotifier) RegisterSpend(outpoint *wire.OutPoint, pkScript []byte,
heightHint uint32) (*SpendRegistration, error) {
select {
case <-n.quit:
return nil, 0, ErrTxNotifierExiting
return nil, ErrTxNotifierExiting
default:
}
// We'll start by performing a series of validation checks.
ntfn, err := n.newSpendNtfn(outpoint, pkScript, heightHint)
if err != nil {
return nil, err
}
// Before proceeding to register the notification, we'll query our spend
// hint cache to determine whether a better one exists.
startHeight := ntfn.HeightHint
@@ -886,9 +1030,16 @@ func (n *TxNotifier) RegisterSpend(ntfn *SpendNtfn) (*HistoricalSpendDispatch,
"registration since rescan has finished",
ntfn.SpendRequest)
return nil, n.currentHeight, n.dispatchSpendDetails(
ntfn, spendSet.details,
)
err := n.dispatchSpendDetails(ntfn, spendSet.details)
if err != nil {
return nil, err
}
return &SpendRegistration{
Event: ntfn.Event,
HistoricalDispatch: nil,
Height: n.currentHeight,
}, nil
// If there is an active rescan to determine whether the request has
// been spent, then we won't trigger another one.
@@ -896,7 +1047,11 @@ func (n *TxNotifier) RegisterSpend(ntfn *SpendNtfn) (*HistoricalSpendDispatch,
Log.Debugf("Waiting for pending rescan to finish before "+
"notifying %v at tip", ntfn.SpendRequest)
return nil, n.currentHeight, nil
return &SpendRegistration{
Event: ntfn.Event,
HistoricalDispatch: nil,
Height: n.currentHeight,
}, nil
// Otherwise, we'll fall through and let the caller know that a rescan
// should be dispatched to determine whether the request has already
@@ -916,7 +1071,11 @@ func (n *TxNotifier) RegisterSpend(ntfn *SpendNtfn) (*HistoricalSpendDispatch,
// spend hints for this request get updated upon
// connected/disconnected blocks.
spendSet.rescanStatus = rescanComplete
return nil, n.currentHeight, nil
return &SpendRegistration{
Event: ntfn.Event,
HistoricalDispatch: nil,
Height: n.currentHeight,
}, nil
}
// We'll set the rescan status to pending to ensure subsequent
@@ -926,11 +1085,15 @@ func (n *TxNotifier) RegisterSpend(ntfn *SpendNtfn) (*HistoricalSpendDispatch,
Log.Debugf("Dispatching historical spend rescan for %v",
ntfn.SpendRequest)
return &HistoricalSpendDispatch{
SpendRequest: ntfn.SpendRequest,
StartHeight: startHeight,
EndHeight: n.currentHeight,
}, n.currentHeight, nil
return &SpendRegistration{
Event: ntfn.Event,
HistoricalDispatch: &HistoricalSpendDispatch{
SpendRequest: ntfn.SpendRequest,
StartHeight: startHeight,
EndHeight: n.currentHeight,
},
Height: n.currentHeight,
}, nil
}
// CancelSpend cancels an existing request for a spend notification of an
@@ -1749,11 +1912,11 @@ func (n *TxNotifier) dispatchSpendReorg(ntfn *SpendNtfn) error {
// closes the event channels of all registered notifications that have not been
// dispatched yet.
func (n *TxNotifier) TearDown() {
close(n.quit)
n.Lock()
defer n.Unlock()
close(n.quit)
for _, confSet := range n.confNotifications {
for _, ntfn := range confSet.ntfns {
close(ntfn.Event.Confirmed)
File diff suppressed because it is too large. Load diff
+4
View File
@@ -43,4 +43,8 @@ type Config struct {
// Sweeper is the central batching engine of lnd. It is responsible for
// sweeping inputs in batches back into the wallet.
Sweeper *sweep.UtxoSweeper
// Chain is an interface that the WalletKit will use to determine state
// about the backing chain of the wallet.
Chain lnwallet.BlockChainIO
}
+8
View File
@@ -51,6 +51,14 @@ func createNewSubServer(configRegistry lnrpc.SubServerConfigDispatcher) (lnrpc.S
case config.KeyRing == nil:
return nil, nil, fmt.Errorf("KeyRing must be set to create " +
"WalletKit RPC server")
case config.Sweeper == nil:
return nil, nil, fmt.Errorf("Sweeper must be set to create " +
"WalletKit RPC server")
case config.Chain == nil:
return nil, nil, fmt.Errorf("Chain must be set to create " +
"WalletKit RPC server")
}
return New(config)
+24 -6
View File
@@ -4,6 +4,7 @@ package walletrpc
import (
"bytes"
"errors"
"fmt"
"io/ioutil"
"os"
@@ -496,27 +497,44 @@ func (w *WalletKit) BumpFee(ctx context.Context,
//
// We'll gather all of the information required by the UtxoSweeper in
// order to sweep the output.
txOut, err := w.cfg.Wallet.FetchInputInfo(op)
utxo, err := w.cfg.Wallet.FetchInputInfo(op)
if err != nil {
return nil, err
}
// We're only able to bump the fee of unconfirmed transactions.
if utxo.Confirmations > 0 {
return nil, errors.New("unable to bump fee of a confirmed " +
"transaction")
}
var witnessType input.WitnessType
switch {
case txscript.IsPayToWitnessPubKeyHash(txOut.PkScript):
switch utxo.AddressType {
case lnwallet.WitnessPubKey:
witnessType = input.WitnessKeyHash
case txscript.IsPayToScriptHash(txOut.PkScript):
case lnwallet.NestedWitnessPubKey:
witnessType = input.NestedWitnessKeyHash
default:
return nil, fmt.Errorf("unknown input witness %v", op)
}
signDesc := &input.SignDescriptor{
Output: txOut,
Output: &wire.TxOut{
PkScript: utxo.PkScript,
Value: int64(utxo.Value),
},
HashType: txscript.SigHashAll,
}
input := input.NewBaseInput(op, witnessType, signDesc, 0)
// We'll use the current height as the height hint since we're dealing
// with an unconfirmed transaction.
_, currentHeight, err := w.cfg.Chain.GetBestBlock()
if err != nil {
return nil, fmt.Errorf("unable to retrieve current height: %v",
err)
}
input := input.NewBaseInput(op, witnessType, signDesc, uint32(currentHeight))
if _, err = w.cfg.Sweeper.SweepInput(input, feePreference); err != nil {
return nil, err
}
-6
View File
@@ -59,11 +59,6 @@ type BtcWallet struct {
netParams *chaincfg.Params
chainKeyScope waddrmgr.KeyScope
// utxoCache is a cache used to speed up repeated calls to
// FetchInputInfo.
utxoCache map[wire.OutPoint]*wire.TxOut
cacheMtx sync.RWMutex
}
// A compile time check to ensure that BtcWallet implements the
@@ -130,7 +125,6 @@ func New(cfg Config) (*BtcWallet, error) {
chain: cfg.ChainSource,
netParams: cfg.NetParams,
chainKeyScope: chainKeyScope,
utxoCache: make(map[wire.OutPoint]*wire.TxOut),
}, nil
}
+34 -22
View File
@@ -1,6 +1,8 @@
package btcwallet
import (
"fmt"
"github.com/btcsuite/btcd/btcec"
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/txscript"
@@ -21,22 +23,8 @@ import (
// of ErrNotMine should be returned instead.
//
// This is a part of the WalletController interface.
func (b *BtcWallet) FetchInputInfo(prevOut *wire.OutPoint) (*wire.TxOut, error) {
var (
err error
output *wire.TxOut
)
// First check to see if the output is already within the utxo cache.
// If so we can return directly saving a disk access.
b.cacheMtx.RLock()
if output, ok := b.utxoCache[*prevOut]; ok {
b.cacheMtx.RUnlock()
return output, nil
}
b.cacheMtx.RUnlock()
// Otherwise, we manually look up the output within the tx store.
func (b *BtcWallet) FetchInputInfo(prevOut *wire.OutPoint) (*lnwallet.Utxo, error) {
// We manually look up the output within the tx store.
txid := &prevOut.Hash
txDetail, err := base.UnstableAPI(b.wallet).TxDetails(txid)
if err != nil {
@@ -49,16 +37,40 @@ func (b *BtcWallet) FetchInputInfo(prevOut *wire.OutPoint) (*wire.TxOut, error)
// we actually have control of this output. We do this because the check
// above only guarantees that the transaction is somehow relevant to us,
// like in the event of us being the sender of the transaction.
output = txDetail.TxRecord.MsgTx.TxOut[prevOut.Index]
if _, err := b.fetchOutputAddr(output.PkScript); err != nil {
pkScript := txDetail.TxRecord.MsgTx.TxOut[prevOut.Index].PkScript
if _, err := b.fetchOutputAddr(pkScript); err != nil {
return nil, err
}
b.cacheMtx.Lock()
b.utxoCache[*prevOut] = output
b.cacheMtx.Unlock()
// Then, we'll populate all of the information required by the struct.
addressType := lnwallet.UnknownAddressType
switch {
case txscript.IsPayToWitnessPubKeyHash(pkScript):
addressType = lnwallet.WitnessPubKey
case txscript.IsPayToScriptHash(pkScript):
addressType = lnwallet.NestedWitnessPubKey
}
return output, nil
// Determine the number of confirmations the output currently has.
_, currentHeight, err := b.GetBestBlock()
if err != nil {
return nil, fmt.Errorf("unable to retrieve current height: %v",
err)
}
confs := int64(0)
if txDetail.Block.Height != -1 {
confs = int64(currentHeight - txDetail.Block.Height)
}
return &lnwallet.Utxo{
AddressType: addressType,
Value: btcutil.Amount(
txDetail.TxRecord.MsgTx.TxOut[prevOut.Index].Value,
),
PkScript: pkScript,
Confirmations: confs,
OutPoint: *prevOut,
}, nil
}
// fetchOutputAddr attempts to fetch the managed address corresponding to the
+1 -3
View File
@@ -59,8 +59,6 @@ type Utxo struct {
Value btcutil.Amount
Confirmations int64
PkScript []byte
RedeemScript []byte
WitnessScript []byte
wire.OutPoint
}
@@ -137,7 +135,7 @@ type WalletController interface {
// passed outpoint. If the base wallet determines this output is under
// its control, then the original txout should be returned. Otherwise,
// a non-nil error value of ErrNotMine should be returned instead.
FetchInputInfo(prevOut *wire.OutPoint) (*wire.TxOut, error)
FetchInputInfo(prevOut *wire.OutPoint) (*Utxo, error)
// ConfirmedBalance returns the sum of all the wallet's unspent outputs
// that have at least confs confirmations. If confs is set to zero,
+4 -1
View File
@@ -774,7 +774,10 @@ func (l *LightningWallet) handleContributionMsg(req *addContributionMsg) {
return
}
signDesc.Output = info
signDesc.Output = &wire.TxOut{
PkScript: info.PkScript,
Value: int64(info.Value),
}
signDesc.InputIndex = i
inputScript, err := l.Cfg.Signer.ComputeInputScript(
+8 -5
View File
@@ -242,12 +242,15 @@ func (*mockWalletController) BackEnd() string {
// FetchInputInfo will be called to get info about the inputs to the funding
// transaction.
func (*mockWalletController) FetchInputInfo(
prevOut *wire.OutPoint) (*wire.TxOut, error) {
txOut := &wire.TxOut{
Value: int64(10 * btcutil.SatoshiPerBitcoin),
PkScript: []byte("dummy"),
prevOut *wire.OutPoint) (*lnwallet.Utxo, error) {
utxo := &lnwallet.Utxo{
AddressType: lnwallet.WitnessPubKey,
Value: 10 * btcutil.SatoshiPerBitcoin,
PkScript: []byte("dummy"),
Confirmations: 1,
OutPoint: *prevOut,
}
return txOut, nil
return utxo, nil
}
func (*mockWalletController) ConfirmedBalance(confs int32) (btcutil.Amount, error) {
return 0, nil
+3
View File
@@ -152,6 +152,9 @@ func (s *subRPCServerConfigs) PopulateDependencies(cc *chainControl,
subCfgValue.FieldByName("Sweeper").Set(
reflect.ValueOf(sweeper),
)
subCfgValue.FieldByName("Chain").Set(
reflect.ValueOf(cc.chainIO),
)
case *autopilotrpc.Config:
subCfgValue := extractReflectValue(subCfg)
+8 -20
View File
@@ -101,11 +101,6 @@ type UtxoSource interface {
// ListUnspentWitness returns all UTXOs from the source that have
// between minConfs and maxConfs number of confirmations.
ListUnspentWitness(minConfs, maxConfs int32) ([]*lnwallet.Utxo, error)
// FetchInputInfo returns the matching output for an outpoint. If the
// outpoint doesn't belong to this UTXO source, then an error should be
// returned.
FetchInputInfo(*wire.OutPoint) (*wire.TxOut, error)
}
// CoinSelectionLocker is an interface that allows the caller to perform an
@@ -217,41 +212,34 @@ func CraftSweepAllTx(feeRate lnwallet.SatPerKWeight, blockHeight uint32,
// sweeper to generate and sign a transaction for us.
var inputsToSweep []input.Input
for _, output := range allOutputs {
// We'll consult the utxoSource for information concerning this
// outpoint, we'll need to properly populate a signDescriptor
// for this output.
outputInfo, err := utxoSource.FetchInputInfo(&output.OutPoint)
if err != nil {
unlockOutputs()
return nil, err
}
// As we'll be signing for outputs under control of the wallet,
// we only need to populate the output value and output script.
// The rest of the items will be populated internally within
// the sweeper via the witness generation function.
signDesc := &input.SignDescriptor{
Output: outputInfo,
Output: &wire.TxOut{
PkScript: output.PkScript,
Value: int64(output.Value),
},
HashType: txscript.SigHashAll,
}
pkScript := outputInfo.PkScript
pkScript := output.PkScript
// Based on the output type, we'll map it to the proper witness
// type so we can generate the set of input scripts needed to
// sweep the output.
var witnessType input.WitnessType
switch {
switch output.AddressType {
// If this is a p2wkh output, then we'll assume it's a witness
// key hash witness type.
case txscript.IsPayToWitnessPubKeyHash(pkScript):
case lnwallet.WitnessPubKey:
witnessType = input.WitnessKeyHash
// If this is a p2sh output, then as since it's under control
// of the wallet, we'll assume it's a nested p2sh output.
case txscript.IsPayToScriptHash(pkScript):
case lnwallet.NestedWitnessPubKey:
witnessType = input.NestedWitnessKeyHash
// All other output types we count as unknown and will fail to
+5 -23
View File
@@ -108,25 +108,13 @@ func TestDetermineFeePerKw(t *testing.T) {
}
type mockUtxoSource struct {
outpoints map[wire.OutPoint]*wire.TxOut
outputs []*lnwallet.Utxo
}
func newMockUtxoSource(utxos []*lnwallet.Utxo) *mockUtxoSource {
m := &mockUtxoSource{
outputs: utxos,
outpoints: make(map[wire.OutPoint]*wire.TxOut),
return &mockUtxoSource{
outputs: utxos,
}
for _, utxo := range utxos {
m.outpoints[utxo.OutPoint] = &wire.TxOut{
Value: int64(utxo.Value),
PkScript: utxo.PkScript,
}
}
return m
}
func (m *mockUtxoSource) ListUnspentWitness(minConfs int32,
@@ -135,15 +123,6 @@ func (m *mockUtxoSource) ListUnspentWitness(minConfs int32,
return m.outputs, nil
}
func (m *mockUtxoSource) FetchInputInfo(op *wire.OutPoint) (*wire.TxOut, error) {
txOut, ok := m.outpoints[*op]
if !ok {
return nil, fmt.Errorf("no output found")
}
return txOut, nil
}
type mockCoinSelectionLocker struct {
fail bool
}
@@ -202,6 +181,7 @@ var deliveryAddr = func() btcutil.Address {
var testUtxos = []*lnwallet.Utxo{
{
// A p2wkh output.
AddressType: lnwallet.WitnessPubKey,
PkScript: []byte{
0x0, 0x14, 0x64, 0x3d, 0x8b, 0x15, 0x69, 0x4a, 0x54,
0x7d, 0x57, 0x33, 0x6e, 0x51, 0xdf, 0xfd, 0x38, 0xe3,
@@ -215,6 +195,7 @@ var testUtxos = []*lnwallet.Utxo{
{
// A np2wkh output.
AddressType: lnwallet.NestedWitnessPubKey,
PkScript: []byte{
0xa9, 0x14, 0x97, 0x17, 0xf7, 0xd1, 0x5f, 0x6f, 0x8b,
0x7, 0xe3, 0x58, 0x43, 0x19, 0xb9, 0x7e, 0xa9, 0x20,
@@ -228,6 +209,7 @@ var testUtxos = []*lnwallet.Utxo{
// A p2wsh output.
{
AddressType: lnwallet.UnknownAddressType,
PkScript: []byte{
0x0, 0x20, 0x70, 0x1a, 0x8d, 0x40, 0x1c, 0x84, 0xfb, 0x13,
0xe6, 0xba, 0xf1, 0x69, 0xd5, 0x96, 0x84, 0xe2, 0x7a, 0xbd,