New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
btc spv: give wallet precedence for block notifications #1250
Conversation
The btcd fix for the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks pretty slick overall.
type tipNotifier interface { | ||
tipFeed() <-chan *block |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we wanted to do totally idiomatic Go, we'd make this tipFeeder
I suppose, but meh.
prevTip := btc.currentTip | ||
btc.currentTip = newTip |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The sameTip
check in run
/watchBlocks
previously was good enough, but given the logic race with Stop
ping the queuedBlock
's timer, it's possible although unlikely that the same tip could be reported twice. So maybe we either check here like:
prevTip := btc.currentTip
if prevTip.hash == newTip.hash {
return // already reported
}
btc.currentTip = newTip
or in watchBlocks
like
case walletTip := <-walletBlock:
if queuedBlock != nil && walletTip.height >= queuedBlock.height {
if !queuedBlock.queue.Stop() && walletTip.hash == queuedBlock.hash {
continue
}
queuedBlock = nil
}
I think it's clearer and just as good here in reportTip
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added the reportTip
solution in the latest commit, but I think I'm going to go with your second one instead. If a wallet tip comes through after the queued block is sent, we'd still want to notify the user to trigger a balance check.
e7b10d3
to
6aa954c
Compare
if err != nil { | ||
btc.log.Errorf("Error retreiving sync status before queuing polled block: %v", err) | ||
} else if syncStatus.Syncing { | ||
blockAllowance *= 10 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Even with the a 10 second allowance, I was seeing warnings during testnet syncing.
When a wallet is able to provide block notifications, allow wallet notifications to take precedence. Queue polled new tips for 10 seconds, canceling their broadcast and sending the notification earlier if the wallet notification comes during this window. If the wallet note doesn't come by 10 seconds, send the queued notification and log a warning. SPV tip updates are provided by the FilteredBlockConnected notification, which can come after neutrino stores the block header(s). Care is taken not to spam the caller with notifications, since they are not limited by the polling frequency. The same mechanism for limiting spam is used to solve the non-existent wallet error messages for *Core though, so you get two for the price of one with that solution.
if walletBlock == nil { | ||
btc.reportNewTip(ctx, newTip) | ||
} else { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like you could continue rather than using the else indention. No difference really though.
@@ -284,9 +285,13 @@ type spvWallet struct { | |||
|
|||
log dex.Logger | |||
loader *wallet.Loader | |||
|
|||
tipChan chan *block | |||
syncTarget int32 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you leave a comment about what syncTarget is? I guess it is the current highest known block number?
if atomic.SwapInt32(&w.syncTarget, target) == 0 && target > 0 { | ||
w.tipChan <- &block{ | ||
hash: blk.Hash, | ||
height: int64(blk.Height), | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// Setting highest known block number if not initiated?
if syncTarget == 0 || (lastBlock.Height < syncTarget && lastBlock.Height%10_000 != 0) { | ||
continue | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what does lastBlock.Height%10_000 != 0
do? this will be true at at 5001 - 9999 and then from 10001?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is working very well now. I'm not seeing any delays or unfound transactions. Just one final request for a log when AttachedBlocks come in a ntfn.
if syncTarget == 0 || (lastBlock.Height < syncTarget && lastBlock.Height%10_000 != 0) { | ||
continue | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could I please get the following here?
for ib := range note.AttachedBlocks {
for _, nt := range note.AttachedBlocks[ib].Transactions {
w.log.Debugf("Block %d contains wallet transaction %v", note.AttachedBlocks[ib].Height, nt.Hash)
}
}
Which change in this PR was to address this? I don't think that's resolved. I'm working on trade_simnet_test.go, and I get that error message, but I don't see a change to how |
|
This solves a couple of issues with bitcoin SPV syncing.
balanceUpdate
requested byCore
can come before the wallet has seen new funds. This was obvious during testnet syncing when outputs from even 1000s of blocks ago were not seen (block headers are stored in batches during initial sync) until an additional block was mined after sync.ExchangeWallet
before thexcWallet
was added to thewallets
map in*Core
, resulting in error messages sayingnon-existent 0 wallet should exist
.This solution gives wallet notifications precedence by queuing polled new tips temporarily to allow the wallet notifications to come through. This is important, because neutrino stores the block headers before requesting filters, and doesn't send the
FilteredBlockConnected
notification until after the filters are received and processed, allowing ample time for a poll to sneak in and grab a header whose corresponding filter hasn't been checked yet.