Skip to content
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

Create watch-only address managers and accounts #667

Merged
merged 7 commits into from
Apr 25, 2020

Conversation

devrandom
Copy link
Contributor

@devrandom devrandom commented Dec 14, 2019

This PR allows the creation of managers and accounts that are watch-only. The state of the database after creation would be identical to the state after calling Manager.ConvertToWatchingOnly, assuming accounts with the right xpubs were created in the former case.

Of course, creating a watch-only database is safer, since you don't have to worry about private data ending up in a swap file or the system having been compromised before creation.

@devrandom devrandom force-pushed the watchonly branch 2 times, most recently from 9221af9 to 82c55fc Compare December 14, 2019 21:49
// particular coin type and BIP0043 purpose. This is useful as it enables
// callers to create an arbitrary BIP0043 like schema with a stand alone
// manager.
func (m *Manager) NewWatchingOnlyScopedKeyManager(ns walletdb.ReadWriteBucket, scope KeyScope,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could be merged with NewScopedKeyManager, with a conditional on watchingOnly. WDYT?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SGTM, there's a ton of duplication as is.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that would definitely be preferable. You could split the content into multiple smaller methods and call them depending on the flag. This would avoid large chunks of code inside if conditions.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let us know if looks good.

Copy link
Member

@Roasbeef Roasbeef left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After meditating on this a bit more, I realized that non of the restrictions (well most of them) that I previously thought were present actually exists. As a result, I think this PR takes a step towards putting some scaffolding in places to address both of our respective goals and more!

I've completed an initial pass through this PR, thanks again for getting this rolling!

// particular coin type and BIP0043 purpose. This is useful as it enables
// callers to create an arbitrary BIP0043 like schema with a stand alone
// manager.
func (m *Manager) NewWatchingOnlyScopedKeyManager(ns walletdb.ReadWriteBucket, scope KeyScope,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SGTM, there's a ton of duplication as is.

@@ -859,6 +860,9 @@ func fetchLastAccount(ns walletdb.ReadBucket, scope *KeyScope) (uint32, error) {
metaBucket := scopedBucket.NestedReadBucket(metaBucketName)

val := metaBucket.Get(lastAccountName)
if val == nil {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why wasn't this needed before? Or is this for the case where we create the new wallet as pure watch-only?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this only occurs when the wallet is created as pure watch-only.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, because no default accounts are created, right.

waddrmgr/manager.go Outdated Show resolved Hide resolved
waddrmgr/manager.go Outdated Show resolved Hide resolved
waddrmgr/manager_test.go Outdated Show resolved Hide resolved
waddrmgr/manager_test.go Outdated Show resolved Hide resolved
waddrmgr/scoped_manager.go Outdated Show resolved Hide resolved
@Roasbeef
Copy link
Member

cc @guggero

Copy link
Collaborator

@guggero guggero left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for the PR, nice work! This does indeed look like what we need for our use case in lnd.
My comments are also mainly around code duplication and putting everything behind a flag (which will also make it easier for us to use the code in lnd).

// particular coin type and BIP0043 purpose. This is useful as it enables
// callers to create an arbitrary BIP0043 like schema with a stand alone
// manager.
func (m *Manager) NewWatchingOnlyScopedKeyManager(ns walletdb.ReadWriteBucket, scope KeyScope,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that would definitely be preferable. You could split the content into multiple smaller methods and call them depending on the flag. This would avoid large chunks of code inside if conditions.

waddrmgr/manager.go Outdated Show resolved Hide resolved
waddrmgr/manager_test.go Outdated Show resolved Hide resolved
waddrmgr/manager_test.go Outdated Show resolved Hide resolved
waddrmgr/manager_test.go Outdated Show resolved Hide resolved
Copy link
Collaborator

@guggero guggero left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks much better without the code duplication, thanks!

Maybe it's a bit late to suggest this, but while figuring out how exactly one would use this new functionality I couldn't help but wonder if it wouldn't be possible to create a new manager with the master xpub directly instead of later adding accounts.

So perhaps creating a new CreateWatchingOnly function and keeping the old one for compatibility but internally all routing it to a non-exported function that has all the logic.

Something like:

func CreateWatchingOnly(ns walletdb.ReadWriteBucket,
	pubKey, pubPassphrase []byte,
	chainParams *chaincfg.Params, config *ScryptOptions,
	birthday time.Time) error {

	return create(
		ns, pubKey, pubPassphrase, nil, chainParams, config,
		birthday, true,
	)
}

func Create(ns walletdb.ReadWriteBucket,
	seed, pubPassphrase, privPassphrase []byte,
	chainParams *chaincfg.Params, config *ScryptOptions,
	birthday time.Time) error {

	return create(
		ns, seed, pubPassphrase, privPassphrase, chainParams, config,
		birthday, false,
	)
}

func create(ns walletdb.ReadWriteBucket,
	seedOrPubKey, pubPassphrase, privPassphrase []byte,
	chainParams *chaincfg.Params, config *ScryptOptions,
	birthday time.Time, isWatchingOnly bool) error {

// logic for parsing the pubKey as a neutered master key and initializing the default accounts with it.
}

This could also be done in a follow-up PR if we want to push forward with this.

waddrmgr/error.go Outdated Show resolved Hide resolved
waddrmgr/error.go Outdated Show resolved Hide resolved
waddrmgr/manager.go Outdated Show resolved Hide resolved
waddrmgr/manager.go Outdated Show resolved Hide resolved
waddrmgr/manager.go Outdated Show resolved Hide resolved
waddrmgr/manager.go Outdated Show resolved Hide resolved
@@ -859,6 +860,9 @@ func fetchLastAccount(ns walletdb.ReadBucket, scope *KeyScope) (uint32, error) {
metaBucket := scopedBucket.NestedReadBucket(metaBucketName)

val := metaBucket.Get(lastAccountName)
if val == nil {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, because no default accounts are created, right.

waddrmgr/scoped_manager.go Show resolved Hide resolved
@devrandom
Copy link
Contributor Author

Maybe it's a bit late to suggest this, but while figuring out how exactly one would use this new functionality I couldn't help but wonder if it wouldn't be possible to create a new manager with the master xpub directly instead of later adding accounts.

Account derivation is hardened, so it's not possible to derive account xpubs from the parent(s).

@guggero
Copy link
Collaborator

guggero commented Mar 6, 2020

Right, I didn't remember that. Then I think the current design of the PR makes a lot of sense 👍

@ksedgwic
Copy link
Contributor

I think everything is addressed, pls let me know if I missed something ...

@guggero
Copy link
Collaborator

guggero commented Mar 24, 2020

Thanks, looks better now!

Just one last question: I started implementing something on top of this and noticed that currently there is no way to create a new wallet wit a nil seed, because by default a random seed is created in the wallet.Create function. Maybe I still don't understand how you intend to use this functionality. Is the idea to create a new wallet normally, create all the key scopes and accounts, then turn it into a watch-only wallet?

@ksedgwic
Copy link
Contributor

No, the idea is definitely to create the wallet without a seed and add accounts using XPUB from the remote signer. Investigating ...

@ksedgwic
Copy link
Contributor

ksedgwic commented Mar 25, 2020

Oliver, I think we missed that. We'll add mechanism to create an empty watch-only wallet to this PR straightaway. Thanks for catching!

@ksedgwic
Copy link
Contributor

Did a force push to rebase on master, fixes coming ...

@ksedgwic
Copy link
Contributor

Ok, I've added Loader.CreateNewWatchingOnlyWallet and Wallet.CreateWatchingOnly, thoughts?

Copy link
Collaborator

@guggero guggero left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, looks good to me!
Just two small nits, but they aren't blocking in my opinion.

wallet/wallet.go Outdated Show resolved Hide resolved
wallet/loader.go Outdated Show resolved Hide resolved
@ksedgwic
Copy link
Contributor

ksedgwic commented Apr 9, 2020

Force pushed rebase onto master, about to fix nits ...

@ksedgwic
Copy link
Contributor

ping, everything look ok with this?

@guggero
Copy link
Collaborator

guggero commented Apr 23, 2020

cc @Roasbeef.

Copy link
Member

@Roasbeef Roasbeef left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM 💎

// except that the manager is created normally with a seed. This test
// shows that watch-only accounts can be added to managers with
// non-watch-only accounts.
func TestNewRawAccountHybrid(t *testing.T) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

@Roasbeef Roasbeef merged commit 4c5bc1b into btcsuite:master Apr 25, 2020
@Roasbeef
Copy link
Member

Y'all can follow the progress for the remaining lnd changes to support a watch-only node and a segregated signer here: lightningnetwork/lnd#3943

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants