chainntnfs: validate conf/spend ntfn registration parameters
A height hint not being set would cause lnd to scan for the confirmation/spend of a txid/outpoint/address from genesis. The number of confirmations not being set within a confirmation request would cause the internal TxNotifier to deadlock when dispatching updates.
This commit is contained in:
parent
7821eb6ffb
commit
17b6205f3a
@ -46,10 +46,22 @@ var (
|
|||||||
// with the TxNotifier but it been shut down.
|
// with the TxNotifier but it been shut down.
|
||||||
ErrTxNotifierExiting = errors.New("TxNotifier is exiting")
|
ErrTxNotifierExiting = errors.New("TxNotifier is exiting")
|
||||||
|
|
||||||
// ErrTxMaxConfs signals that the user requested a number of
|
// ErrNoScript is an error returned when a confirmation/spend
|
||||||
// confirmations beyond the reorg safety limit.
|
// registration is attempted without providing an accompanying output
|
||||||
ErrTxMaxConfs = fmt.Errorf("too many confirmations requested, max is %d",
|
// script.
|
||||||
MaxNumConfs)
|
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
|
// rescanState indicates the progression of a registration before the notifier
|
||||||
@ -531,15 +543,27 @@ func NewTxNotifier(startHeight uint32, reorgSafetyLimit uint32,
|
|||||||
func (n *TxNotifier) newConfNtfn(txid *chainhash.Hash,
|
func (n *TxNotifier) newConfNtfn(txid *chainhash.Hash,
|
||||||
pkScript []byte, numConfs, heightHint uint32) (*ConfNtfn, error) {
|
pkScript []byte, numConfs, heightHint uint32) (*ConfNtfn, error) {
|
||||||
|
|
||||||
confRequest, err := NewConfRequest(txid, pkScript)
|
// An accompanying output script must always be provided.
|
||||||
if err != nil {
|
if len(pkScript) == 0 {
|
||||||
return nil, err
|
return nil, ErrNoScript
|
||||||
}
|
}
|
||||||
|
|
||||||
// Enforce that we will not dispatch confirmations beyond the reorg
|
// Enforce that we will not dispatch confirmations beyond the reorg
|
||||||
// safety limit.
|
// safety limit.
|
||||||
if numConfs > n.reorgSafetyLimit {
|
if numConfs == 0 || numConfs > n.reorgSafetyLimit {
|
||||||
return nil, ErrTxMaxConfs
|
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)
|
confID := atomic.AddUint64(&n.confClientCounter, 1)
|
||||||
@ -910,6 +934,18 @@ func (n *TxNotifier) dispatchConfDetails(
|
|||||||
func (n *TxNotifier) newSpendNtfn(outpoint *wire.OutPoint,
|
func (n *TxNotifier) newSpendNtfn(outpoint *wire.OutPoint,
|
||||||
pkScript []byte, heightHint uint32) (*SpendNtfn, error) {
|
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)
|
spendRequest, err := NewSpendRequest(outpoint, pkScript)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -124,30 +124,81 @@ func newMockHintCache() *mockHintCache {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestTxNotifierMaxConfs ensures that we are not able to register for more
|
// TestTxNotifierRegistrationValidation ensures that we are not able to register
|
||||||
// confirmations on a transaction than the maximum supported.
|
// requests with invalid parameters.
|
||||||
func TestTxNotifierMaxConfs(t *testing.T) {
|
func TestTxNotifierRegistrationValidation(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
hintCache := newMockHintCache()
|
testCases := []struct {
|
||||||
n := chainntnfs.NewTxNotifier(
|
name string
|
||||||
10, chainntnfs.ReorgSafetyLimit, hintCache, hintCache,
|
pkScript []byte
|
||||||
)
|
numConfs uint32
|
||||||
|
heightHint uint32
|
||||||
// Registering one confirmation above the maximum should fail with
|
checkSpend bool
|
||||||
// ErrTxMaxConfs.
|
err error
|
||||||
_, err := n.RegisterConf(
|
}{
|
||||||
&chainntnfs.ZeroHash, testRawScript, chainntnfs.MaxNumConfs+1, 1,
|
{
|
||||||
)
|
name: "empty output script",
|
||||||
if err != chainntnfs.ErrTxMaxConfs {
|
pkScript: nil,
|
||||||
t.Fatalf("expected chainntnfs.ErrTxMaxConfs, got %v", err)
|
numConfs: 1,
|
||||||
|
heightHint: 1,
|
||||||
|
checkSpend: true,
|
||||||
|
err: chainntnfs.ErrNoScript,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "zero num confs",
|
||||||
|
pkScript: testRawScript,
|
||||||
|
numConfs: 0,
|
||||||
|
heightHint: 1,
|
||||||
|
err: chainntnfs.ErrNumConfsOutOfRange,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "exceed max num confs",
|
||||||
|
pkScript: testRawScript,
|
||||||
|
numConfs: chainntnfs.MaxNumConfs + 1,
|
||||||
|
heightHint: 1,
|
||||||
|
err: chainntnfs.ErrNumConfsOutOfRange,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "empty height hint",
|
||||||
|
pkScript: testRawScript,
|
||||||
|
numConfs: 1,
|
||||||
|
heightHint: 0,
|
||||||
|
checkSpend: true,
|
||||||
|
err: chainntnfs.ErrNoHeightHint,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = n.RegisterConf(
|
for _, testCase := range testCases {
|
||||||
&chainntnfs.ZeroHash, testRawScript, chainntnfs.MaxNumConfs, 1,
|
testCase := testCase
|
||||||
)
|
t.Run(testCase.name, func(t *testing.T) {
|
||||||
if err != nil {
|
hintCache := newMockHintCache()
|
||||||
t.Fatalf("unable to register conf ntfn: %v", err)
|
n := chainntnfs.NewTxNotifier(
|
||||||
|
10, chainntnfs.ReorgSafetyLimit, hintCache, hintCache,
|
||||||
|
)
|
||||||
|
|
||||||
|
_, err := n.RegisterConf(
|
||||||
|
&chainntnfs.ZeroHash, testCase.pkScript,
|
||||||
|
testCase.numConfs, testCase.heightHint,
|
||||||
|
)
|
||||||
|
if err != testCase.err {
|
||||||
|
t.Fatalf("conf registration expected error "+
|
||||||
|
"\"%v\", got \"%v\"", testCase.err, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !testCase.checkSpend {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = n.RegisterSpend(
|
||||||
|
&chainntnfs.ZeroOutPoint, testCase.pkScript,
|
||||||
|
testCase.heightHint,
|
||||||
|
)
|
||||||
|
if err != testCase.err {
|
||||||
|
t.Fatalf("spend registration expected error "+
|
||||||
|
"\"%v\", got \"%v\"", testCase.err, err)
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user