-
Notifications
You must be signed in to change notification settings - Fork 2
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
chore(btcclient): use notifier interface #20
Conversation
Can we remove vigilante/btcclient/client_wallet.go Line 111 in 998cee9
func (c *Client) DumpPrivKey(address btcutil.Address) (*btcutil.WIF, error) {
return c.Client.DumpPrivKey(address)
} |
Yep, I've opened an issue to cleanup btc client and btc wallet interaces, they can be merged. So will be done in a separate PR |
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.
LGTM, tested the vigilante with bitcoind on deployments/local and was able to have healthy BTC delegations again 🎉
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.
Nice work! Generally, different vigilante programs share a lot of code. So one point of the refactoring is to extract common parts into a libary (e.g., lnd notifier) and reduce duplicate code as much as possible. This PR is a good step heading this direction. We can continue this in future prs
cmd/vigilante/cmd/monitor.go
Outdated
if err != nil { | ||
panic(fmt.Errorf("failed to get BTC net params: %w", err)) | ||
} | ||
btcCfg := btcclient.CfgToBtcNodeBackendConfig(cfg.BTC, "") |
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.
rawCert
is needed for btcd, which we wanted to remove, correct?
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.
Yep, there's a new issue to remove btcd from repo (previously merged PR regarding that was to use it in e2e)
cmd/vigilante/cmd/reporter.go
Outdated
// create the chain notifier | ||
btcParams, err := netparams.GetBTCParams(cfg.BTC.NetParams) | ||
if err != nil { | ||
panic(fmt.Errorf("failed to get BTC net params: %w", err)) | ||
} | ||
btcCfg := btcclient.CfgToBtcNodeBackendConfig(cfg.BTC, "") | ||
btcNotifier, err := btcclient.NewNodeBackend(btcCfg, btcParams, &btcclient.EmptyHintCache{}) | ||
if err != nil { | ||
panic(fmt.Errorf("failed to initialize notifier: %w", err)) | ||
} |
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 chunk is duplicated across several programs. Maybe we can wrap it into a general constructor?
monitor/btcscanner/block_handler.go
Outdated
if err := bs.btcNotifier.Start(); err != nil { | ||
bs.logger.Errorf("Failed starting notifier") | ||
return | ||
} | ||
|
||
blockNotifier, err := bs.btcNotifier.RegisterBlockEpochNtfn(nil) | ||
if err != nil { | ||
bs.logger.Errorf("Failed registering block epoch notifier") | ||
return | ||
} | ||
|
||
defer blockNotifier.Cancel() |
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.
Maybe it's better to ducouple the start of the notifier and the block handler by passing the block channel to the handler. See
go uw.handleNewBlocks(blockEventNotifier) |
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.
Yup I would start notifier in start function
reporter/block_handler.go
Outdated
if err := r.btcNotifier.Start(); err != nil { | ||
r.logger.Errorf("Failed starting notifier") | ||
return | ||
} | ||
|
||
blockNotifier, err := r.btcNotifier.RegisterBlockEpochNtfn(nil) | ||
if err != nil { | ||
r.logger.Errorf("Failed registering block epoch notifier") | ||
return | ||
} | ||
|
||
defer blockNotifier.Cancel() |
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.
Same here
reporter/block_handler.go
Outdated
tip := r.btcCache.Tip() | ||
|
||
// Determine if a reorg happened to we know which flow to continue | ||
// if the new block has the same height but a different hash. | ||
reorg := false | ||
if tip != nil { | ||
if epoch.Height < tip.Height || | ||
(epoch.Height == tip.Height && epoch.BlockHeader.BlockHash() != tip.Header.BlockHash()) { | ||
reorg = true | ||
} | ||
} | ||
|
||
var errorRequiringBootstrap error | ||
if event.EventType == types.BlockConnected { | ||
errorRequiringBootstrap = r.handleConnectedBlocks(event) | ||
} else if event.EventType == types.BlockDisconnected { | ||
errorRequiringBootstrap = r.handleDisconnectedBlocks(event) | ||
if !reorg { | ||
errorRequiringBootstrap = r.handleConnectedBlocks(epoch.Height, epoch.BlockHeader) | ||
} else { | ||
errorRequiringBootstrap = r.handleDisconnectedBlocks(epoch.BlockHeader) |
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.
It seems that we do the same chaining check (from L35 to L45) in both handleDisconnectedBlocks
and handleDisconnectedBlocks
. I believe we can combine the logic in one handleNewBlock()
method. Basically, if the new block cannot connect to the cache tip, we bootstrap. Otherwise, we add it to the cache, no needing of separate logic
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.
oh that this good idea 👍
My initial thought was that we could, translate new block events into connected / disconnected block events i.e
If we have the chain:
H1 -> H2 -> H3
, now the the reorg happens and the chain becomes H1 - H2' - H3'
, and now we receive H3'
then we can:
- Find the common ancestor between forks i.e
H1
- Calculate that
H2
andH3
were removed, so send to disconnection events starting from the top - Now send connection events for
H2
andH3
Bootstrapping after reorg is a bit brutal i.e it is costly operation, but given that re-orgs are pretty rare imo it is possible route to take and if we encounter some problems with it we can always go for more fancy approach.
refactor notifier init merge reporter block handles
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.
Much cleaner! One last concern is whether the btc scanner can output a canonical chain in expected sequence after bootstraping and event handler switch. It would be good to have a test to cover the cases where bootstraping
-> block handler
, block handler
-> reorg happens
-> bootstraping
-> block handler
. Currently we only have test for the bootstraping case. The tests can be done in this or separate pr
btcNotifier, err := btcclient.NewNodeBackend(btcCfg, btcParams, &btcclient.EmptyHintCache{}) | ||
if err != nil { | ||
panic(fmt.Errorf("failed to create btc chain notifier: %w", err)) | ||
panic(err) |
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.
panic(err) | |
panic(fmt.Errorf("failed to create btc chain notifier: %w", err)) |
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.
errors are already wrapped in the NewNodeBackendWithParams
monitor/btcscanner/block_handler.go
Outdated
// failing to request the block, which means a bug | ||
panic(err) |
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.
// failing to request the block, which means a bug | |
panic(err) | |
// failing to request the block | |
panic(fmt.Errorf("failed to request block by hash: %s", blockHash.String)) |
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 could also be network issue
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.
good spot, typo in suggestion
monitor/btcscanner/btc_scanner.go
Outdated
go bs.Bootstrap() | ||
|
||
bs.BtcClient.MustSubscribeBlocks() | ||
|
||
bs.Started.Store(true) | ||
bs.logger.Info("the BTC scanner is started") | ||
|
||
if err := bs.btcNotifier.Start(); err != nil { | ||
bs.logger.Errorf("Failed starting notifier") | ||
return | ||
} | ||
|
||
blockNotifier, err := bs.btcNotifier.RegisterBlockEpochNtfn(nil) | ||
if err != nil { | ||
bs.logger.Errorf("Failed registering block epoch notifier") | ||
return | ||
} | ||
|
||
// start handling new blocks | ||
bs.wg.Add(1) | ||
go bs.blockEventHandler() | ||
go bs.blockEventHandler(blockNotifier) |
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 was worried about possible racing issue between the Bootstrap
and blockEventHandler
threads. They both produce confirmed blocks to the same channel, so they need to be sequencial. I can think of two options:
- keep them as two separate go routines and use the
Synced
controller to ensureblockEventHandler
happens afterBootstrap
- combine them into a single goroutine and when creating the notifier
I'm in favor of option (2) in which we probably can remove the Synced
controller. Nevertheless, after bootstrapping, we should be able to get the best known block and put it in btcNotifier.RegisterBlockEpochNtfn(bestKnownBlock)
, so that we don't duplicate the chain tip
return | ||
} | ||
|
||
blockNotifier, err := r.btcNotifier.RegisterBlockEpochNtfn(nil) |
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.
Can we get the best known block after bootstrapping and set it as the parameter? Otherwise, the notifier will immediately get the chain tip which could be duplicate with the one processed during bootstrap
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.
Thanks for all the changes. Lgtm! It would be nice to have comprehensive tests about boostraping and normal block events but can be done in a separate pr
blockHash := header.BlockHash() | ||
ib, _, err := bs.BtcClient.GetBlockByHash(&blockHash) | ||
if err != nil { | ||
// failing to request the block, which means a bug |
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.
// failing to request the block, which means a bug | |
// failing to request the block |
Thanks, created a new issue for that |
References issue