chainntnfs/bitcoindnotify: make historical spend rescans async
This commit is contained in:
parent
12816a910d
commit
65b6257e1e
@ -19,7 +19,6 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|
||||||
// notifierType uniquely identifies this concrete implementation of the
|
// notifierType uniquely identifies this concrete implementation of the
|
||||||
// ChainNotifier interface.
|
// ChainNotifier interface.
|
||||||
notifierType = "bitcoind"
|
notifierType = "bitcoind"
|
||||||
@ -35,6 +34,11 @@ var (
|
|||||||
// measure a spend notification when notifier is already stopped.
|
// measure a spend notification when notifier is already stopped.
|
||||||
ErrChainNotifierShuttingDown = errors.New("chainntnfs: system interrupt " +
|
ErrChainNotifierShuttingDown = errors.New("chainntnfs: system interrupt " +
|
||||||
"while attempting to register for spend notification.")
|
"while attempting to register for spend notification.")
|
||||||
|
|
||||||
|
// ErrTransactionNotFound is an error returned when we attempt to find a
|
||||||
|
// transaction by manually scanning the chain within a specific range
|
||||||
|
// but it is not found.
|
||||||
|
ErrTransactionNotFound = errors.New("transaction not found within range")
|
||||||
)
|
)
|
||||||
|
|
||||||
// chainUpdate encapsulates an update to the current main chain. This struct is
|
// chainUpdate encapsulates an update to the current main chain. This struct is
|
||||||
@ -237,7 +241,7 @@ out:
|
|||||||
b.spendNotifications[op] = make(map[uint64]*spendNotification)
|
b.spendNotifications[op] = make(map[uint64]*spendNotification)
|
||||||
}
|
}
|
||||||
b.spendNotifications[op][msg.spendID] = msg
|
b.spendNotifications[op][msg.spendID] = msg
|
||||||
b.chainConn.NotifySpent([]*wire.OutPoint{&op})
|
|
||||||
case *confirmationNotification:
|
case *confirmationNotification:
|
||||||
chainntnfs.Log.Infof("New confirmation "+
|
chainntnfs.Log.Infof("New confirmation "+
|
||||||
"subscription: txid=%v, numconfs=%v",
|
"subscription: txid=%v, numconfs=%v",
|
||||||
@ -654,43 +658,22 @@ func (b *BitcoindNotifier) RegisterSpendNtfn(outpoint *wire.OutPoint,
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
out:
|
// In order to ensure we don't block the caller on what
|
||||||
for i := startHeight; i <= endHeight; i++ {
|
// may be a long rescan, we'll launch a goroutine to do
|
||||||
blockHash, err := b.chainConn.GetBlockHash(int64(i))
|
// so in the background.
|
||||||
|
b.wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
defer b.wg.Done()
|
||||||
|
|
||||||
|
err := b.dispatchSpendDetailsManually(
|
||||||
|
*outpoint, startHeight, endHeight,
|
||||||
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
chainntnfs.Log.Errorf("Rescan for spend "+
|
||||||
}
|
"notification txout(%x) "+
|
||||||
block, err := b.chainConn.GetBlock(blockHash)
|
"failed: %v", outpoint, err)
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
for _, tx := range block.Transactions {
|
|
||||||
for _, in := range tx.TxIn {
|
|
||||||
if in.PreviousOutPoint == *outpoint {
|
|
||||||
relTx := chain.RelevantTx{
|
|
||||||
TxRecord: &wtxmgr.TxRecord{
|
|
||||||
MsgTx: *tx,
|
|
||||||
Hash: tx.TxHash(),
|
|
||||||
Received: block.Header.Timestamp,
|
|
||||||
},
|
|
||||||
Block: &wtxmgr.BlockMeta{
|
|
||||||
Block: wtxmgr.Block{
|
|
||||||
Hash: block.BlockHash(),
|
|
||||||
Height: i,
|
|
||||||
},
|
|
||||||
Time: block.Header.Timestamp,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
select {
|
|
||||||
case <-b.quit:
|
|
||||||
return nil, ErrChainNotifierShuttingDown
|
|
||||||
case b.notificationRegistry <- relTx:
|
|
||||||
}
|
|
||||||
break out
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -705,8 +688,9 @@ func (b *BitcoindNotifier) RegisterSpendNtfn(outpoint *wire.OutPoint,
|
|||||||
// Submit spend cancellation to notification dispatcher.
|
// Submit spend cancellation to notification dispatcher.
|
||||||
select {
|
select {
|
||||||
case b.notificationCancels <- cancel:
|
case b.notificationCancels <- cancel:
|
||||||
// Cancellation is being handled, drain the spend chan until it is
|
// Cancellation is being handled, drain the
|
||||||
// closed before yielding to the caller.
|
// spend chan until it is closed before yielding
|
||||||
|
// to the caller.
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case _, ok := <-ntfn.spendChan:
|
case _, ok := <-ntfn.spendChan:
|
||||||
@ -723,6 +707,72 @@ func (b *BitcoindNotifier) RegisterSpendNtfn(outpoint *wire.OutPoint,
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// disaptchSpendDetailsManually attempts to manually scan the chain within the
|
||||||
|
// given height range for a transaction that spends the given outpoint. If one
|
||||||
|
// is found, it's spending details are sent to the notifier dispatcher, which
|
||||||
|
// will then dispatch the notification to all of its clients.
|
||||||
|
func (b *BitcoindNotifier) dispatchSpendDetailsManually(op wire.OutPoint,
|
||||||
|
startHeight, endHeight int32) error {
|
||||||
|
|
||||||
|
// Begin scanning blocks at every height to determine if the outpoint
|
||||||
|
// was spent.
|
||||||
|
for height := startHeight; height <= endHeight; height++ {
|
||||||
|
// Ensure we haven't been requested to shut down before
|
||||||
|
// processing the next height.
|
||||||
|
select {
|
||||||
|
case <-b.quit:
|
||||||
|
return ErrChainNotifierShuttingDown
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
blockHash, err := b.chainConn.GetBlockHash(int64(height))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
block, err := b.chainConn.GetBlock(blockHash)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tx := range block.Transactions {
|
||||||
|
for _, in := range tx.TxIn {
|
||||||
|
if in.PreviousOutPoint != op {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// If this transaction input spends the
|
||||||
|
// outpoint, we'll gather the details of the
|
||||||
|
// spending transaction and dispatch a spend
|
||||||
|
// notification to our clients.
|
||||||
|
relTx := chain.RelevantTx{
|
||||||
|
TxRecord: &wtxmgr.TxRecord{
|
||||||
|
MsgTx: *tx,
|
||||||
|
Hash: tx.TxHash(),
|
||||||
|
Received: block.Header.Timestamp,
|
||||||
|
},
|
||||||
|
Block: &wtxmgr.BlockMeta{
|
||||||
|
Block: wtxmgr.Block{
|
||||||
|
Hash: *blockHash,
|
||||||
|
Height: height,
|
||||||
|
},
|
||||||
|
Time: block.Header.Timestamp,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
case b.notificationRegistry <- relTx:
|
||||||
|
case <-b.quit:
|
||||||
|
return ErrChainNotifierShuttingDown
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ErrTransactionNotFound
|
||||||
|
}
|
||||||
|
|
||||||
// confirmationNotification represents a client's intent to receive a
|
// confirmationNotification represents a client's intent to receive a
|
||||||
// notification once the target txid reaches numConfirmations confirmations.
|
// notification once the target txid reaches numConfirmations confirmations.
|
||||||
type confirmationNotification struct {
|
type confirmationNotification struct {
|
||||||
|
Loading…
Reference in New Issue
Block a user