Merge pull request #6730 from Roasbeef/notifier-optional-block

chainntfns: add new option for conf notifications to send block
This commit is contained in:
Oliver Gugger 2022-08-10 13:52:49 +02:00 committed by GitHub
commit 17014b592e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 392 additions and 194 deletions

View File

@ -564,6 +564,7 @@ func (b *BitcoindNotifier) confDetailsManually(confRequest chainntnfs.ConfReques
BlockHash: blockHash,
BlockHeight: height,
TxIndex: uint32(txIndex),
Block: block,
}, chainntnfs.TxFoundManually, nil
}
}
@ -584,12 +585,12 @@ func (b *BitcoindNotifier) handleBlockConnected(block chainntnfs.BlockEpoch) err
if err != nil {
return fmt.Errorf("unable to get block: %v", err)
}
txns := btcutil.NewBlock(rawBlock).Transactions()
utilBlock := btcutil.NewBlock(rawBlock)
// We'll then extend the txNotifier's height with the information of
// this new block, which will handle all of the notification logic for
// us.
err = b.txNotifier.ConnectTip(block.Hash, uint32(block.Height), txns)
err = b.txNotifier.ConnectTip(utilBlock, uint32(block.Height))
if err != nil {
return fmt.Errorf("unable to connect tip: %v", err)
}
@ -844,15 +845,15 @@ func (b *BitcoindNotifier) historicalSpendDetails(
// channel. Once it has reached all of its confirmations, a notification will be
// sent across the 'Confirmed' channel.
func (b *BitcoindNotifier) RegisterConfirmationsNtfn(txid *chainhash.Hash,
pkScript []byte,
numConfs, heightHint uint32) (*chainntnfs.ConfirmationEvent, error) {
pkScript []byte, numConfs, heightHint uint32,
opts ...chainntnfs.NotifierOption) (*chainntnfs.ConfirmationEvent, error) {
// 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.RegisterConf(
txid, pkScript, numConfs, heightHint,
txid, pkScript, numConfs, heightHint, opts...,
)
if err != nil {
return nil, err

View File

@ -263,13 +263,14 @@ func (b *BtcdNotifier) onBlockConnected(hash *chainhash.Hash, height int32, t ti
// chain. The slice of transactions will only be populated if the block
// includes a transaction that confirmed one of our watched txids, or spends
// one of the outputs currently being watched.
//
// TODO(halseth): this is currently used for complete blocks. Change to use
// onFilteredBlockConnected and onFilteredBlockDisconnected, making it easier
// to unify with the Neutrino implementation.
type filteredBlock struct {
hash chainhash.Hash
height uint32
txns []*btcutil.Tx
block *btcutil.Block
// connected is true if this update is a new block and false if it is a
// disconnected block.
@ -619,6 +620,7 @@ func (b *BtcdNotifier) confDetailsManually(confRequest chainntnfs.ConfRequest,
BlockHash: blockHash,
BlockHeight: height,
TxIndex: uint32(txIndex),
Block: block,
}, chainntnfs.TxFoundManually, nil
}
}
@ -644,16 +646,14 @@ func (b *BtcdNotifier) handleBlockConnected(epoch chainntnfs.BlockEpoch) error {
newBlock := &filteredBlock{
hash: *epoch.Hash,
height: uint32(epoch.Height),
txns: btcutil.NewBlock(rawBlock).Transactions(),
block: btcutil.NewBlock(rawBlock),
connect: true,
}
// We'll then extend the txNotifier's height with the information of
// this new block, which will handle all of the notification logic for
// us.
err = b.txNotifier.ConnectTip(
&newBlock.hash, newBlock.height, newBlock.txns,
)
err = b.txNotifier.ConnectTip(newBlock.block, newBlock.height)
if err != nil {
return fmt.Errorf("unable to connect tip: %v", err)
}
@ -903,15 +903,15 @@ func (b *BtcdNotifier) RegisterSpendNtfn(outpoint *wire.OutPoint,
// channel. Once it has reached all of its confirmations, a notification will be
// sent across the 'Confirmed' channel.
func (b *BtcdNotifier) RegisterConfirmationsNtfn(txid *chainhash.Hash,
pkScript []byte,
numConfs, heightHint uint32) (*chainntnfs.ConfirmationEvent, error) {
pkScript []byte, numConfs, heightHint uint32,
opts ...chainntnfs.NotifierOption) (*chainntnfs.ConfirmationEvent, error) {
// 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.RegisterConf(
txid, pkScript, numConfs, heightHint,
txid, pkScript, numConfs, heightHint, opts...,
)
if err != nil {
return nil, err

View File

@ -8,7 +8,9 @@ import (
"strings"
"sync"
"github.com/btcsuite/btcd/blockchain"
"github.com/btcsuite/btcd/btcjson"
"github.com/btcsuite/btcd/btcutil"
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/wire"
)
@ -67,6 +69,31 @@ func (t TxConfStatus) String() string {
}
}
// notifierOptions is a set of functional options that allow callers to further
// modify the type of chain event notifications they receive.
type notifierOptions struct {
// includeBlock if true, then the dispatched confirmation notification
// will include the block that mined the transaction.
includeBlock bool
}
// defaultNotifierOptions returns the set of default options for the notifier.
func defaultNotifierOptions() *notifierOptions {
return &notifierOptions{}
}
// NotifierOption is a functional option that allows a caller to modify the
// events received from the notifier.
type NotifierOption func(*notifierOptions)
// WithIncludeBlock is an optional argument that allows the calelr to specify
// that the block that mined a transaction should be included in the response.
func WithIncludeBlock() NotifierOption {
return func(o *notifierOptions) {
o.includeBlock = true
}
}
// ChainNotifier represents a trusted source to receive notifications concerning
// targeted events on the Bitcoin blockchain. The interface specification is
// intentionally general in order to support a wide array of chain notification
@ -97,7 +124,8 @@ type ChainNotifier interface {
// NOTE: Dispatching notifications to multiple clients subscribed to
// the same (txid, numConfs) tuple MUST be supported.
RegisterConfirmationsNtfn(txid *chainhash.Hash, pkScript []byte,
numConfs, heightHint uint32) (*ConfirmationEvent, error)
numConfs, heightHint uint32,
opts ...NotifierOption) (*ConfirmationEvent, error)
// RegisterSpendNtfn registers an intent to be notified once the target
// outpoint is successfully spent within a transaction. The script that
@ -166,6 +194,12 @@ type TxConfirmation struct {
// Tx is the transaction for which the notification was requested for.
Tx *wire.MsgTx
// Block is the block that contains the transaction referenced above.
//
// NOTE: This is only specified if the confirmation request opts to
// have the response include the block itself.
Block *wire.MsgBlock
}
// ConfirmationEvent encapsulates a confirmation notification. With this struct,
@ -628,9 +662,8 @@ type TxIndexConn interface {
// block that the transaction confirmed.
GetRawTransactionVerbose(*chainhash.Hash) (*btcjson.TxRawResult, error)
// GetBlockVerbose returns the block identified by the chain hash along
// with additional information such as the block's height in the chain.
GetBlockVerbose(*chainhash.Hash) (*btcjson.GetBlockVerboseResult, error)
// GetBlock returns the block identified by the chain hash.
GetBlock(*chainhash.Hash) (*wire.MsgBlock, error)
}
// ConfDetailsFromTxIndex looks up whether a transaction is already included in
@ -700,26 +733,38 @@ func ConfDetailsFromTxIndex(chainConn TxIndexConn, r ConfRequest,
fmt.Errorf("unable to get block hash %v for "+
"historical dispatch: %v", rawTxRes.BlockHash, err)
}
block, err := chainConn.GetBlockVerbose(blockHash)
block, err := chainConn.GetBlock(blockHash)
if err != nil {
return nil, TxNotFoundIndex,
fmt.Errorf("unable to get block with hash %v for "+
"historical dispatch: %v", blockHash, err)
}
// In the modern chain (the only one we really care about for LN), the
// coinbase transaction of all blocks will include the block height.
// Therefore we can save another query, and just use that height
// directly.
blockHeight, err := blockchain.ExtractCoinbaseHeight(
btcutil.NewTx(block.Transactions[0]),
)
if err != nil {
return nil, TxNotFoundIndex, fmt.Errorf("unable to extract "+
"coinbase height: %w", err)
}
// If the block was obtained, locate the transaction's index within the
// block so we can give the subscriber full confirmation details.
txidStr := r.TxID.String()
for txIndex, txHash := range block.Tx {
if txHash != txidStr {
for txIndex, blockTx := range block.Transactions {
if blockTx.TxHash() != r.TxID {
continue
}
return &TxConfirmation{
Tx: &tx,
BlockHash: blockHash,
BlockHeight: uint32(block.Height),
BlockHeight: uint32(blockHeight),
TxIndex: uint32(txIndex),
Block: block,
}, TxFoundIndex, nil
}

View File

@ -636,6 +636,7 @@ func (n *NeutrinoNotifier) historicalConfDetails(confRequest chainntnfs.ConfRequ
BlockHash: blockHash,
BlockHeight: scanHeight,
TxIndex: uint32(i),
Block: block.MsgBlock(),
}, nil
}
}
@ -649,11 +650,19 @@ func (n *NeutrinoNotifier) historicalConfDetails(confRequest chainntnfs.ConfRequ
//
// NOTE: This method must be called with the bestBlockMtx lock held.
func (n *NeutrinoNotifier) handleBlockConnected(newBlock *filteredBlock) error {
// We'll extend the txNotifier's height with the information of this new
// block, which will handle all of the notification logic for us.
err := n.txNotifier.ConnectTip(
&newBlock.hash, newBlock.height, newBlock.txns,
)
// We'll extend the txNotifier's height with the information of this
// new block, which will handle all of the notification logic for us.
//
// We actually need the _full_ block here as well in order to be able
// to send the full block back up to the client. The neutrino client
// itself will only dispatch a block if one of the items we're looking
// for matches, so ultimately passing it the full block will still only
// result in the items we care about being dispatched.
rawBlock, err := n.GetBlock(newBlock.hash)
if err != nil {
return fmt.Errorf("unable to get full block: %v", err)
}
err = n.txNotifier.ConnectTip(rawBlock, newBlock.height)
if err != nil {
return fmt.Errorf("unable to connect tip: %v", err)
}
@ -899,15 +908,15 @@ func (n *NeutrinoNotifier) RegisterSpendNtfn(outpoint *wire.OutPoint,
// channel. Once it has reached all of its confirmations, a notification will be
// sent across the 'Confirmed' channel.
func (n *NeutrinoNotifier) RegisterConfirmationsNtfn(txid *chainhash.Hash,
pkScript []byte,
numConfs, heightHint uint32) (*chainntnfs.ConfirmationEvent, error) {
pkScript []byte, numConfs, heightHint uint32,
opts ...chainntnfs.NotifierOption) (*chainntnfs.ConfirmationEvent, error) {
// 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.RegisterConf(
txid, pkScript, numConfs, heightHint,
txid, pkScript, numConfs, heightHint, opts...,
)
if err != nil {
return nil, err

View File

@ -160,6 +160,13 @@ func testBatchConfirmationNotification(miner *rpctest.Harness,
// verify they're each notified at the proper number of confirmations
// below.
for i, numConfs := range confSpread {
// All the clients with an even index will ask for the block
// along side the conf ntfn.
var opts []chainntnfs.NotifierOption
if i%2 == 0 {
opts = append(opts, chainntnfs.WithIncludeBlock())
}
txid, pkScript, err := chainntnfs.GetTestTxidAndScript(miner)
if err != nil {
t.Fatalf("unable to create test addr: %v", err)
@ -168,10 +175,12 @@ func testBatchConfirmationNotification(miner *rpctest.Harness,
if scriptDispatch {
confIntent, err = notifier.RegisterConfirmationsNtfn(
nil, pkScript, numConfs, uint32(currentHeight),
opts...,
)
} else {
confIntent, err = notifier.RegisterConfirmationsNtfn(
txid, pkScript, numConfs, uint32(currentHeight),
opts...,
)
}
if err != nil {
@ -218,6 +227,12 @@ func testBatchConfirmationNotification(miner *rpctest.Harness,
"conf height: expected %v, got %v",
initialConfHeight, conf.BlockHeight)
}
// If this is an even client index, then we expect the
// block to be populated. Otherwise, it should be
// empty.
expectBlock := i%2 == 0
require.Equal(t, expectBlock, conf.Block != nil)
continue
case <-time.After(20 * time.Second):
t.Fatalf("confirmation notification never received: %v", numConfs)
@ -547,6 +562,7 @@ func testTxConfirmedBeforeNtfnRegistration(miner *rpctest.Harness,
if scriptDispatch {
ntfn1, err = notifier.RegisterConfirmationsNtfn(
nil, pkScript1, 1, uint32(currentHeight),
chainntnfs.WithIncludeBlock(),
)
} else {
ntfn1, err = notifier.RegisterConfirmationsNtfn(
@ -579,6 +595,13 @@ func testTxConfirmedBeforeNtfnRegistration(miner *rpctest.Harness,
t.Fatalf("incorrect block height: expected %v, got %v",
confInfo.BlockHeight, currentHeight)
}
// Ensure that if this was a script dispatch, the block is set
// as well.
if scriptDispatch {
require.NotNil(t, confInfo.Block)
}
break
case <-time.After(20 * time.Second):
t.Fatalf("confirmation notification never received")
@ -591,6 +614,7 @@ func testTxConfirmedBeforeNtfnRegistration(miner *rpctest.Harness,
if scriptDispatch {
ntfn2, err = notifier.RegisterConfirmationsNtfn(
nil, pkScript2, 3, uint32(currentHeight),
chainntnfs.WithIncludeBlock(),
)
} else {
ntfn2, err = notifier.RegisterConfirmationsNtfn(
@ -622,6 +646,7 @@ func testTxConfirmedBeforeNtfnRegistration(miner *rpctest.Harness,
if scriptDispatch {
ntfn3, err = notifier.RegisterConfirmationsNtfn(
nil, pkScript3, 1, uint32(currentHeight-1),
chainntnfs.WithIncludeBlock(),
)
} else {
ntfn3, err = notifier.RegisterConfirmationsNtfn(
@ -640,7 +665,10 @@ func testTxConfirmedBeforeNtfnRegistration(miner *rpctest.Harness,
require.NoError(t, err, "unable to register ntfn")
select {
case <-ntfn3.Confirmed:
case confInfo := <-ntfn3.Confirmed:
if scriptDispatch {
require.NotNil(t, confInfo.Block)
}
case <-time.After(10 * time.Second):
t.Fatalf("confirmation notification never received")
}

View File

@ -265,6 +265,10 @@ type ConfNtfn struct {
// dispatched is false if the confirmed notification has not been sent yet.
dispatched bool
// includeBlock is true if the dispatched notification should also have
// the block included with it.
includeBlock bool
}
// HistoricalConfDispatch parametrizes a manual rescan for a particular
@ -576,7 +580,8 @@ 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) {
pkScript []byte, numConfs, heightHint uint32,
opts *notifierOptions) (*ConfNtfn, error) {
// An accompanying output script must always be provided.
if len(pkScript) == 0 {
@ -609,7 +614,8 @@ func (n *TxNotifier) newConfNtfn(txid *chainhash.Hash,
Event: NewConfirmationEvent(numConfs, func() {
n.CancelConf(confRequest, confID)
}),
HeightHint: heightHint,
HeightHint: heightHint,
includeBlock: opts.includeBlock,
}, nil
}
@ -622,7 +628,8 @@ func (n *TxNotifier) newConfNtfn(txid *chainhash.Hash,
// UpdateConfDetails method, otherwise we will wait for the transaction/output
// script to confirm even though it already has.
func (n *TxNotifier) RegisterConf(txid *chainhash.Hash, pkScript []byte,
numConfs, heightHint uint32) (*ConfRegistration, error) {
numConfs, heightHint uint32,
optFuncs ...NotifierOption) (*ConfRegistration, error) {
select {
case <-n.quit:
@ -630,8 +637,13 @@ func (n *TxNotifier) RegisterConf(txid *chainhash.Hash, pkScript []byte,
default:
}
opts := defaultNotifierOptions()
for _, optFunc := range optFuncs {
optFunc(opts)
}
// We'll start by performing a series of validation checks.
ntfn, err := n.newConfNtfn(txid, pkScript, numConfs, heightHint)
ntfn, err := n.newConfNtfn(txid, pkScript, numConfs, heightHint, opts)
if err != nil {
return nil, err
}
@ -682,6 +694,14 @@ func (n *TxNotifier) RegisterConf(txid *chainhash.Hash, pkScript []byte,
"registration since rescan has finished",
ntfn.ConfRequest)
// The default notification we assigned above includes the
// block along with the rest of the details. However not all
// clients want the block, so we make a copy here w/o the block
// if needed so we can give clients only what they ask for.
if !ntfn.includeBlock && confSet.details != nil {
confSet.details.Block = nil
}
err := n.dispatchConfDetails(ntfn, confSet.details)
if err != nil {
return nil, err
@ -888,7 +908,16 @@ func (n *TxNotifier) UpdateConfDetails(confRequest ConfRequest,
// notifications that have not yet been delivered.
confSet.details = details
for _, ntfn := range confSet.ntfns {
err = n.dispatchConfDetails(ntfn, details)
// The default notification we assigned above includes the
// block along with the rest of the details. However not all
// clients want the block, so we make a copy here w/o the block
// if needed so we can give clients only what they ask for.
confDetails := *details
if !ntfn.includeBlock {
confDetails.Block = nil
}
err = n.dispatchConfDetails(ntfn, &confDetails)
if err != nil {
return err
}
@ -1207,7 +1236,7 @@ func (n *TxNotifier) ProcessRelevantSpendTx(tx *btcutil.Tx,
onSpend := func(request SpendRequest, details *SpendDetail) {
spends = append(spends, spend{&request, details})
}
n.filterTx(tx, nil, blockHeight, nil, onSpend)
n.filterTx(nil, tx, blockHeight, nil, onSpend)
// After the transaction has been filtered, we can finally dispatch
// notifications for each request.
@ -1391,8 +1420,8 @@ func (n *TxNotifier) dispatchSpendDetails(ntfn *SpendNtfn, details *SpendDetail)
// NOTE: In order to actually dispatch the relevant transaction notifications to
// clients, NotifyHeight must be called with the same block height in order to
// maintain correctness.
func (n *TxNotifier) ConnectTip(blockHash *chainhash.Hash, blockHeight uint32,
txns []*btcutil.Tx) error {
func (n *TxNotifier) ConnectTip(block *btcutil.Block,
blockHeight uint32) error {
select {
case <-n.quit:
@ -1413,13 +1442,18 @@ func (n *TxNotifier) ConnectTip(blockHash *chainhash.Hash, blockHeight uint32,
// First, we'll iterate over all the transactions found in this block to
// determine if it includes any relevant transactions to the TxNotifier.
Log.Debugf("Filtering %d txns for %d spend requests at height %d",
len(txns), len(n.spendNotifications), blockHeight)
for _, tx := range txns {
n.filterTx(
tx, blockHash, blockHeight, n.handleConfDetailsAtTip,
n.handleSpendDetailsAtTip,
)
if block != nil {
Log.Debugf("Filtering %d txns for %d spend requests at "+
"height %d", len(block.Transactions()),
len(n.spendNotifications), blockHeight)
for _, tx := range block.Transactions() {
n.filterTx(
block, tx, blockHeight,
n.handleConfDetailsAtTip,
n.handleSpendDetailsAtTip,
)
}
}
// Now that we've determined which requests were confirmed and spent
@ -1469,7 +1503,7 @@ func (n *TxNotifier) ConnectTip(blockHash *chainhash.Hash, blockHeight uint32,
// filterTx determines whether the transaction spends or confirms any
// outstanding pending requests. The onConf and onSpend callbacks can be used to
// retrieve all the requests fulfilled by this transaction as they occur.
func (n *TxNotifier) filterTx(tx *btcutil.Tx, blockHash *chainhash.Hash,
func (n *TxNotifier) filterTx(block *btcutil.Block, tx *btcutil.Tx,
blockHeight uint32, onConf func(ConfRequest, *TxConfirmation),
onSpend func(SpendRequest, *SpendDetail)) {
@ -1548,13 +1582,14 @@ func (n *TxNotifier) filterTx(tx *btcutil.Tx, blockHash *chainhash.Hash,
notifyDetails := func(confRequest ConfRequest) {
Log.Debugf("Found initial confirmation of %v: "+
"height=%d, hash=%v", confRequest,
blockHeight, blockHash)
blockHeight, block.Hash())
details := &TxConfirmation{
Tx: tx.MsgTx(),
BlockHash: blockHash,
BlockHash: block.Hash(),
BlockHeight: blockHeight,
TxIndex: uint32(tx.Index()),
Block: block.MsgBlock(),
}
onConf(confRequest, details)
@ -1721,8 +1756,17 @@ func (n *TxNotifier) NotifyHeight(height uint32) error {
Log.Infof("Dispatching %v confirmation notification for %v",
ntfn.NumConfirmations, ntfn.ConfRequest)
// The default notification we assigned above includes the
// block along with the rest of the details. However not all
// clients want the block, so we make a copy here w/o the block
// if needed so we can give clients only what they ask for.
confDetails := *confSet.details
if !ntfn.includeBlock {
confDetails.Block = nil
}
select {
case ntfn.Event.Confirmed <- confSet.details:
case ntfn.Event.Confirmed <- &confDetails:
ntfn.dispatched = true
case <-n.quit:
return ErrTxNotifierExiting

View File

@ -256,7 +256,7 @@ func TestTxNotifierFutureConfDispatch(t *testing.T) {
Transactions: []*wire.MsgTx{&tx1, &tx2},
})
err = n.ConnectTip(block1.Hash(), 11, block1.Transactions())
err = n.ConnectTip(block1, 11)
require.NoError(t, err, "Failed to connect block")
if err := n.NotifyHeight(11); err != nil {
t.Fatalf("unable to dispatch notifications: %v", err)
@ -316,7 +316,7 @@ func TestTxNotifierFutureConfDispatch(t *testing.T) {
// Create a new block and add it to the TxNotifier at the next height.
// This should confirm tx2.
block2 := btcutil.NewBlock(&wire.MsgBlock{})
err = n.ConnectTip(block2.Hash(), 12, block2.Transactions())
err = n.ConnectTip(block2, 12)
require.NoError(t, err, "Failed to connect block")
if err := n.NotifyHeight(12); err != nil {
t.Fatalf("unable to dispatch notifications: %v", err)
@ -460,7 +460,7 @@ func TestTxNotifierHistoricalConfDispatch(t *testing.T) {
Transactions: []*wire.MsgTx{&tx3},
})
err = n.ConnectTip(block.Hash(), 11, block.Transactions())
err = n.ConnectTip(block, 11)
require.NoError(t, err, "Failed to connect block")
if err := n.NotifyHeight(11); err != nil {
t.Fatalf("unable to dispatch notifications: %v", err)
@ -536,7 +536,7 @@ func TestTxNotifierFutureSpendDispatch(t *testing.T) {
block := btcutil.NewBlock(&wire.MsgBlock{
Transactions: []*wire.MsgTx{spendTx},
})
err = n.ConnectTip(block.Hash(), 11, block.Transactions())
err = n.ConnectTip(block, 11)
require.NoError(t, err, "unable to connect block")
if err := n.NotifyHeight(11); err != nil {
t.Fatalf("unable to dispatch notifications: %v", err)
@ -569,7 +569,7 @@ func TestTxNotifierFutureSpendDispatch(t *testing.T) {
block = btcutil.NewBlock(&wire.MsgBlock{
Transactions: []*wire.MsgTx{spendOfSpend},
})
err = n.ConnectTip(block.Hash(), 12, block.Transactions())
err = n.ConnectTip(block, 12)
require.NoError(t, err, "unable to connect block")
if err := n.NotifyHeight(12); err != nil {
t.Fatalf("unable to dispatch notifications: %v", err)
@ -609,7 +609,7 @@ func TestTxNotifierFutureConfDispatchReuseSafe(t *testing.T) {
Transactions: []*wire.MsgTx{&tx1},
})
currentBlock++
err = n.ConnectTip(block.Hash(), currentBlock, block.Transactions())
err = n.ConnectTip(block, currentBlock)
require.NoError(t, err, "unable to connect block")
if err := n.NotifyHeight(currentBlock); err != nil {
t.Fatalf("unable to dispatch notifications: %v", err)
@ -662,7 +662,7 @@ func TestTxNotifierFutureConfDispatchReuseSafe(t *testing.T) {
Transactions: []*wire.MsgTx{&tx2},
})
currentBlock++
err = n.ConnectTip(block2.Hash(), currentBlock, block2.Transactions())
err = n.ConnectTip(block2, currentBlock)
require.NoError(t, err, "unable to connect block")
if err := n.NotifyHeight(currentBlock); err != nil {
t.Fatalf("unable to dispatch notifications: %v", err)
@ -706,9 +706,7 @@ func TestTxNotifierFutureConfDispatchReuseSafe(t *testing.T) {
for currentBlock < 15 {
block := btcutil.NewBlock(&wire.MsgBlock{})
currentBlock++
err = n.ConnectTip(
block.Hash(), currentBlock, block.Transactions(),
)
err = n.ConnectTip(block, currentBlock)
if err != nil {
t.Fatalf("unable to connect block: %v", err)
}
@ -801,7 +799,7 @@ func TestTxNotifierHistoricalSpendDispatch(t *testing.T) {
block := btcutil.NewBlock(&wire.MsgBlock{
Transactions: []*wire.MsgTx{spendOfSpend},
})
err = n.ConnectTip(block.Hash(), startingHeight+1, block.Transactions())
err = n.ConnectTip(block, startingHeight+1)
require.NoError(t, err, "unable to connect block")
if err := n.NotifyHeight(startingHeight + 1); err != nil {
t.Fatalf("unable to dispatch notifications: %v", err)
@ -1110,7 +1108,7 @@ func TestTxNotifierCancelConf(t *testing.T) {
// Cancel the second notification before connecting the block.
ntfn2.Event.Cancel()
err = n.ConnectTip(block.Hash(), startingHeight+1, block.Transactions())
err = n.ConnectTip(block, startingHeight+1)
require.NoError(t, err, "unable to connect block")
// Cancel the third notification before notifying to ensure its queued
@ -1155,7 +1153,7 @@ func TestTxNotifierCancelConf(t *testing.T) {
Transactions: []*wire.MsgTx{},
})
err = n.ConnectTip(block1.Hash(), startingHeight+2, block1.Transactions())
err = n.ConnectTip(block1, startingHeight+2)
require.NoError(t, err, "unable to connect block")
if err := n.NotifyHeight(startingHeight + 2); err != nil {
@ -1187,7 +1185,7 @@ func TestTxNotifierCancelConf(t *testing.T) {
Transactions: []*wire.MsgTx{},
})
err = n.ConnectTip(block2.Hash(), startingHeight+3, block2.Transactions())
err = n.ConnectTip(block2, startingHeight+3)
require.NoError(t, err, "unable to connect block")
if err := n.NotifyHeight(startingHeight + 3); err != nil {
@ -1241,7 +1239,7 @@ func TestTxNotifierCancelSpend(t *testing.T) {
// cancel the second request.
n.CancelSpend(ntfn2.HistoricalDispatch.SpendRequest, 2)
err = n.ConnectTip(block.Hash(), startingHeight+1, block.Transactions())
err = n.ConnectTip(block, startingHeight+1)
require.NoError(t, err, "unable to connect block")
if err := n.NotifyHeight(startingHeight + 1); err != nil {
t.Fatalf("unable to dispatch notifications: %v", err)
@ -1320,13 +1318,13 @@ func TestTxNotifierConfReorg(t *testing.T) {
block1 := btcutil.NewBlock(&wire.MsgBlock{
Transactions: []*wire.MsgTx{&tx1},
})
if err := n.ConnectTip(nil, 8, block1.Transactions()); err != nil {
if err := n.ConnectTip(block1, 8); err != nil {
t.Fatalf("Failed to connect block: %v", err)
}
if err := n.NotifyHeight(8); err != nil {
t.Fatalf("unable to dispatch notifications: %v", err)
}
if err := n.ConnectTip(nil, 9, nil); err != nil {
if err := n.ConnectTip(nil, 9); err != nil {
t.Fatalf("Failed to connect block: %v", err)
}
if err := n.NotifyHeight(9); err != nil {
@ -1336,7 +1334,7 @@ func TestTxNotifierConfReorg(t *testing.T) {
block2 := btcutil.NewBlock(&wire.MsgBlock{
Transactions: []*wire.MsgTx{&tx2, &tx3},
})
if err := n.ConnectTip(nil, 10, block2.Transactions()); err != nil {
if err := n.ConnectTip(block2, 10); err != nil {
t.Fatalf("Failed to connect block: %v", err)
}
if err := n.NotifyHeight(10); err != nil {
@ -1399,14 +1397,14 @@ func TestTxNotifierConfReorg(t *testing.T) {
t.Fatalf("Failed to connect block: %v", err)
}
if err := n.ConnectTip(nil, 10, nil); err != nil {
if err := n.ConnectTip(nil, 10); err != nil {
t.Fatalf("Failed to connect block: %v", err)
}
if err := n.NotifyHeight(10); err != nil {
t.Fatalf("unable to dispatch notifications: %v", err)
}
if err := n.ConnectTip(nil, 11, nil); err != nil {
if err := n.ConnectTip(nil, 11); err != nil {
t.Fatalf("Failed to connect block: %v", err)
}
if err := n.NotifyHeight(11); err != nil {
@ -1456,13 +1454,13 @@ func TestTxNotifierConfReorg(t *testing.T) {
})
block4 := btcutil.NewBlock(&wire.MsgBlock{})
err = n.ConnectTip(block3.Hash(), 12, block3.Transactions())
err = n.ConnectTip(block3, 12)
require.NoError(t, err, "Failed to connect block")
if err := n.NotifyHeight(12); err != nil {
t.Fatalf("unable to dispatch notifications: %v", err)
}
err = n.ConnectTip(block4.Hash(), 13, block4.Transactions())
err = n.ConnectTip(block4, 13)
require.NoError(t, err, "Failed to connect block")
if err := n.NotifyHeight(13); err != nil {
t.Fatalf("unable to dispatch notifications: %v", err)
@ -1600,7 +1598,7 @@ func TestTxNotifierSpendReorg(t *testing.T) {
block1 := btcutil.NewBlock(&wire.MsgBlock{
Transactions: []*wire.MsgTx{spendTx1},
})
err = n.ConnectTip(block1.Hash(), startingHeight+1, block1.Transactions())
err = n.ConnectTip(block1, startingHeight+1)
require.NoError(t, err, "unable to connect block")
if err := n.NotifyHeight(startingHeight + 1); err != nil {
t.Fatalf("unable to dispatch notifications: %v", err)
@ -1628,7 +1626,7 @@ func TestTxNotifierSpendReorg(t *testing.T) {
block2 := btcutil.NewBlock(&wire.MsgBlock{
Transactions: []*wire.MsgTx{spendTx2},
})
err = n.ConnectTip(block2.Hash(), startingHeight+2, block2.Transactions())
err = n.ConnectTip(block2, startingHeight+2)
require.NoError(t, err, "unable to connect block")
if err := n.NotifyHeight(startingHeight + 2); err != nil {
t.Fatalf("unable to dispatch notifications: %v", err)
@ -1680,9 +1678,7 @@ func TestTxNotifierSpendReorg(t *testing.T) {
// We'll now extend the chain with an empty block, to ensure that we can
// properly detect when an outpoint has been re-spent at a later height.
emptyBlock := btcutil.NewBlock(&wire.MsgBlock{})
err = n.ConnectTip(
emptyBlock.Hash(), startingHeight+2, emptyBlock.Transactions(),
)
err = n.ConnectTip(emptyBlock, startingHeight+2)
require.NoError(t, err, "unable to disconnect block")
if err := n.NotifyHeight(startingHeight + 2); err != nil {
t.Fatalf("unable to dispatch notifications: %v", err)
@ -1703,9 +1699,7 @@ func TestTxNotifierSpendReorg(t *testing.T) {
// Finally, extend the chain with another block containing the same
// spending transaction of the second outpoint.
err = n.ConnectTip(
block2.Hash(), startingHeight+3, block2.Transactions(),
)
err = n.ConnectTip(block2, startingHeight+3)
require.NoError(t, err, "unable to connect block")
if err := n.NotifyHeight(startingHeight + 3); err != nil {
t.Fatalf("unable to dispatch notifications: %v", err)
@ -1767,7 +1761,7 @@ func TestTxNotifierSpendReorgMissed(t *testing.T) {
block := btcutil.NewBlock(&wire.MsgBlock{
Transactions: []*wire.MsgTx{spendTx},
})
err := n.ConnectTip(block.Hash(), startingHeight+1, block.Transactions())
err := n.ConnectTip(block, startingHeight+1)
require.NoError(t, err, "unable to connect block")
if err := n.NotifyHeight(startingHeight + 1); err != nil {
t.Fatalf("unable to dispatch notifications: %v", err)
@ -1875,7 +1869,7 @@ func TestTxNotifierConfirmHintCache(t *testing.T) {
Transactions: []*wire.MsgTx{&txDummy},
})
err = n.ConnectTip(block1.Hash(), txDummyHeight, block1.Transactions())
err = n.ConnectTip(block1, txDummyHeight)
require.NoError(t, err, "Failed to connect block")
if err := n.NotifyHeight(txDummyHeight); err != nil {
t.Fatalf("unable to dispatch notifications: %v", err)
@ -1912,7 +1906,7 @@ func TestTxNotifierConfirmHintCache(t *testing.T) {
Transactions: []*wire.MsgTx{&tx1},
})
err = n.ConnectTip(block2.Hash(), tx1Height, block2.Transactions())
err = n.ConnectTip(block2, tx1Height)
require.NoError(t, err, "Failed to connect block")
if err := n.NotifyHeight(tx1Height); err != nil {
t.Fatalf("unable to dispatch notifications: %v", err)
@ -1941,7 +1935,7 @@ func TestTxNotifierConfirmHintCache(t *testing.T) {
Transactions: []*wire.MsgTx{&tx2},
})
err = n.ConnectTip(block3.Hash(), tx2Height, block3.Transactions())
err = n.ConnectTip(block3, tx2Height)
require.NoError(t, err, "Failed to connect block")
if err := n.NotifyHeight(tx2Height); err != nil {
t.Fatalf("unable to dispatch notifications: %v", err)
@ -2037,9 +2031,7 @@ func TestTxNotifierSpendHintCache(t *testing.T) {
// Create a new empty block and extend the chain.
emptyBlock := btcutil.NewBlock(&wire.MsgBlock{})
err = n.ConnectTip(
emptyBlock.Hash(), dummyHeight, emptyBlock.Transactions(),
)
err = n.ConnectTip(emptyBlock, dummyHeight)
require.NoError(t, err, "unable to connect block")
if err := n.NotifyHeight(dummyHeight); err != nil {
t.Fatalf("unable to dispatch notifications: %v", err)
@ -2079,7 +2071,7 @@ func TestTxNotifierSpendHintCache(t *testing.T) {
block1 := btcutil.NewBlock(&wire.MsgBlock{
Transactions: []*wire.MsgTx{spendTx1},
})
err = n.ConnectTip(block1.Hash(), op1Height, block1.Transactions())
err = n.ConnectTip(block1, op1Height)
require.NoError(t, err, "unable to connect block")
if err := n.NotifyHeight(op1Height); err != nil {
t.Fatalf("unable to dispatch notifications: %v", err)
@ -2108,7 +2100,7 @@ func TestTxNotifierSpendHintCache(t *testing.T) {
block2 := btcutil.NewBlock(&wire.MsgBlock{
Transactions: []*wire.MsgTx{spendTx2},
})
err = n.ConnectTip(block2.Hash(), op2Height, block2.Transactions())
err = n.ConnectTip(block2, op2Height)
require.NoError(t, err, "unable to connect block")
if err := n.NotifyHeight(op2Height); err != nil {
t.Fatalf("unable to dispatch notifications: %v", err)
@ -2194,9 +2186,7 @@ func TestTxNotifierSpendDuringHistoricalRescan(t *testing.T) {
// Create a new empty block and extend the chain.
height := uint32(startingHeight) + 1
emptyBlock := btcutil.NewBlock(&wire.MsgBlock{})
err = n.ConnectTip(
emptyBlock.Hash(), height, emptyBlock.Transactions(),
)
err = n.ConnectTip(emptyBlock, height)
require.NoError(t, err, "unable to connect block")
if err := n.NotifyHeight(height); err != nil {
t.Fatalf("unable to dispatch notifications: %v", err)
@ -2237,9 +2227,7 @@ func TestTxNotifierSpendDuringHistoricalRescan(t *testing.T) {
block = btcutil.NewBlock(&wire.MsgBlock{})
}
err = n.ConnectTip(
block.Hash(), height, block.Transactions(),
)
err = n.ConnectTip(block, height)
if err != nil {
t.Fatalf("unable to connect block: %v", err)
}
@ -2290,7 +2278,7 @@ func TestTxNotifierSpendDuringHistoricalRescan(t *testing.T) {
block2 := btcutil.NewBlock(&wire.MsgBlock{
Transactions: []*wire.MsgTx{spendTx2},
})
err = n.ConnectTip(block2.Hash(), height, block2.Transactions())
err = n.ConnectTip(block2, height)
require.NoError(t, err, "unable to connect block")
if err := n.NotifyHeight(height); err != nil {
t.Fatalf("unable to dispatch notifications: %v", err)
@ -2309,9 +2297,7 @@ func TestTxNotifierSpendDuringHistoricalRescan(t *testing.T) {
height++
block := btcutil.NewBlock(&wire.MsgBlock{})
err := n.ConnectTip(
block.Hash(), height, block.Transactions(),
)
err := n.ConnectTip(block, height)
if err != nil {
t.Fatalf("unable to connect block: %v", err)
}
@ -2368,7 +2354,7 @@ func TestTxNotifierNtfnDone(t *testing.T) {
Transactions: []*wire.MsgTx{tx, spendTx},
})
err = n.ConnectTip(block.Hash(), 11, block.Transactions())
err = n.ConnectTip(block, 11)
require.NoError(t, err, "unable to connect block")
if err := n.NotifyHeight(11); err != nil {
t.Fatalf("unable to dispatch notifications: %v", err)
@ -2418,7 +2404,7 @@ func TestTxNotifierNtfnDone(t *testing.T) {
// We'll reconnect the block that satisfies both of these requests.
// We should see notifications dispatched for both once again.
err = n.ConnectTip(block.Hash(), 11, block.Transactions())
err = n.ConnectTip(block, 11)
require.NoError(t, err, "unable to connect block")
if err := n.NotifyHeight(11); err != nil {
t.Fatalf("unable to dispatch notifications: %v", err)
@ -2442,7 +2428,7 @@ func TestTxNotifierNtfnDone(t *testing.T) {
nextHeight := uint32(12)
for i := nextHeight; i < nextHeight+reorgSafetyLimit; i++ {
dummyBlock := btcutil.NewBlock(&wire.MsgBlock{})
if err := n.ConnectTip(dummyBlock.Hash(), i, nil); err != nil {
if err := n.ConnectTip(dummyBlock, i); err != nil {
t.Fatalf("unable to connect block: %v", err)
}
}

View File

@ -57,7 +57,8 @@ func (n *NoChainBackend) RelayFeePerKW() chainfee.SatPerKWeight {
}
func (n *NoChainBackend) RegisterConfirmationsNtfn(*chainhash.Hash, []byte,
uint32, uint32) (*chainntnfs.ConfirmationEvent, error) {
uint32, uint32,
...chainntnfs.NotifierOption) (*chainntnfs.ConfirmationEvent, error) {
return nil, errNotImplemented
}

View File

@ -420,7 +420,8 @@ func newMockNotifier() *mockNotifier {
}
func (m *mockNotifier) RegisterConfirmationsNtfn(txid *chainhash.Hash,
_ []byte, numConfs, _ uint32) (*chainntnfs.ConfirmationEvent, error) {
_ []byte, numConfs, _ uint32,
opts ...chainntnfs.NotifierOption) (*chainntnfs.ConfirmationEvent, error) {
return nil, nil
}

View File

@ -1,5 +1,11 @@
# Release Notes
## RPC
The `RegisterConfirmationsNtfn` call of the `chainnotifier` RPC sub-server [now
optionally supports returning the entire block that confirmed the
transaction](https://github.com/lightningnetwork/lnd/pull/6730).
## Misc
* Warning messages from peers are now recognized and
[logged](https://github.com/lightningnetwork/lnd/pull/6546) by lnd.

View File

@ -164,7 +164,7 @@ type mockNotifier struct {
}
func (m *mockNotifier) RegisterConfirmationsNtfn(txid *chainhash.Hash,
_ []byte, numConfs, heightHint uint32) (*chainntnfs.ConfirmationEvent, error) {
_ []byte, numConfs, heightHint uint32, opts ...chainntnfs.NotifierOption) (*chainntnfs.ConfirmationEvent, error) {
if numConfs == 6 {
return &chainntnfs.ConfirmationEvent{

View File

@ -45,6 +45,10 @@ type ConfRequest struct {
//could have been included in a block. This should in most cases be set to the
//broadcast height of the transaction/output script.
HeightHint uint32 `protobuf:"varint,4,opt,name=height_hint,json=heightHint,proto3" json:"height_hint,omitempty"`
//
//If true, then the block that mines the specified txid/script will be
//included in eventual the notification event.
IncludeBlock bool `protobuf:"varint,5,opt,name=include_block,json=includeBlock,proto3" json:"include_block,omitempty"`
}
func (x *ConfRequest) Reset() {
@ -107,6 +111,13 @@ func (x *ConfRequest) GetHeightHint() uint32 {
return 0
}
func (x *ConfRequest) GetIncludeBlock() bool {
if x != nil {
return x.IncludeBlock
}
return false
}
type ConfDetails struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
@ -121,6 +132,10 @@ type ConfDetails struct {
BlockHeight uint32 `protobuf:"varint,3,opt,name=block_height,json=blockHeight,proto3" json:"block_height,omitempty"`
// The index of the confirmed transaction within the transaction.
TxIndex uint32 `protobuf:"varint,4,opt,name=tx_index,json=txIndex,proto3" json:"tx_index,omitempty"`
//
//The raw bytes of the block that mined the transaction. Only included if
//include_block was set in the request.
RawBlock []byte `protobuf:"bytes,5,opt,name=raw_block,json=rawBlock,proto3" json:"raw_block,omitempty"`
}
func (x *ConfDetails) Reset() {
@ -183,6 +198,13 @@ func (x *ConfDetails) GetTxIndex() uint32 {
return 0
}
func (x *ConfDetails) GetRawBlock() []byte {
if x != nil {
return x.RawBlock
}
return nil
}
type Reorg struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
@ -675,86 +697,90 @@ var File_chainrpc_chainnotifier_proto protoreflect.FileDescriptor
var file_chainrpc_chainnotifier_proto_rawDesc = []byte{
0x0a, 0x1c, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x72, 0x70, 0x63, 0x2f, 0x63, 0x68, 0x61, 0x69, 0x6e,
0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x08,
0x63, 0x68, 0x61, 0x69, 0x6e, 0x72, 0x70, 0x63, 0x22, 0x77, 0x0a, 0x0b, 0x43, 0x6f, 0x6e, 0x66,
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x78, 0x69, 0x64, 0x18,
0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x74, 0x78, 0x69, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x73,
0x63, 0x72, 0x69, 0x70, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x73, 0x63, 0x72,
0x69, 0x70, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x6e, 0x75, 0x6d, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x73,
0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x6e, 0x75, 0x6d, 0x43, 0x6f, 0x6e, 0x66, 0x73,
0x12, 0x1f, 0x0a, 0x0b, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x5f, 0x68, 0x69, 0x6e, 0x74, 0x18,
0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x48, 0x69, 0x6e,
0x74, 0x22, 0x81, 0x01, 0x0a, 0x0b, 0x43, 0x6f, 0x6e, 0x66, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c,
0x73, 0x12, 0x15, 0x0a, 0x06, 0x72, 0x61, 0x77, 0x5f, 0x74, 0x78, 0x18, 0x01, 0x20, 0x01, 0x28,
0x0c, 0x52, 0x05, 0x72, 0x61, 0x77, 0x54, 0x78, 0x12, 0x1d, 0x0a, 0x0a, 0x62, 0x6c, 0x6f, 0x63,
0x6b, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x62, 0x6c,
0x6f, 0x63, 0x6b, 0x48, 0x61, 0x73, 0x68, 0x12, 0x21, 0x0a, 0x0c, 0x62, 0x6c, 0x6f, 0x63, 0x6b,
0x5f, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x62,
0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x74, 0x78,
0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x74, 0x78,
0x49, 0x6e, 0x64, 0x65, 0x78, 0x22, 0x07, 0x0a, 0x05, 0x52, 0x65, 0x6f, 0x72, 0x67, 0x22, 0x6a,
0x0a, 0x09, 0x43, 0x6f, 0x6e, 0x66, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x2b, 0x0a, 0x04, 0x63,
0x6f, 0x6e, 0x66, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x63, 0x68, 0x61, 0x69,
0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73,
0x48, 0x00, 0x52, 0x04, 0x63, 0x6f, 0x6e, 0x66, 0x12, 0x27, 0x0a, 0x05, 0x72, 0x65, 0x6f, 0x72,
0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x72,
0x70, 0x63, 0x2e, 0x52, 0x65, 0x6f, 0x72, 0x67, 0x48, 0x00, 0x52, 0x05, 0x72, 0x65, 0x6f, 0x72,
0x67, 0x42, 0x07, 0x0a, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x22, 0x34, 0x0a, 0x08, 0x4f, 0x75,
0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x61, 0x73, 0x68, 0x18, 0x01,
0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x68, 0x61, 0x73, 0x68, 0x12, 0x14, 0x0a, 0x05, 0x69, 0x6e,
0x64, 0x65, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78,
0x22, 0x77, 0x0a, 0x0c, 0x53, 0x70, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
0x12, 0x2e, 0x0a, 0x08, 0x6f, 0x75, 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01,
0x28, 0x0b, 0x32, 0x12, 0x2e, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x75,
0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x08, 0x6f, 0x75, 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74,
0x12, 0x16, 0x0a, 0x06, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c,
0x52, 0x06, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x68, 0x65, 0x69, 0x67,
0x68, 0x74, 0x5f, 0x68, 0x69, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x68,
0x65, 0x69, 0x67, 0x68, 0x74, 0x48, 0x69, 0x6e, 0x74, 0x22, 0xfc, 0x01, 0x0a, 0x0c, 0x53, 0x70,
0x65, 0x6e, 0x64, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x3f, 0x0a, 0x11, 0x73, 0x70,
0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x6f, 0x75, 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18,
0x63, 0x68, 0x61, 0x69, 0x6e, 0x72, 0x70, 0x63, 0x22, 0x9c, 0x01, 0x0a, 0x0b, 0x43, 0x6f, 0x6e,
0x66, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x78, 0x69, 0x64,
0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x74, 0x78, 0x69, 0x64, 0x12, 0x16, 0x0a, 0x06,
0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x73, 0x63,
0x72, 0x69, 0x70, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x6e, 0x75, 0x6d, 0x5f, 0x63, 0x6f, 0x6e, 0x66,
0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x6e, 0x75, 0x6d, 0x43, 0x6f, 0x6e, 0x66,
0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x5f, 0x68, 0x69, 0x6e, 0x74,
0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x48, 0x69,
0x6e, 0x74, 0x12, 0x23, 0x0a, 0x0d, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x5f, 0x62, 0x6c,
0x6f, 0x63, 0x6b, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x69, 0x6e, 0x63, 0x6c, 0x75,
0x64, 0x65, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x22, 0x9e, 0x01, 0x0a, 0x0b, 0x43, 0x6f, 0x6e, 0x66,
0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x15, 0x0a, 0x06, 0x72, 0x61, 0x77, 0x5f, 0x74,
0x78, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x72, 0x61, 0x77, 0x54, 0x78, 0x12, 0x1d,
0x0a, 0x0a, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x02, 0x20, 0x01,
0x28, 0x0c, 0x52, 0x09, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x61, 0x73, 0x68, 0x12, 0x21, 0x0a,
0x0c, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x03, 0x20,
0x01, 0x28, 0x0d, 0x52, 0x0b, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74,
0x12, 0x19, 0x0a, 0x08, 0x74, 0x78, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x04, 0x20, 0x01,
0x28, 0x0d, 0x52, 0x07, 0x74, 0x78, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x1b, 0x0a, 0x09, 0x72,
0x61, 0x77, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08,
0x72, 0x61, 0x77, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x22, 0x07, 0x0a, 0x05, 0x52, 0x65, 0x6f, 0x72,
0x67, 0x22, 0x6a, 0x0a, 0x09, 0x43, 0x6f, 0x6e, 0x66, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x2b,
0x0a, 0x04, 0x63, 0x6f, 0x6e, 0x66, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x63,
0x68, 0x61, 0x69, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x44, 0x65, 0x74, 0x61,
0x69, 0x6c, 0x73, 0x48, 0x00, 0x52, 0x04, 0x63, 0x6f, 0x6e, 0x66, 0x12, 0x27, 0x0a, 0x05, 0x72,
0x65, 0x6f, 0x72, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x63, 0x68, 0x61,
0x69, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x6f, 0x72, 0x67, 0x48, 0x00, 0x52, 0x05, 0x72,
0x65, 0x6f, 0x72, 0x67, 0x42, 0x07, 0x0a, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x22, 0x34, 0x0a,
0x08, 0x4f, 0x75, 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x61, 0x73,
0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x68, 0x61, 0x73, 0x68, 0x12, 0x14, 0x0a,
0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x69, 0x6e,
0x64, 0x65, 0x78, 0x22, 0x77, 0x0a, 0x0c, 0x53, 0x70, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75,
0x65, 0x73, 0x74, 0x12, 0x2e, 0x0a, 0x08, 0x6f, 0x75, 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18,
0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x72, 0x70, 0x63,
0x2e, 0x4f, 0x75, 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x10, 0x73, 0x70, 0x65, 0x6e, 0x64,
0x69, 0x6e, 0x67, 0x4f, 0x75, 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x26, 0x0a, 0x0f, 0x72,
0x61, 0x77, 0x5f, 0x73, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x74, 0x78, 0x18, 0x02,
0x20, 0x01, 0x28, 0x0c, 0x52, 0x0d, 0x72, 0x61, 0x77, 0x53, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e,
0x67, 0x54, 0x78, 0x12, 0x28, 0x0a, 0x10, 0x73, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f,
0x74, 0x78, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0e, 0x73,
0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x54, 0x78, 0x48, 0x61, 0x73, 0x68, 0x12, 0x30, 0x0a,
0x14, 0x73, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x5f,
0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x12, 0x73, 0x70, 0x65,
0x6e, 0x64, 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12,
0x27, 0x0a, 0x0f, 0x73, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x68, 0x65, 0x69, 0x67,
0x68, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0e, 0x73, 0x70, 0x65, 0x6e, 0x64, 0x69,
0x6e, 0x67, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x22, 0x6e, 0x0a, 0x0a, 0x53, 0x70, 0x65, 0x6e,
0x64, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x2e, 0x0a, 0x05, 0x73, 0x70, 0x65, 0x6e, 0x64, 0x18,
0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x72, 0x70, 0x63,
0x2e, 0x53, 0x70, 0x65, 0x6e, 0x64, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x48, 0x00, 0x52,
0x05, 0x73, 0x70, 0x65, 0x6e, 0x64, 0x12, 0x27, 0x0a, 0x05, 0x72, 0x65, 0x6f, 0x72, 0x67, 0x18,
0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x72, 0x70, 0x63,
0x2e, 0x52, 0x65, 0x6f, 0x72, 0x67, 0x48, 0x00, 0x52, 0x05, 0x72, 0x65, 0x6f, 0x72, 0x67, 0x42,
0x07, 0x0a, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x22, 0x38, 0x0a, 0x0a, 0x42, 0x6c, 0x6f, 0x63,
0x6b, 0x45, 0x70, 0x6f, 0x63, 0x68, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x61, 0x73, 0x68, 0x18, 0x01,
0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x68, 0x61, 0x73, 0x68, 0x12, 0x16, 0x0a, 0x06, 0x68, 0x65,
0x69, 0x67, 0x68, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x68, 0x65, 0x69, 0x67,
0x68, 0x74, 0x32, 0xe7, 0x01, 0x0a, 0x0d, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x4e, 0x6f, 0x74, 0x69,
0x66, 0x69, 0x65, 0x72, 0x12, 0x49, 0x0a, 0x19, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72,
0x43, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x4e, 0x74, 0x66,
0x6e, 0x12, 0x15, 0x2e, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6f, 0x6e,
0x66, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e, 0x63, 0x68, 0x61, 0x69, 0x6e,
0x72, 0x70, 0x63, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x30, 0x01, 0x12,
0x43, 0x0a, 0x11, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x53, 0x70, 0x65, 0x6e, 0x64,
0x4e, 0x74, 0x66, 0x6e, 0x12, 0x16, 0x2e, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x72, 0x70, 0x63, 0x2e,
0x53, 0x70, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x14, 0x2e, 0x63,
0x68, 0x61, 0x69, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x70, 0x65, 0x6e, 0x64, 0x45, 0x76, 0x65,
0x6e, 0x74, 0x30, 0x01, 0x12, 0x46, 0x0a, 0x16, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72,
0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x45, 0x70, 0x6f, 0x63, 0x68, 0x4e, 0x74, 0x66, 0x6e, 0x12, 0x14,
0x2e, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x45,
0x70, 0x6f, 0x63, 0x68, 0x1a, 0x14, 0x2e, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x72, 0x70, 0x63, 0x2e,
0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x45, 0x70, 0x6f, 0x63, 0x68, 0x30, 0x01, 0x42, 0x30, 0x5a, 0x2e,
0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6c, 0x69, 0x67, 0x68, 0x74,
0x6e, 0x69, 0x6e, 0x67, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x2f, 0x6c, 0x6e, 0x64, 0x2f,
0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2f, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x72, 0x70, 0x63, 0x62, 0x06,
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
0x2e, 0x4f, 0x75, 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x08, 0x6f, 0x75, 0x74, 0x70, 0x6f,
0x69, 0x6e, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x18, 0x02, 0x20,
0x01, 0x28, 0x0c, 0x52, 0x06, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x68,
0x65, 0x69, 0x67, 0x68, 0x74, 0x5f, 0x68, 0x69, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d,
0x52, 0x0a, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x48, 0x69, 0x6e, 0x74, 0x22, 0xfc, 0x01, 0x0a,
0x0c, 0x53, 0x70, 0x65, 0x6e, 0x64, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x3f, 0x0a,
0x11, 0x73, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x6f, 0x75, 0x74, 0x70, 0x6f, 0x69,
0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x63, 0x68, 0x61, 0x69, 0x6e,
0x72, 0x70, 0x63, 0x2e, 0x4f, 0x75, 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x10, 0x73, 0x70,
0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x4f, 0x75, 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x26,
0x0a, 0x0f, 0x72, 0x61, 0x77, 0x5f, 0x73, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x74,
0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0d, 0x72, 0x61, 0x77, 0x53, 0x70, 0x65, 0x6e,
0x64, 0x69, 0x6e, 0x67, 0x54, 0x78, 0x12, 0x28, 0x0a, 0x10, 0x73, 0x70, 0x65, 0x6e, 0x64, 0x69,
0x6e, 0x67, 0x5f, 0x74, 0x78, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c,
0x52, 0x0e, 0x73, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x54, 0x78, 0x48, 0x61, 0x73, 0x68,
0x12, 0x30, 0x0a, 0x14, 0x73, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x69, 0x6e, 0x70,
0x75, 0x74, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x12,
0x73, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x49, 0x6e, 0x64,
0x65, 0x78, 0x12, 0x27, 0x0a, 0x0f, 0x73, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x68,
0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0e, 0x73, 0x70, 0x65,
0x6e, 0x64, 0x69, 0x6e, 0x67, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x22, 0x6e, 0x0a, 0x0a, 0x53,
0x70, 0x65, 0x6e, 0x64, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x2e, 0x0a, 0x05, 0x73, 0x70, 0x65,
0x6e, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x63, 0x68, 0x61, 0x69, 0x6e,
0x72, 0x70, 0x63, 0x2e, 0x53, 0x70, 0x65, 0x6e, 0x64, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73,
0x48, 0x00, 0x52, 0x05, 0x73, 0x70, 0x65, 0x6e, 0x64, 0x12, 0x27, 0x0a, 0x05, 0x72, 0x65, 0x6f,
0x72, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x63, 0x68, 0x61, 0x69, 0x6e,
0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x6f, 0x72, 0x67, 0x48, 0x00, 0x52, 0x05, 0x72, 0x65, 0x6f,
0x72, 0x67, 0x42, 0x07, 0x0a, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x22, 0x38, 0x0a, 0x0a, 0x42,
0x6c, 0x6f, 0x63, 0x6b, 0x45, 0x70, 0x6f, 0x63, 0x68, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x61, 0x73,
0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x68, 0x61, 0x73, 0x68, 0x12, 0x16, 0x0a,
0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x68,
0x65, 0x69, 0x67, 0x68, 0x74, 0x32, 0xe7, 0x01, 0x0a, 0x0d, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x4e,
0x6f, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x12, 0x49, 0x0a, 0x19, 0x52, 0x65, 0x67, 0x69, 0x73,
0x74, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73,
0x4e, 0x74, 0x66, 0x6e, 0x12, 0x15, 0x2e, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x72, 0x70, 0x63, 0x2e,
0x43, 0x6f, 0x6e, 0x66, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e, 0x63, 0x68,
0x61, 0x69, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x45, 0x76, 0x65, 0x6e, 0x74,
0x30, 0x01, 0x12, 0x43, 0x0a, 0x11, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x53, 0x70,
0x65, 0x6e, 0x64, 0x4e, 0x74, 0x66, 0x6e, 0x12, 0x16, 0x2e, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x72,
0x70, 0x63, 0x2e, 0x53, 0x70, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
0x14, 0x2e, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x70, 0x65, 0x6e, 0x64,
0x45, 0x76, 0x65, 0x6e, 0x74, 0x30, 0x01, 0x12, 0x46, 0x0a, 0x16, 0x52, 0x65, 0x67, 0x69, 0x73,
0x74, 0x65, 0x72, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x45, 0x70, 0x6f, 0x63, 0x68, 0x4e, 0x74, 0x66,
0x6e, 0x12, 0x14, 0x2e, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x42, 0x6c, 0x6f,
0x63, 0x6b, 0x45, 0x70, 0x6f, 0x63, 0x68, 0x1a, 0x14, 0x2e, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x72,
0x70, 0x63, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x45, 0x70, 0x6f, 0x63, 0x68, 0x30, 0x01, 0x42,
0x30, 0x5a, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6c, 0x69,
0x67, 0x68, 0x74, 0x6e, 0x69, 0x6e, 0x67, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x2f, 0x6c,
0x6e, 0x64, 0x2f, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2f, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x72, 0x70,
0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (

View File

@ -72,6 +72,12 @@ message ConfRequest {
broadcast height of the transaction/output script.
*/
uint32 height_hint = 4;
/*
If true, then the block that mines the specified txid/script will be
included in eventual the notification event.
*/
bool include_block = 5;
}
message ConfDetails {
@ -87,6 +93,12 @@ message ConfDetails {
// The index of the confirmed transaction within the transaction.
uint32 tx_index = 4;
/*
The raw bytes of the block that mined the transaction. Only included if
include_block was set in the request.
*/
bytes raw_block = 5;
}
message Reorg {

View File

@ -184,6 +184,11 @@
"type": "integer",
"format": "int64",
"description": "The index of the confirmed transaction within the transaction."
},
"raw_block": {
"type": "string",
"format": "byte",
"description": "The raw bytes of the block that mined the transaction. Only included if\ninclude_block was set in the request."
}
}
},
@ -222,6 +227,10 @@
"type": "integer",
"format": "int64",
"description": "The earliest height in the chain for which the transaction/output script\ncould have been included in a block. This should in most cases be set to the\nbroadcast height of the transaction/output script."
},
"include_block": {
"type": "boolean",
"description": "If true, then the block that mines the specified txid/script will be\nincluded in eventual the notification event."
}
}
},

View File

@ -257,9 +257,14 @@ func (s *Server) RegisterConfirmationsNtfn(in *ConfRequest,
var txid chainhash.Hash
copy(txid[:], in.Txid)
var opts []chainntnfs.NotifierOption
if in.IncludeBlock {
opts = append(opts, chainntnfs.WithIncludeBlock())
}
// We'll then register for the spend notification of the request.
confEvent, err := s.cfg.ChainNotifier.RegisterConfirmationsNtfn(
&txid, in.Script, in.NumConfs, in.HeightHint,
&txid, in.Script, in.NumConfs, in.HeightHint, opts...,
)
if err != nil {
return err
@ -284,11 +289,26 @@ func (s *Server) RegisterConfirmationsNtfn(in *ConfRequest,
return err
}
// If the block was included (should only be there if
// IncludeBlock is true), then we'll encode the bytes
// to send with the response.
var blockBytes []byte
if details.Block != nil {
var blockBuf bytes.Buffer
err := details.Block.Serialize(&blockBuf)
if err != nil {
return err
}
blockBytes = blockBuf.Bytes()
}
rpcConfDetails := &ConfDetails{
RawTx: rawTxBuf.Bytes(),
BlockHash: details.BlockHash[:],
BlockHeight: details.BlockHeight,
TxIndex: details.TxIndex,
RawBlock: blockBytes,
}
conf := &ConfEvent{

View File

@ -1297,12 +1297,16 @@ func confirmAddress(ctx context.Context, t *harnessTest,
_, currentHeight, err := net.Miner.Client.GetBestBlock()
require.NoError(t.t, err)
// We'll register for a conf notification, and also request the block
// that included it as well.
confClient, err := node.ChainClient.RegisterConfirmationsNtfn(
ctx, &chainrpc.ConfRequest{
Script: addrPkScript,
Txid: txid[:],
HeightHint: uint32(currentHeight),
NumConfs: 1,
Script: addrPkScript,
Txid: txid[:],
HeightHint: uint32(currentHeight),
NumConfs: 1,
IncludeBlock: true,
},
)
require.NoError(t.t, err)
@ -1310,12 +1314,18 @@ func confirmAddress(ctx context.Context, t *harnessTest,
// Mine another block to clean up the mempool.
mineBlocks(t, net, 1, 1)
// We now expect our confirmation to go through.
// We now expect our confirmation to go through, and also that the
// block was specified.
confMsg, err := confClient.Recv()
require.NoError(t.t, err)
conf := confMsg.GetConf()
require.NotNil(t.t, conf)
require.Equal(t.t, conf.BlockHeight, uint32(currentHeight+1))
require.NotNil(t.t, conf.RawBlock)
// We should also be able to decode the raw block.
var blk wire.MsgBlock
require.NoError(t.t, blk.Deserialize(bytes.NewReader(conf.RawBlock)))
}
// deriveSigningKeys derives three signing keys and returns their descriptors,

View File

@ -16,8 +16,8 @@ type ChainNotifier struct {
// RegisterConfirmationsNtfn returns a ConfirmationEvent that contains a channel
// that the tx confirmation will go over.
func (c *ChainNotifier) RegisterConfirmationsNtfn(txid *chainhash.Hash,
pkScript []byte, numConfs, heightHint uint32) (*chainntnfs.ConfirmationEvent,
error) {
pkScript []byte, numConfs, heightHint uint32,
opts ...chainntnfs.NotifierOption) (*chainntnfs.ConfirmationEvent, error) {
return &chainntnfs.ConfirmationEvent{
Confirmed: c.ConfChan,

View File

@ -109,8 +109,8 @@ func (m *MockNotifier) sendSpend(channel chan *chainntnfs.SpendDetail,
// RegisterConfirmationsNtfn registers for tx confirm notifications.
func (m *MockNotifier) RegisterConfirmationsNtfn(txid *chainhash.Hash,
_ []byte, numConfs, heightHint uint32) (*chainntnfs.ConfirmationEvent,
error) {
_ []byte, numConfs, heightHint uint32,
opt ...chainntnfs.NotifierOption) (*chainntnfs.ConfirmationEvent, error) {
return &chainntnfs.ConfirmationEvent{
Confirmed: m.getConfChannel(txid),