-
Notifications
You must be signed in to change notification settings - Fork 156
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
Allow arbitrary xpub account imports. #1471
Conversation
As mentioned, this change contains a database upgrade to version 12. This conflicts with #1330 and I would like to merge that PR to master first, and then I will rebase over and upgrade this PR to use version 13. |
This is causing 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.
Seems reasonable. Testing on decrediton after importing via CLI returned the following error when generating a new address for the imported account:
2019-06-06 09:23:01.985 [ERR] GRPC: Unary method /walletrpc.WalletService/NextAddress invoked by 127.0.0.1:58648 errored: rpc error: code = NotFound desc = wallet.PubKeyForAddress: item does not exist: no address TsmDjaV6Ck1LCjUba9UvCU15is3Ev94kURP
This is the specific call that's failing:
dcrwallet/rpc/rpcserver/server.go
Line 484 in 6867c05
pubKey, err := s.wallet.PubKeyForAddress(addr) |
Hmm that is going to need an interesting fix. It fails because it is trying to look up the public key (which it just derived internally), saved by the DB (which it is not in these imported accounts, and I don't think it's a good idea to until we start watching). If instead of Wallet.New{Internal,External}Address it called an api which returned both the xpub and child index, the RPC method handler could derive both. |
Thinking ahead to some things we may want to do with accounts in the future (such as creating an account which derives p2sh multisig addresses from two or more other xpub accounts in sequence), returning an interface (which Address already is so we could piggy back off that) and then type asserting as a more specific interface with xpub and child accessors would be a possible way to support this without adding many new APIs. Unfortunately, there's probably code out there that expects all p2pkh addresses returned to be the dcrutil.AddressPubKeyHash concrete type. |
The solution to this specific error might be to just tweak the grpc endpoint to check for the special imported accounts and not return the public key for the moment (until the address is watched and saved in the db as you point out). Or add an "AddressKind" or something to the grpc response so that in the future we can indicate whether the returned address is from an imported account, multisig voting account, etc. Unless the multisig-generating account will also be another fixed account (similar to the |
Yes, idea with that account is that it would only be used for p2sh-multisig from other stored xpubs. This would enable the stakepool idea in #1217. It's true that code should not be switching on the concrete type since it's not guaranteed by the API. Perhaps we should try it and see what breaks? |
Heh, I'd argue that since the concrete type is not enforced (since it's the interface type that is returned) and it's not explicitly documented that only p2pkh's are returned, then it's fine to change the concrete type.
Sure thing. |
Have something like this in mind:
|
Seems nice! |
See #1474. |
4c2b098
to
eac9a4f
Compare
a1f4777
to
34d56b7
Compare
9b70e21
to
f8745ab
Compare
dc9f541
to
56e2049
Compare
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 know you're aware of these, leaving here for completenes' sake)
-
Address discovery requires restarting after importing the xpub and waiting for a block to be mined. We can solve this by either documenting or creating an endpoint to perform a rescan+address discovery (either completely or only of specific accounts).
-
Imported accounts show a balance in some situations via gRPC (annotated inline).
rpc/jsonrpc/methods.go
Outdated
@@ -682,6 +683,11 @@ func (s *Server) getBalance(ctx context.Context, icmd interface{}) (interface{}, | |||
result.Balances = make([]types.GetAccountBalanceResult, balancesLen) | |||
|
|||
for _, bal := range balances { | |||
// Transactions are not tracked for imported xpub accounts. | |||
if bal.Account > udb.ImportedAddrAccount { |
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 check needs to be added to grpc as well if this is going to be done at this level of abstraction (rpc endpoints).
Another alternative would be to add this directly in the address manager when calculating the balances so it's consistent over all current (and future) comm endpoints.
pk, err := childKey.ECPubKey() | ||
if err != nil { | ||
return nil, errors.E(op, err) | ||
} | ||
pkh := dcrutil.Hash160(pk.Serialize()) | ||
apkh, err := dcrutil.NewAddressPubKeyHash(pkh, w.chainParams, | ||
dcrec.STEcdsaSecp256k1) |
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 simplify to apkh, err := compat.HD2Address(childKey, w.chainParams)
@@ -576,7 +576,7 @@ func (m *Manager) AccountExtendedPubKey(dbtx walletdb.ReadTx, account uint32) (* | |||
// unlocked for this operation to complete. | |||
func (m *Manager) AccountExtendedPrivKey(dbtx walletdb.ReadTx, account uint32) (*hdkeychain.ExtendedKey, error) { | |||
if account == ImportedAddrAccount { | |||
return nil, errors.E(errors.Invalid, "imported account has no extended pubkey") | |||
return nil, errors.E(errors.Invalid, "imported address account has no extended pubkey") |
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.
return nil, errors.E(errors.Invalid, "imported address account has no extended pubkey") | |
return nil, errors.E(errors.Invalid, "imported address account has no extended privkey") |
return err | ||
} | ||
|
||
// There may not be an account by the same name |
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.
// There may not be an account by the same name | |
// There may already be an account by the same name |
return err | ||
} | ||
account++ | ||
if account < MaxAccountNum { |
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 check should be unnecessary as fetchLastImportedAccount
is guaranteed to return account >= ImportedAddrAccount
and ImportedAddrAccount > MaxAccountNum
// We have the encrypted account extended keys, so save them to the | ||
// database | ||
row := bip0044AccountInfo(acctPubEnc, nil, 0, 0, | ||
^uint32(0), ^uint32(0), ^uint32(0), ^uint32(0), name, DBVersion) |
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.
importedXpubAccountVersion
instead of DBVersion
?
This commit implements a new JSON-RPC method "importxpub" which records an arbitrary HD extended pubkey to the wallet database and allows new addresses to be derived for the account. It takes two positional parameters: an account name and the HD extended pubkey in string format. This change contains a database upgrade and it is not possible to downgrade a wallet database once run. Reverting to older code requires restoring the database from backup or seed restoring.
This commit implements a new JSON-RPC method "importxpub" which
records an arbitrary HD extended pubkey to the wallet database and
allows new addresses to be derived for the account. It takes two
positional parameters: an account name and the HD extended pubkey in
string format.
Addresses of imported xpub accounts are not watched and transactions
are not saved. This may change later, but the purpose of this feature
is to allow the automatic ticket buyer to derive unique voting
addresses in sequence, avoiding address reuse.
This change contains a database upgrade and it is not possible to
downgrade a wallet database once run. Reverting to older code
requires restoring the database from backup or seed restoring.