Skip to content

Commit

Permalink
multi: verifySeed RPC (#1037)
Browse files Browse the repository at this point in the history
fixup:

update

update
  • Loading branch information
githubsands committed Sep 30, 2018
1 parent 01ad4d5 commit 6819a23
Show file tree
Hide file tree
Showing 5 changed files with 199 additions and 115 deletions.
11 changes: 10 additions & 1 deletion internal/rpchelp/helpdescs_en_US.go
Original file line number Diff line number Diff line change
Expand Up @@ -541,14 +541,23 @@ var helpDescsEnUS = map[string]string{
"verifymessage-message": "The message to verify",
"verifymessage--result0": "Whether the message was signed with the private key of 'address'",

// VerifySeedCmd help.
"verifyseed--synopsis": "Verifes if the inputted seed derived account key is associated with the running wallet account key",
"verifyseed-seed": "Seed to be checked against the running wallets",
"verifyseed-account": "Used to check if a watching only wallets public key is the same as the running wallets",

// VerifySeedResult help.
"verifyseedresult-keyresult": "The result of comparing an inputted seed with the running wallets. Useful to check if a watching only wallet's public key is the same as the running wallet",
"verifyseedresult-cointype": "Outputs the current cointype of the running wallet",

// Version help
"version--synopsis": "Returns application and API versions (semver) keyed by their names",
"version--result0--desc": "Version objects keyed by the program or API name",
"version--result0--key": "Program or API name",
"version--result0--value": "Object containing the semantic version",

// WalletLockCmd help.
"walletlock--synopsis": "Lock the wallet.",
"walletlock--synopsis": "Lock the wallet",

// WalletPassphraseCmd help.
"walletpassphrase--synopsis": "Unlock the wallet.",
Expand Down
1 change: 1 addition & 0 deletions internal/rpchelp/methods.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ var Methods = []struct {
{"sweepaccount", []interface{}{(*dcrjson.SweepAccountResult)(nil)}},
{"validateaddress", []interface{}{(*dcrjson.ValidateAddressWalletResult)(nil)}},
{"verifymessage", returnsBool},
{"verifyseed", []interface{}{(*dcrjson.VerifySeedResult)(nil)}},
{"version", []interface{}{(*map[string]dcrjson.VersionResult)(nil)}},
{"walletlock", nil},
{"walletpassphrase", nil},
Expand Down
91 changes: 91 additions & 0 deletions rpc/legacyrpc/methods.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import (
"github.com/decred/dcrwallet/wallet"
"github.com/decred/dcrwallet/wallet/txrules"
"github.com/decred/dcrwallet/wallet/udb"
"github.com/decred/dcrwallet/walletseed"
)

// API version constants
Expand Down Expand Up @@ -123,6 +124,7 @@ var handlers = map[string]handler{
"ticketsforaddress": {fn: ticketsForAddress},
"validateaddress": {fn: validateAddress},
"verifymessage": {fn: verifyMessage},
"verifyseed": {fn: verifySeed},
"version": {fn: version},
"walletinfo": {fn: walletInfo},
"walletlock": {fn: walletLock},
Expand Down Expand Up @@ -3423,6 +3425,95 @@ WrongAddrKind:
return nil, rpcErrorf(dcrjson.ErrRPCInvalidParameter, "address must be secp256k1 P2PK or P2PKH")
}

func deriveCoinTypeKey(seed []byte, coinType uint32, params *chaincfg.Params) (*hdkeychain.ExtendedKey, error) {
// Create new root from the inputted seed and the current net
root, err := hdkeychain.NewMaster(seed[:], params)
if err != nil {
return nil, err
}

// BIP0032 hierarchy: m/<purpose>'/
// Where purpose = 44 and the ' indicates hardening with the HardenedKeyStart 0x80000000
purpose, err := root.Child(44 + hdkeychain.HardenedKeyStart)
if err != nil {
return nil, err
}
defer purpose.Zero()

// BIP0044 hierarchy: m/<purpose>'/<coin type>'
// Where coin type is either the legacy coin type, 20, or the coin type described in SLIP0044, 44. Note these parameters
// are only appropraite for main net.
coinTypePrivKey, err := purpose.Child(coinType + hdkeychain.HardenedKeyStart)
if err != nil {
return nil, err
}

return coinTypePrivKey, nil
}

// verifySeed checks if a user inputted seed is equivelent to the running wallets.
// Returns a a JSON object, with the dscribed bool and coin type.
func verifySeed(s *Server, icmd interface{}) (interface{}, error) {
cmd := icmd.(*dcrjson.VerifySeedCmd)
w, ok := s.walletLoader.LoadedWallet()
if !ok {
return nil, errUnloadedWallet
}

// obtain the wallet public key to check agaisnt the wallet derived seed
var account uint32 = 0
if cmd.Account != nil {
account = uint32(cmd.Account)
}

// do whats necessary to derive the key from the seed
c := make(chan *hdkeychain.ExtendedKey)
go func(seed string) {
coinType, err := w.CoinType()
if err != nil {
return nil, err
}

decodedSeed, err := walletseed.DecodeUserInput(cmd.Seed)
if err != nil {
return nil, err
}

coinTypePrivKey, err := deriveCoinTypeKey(decodedSeed, coinType, w.ChainParams())
if err != nil {
return nil, err
}
defer coinTypePrivKey.Zero()

// Both derivedAccountKey and walletDerivedAccountKey use the BIP044 hierachy: m/44'/<coin type>'/<account>'
accountKey, err := coinTypePrivKey.Child(account + hdkeychain.HardenedKeyStart)
if err != nil {
return nil, err
}
defer accountKey.Zero()

// To be matched with walletxPubKey.
seedxPubKey, err := accountKey.Neuter()
if err != nil {
return nil, err
}

c <- seedxPubKey
}(cmd.Seed)

walletxPubKey := w.MasterPubKey(account)
if err != nil {
return nil, err
}

seedxPubKey := <-c

return &dcrjson.VerifySeedResult{
Result: walletxPubKey.String() == seedxPubKey.String(),
CoinType: coinType,
}, nil
}

// version handles the version command by returning the RPC API versions of the
// wallet and, optionally, the consensus RPC server as well if it is associated
// with the server. The chainClient is optional, and this is simply a helper
Expand Down
Loading

0 comments on commit 6819a23

Please sign in to comment.