diff --git a/chainntnfs/btcdnotify/btcd.go b/chainntnfs/btcdnotify/btcd.go index eab0a5ea..335fb869 100644 --- a/chainntnfs/btcdnotify/btcd.go +++ b/chainntnfs/btcdnotify/btcd.go @@ -618,7 +618,8 @@ type spendCancel struct { // outpoint has been spent by a transaction on-chain. Once a spend of the target // outpoint has been detected, the details of the spending event will be sent // across the 'Spend' channel. -func (b *BtcdNotifier) RegisterSpendNtfn(outpoint *wire.OutPoint) (*chainntnfs.SpendEvent, error) { +func (b *BtcdNotifier) RegisterSpendNtfn(outpoint *wire.OutPoint, + _ uint32) (*chainntnfs.SpendEvent, error) { if err := b.chainConn.NotifySpent([]*wire.OutPoint{outpoint}); err != nil { return nil, err @@ -694,7 +695,7 @@ type confirmationsNotification struct { // which will be triggered once the txid reaches numConfs number of // confirmations. func (b *BtcdNotifier) RegisterConfirmationsNtfn(txid *chainhash.Hash, - numConfs uint32) (*chainntnfs.ConfirmationEvent, error) { + numConfs, _ uint32) (*chainntnfs.ConfirmationEvent, error) { ntfn := &confirmationsNotification{ txid: txid, diff --git a/chainntnfs/interface.go b/chainntnfs/interface.go index ee08d5e2..7ee1817d 100644 --- a/chainntnfs/interface.go +++ b/chainntnfs/interface.go @@ -23,23 +23,32 @@ type ChainNotifier interface { // txid reaches numConfs confirmations. The returned ConfirmationEvent // should properly notify the client once the specified number of // confirmations has been reached for the txid, as well as if the - // original tx gets re-org'd out of the mainchain. + // original tx gets re-org'd out of the mainchain. The heightHint + // parameter is provided as a convenience to light clients. The + // heightHint denotes the earlies height in the blockchain in which the + // target txid _could_ have been included in the chain. This can be + // used to bound the search space when checking to see if a + // notification can immediately be dispatched due to historical data. // // NOTE: Dispatching notifications to multiple clients subscribed to // the same (txid, numConfs) tuple MUST be supported. - RegisterConfirmationsNtfn(txid *chainhash.Hash, numConfs uint32) (*ConfirmationEvent, error) + RegisterConfirmationsNtfn(txid *chainhash.Hash, numConfs, + heightHint uint32) (*ConfirmationEvent, error) // RegisterSpendNtfn registers an intent to be notified once the target // outpoint is succesfully spent within a confirmed transaction. The // returned SpendEvent will receive a send on the 'Spend' transaction // once a transaction spending the input is detected on the blockchain. + // The heightHint parameter is provided as a convenience to light + // clients. The heightHint denotes the earlies height in the blockchain + // in which the target output could've been created. // // NOTE: This notifications should be triggered once the transaction is // *seen* on the network, not when it has received a single confirmation. // // NOTE: Dispatching notifications to multiple clients subscribed to a // spend of the same outpoint MUST be supported. - RegisterSpendNtfn(outpoint *wire.OutPoint) (*SpendEvent, error) + RegisterSpendNtfn(outpoint *wire.OutPoint, heightHint uint32) (*SpendEvent, error) // RegisterBlockEpochNtfn registers an intent to be notified of each // new block connected to the tip of the main chain. The returned diff --git a/chainntnfs/interface_test.go b/chainntnfs/interface_test.go index 045510f3..d622a37d 100644 --- a/chainntnfs/interface_test.go +++ b/chainntnfs/interface_test.go @@ -66,10 +66,16 @@ func testSingleConfirmationNotification(miner *rpctest.Harness, t.Fatalf("unable to create test tx: %v", err) } + _, currentHeight, err := miner.Node.GetBestBlock() + if err != nil { + t.Fatalf("unable to get current height: %v", err) + } + // Now that we have a txid, register a confirmation notiication with // the chainntfn source. numConfs := uint32(1) - confIntent, err := notifier.RegisterConfirmationsNtfn(txid, numConfs) + confIntent, err := notifier.RegisterConfirmationsNtfn(txid, numConfs, + uint32(currentHeight)) if err != nil { t.Fatalf("unable to register ntfn: %v", err) } @@ -107,8 +113,14 @@ func testMultiConfirmationNotification(miner *rpctest.Harness, t.Fatalf("unable to create test addr: %v", err) } + _, currentHeight, err := miner.Node.GetBestBlock() + if err != nil { + t.Fatalf("unable to get current height: %v", err) + } + numConfs := uint32(6) - confIntent, err := notifier.RegisterConfirmationsNtfn(txid, numConfs) + confIntent, err := notifier.RegisterConfirmationsNtfn(txid, numConfs, + uint32(currentHeight)) if err != nil { t.Fatalf("unable to register ntfn: %v", err) } @@ -143,6 +155,11 @@ func testBatchConfirmationNotification(miner *rpctest.Harness, confSpread := [6]uint32{1, 2, 3, 6, 20, 22} confIntents := make([]*chainntnfs.ConfirmationEvent, len(confSpread)) + _, currentHeight, err := miner.Node.GetBestBlock() + if err != nil { + t.Fatalf("unable to get current height: %v", err) + } + // Create a new txid spending miner coins for each confirmation entry // in confSpread, we collect each conf intent into a slice so we can // verify they're each notified at the proper number of confirmations @@ -152,7 +169,8 @@ func testBatchConfirmationNotification(miner *rpctest.Harness, if err != nil { t.Fatalf("unable to create test addr: %v", err) } - confIntent, err := notifier.RegisterConfirmationsNtfn(txid, numConfs) + confIntent, err := notifier.RegisterConfirmationsNtfn(txid, + numConfs, uint32(currentHeight)) if err != nil { t.Fatalf("unable to register ntfn: %v", err) } @@ -263,6 +281,11 @@ func testSpendNotification(miner *rpctest.Harness, // To do so, we first create a new output to our test target address. outpoint, pkScript := createSpendableOutput(miner, t) + _, currentHeight, err := miner.Node.GetBestBlock() + if err != nil { + t.Fatalf("unable to get current height: %v", err) + } + // Now that we have a output index and the pkScript, register for a // spentness notification for the newly created output with multiple // clients in order to ensure the implementation can support @@ -270,7 +293,8 @@ func testSpendNotification(miner *rpctest.Harness, const numClients = 5 spendClients := make([]*chainntnfs.SpendEvent, numClients) for i := 0; i < numClients; i++ { - spentIntent, err := notifier.RegisterSpendNtfn(outpoint) + spentIntent, err := notifier.RegisterSpendNtfn(outpoint, + uint32(currentHeight)) if err != nil { t.Fatalf("unable to register for spend ntfn: %v", err) } @@ -408,10 +432,16 @@ func testMultiClientConfirmationNotification(miner *rpctest.Harness, numConfs = 1 ) + _, currentHeight, err := miner.Node.GetBestBlock() + if err != nil { + t.Fatalf("unable to get current height: %v", err) + } + // Register for a conf notification for the above generated txid with // numConfsClients distinct clients. for i := 0; i < numConfsClients; i++ { - confClient, err := notifier.RegisterConfirmationsNtfn(txid, numConfs) + confClient, err := notifier.RegisterConfirmationsNtfn(txid, + numConfs, uint32(currentHeight)) if err != nil { t.Fatalf("unable to register for confirmation: %v", err) } @@ -467,10 +497,16 @@ func testTxConfirmedBeforeNtfnRegistration(miner *rpctest.Harness, t.Fatalf("unable to generate two blocks: %v", err) } + _, currentHeight, err := miner.Node.GetBestBlock() + if err != nil { + t.Fatalf("unable to get current height: %v", err) + } + // Now that we have a txid, register a confirmation notification with // the chainntfn source. numConfs := uint32(1) - confIntent, err := notifier.RegisterConfirmationsNtfn(txid, numConfs) + confIntent, err := notifier.RegisterConfirmationsNtfn(txid, numConfs, + uint32(currentHeight)) if err != nil { t.Fatalf("unable to register ntfn: %v", err) } @@ -495,6 +531,11 @@ func testTxConfirmedBeforeNtfnRegistration(miner *rpctest.Harness, t.Fatalf("unable to create test tx: %v", err) } + _, currentHeight, err = miner.Node.GetBestBlock() + if err != nil { + t.Fatalf("unable to get current height: %v", err) + } + // We'll request 6 confirmations for the above generated txid, but we // will generate the confirmations in chunks. numConfs = 6 @@ -506,7 +547,8 @@ func testTxConfirmedBeforeNtfnRegistration(miner *rpctest.Harness, // Next, register for the notification *after* the transition has // already been partially confirmed. - confIntent, err = notifier.RegisterConfirmationsNtfn(txid, numConfs) + confIntent, err = notifier.RegisterConfirmationsNtfn(txid, numConfs, + uint32(currentHeight)) if err != nil { t.Fatalf("unable to register ntfn: %v", err) } @@ -605,10 +647,16 @@ func testSpendBeforeNtfnRegistration(miner *rpctest.Harness, t.Fatalf("unable to generate single block: %v", err) } + _, currentHeight, err := miner.Node.GetBestBlock() + if err != nil { + t.Fatalf("unable to get current height: %v", err) + } + // Now, we register to be notified of a spend that has already // happened. The notifier should dispatch a spend notification // immediately. - spentIntent, err := notifier.RegisterSpendNtfn(outpoint) + spentIntent, err := notifier.RegisterSpendNtfn(outpoint, + uint32(currentHeight)) if err != nil { t.Fatalf("unable to register for spend ntfn: %v", err) } @@ -622,7 +670,7 @@ func testSpendBeforeNtfnRegistration(miner *rpctest.Harness, case ntfn := <-spentNtfn: // We've received the spend nftn. So now verify all the fields // have been set properly. - if ntfn.SpentOutPoint != outpoint { + if *ntfn.SpentOutPoint != *outpoint { t.Fatalf("ntfn includes wrong output, reports %v instead of %v", ntfn.SpentOutPoint, outpoint) } @@ -649,13 +697,19 @@ func testCancelSpendNtfn(node *rpctest.Harness, // ourselves. outpoint, pkScript := createSpendableOutput(node, t) + _, currentHeight, err := node.Node.GetBestBlock() + if err != nil { + t.Fatalf("unable to get current height: %v", err) + } + // Create two clients that each registered to the spend notification. // We'll cancel the notification for the first client and leave the // notification for the second client enabled. const numClients = 2 spendClients := make([]*chainntnfs.SpendEvent, numClients) for i := 0; i < numClients; i++ { - spentIntent, err := notifier.RegisterSpendNtfn(outpoint) + spentIntent, err := notifier.RegisterSpendNtfn(outpoint, + uint32(currentHeight)) if err != nil { t.Fatalf("unable to register for spend ntfn: %v", err) } @@ -688,7 +742,7 @@ func testCancelSpendNtfn(node *rpctest.Harness, case ntfn := <-spendClients[0].Spend: // We've received the spend nftn. So now verify all the // fields have been set properly. - if ntfn.SpentOutPoint != outpoint { + if *ntfn.SpentOutPoint != *outpoint { t.Fatalf("ntfn includes wrong output, reports "+ "%v instead of %v", ntfn.SpentOutPoint, outpoint)