mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-10-02 23:03:41 +02:00
Merge pull request #4717 from guggero/xprv-wallet-init
walletunlocker: Allow wallet to be created from extended master root key (xprv)
This commit is contained in:
@@ -214,16 +214,21 @@ func create(ctx *cli.Context) error {
|
||||
}
|
||||
|
||||
// Next, we'll see if the user has 24-word mnemonic they want to use to
|
||||
// derive a seed within the wallet.
|
||||
// derive a seed within the wallet or if they want to specify an
|
||||
// extended master root key (xprv) directly.
|
||||
var (
|
||||
hasMnemonic bool
|
||||
hasXprv bool
|
||||
)
|
||||
|
||||
mnemonicCheck:
|
||||
for {
|
||||
fmt.Println()
|
||||
fmt.Printf("Do you have an existing cipher seed " +
|
||||
"mnemonic you want to use? (Enter y/n): ")
|
||||
"mnemonic or extended master root key you want to " +
|
||||
"use?\nEnter 'y' to use an existing cipher seed " +
|
||||
"mnemonic, 'x' to use an extended master root key " +
|
||||
"\nor 'n' to create a new seed (Enter y/x/n): ")
|
||||
|
||||
reader := bufio.NewReader(os.Stdin)
|
||||
answer, err := reader.ReadString('\n')
|
||||
@@ -240,20 +245,28 @@ mnemonicCheck:
|
||||
case "y":
|
||||
hasMnemonic = true
|
||||
break mnemonicCheck
|
||||
|
||||
case "x":
|
||||
hasXprv = true
|
||||
break mnemonicCheck
|
||||
|
||||
case "n":
|
||||
hasMnemonic = false
|
||||
break mnemonicCheck
|
||||
}
|
||||
}
|
||||
|
||||
// If the user *does* have an existing seed they want to use, then
|
||||
// we'll read that in directly from the terminal.
|
||||
// If the user *does* have an existing seed or root key they want to
|
||||
// use, then we'll read that in directly from the terminal.
|
||||
var (
|
||||
cipherSeedMnemonic []string
|
||||
aezeedPass []byte
|
||||
recoveryWindow int32
|
||||
cipherSeedMnemonic []string
|
||||
aezeedPass []byte
|
||||
extendedRootKey string
|
||||
extendedRootKeyBirthday uint64
|
||||
recoveryWindow int32
|
||||
)
|
||||
if hasMnemonic {
|
||||
switch {
|
||||
// Use an existing cipher seed mnemonic in the aezeed format.
|
||||
case hasMnemonic:
|
||||
// We'll now prompt the user to enter in their 24-word
|
||||
// mnemonic.
|
||||
fmt.Printf("Input your 24-word mnemonic separated by spaces: ")
|
||||
@@ -288,38 +301,37 @@ mnemonicCheck:
|
||||
return err
|
||||
}
|
||||
|
||||
for {
|
||||
fmt.Println()
|
||||
fmt.Printf("Input an optional address look-ahead "+
|
||||
"used to scan for used keys (default %d): ",
|
||||
defaultRecoveryWindow)
|
||||
|
||||
reader := bufio.NewReader(os.Stdin)
|
||||
answer, err := reader.ReadString('\n')
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Println()
|
||||
|
||||
answer = strings.TrimSpace(answer)
|
||||
|
||||
if len(answer) == 0 {
|
||||
recoveryWindow = defaultRecoveryWindow
|
||||
break
|
||||
}
|
||||
|
||||
lookAhead, err := strconv.Atoi(answer)
|
||||
if err != nil {
|
||||
fmt.Printf("Unable to parse recovery "+
|
||||
"window: %v\n", err)
|
||||
continue
|
||||
}
|
||||
|
||||
recoveryWindow = int32(lookAhead)
|
||||
break
|
||||
recoveryWindow, err = askRecoveryWindow()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
|
||||
// Use an existing extended master root key to create the wallet.
|
||||
case hasXprv:
|
||||
// We'll now prompt the user to enter in their extended master
|
||||
// root key.
|
||||
fmt.Printf("Input your extended master root key (usually " +
|
||||
"starting with xprv... on mainnet): ")
|
||||
reader := bufio.NewReader(os.Stdin)
|
||||
extendedRootKey, err = reader.ReadString('\n')
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
extendedRootKey = strings.TrimSpace(extendedRootKey)
|
||||
|
||||
extendedRootKeyBirthday, err = askBirthdayTimestamp()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
recoveryWindow, err = askRecoveryWindow()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Neither a seed nor a master root key was specified, the user wants
|
||||
// to create a new seed.
|
||||
default:
|
||||
// Otherwise, if the user doesn't have a mnemonic that they
|
||||
// want to use, we'll generate a fresh one with the GenSeed
|
||||
// command.
|
||||
@@ -352,36 +364,21 @@ mnemonicCheck:
|
||||
|
||||
// Before we initialize the wallet, we'll display the cipher seed to
|
||||
// the user so they can write it down.
|
||||
mnemonicWords := cipherSeedMnemonic
|
||||
|
||||
fmt.Println("!!!YOU MUST WRITE DOWN THIS SEED TO BE ABLE TO " +
|
||||
"RESTORE THE WALLET!!!")
|
||||
fmt.Println()
|
||||
|
||||
fmt.Println("---------------BEGIN LND CIPHER SEED---------------")
|
||||
|
||||
numCols := 4
|
||||
colWords := monowidthColumns(mnemonicWords, numCols)
|
||||
for i := 0; i < len(colWords); i += numCols {
|
||||
fmt.Printf("%2d. %3s %2d. %3s %2d. %3s %2d. %3s\n",
|
||||
i+1, colWords[i], i+2, colWords[i+1], i+3,
|
||||
colWords[i+2], i+4, colWords[i+3])
|
||||
if len(cipherSeedMnemonic) > 0 {
|
||||
printCipherSeedWords(cipherSeedMnemonic)
|
||||
}
|
||||
|
||||
fmt.Println("---------------END LND CIPHER SEED-----------------")
|
||||
|
||||
fmt.Println("\n!!!YOU MUST WRITE DOWN THIS SEED TO BE ABLE TO " +
|
||||
"RESTORE THE WALLET!!!")
|
||||
|
||||
// With either the user's prior cipher seed, or a newly generated one,
|
||||
// we'll go ahead and initialize the wallet.
|
||||
req := &lnrpc.InitWalletRequest{
|
||||
WalletPassword: walletPassword,
|
||||
CipherSeedMnemonic: cipherSeedMnemonic,
|
||||
AezeedPassphrase: aezeedPass,
|
||||
RecoveryWindow: recoveryWindow,
|
||||
ChannelBackups: chanBackups,
|
||||
StatelessInit: statelessInit,
|
||||
WalletPassword: walletPassword,
|
||||
CipherSeedMnemonic: cipherSeedMnemonic,
|
||||
AezeedPassphrase: aezeedPass,
|
||||
ExtendedMasterKey: extendedRootKey,
|
||||
ExtendedMasterKeyBirthdayTimestamp: extendedRootKeyBirthday,
|
||||
RecoveryWindow: recoveryWindow,
|
||||
ChannelBackups: chanBackups,
|
||||
StatelessInit: statelessInit,
|
||||
}
|
||||
response, err := client.InitWallet(ctxc, req)
|
||||
if err != nil {
|
||||
@@ -663,3 +660,86 @@ func storeOrPrintAdminMac(ctx *cli.Context, adminMac []byte) error {
|
||||
fmt.Printf("Admin macaroon: %s\n", hex.EncodeToString(adminMac))
|
||||
return nil
|
||||
}
|
||||
|
||||
func askRecoveryWindow() (int32, error) {
|
||||
for {
|
||||
fmt.Println()
|
||||
fmt.Printf("Input an optional address look-ahead used to scan "+
|
||||
"for used keys (default %d): ", defaultRecoveryWindow)
|
||||
|
||||
reader := bufio.NewReader(os.Stdin)
|
||||
answer, err := reader.ReadString('\n')
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
fmt.Println()
|
||||
|
||||
answer = strings.TrimSpace(answer)
|
||||
|
||||
if len(answer) == 0 {
|
||||
return defaultRecoveryWindow, nil
|
||||
}
|
||||
|
||||
lookAhead, err := strconv.Atoi(answer)
|
||||
if err != nil {
|
||||
fmt.Printf("Unable to parse recovery window: %v\n", err)
|
||||
continue
|
||||
}
|
||||
|
||||
return int32(lookAhead), nil
|
||||
}
|
||||
}
|
||||
|
||||
func askBirthdayTimestamp() (uint64, error) {
|
||||
for {
|
||||
fmt.Println()
|
||||
fmt.Printf("Input an optional wallet birthday unix timestamp " +
|
||||
"of first block to start scanning from (default 0): ")
|
||||
|
||||
reader := bufio.NewReader(os.Stdin)
|
||||
answer, err := reader.ReadString('\n')
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
fmt.Println()
|
||||
|
||||
answer = strings.TrimSpace(answer)
|
||||
|
||||
if len(answer) == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
birthdayTimestamp, err := strconv.ParseUint(answer, 10, 64)
|
||||
if err != nil {
|
||||
fmt.Printf("Unable to parse birthday timestamp: %v\n",
|
||||
err)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
return birthdayTimestamp, nil
|
||||
}
|
||||
}
|
||||
|
||||
func printCipherSeedWords(mnemonicWords []string) {
|
||||
fmt.Println("!!!YOU MUST WRITE DOWN THIS SEED TO BE ABLE TO " +
|
||||
"RESTORE THE WALLET!!!")
|
||||
fmt.Println()
|
||||
|
||||
fmt.Println("---------------BEGIN LND CIPHER SEED---------------")
|
||||
|
||||
numCols := 4
|
||||
colWords := monowidthColumns(mnemonicWords, numCols)
|
||||
for i := 0; i < len(colWords); i += numCols {
|
||||
fmt.Printf("%2d. %3s %2d. %3s %2d. %3s %2d. %3s\n",
|
||||
i+1, colWords[i], i+2, colWords[i+1], i+3,
|
||||
colWords[i+2], i+4, colWords[i+3])
|
||||
}
|
||||
|
||||
fmt.Println("---------------END LND CIPHER SEED-----------------")
|
||||
|
||||
fmt.Println("\n!!!YOU MUST WRITE DOWN THIS SEED TO BE ABLE TO " +
|
||||
"RESTORE THE WALLET!!!")
|
||||
}
|
||||
|
@@ -43,6 +43,12 @@ for more information.
|
||||
* It is now possible to fund a psbt [without specifying any
|
||||
outputs](https://github.com/lightningnetwork/lnd/pull/5442). This option is
|
||||
useful for CPFP bumping of unconfirmed outputs or general utxo consolidation.
|
||||
* The internal wallet can now also be created or restored by using an [extended
|
||||
master root key (`xprv`) instead of an
|
||||
`aezeed`](https://github.com/lightningnetwork/lnd/pull/4717) only. This allows
|
||||
wallet integrators to use existing seed mechanism that might already be in
|
||||
place. **It is still not supported to use the same seed/root key on multiple
|
||||
`lnd` instances simultaneously** though.
|
||||
|
||||
## Security
|
||||
|
||||
|
6
go.mod
6
go.mod
@@ -5,15 +5,15 @@ require (
|
||||
github.com/NebulousLabs/fastrand v0.0.0-20181203155948-6fb6489aac4e // indirect
|
||||
github.com/NebulousLabs/go-upnp v0.0.0-20180202185039-29b680b06c82
|
||||
github.com/Yawning/aez v0.0.0-20180114000226-4dad034d9db2
|
||||
github.com/btcsuite/btcd v0.21.0-beta.0.20210513141527-ee5896bad5be
|
||||
github.com/btcsuite/btcd v0.22.0-beta.0.20210803133449-f5a1fb9965e4
|
||||
github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f
|
||||
github.com/btcsuite/btcutil v1.0.3-0.20210527170813-e2ba6805a890
|
||||
github.com/btcsuite/btcutil/psbt v1.0.3-0.20210527170813-e2ba6805a890
|
||||
github.com/btcsuite/btcwallet v0.12.1-0.20210803004036-eebed51155ec
|
||||
github.com/btcsuite/btcwallet v0.12.1-0.20210822222949-9b5a201c344c
|
||||
github.com/btcsuite/btcwallet/wallet/txauthor v1.0.2-0.20210803004036-eebed51155ec
|
||||
github.com/btcsuite/btcwallet/wallet/txrules v1.0.0
|
||||
github.com/btcsuite/btcwallet/walletdb v1.3.6-0.20210803004036-eebed51155ec
|
||||
github.com/btcsuite/btcwallet/wtxmgr v1.3.1-0.20210803004036-eebed51155ec
|
||||
github.com/btcsuite/btcwallet/wtxmgr v1.3.1-0.20210822222949-9b5a201c344c
|
||||
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e
|
||||
github.com/davecgh/go-spew v1.1.1
|
||||
github.com/fsnotify/fsnotify v1.4.9 // indirect
|
||||
|
13
go.sum
13
go.sum
@@ -72,9 +72,8 @@ github.com/btcsuite/btcd v0.0.0-20190629003639-c26ffa870fd8/go.mod h1:3J08xEfcug
|
||||
github.com/btcsuite/btcd v0.0.0-20190824003749-130ea5bddde3/go.mod h1:3J08xEfcugPacsc34/LKRU2yO7YmuT8yt28J8k2+rrI=
|
||||
github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ=
|
||||
github.com/btcsuite/btcd v0.21.0-beta.0.20201208033208-6bd4c64a54fa/go.mod h1:Sv4JPQ3/M+teHz9Bo5jBpkNcP0x6r7rdihlNL/7tTAs=
|
||||
github.com/btcsuite/btcd v0.21.0-beta.0.20210426180113-7eba688b65e5/go.mod h1:9n5ntfhhHQBIhUvlhDvD3Qg6fRUj4jkN0VB8L8svzOA=
|
||||
github.com/btcsuite/btcd v0.21.0-beta.0.20210513141527-ee5896bad5be h1:vDD/JWWS2v4GJUG/RZE/50wT6Saerbujijd7mFqgsKI=
|
||||
github.com/btcsuite/btcd v0.21.0-beta.0.20210513141527-ee5896bad5be/go.mod h1:9n5ntfhhHQBIhUvlhDvD3Qg6fRUj4jkN0VB8L8svzOA=
|
||||
github.com/btcsuite/btcd v0.22.0-beta.0.20210803133449-f5a1fb9965e4 h1:EmyLrldY44jDVa3dQ2iscj1S6ExuVJhRzCZBOXo93r0=
|
||||
github.com/btcsuite/btcd v0.22.0-beta.0.20210803133449-f5a1fb9965e4/go.mod h1:9n5ntfhhHQBIhUvlhDvD3Qg6fRUj4jkN0VB8L8svzOA=
|
||||
github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f h1:bAs4lUbRJpnnkd9VhRV3jjAVU7DJVjMaK+IsvSeZvFo=
|
||||
github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA=
|
||||
github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg=
|
||||
@@ -85,8 +84,8 @@ github.com/btcsuite/btcutil v1.0.3-0.20210527170813-e2ba6805a890/go.mod h1:0DVlH
|
||||
github.com/btcsuite/btcutil/psbt v1.0.3-0.20201208143702-a53e38424cce/go.mod h1:LVveMu4VaNSkIRTZu2+ut0HDBRuYjqGocxDMNS1KuGQ=
|
||||
github.com/btcsuite/btcutil/psbt v1.0.3-0.20210527170813-e2ba6805a890 h1:0xUNvvwJ7RjzBs4nCF+YrK28S5P/b4uHkpPxY1ovGY4=
|
||||
github.com/btcsuite/btcutil/psbt v1.0.3-0.20210527170813-e2ba6805a890/go.mod h1:LVveMu4VaNSkIRTZu2+ut0HDBRuYjqGocxDMNS1KuGQ=
|
||||
github.com/btcsuite/btcwallet v0.12.1-0.20210803004036-eebed51155ec h1:MAAR//aKu+I7bnxmWJZqGTX7fU7abWFBRoSzX6ty8zw=
|
||||
github.com/btcsuite/btcwallet v0.12.1-0.20210803004036-eebed51155ec/go.mod h1:LNhKxGlbwEGVQFjS4Qa7BgR6NipPhTd1/93Ay049pBw=
|
||||
github.com/btcsuite/btcwallet v0.12.1-0.20210822222949-9b5a201c344c h1:lOUYaSw0aqCHgLk+hA2QSYXhquRXdAvT6rB3sJMXI8w=
|
||||
github.com/btcsuite/btcwallet v0.12.1-0.20210822222949-9b5a201c344c/go.mod h1:SdqXKJoEEi5LJq6zU67PcKiyqF97AcUOfBfyQHC7rqQ=
|
||||
github.com/btcsuite/btcwallet/wallet/txauthor v1.0.0/go.mod h1:VufDts7bd/zs3GV13f/lXc/0lXrPnvxD/NvmpG/FEKU=
|
||||
github.com/btcsuite/btcwallet/wallet/txauthor v1.0.1-0.20210329233242-e0607006dce6/go.mod h1:VufDts7bd/zs3GV13f/lXc/0lXrPnvxD/NvmpG/FEKU=
|
||||
github.com/btcsuite/btcwallet/wallet/txauthor v1.0.2-0.20210803004036-eebed51155ec h1:nuO8goa4gbgDM4iegCztF7mTq8io9NT1DAMoPrEI6S4=
|
||||
@@ -101,8 +100,8 @@ github.com/btcsuite/btcwallet/walletdb v1.3.5/go.mod h1:oJDxAEUHVtnmIIBaa22wSBPT
|
||||
github.com/btcsuite/btcwallet/walletdb v1.3.6-0.20210803004036-eebed51155ec h1:zcAU3Ij8SmqaE+ITtS76fua2Niq7DRNp46sJRhi8PiI=
|
||||
github.com/btcsuite/btcwallet/walletdb v1.3.6-0.20210803004036-eebed51155ec/go.mod h1:oJDxAEUHVtnmIIBaa22wSBPTVcs6hUp5NKWmI8xDwwU=
|
||||
github.com/btcsuite/btcwallet/wtxmgr v1.3.0/go.mod h1:awQsh1n/0ZrEQ+JZgWvHeo153ubzEisf/FyNtwI0dDk=
|
||||
github.com/btcsuite/btcwallet/wtxmgr v1.3.1-0.20210803004036-eebed51155ec h1:q2OVY/GUKpdpfaVYztVrWoTRVzyzdDQftRcgHs/6cXI=
|
||||
github.com/btcsuite/btcwallet/wtxmgr v1.3.1-0.20210803004036-eebed51155ec/go.mod h1:UM38ixX8VwJ9qey4umf//0H3ndn5kSImFZ46V54Nd5Q=
|
||||
github.com/btcsuite/btcwallet/wtxmgr v1.3.1-0.20210822222949-9b5a201c344c h1:owWPexGfK4eSK4/Zy+XK2lET5qsnW7FRAc8OCOdD0Fg=
|
||||
github.com/btcsuite/btcwallet/wtxmgr v1.3.1-0.20210822222949-9b5a201c344c/go.mod h1:UM38ixX8VwJ9qey4umf//0H3ndn5kSImFZ46V54Nd5Q=
|
||||
github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd h1:R/opQEbFEy9JGkIguV40SvRY1uliPX8ifOvi6ICsFCw=
|
||||
github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg=
|
||||
github.com/btcsuite/golangcrypto v0.0.0-20150304025918-53f62d9b43e8/go.mod h1:tYvUd8KLhm/oXvUeSEs2VlLghFjQt9+ZaF9ghH0JNjc=
|
||||
|
38
lnd.go
38
lnd.go
@@ -1492,13 +1492,17 @@ func waitForWalletPassword(cfg *Config,
|
||||
case initMsg := <-pwService.InitMsgs:
|
||||
password := initMsg.Passphrase
|
||||
cipherSeed := initMsg.WalletSeed
|
||||
extendedKey := initMsg.WalletExtendedKey
|
||||
recoveryWindow := initMsg.RecoveryWindow
|
||||
|
||||
// Before we proceed, we'll check the internal version of the
|
||||
// seed. If it's greater than the current key derivation
|
||||
// version, then we'll return an error as we don't understand
|
||||
// this.
|
||||
if cipherSeed.InternalVersion != keychain.KeyDerivationVersion {
|
||||
const latestVersion = keychain.KeyDerivationVersion
|
||||
if cipherSeed != nil &&
|
||||
cipherSeed.InternalVersion != latestVersion {
|
||||
|
||||
return nil, fmt.Errorf("invalid internal "+
|
||||
"seed version %v, current version is %v",
|
||||
cipherSeed.InternalVersion,
|
||||
@@ -1515,10 +1519,36 @@ func waitForWalletPassword(cfg *Config,
|
||||
|
||||
// With the seed, we can now use the wallet loader to create
|
||||
// the wallet, then pass it back to avoid unlocking it again.
|
||||
birthday := cipherSeed.BirthdayTime()
|
||||
newWallet, err := loader.CreateNewWallet(
|
||||
password, password, cipherSeed.Entropy[:], birthday,
|
||||
var (
|
||||
birthday time.Time
|
||||
newWallet *wallet.Wallet
|
||||
)
|
||||
switch {
|
||||
// A normal cipher seed was given, use the birthday encoded in
|
||||
// it and create the wallet from that.
|
||||
case cipherSeed != nil:
|
||||
birthday = cipherSeed.BirthdayTime()
|
||||
newWallet, err = loader.CreateNewWallet(
|
||||
password, password, cipherSeed.Entropy[:],
|
||||
birthday,
|
||||
)
|
||||
|
||||
// No seed was given, we're importing a wallet from its extended
|
||||
// private key.
|
||||
case extendedKey != nil:
|
||||
birthday = initMsg.ExtendedKeyBirthday
|
||||
newWallet, err = loader.CreateNewWalletExtendedKey(
|
||||
password, password, extendedKey, birthday,
|
||||
)
|
||||
|
||||
default:
|
||||
// The unlocker service made sure either the cipher seed
|
||||
// or the extended key is set so, we shouldn't get here.
|
||||
// The default case is just here for readability and
|
||||
// completeness.
|
||||
err = fmt.Errorf("cannot create wallet, neither seed " +
|
||||
"nor extended key was given")
|
||||
}
|
||||
if err != nil {
|
||||
// Don't leave the file open in case the new wallet
|
||||
// could not be created for whatever reason.
|
||||
|
@@ -189,6 +189,29 @@ type InitWalletRequest struct {
|
||||
//admin macaroon returned in the response MUST be stored by the caller of the
|
||||
//RPC as otherwise all access to the daemon will be lost!
|
||||
StatelessInit bool `protobuf:"varint,6,opt,name=stateless_init,json=statelessInit,proto3" json:"stateless_init,omitempty"`
|
||||
//
|
||||
//extended_master_key is an alternative to specifying cipher_seed_mnemonic and
|
||||
//aezeed_passphrase. Instead of deriving the master root key from the entropy
|
||||
//of an aezeed cipher seed, the given extended master root key is used
|
||||
//directly as the wallet's master key. This allows users to import/use a
|
||||
//master key from another wallet. When doing so, lnd still uses its default
|
||||
//SegWit only (BIP49/84) derivation paths and funds from custom/non-default
|
||||
//derivation paths will not automatically appear in the on-chain wallet. Using
|
||||
//an 'xprv' instead of an aezeed also has the disadvantage that the wallet's
|
||||
//birthday is not known as that is an information that's only encoded in the
|
||||
//aezeed, not the xprv. Therefore a birthday needs to be specified in
|
||||
//extended_master_key_birthday_timestamp or a "safe" default value will be
|
||||
//used.
|
||||
ExtendedMasterKey string `protobuf:"bytes,7,opt,name=extended_master_key,json=extendedMasterKey,proto3" json:"extended_master_key,omitempty"`
|
||||
//
|
||||
//extended_master_key_birthday_timestamp is the optional unix timestamp in
|
||||
//seconds to use as the wallet's birthday when using an extended master key
|
||||
//to restore the wallet. lnd will only start scanning for funds in blocks that
|
||||
//are after the birthday which can speed up the process significantly. If the
|
||||
//birthday is not known, this should be left at its default value of 0 in
|
||||
//which case lnd will start scanning from the first SegWit block (481824 on
|
||||
//mainnet).
|
||||
ExtendedMasterKeyBirthdayTimestamp uint64 `protobuf:"varint,8,opt,name=extended_master_key_birthday_timestamp,json=extendedMasterKeyBirthdayTimestamp,proto3" json:"extended_master_key_birthday_timestamp,omitempty"`
|
||||
}
|
||||
|
||||
func (x *InitWalletRequest) Reset() {
|
||||
@@ -265,6 +288,20 @@ func (x *InitWalletRequest) GetStatelessInit() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (x *InitWalletRequest) GetExtendedMasterKey() string {
|
||||
if x != nil {
|
||||
return x.ExtendedMasterKey
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *InitWalletRequest) GetExtendedMasterKeyBirthdayTimestamp() uint64 {
|
||||
if x != nil {
|
||||
return x.ExtendedMasterKeyBirthdayTimestamp
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
type InitWalletResponse struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
@@ -605,8 +642,8 @@ var file_walletunlocker_proto_rawDesc = []byte{
|
||||
0x09, 0x52, 0x12, 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, 0x53, 0x65, 0x65, 0x64, 0x4d, 0x6e, 0x65,
|
||||
0x6d, 0x6f, 0x6e, 0x69, 0x63, 0x12, 0x27, 0x0a, 0x0f, 0x65, 0x6e, 0x63, 0x69, 0x70, 0x68, 0x65,
|
||||
0x72, 0x65, 0x64, 0x5f, 0x73, 0x65, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0e,
|
||||
0x65, 0x6e, 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, 0x65, 0x64, 0x53, 0x65, 0x65, 0x64, 0x22, 0xaf,
|
||||
0x02, 0x0a, 0x11, 0x49, 0x6e, 0x69, 0x74, 0x57, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x52, 0x65, 0x71,
|
||||
0x65, 0x6e, 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, 0x65, 0x64, 0x53, 0x65, 0x65, 0x64, 0x22, 0xb3,
|
||||
0x03, 0x0a, 0x11, 0x49, 0x6e, 0x69, 0x74, 0x57, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x52, 0x65, 0x71,
|
||||
0x75, 0x65, 0x73, 0x74, 0x12, 0x27, 0x0a, 0x0f, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x5f, 0x70,
|
||||
0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0e, 0x77,
|
||||
0x61, 0x6c, 0x6c, 0x65, 0x74, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x12, 0x30, 0x0a,
|
||||
@@ -625,63 +662,71 @@ var file_walletunlocker_proto_rawDesc = []byte{
|
||||
0x65, 0x6c, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x73, 0x12, 0x25, 0x0a, 0x0e, 0x73, 0x74, 0x61,
|
||||
0x74, 0x65, 0x6c, 0x65, 0x73, 0x73, 0x5f, 0x69, 0x6e, 0x69, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28,
|
||||
0x08, 0x52, 0x0d, 0x73, 0x74, 0x61, 0x74, 0x65, 0x6c, 0x65, 0x73, 0x73, 0x49, 0x6e, 0x69, 0x74,
|
||||
0x22, 0x3b, 0x0a, 0x12, 0x49, 0x6e, 0x69, 0x74, 0x57, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x52, 0x65,
|
||||
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x5f,
|
||||
0x6d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0d,
|
||||
0x61, 0x64, 0x6d, 0x69, 0x6e, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x22, 0xd2, 0x01,
|
||||
0x0a, 0x13, 0x55, 0x6e, 0x6c, 0x6f, 0x63, 0x6b, 0x57, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x52, 0x65,
|
||||
0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x27, 0x0a, 0x0f, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x5f,
|
||||
0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0e,
|
||||
0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x12, 0x27,
|
||||
0x0a, 0x0f, 0x72, 0x65, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x5f, 0x77, 0x69, 0x6e, 0x64, 0x6f,
|
||||
0x77, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0e, 0x72, 0x65, 0x63, 0x6f, 0x76, 0x65, 0x72,
|
||||
0x79, 0x57, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x12, 0x42, 0x0a, 0x0f, 0x63, 0x68, 0x61, 0x6e, 0x6e,
|
||||
0x65, 0x6c, 0x5f, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b,
|
||||
0x32, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x42, 0x61, 0x63,
|
||||
0x6b, 0x75, 0x70, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x52, 0x0e, 0x63, 0x68, 0x61,
|
||||
0x6e, 0x6e, 0x65, 0x6c, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x73, 0x12, 0x25, 0x0a, 0x0e, 0x73,
|
||||
0x74, 0x61, 0x74, 0x65, 0x6c, 0x65, 0x73, 0x73, 0x5f, 0x69, 0x6e, 0x69, 0x74, 0x18, 0x04, 0x20,
|
||||
0x01, 0x28, 0x08, 0x52, 0x0d, 0x73, 0x74, 0x61, 0x74, 0x65, 0x6c, 0x65, 0x73, 0x73, 0x49, 0x6e,
|
||||
0x69, 0x74, 0x22, 0x16, 0x0a, 0x14, 0x55, 0x6e, 0x6c, 0x6f, 0x63, 0x6b, 0x57, 0x61, 0x6c, 0x6c,
|
||||
0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xbf, 0x01, 0x0a, 0x15, 0x43,
|
||||
0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x52, 0x65, 0x71,
|
||||
0x75, 0x65, 0x73, 0x74, 0x12, 0x29, 0x0a, 0x10, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x5f,
|
||||
0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0f,
|
||||
0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x12,
|
||||
0x21, 0x0a, 0x0c, 0x6e, 0x65, 0x77, 0x5f, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18,
|
||||
0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x6e, 0x65, 0x77, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f,
|
||||
0x72, 0x64, 0x12, 0x25, 0x0a, 0x0e, 0x73, 0x74, 0x61, 0x74, 0x65, 0x6c, 0x65, 0x73, 0x73, 0x5f,
|
||||
0x69, 0x6e, 0x69, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x73, 0x74, 0x61, 0x74,
|
||||
0x65, 0x6c, 0x65, 0x73, 0x73, 0x49, 0x6e, 0x69, 0x74, 0x12, 0x31, 0x0a, 0x15, 0x6e, 0x65, 0x77,
|
||||
0x5f, 0x6d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x5f, 0x6b,
|
||||
0x65, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x12, 0x6e, 0x65, 0x77, 0x4d, 0x61, 0x63,
|
||||
0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x52, 0x6f, 0x6f, 0x74, 0x4b, 0x65, 0x79, 0x22, 0x3f, 0x0a, 0x16,
|
||||
0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x52, 0x65,
|
||||
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x5f,
|
||||
0x6d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0d,
|
||||
0x61, 0x64, 0x6d, 0x69, 0x6e, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x32, 0xa5, 0x02,
|
||||
0x0a, 0x0e, 0x57, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x55, 0x6e, 0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x72,
|
||||
0x12, 0x38, 0x0a, 0x07, 0x47, 0x65, 0x6e, 0x53, 0x65, 0x65, 0x64, 0x12, 0x15, 0x2e, 0x6c, 0x6e,
|
||||
0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x53, 0x65, 0x65, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65,
|
||||
0x73, 0x74, 0x1a, 0x16, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x53, 0x65,
|
||||
0x65, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x41, 0x0a, 0x0a, 0x49, 0x6e,
|
||||
0x69, 0x74, 0x57, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x12, 0x18, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63,
|
||||
0x2e, 0x49, 0x6e, 0x69, 0x74, 0x57, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65,
|
||||
0x73, 0x74, 0x1a, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x49, 0x6e, 0x69, 0x74, 0x57,
|
||||
0x61, 0x6c, 0x6c, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x47, 0x0a,
|
||||
0x0c, 0x55, 0x6e, 0x6c, 0x6f, 0x63, 0x6b, 0x57, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x12, 0x1a, 0x2e,
|
||||
0x12, 0x2e, 0x0a, 0x13, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x64, 0x65, 0x64, 0x5f, 0x6d, 0x61, 0x73,
|
||||
0x74, 0x65, 0x72, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x65,
|
||||
0x78, 0x74, 0x65, 0x6e, 0x64, 0x65, 0x64, 0x4d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x4b, 0x65, 0x79,
|
||||
0x12, 0x52, 0x0a, 0x26, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x64, 0x65, 0x64, 0x5f, 0x6d, 0x61, 0x73,
|
||||
0x74, 0x65, 0x72, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x62, 0x69, 0x72, 0x74, 0x68, 0x64, 0x61, 0x79,
|
||||
0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x08, 0x20, 0x01, 0x28, 0x04,
|
||||
0x52, 0x22, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x64, 0x65, 0x64, 0x4d, 0x61, 0x73, 0x74, 0x65, 0x72,
|
||||
0x4b, 0x65, 0x79, 0x42, 0x69, 0x72, 0x74, 0x68, 0x64, 0x61, 0x79, 0x54, 0x69, 0x6d, 0x65, 0x73,
|
||||
0x74, 0x61, 0x6d, 0x70, 0x22, 0x3b, 0x0a, 0x12, 0x49, 0x6e, 0x69, 0x74, 0x57, 0x61, 0x6c, 0x6c,
|
||||
0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x61, 0x64,
|
||||
0x6d, 0x69, 0x6e, 0x5f, 0x6d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01,
|
||||
0x28, 0x0c, 0x52, 0x0d, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f,
|
||||
0x6e, 0x22, 0xd2, 0x01, 0x0a, 0x13, 0x55, 0x6e, 0x6c, 0x6f, 0x63, 0x6b, 0x57, 0x61, 0x6c, 0x6c,
|
||||
0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x27, 0x0a, 0x0f, 0x77, 0x61, 0x6c,
|
||||
0x6c, 0x65, 0x74, 0x5f, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x01, 0x20, 0x01,
|
||||
0x28, 0x0c, 0x52, 0x0e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f,
|
||||
0x72, 0x64, 0x12, 0x27, 0x0a, 0x0f, 0x72, 0x65, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x5f, 0x77,
|
||||
0x69, 0x6e, 0x64, 0x6f, 0x77, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0e, 0x72, 0x65, 0x63,
|
||||
0x6f, 0x76, 0x65, 0x72, 0x79, 0x57, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x12, 0x42, 0x0a, 0x0f, 0x63,
|
||||
0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x73, 0x18, 0x03,
|
||||
0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61,
|
||||
0x6e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x52,
|
||||
0x0e, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x73, 0x12,
|
||||
0x25, 0x0a, 0x0e, 0x73, 0x74, 0x61, 0x74, 0x65, 0x6c, 0x65, 0x73, 0x73, 0x5f, 0x69, 0x6e, 0x69,
|
||||
0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x73, 0x74, 0x61, 0x74, 0x65, 0x6c, 0x65,
|
||||
0x73, 0x73, 0x49, 0x6e, 0x69, 0x74, 0x22, 0x16, 0x0a, 0x14, 0x55, 0x6e, 0x6c, 0x6f, 0x63, 0x6b,
|
||||
0x57, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xbf,
|
||||
0x01, 0x0a, 0x15, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72,
|
||||
0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x29, 0x0a, 0x10, 0x63, 0x75, 0x72, 0x72,
|
||||
0x65, 0x6e, 0x74, 0x5f, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x01, 0x20, 0x01,
|
||||
0x28, 0x0c, 0x52, 0x0f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x50, 0x61, 0x73, 0x73, 0x77,
|
||||
0x6f, 0x72, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x6e, 0x65, 0x77, 0x5f, 0x70, 0x61, 0x73, 0x73, 0x77,
|
||||
0x6f, 0x72, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x6e, 0x65, 0x77, 0x50, 0x61,
|
||||
0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x12, 0x25, 0x0a, 0x0e, 0x73, 0x74, 0x61, 0x74, 0x65, 0x6c,
|
||||
0x65, 0x73, 0x73, 0x5f, 0x69, 0x6e, 0x69, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d,
|
||||
0x73, 0x74, 0x61, 0x74, 0x65, 0x6c, 0x65, 0x73, 0x73, 0x49, 0x6e, 0x69, 0x74, 0x12, 0x31, 0x0a,
|
||||
0x15, 0x6e, 0x65, 0x77, 0x5f, 0x6d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x5f, 0x72, 0x6f,
|
||||
0x6f, 0x74, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x12, 0x6e, 0x65,
|
||||
0x77, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x52, 0x6f, 0x6f, 0x74, 0x4b, 0x65, 0x79,
|
||||
0x22, 0x3f, 0x0a, 0x16, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f,
|
||||
0x72, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x61, 0x64,
|
||||
0x6d, 0x69, 0x6e, 0x5f, 0x6d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01,
|
||||
0x28, 0x0c, 0x52, 0x0d, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f,
|
||||
0x6e, 0x32, 0xa5, 0x02, 0x0a, 0x0e, 0x57, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x55, 0x6e, 0x6c, 0x6f,
|
||||
0x63, 0x6b, 0x65, 0x72, 0x12, 0x38, 0x0a, 0x07, 0x47, 0x65, 0x6e, 0x53, 0x65, 0x65, 0x64, 0x12,
|
||||
0x15, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x53, 0x65, 0x65, 0x64, 0x52,
|
||||
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x47,
|
||||
0x65, 0x6e, 0x53, 0x65, 0x65, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x41,
|
||||
0x0a, 0x0a, 0x49, 0x6e, 0x69, 0x74, 0x57, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x12, 0x18, 0x2e, 0x6c,
|
||||
0x6e, 0x72, 0x70, 0x63, 0x2e, 0x49, 0x6e, 0x69, 0x74, 0x57, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x52,
|
||||
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x49,
|
||||
0x6e, 0x69, 0x74, 0x57, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
|
||||
0x65, 0x12, 0x47, 0x0a, 0x0c, 0x55, 0x6e, 0x6c, 0x6f, 0x63, 0x6b, 0x57, 0x61, 0x6c, 0x6c, 0x65,
|
||||
0x74, 0x12, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x55, 0x6e, 0x6c, 0x6f, 0x63, 0x6b,
|
||||
0x57, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e,
|
||||
0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x55, 0x6e, 0x6c, 0x6f, 0x63, 0x6b, 0x57, 0x61, 0x6c, 0x6c,
|
||||
0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x6c, 0x6e, 0x72, 0x70,
|
||||
0x63, 0x2e, 0x55, 0x6e, 0x6c, 0x6f, 0x63, 0x6b, 0x57, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x52, 0x65,
|
||||
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4d, 0x0a, 0x0e, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65,
|
||||
0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x12, 0x1c, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63,
|
||||
0x2e, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x52,
|
||||
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43,
|
||||
0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x52, 0x65, 0x73,
|
||||
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x27, 0x5a, 0x25, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e,
|
||||
0x63, 0x6f, 0x6d, 0x2f, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x6e, 0x69, 0x6e, 0x67, 0x6e, 0x65, 0x74,
|
||||
0x77, 0x6f, 0x72, 0x6b, 0x2f, 0x6c, 0x6e, 0x64, 0x2f, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x62, 0x06,
|
||||
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4d, 0x0a, 0x0e, 0x43, 0x68,
|
||||
0x61, 0x6e, 0x67, 0x65, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x12, 0x1c, 0x2e, 0x6c,
|
||||
0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, 0x61, 0x73, 0x73, 0x77,
|
||||
0x6f, 0x72, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x6c, 0x6e, 0x72,
|
||||
0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72,
|
||||
0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x27, 0x5a, 0x25, 0x67, 0x69, 0x74,
|
||||
0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x6e, 0x69, 0x6e,
|
||||
0x67, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x2f, 0x6c, 0x6e, 0x64, 0x2f, 0x6c, 0x6e, 0x72,
|
||||
0x70, 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
|
@@ -149,6 +149,33 @@ message InitWalletRequest {
|
||||
RPC as otherwise all access to the daemon will be lost!
|
||||
*/
|
||||
bool stateless_init = 6;
|
||||
|
||||
/*
|
||||
extended_master_key is an alternative to specifying cipher_seed_mnemonic and
|
||||
aezeed_passphrase. Instead of deriving the master root key from the entropy
|
||||
of an aezeed cipher seed, the given extended master root key is used
|
||||
directly as the wallet's master key. This allows users to import/use a
|
||||
master key from another wallet. When doing so, lnd still uses its default
|
||||
SegWit only (BIP49/84) derivation paths and funds from custom/non-default
|
||||
derivation paths will not automatically appear in the on-chain wallet. Using
|
||||
an 'xprv' instead of an aezeed also has the disadvantage that the wallet's
|
||||
birthday is not known as that is an information that's only encoded in the
|
||||
aezeed, not the xprv. Therefore a birthday needs to be specified in
|
||||
extended_master_key_birthday_timestamp or a "safe" default value will be
|
||||
used.
|
||||
*/
|
||||
string extended_master_key = 7;
|
||||
|
||||
/*
|
||||
extended_master_key_birthday_timestamp is the optional unix timestamp in
|
||||
seconds to use as the wallet's birthday when using an extended master key
|
||||
to restore the wallet. lnd will only start scanning for funds in blocks that
|
||||
are after the birthday which can speed up the process significantly. If the
|
||||
birthday is not known, this should be left at its default value of 0 in
|
||||
which case lnd will start scanning from the first SegWit block (481824 on
|
||||
mainnet).
|
||||
*/
|
||||
uint64 extended_master_key_birthday_timestamp = 8;
|
||||
}
|
||||
message InitWalletResponse {
|
||||
/*
|
||||
|
@@ -300,6 +300,15 @@
|
||||
"stateless_init": {
|
||||
"type": "boolean",
|
||||
"title": "stateless_init is an optional argument instructing the daemon NOT to create\nany *.macaroon files in its filesystem. If this parameter is set, then the\nadmin macaroon returned in the response MUST be stored by the caller of the\nRPC as otherwise all access to the daemon will be lost!"
|
||||
},
|
||||
"extended_master_key": {
|
||||
"type": "string",
|
||||
"description": "extended_master_key is an alternative to specifying cipher_seed_mnemonic and\naezeed_passphrase. Instead of deriving the master root key from the entropy\nof an aezeed cipher seed, the given extended master root key is used\ndirectly as the wallet's master key. This allows users to import/use a\nmaster key from another wallet. When doing so, lnd still uses its default\nSegWit only (BIP49/84) derivation paths and funds from custom/non-default\nderivation paths will not automatically appear in the on-chain wallet. Using\nan 'xprv' instead of an aezeed also has the disadvantage that the wallet's\nbirthday is not known as that is an information that's only encoded in the\naezeed, not the xprv. Therefore a birthday needs to be specified in\nextended_master_key_birthday_timestamp or a \"safe\" default value will be\nused."
|
||||
},
|
||||
"extended_master_key_birthday_timestamp": {
|
||||
"type": "string",
|
||||
"format": "uint64",
|
||||
"description": "extended_master_key_birthday_timestamp is the optional unix timestamp in\nseconds to use as the wallet's birthday when using an extended master key\nto restore the wallet. lnd will only start scanning for funds in blocks that\nare after the birthday which can speed up the process significantly. If the\nbirthday is not known, this should be left at its default value of 0 in\nwhich case lnd will start scanning from the first SegWit block (481824 on\nmainnet)."
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@@ -426,7 +426,7 @@ func (n *NetworkHarness) newNodeWithSeed(name string, extraArgs []string,
|
||||
// will finish initializing the LightningClient such that the HarnessNode can
|
||||
// be used for regular rpc operations.
|
||||
func (n *NetworkHarness) RestoreNodeWithSeed(name string, extraArgs []string,
|
||||
password []byte, mnemonic []string, recoveryWindow int32,
|
||||
password []byte, mnemonic []string, rootKey string, recoveryWindow int32,
|
||||
chanBackups *lnrpc.ChanBackupSnapshot,
|
||||
opts ...NodeOption) (*HarnessNode, error) {
|
||||
|
||||
@@ -441,6 +441,7 @@ func (n *NetworkHarness) RestoreNodeWithSeed(name string, extraArgs []string,
|
||||
WalletPassword: password,
|
||||
CipherSeedMnemonic: mnemonic,
|
||||
AezeedPassphrase: password,
|
||||
ExtendedMasterKey: rootKey,
|
||||
RecoveryWindow: recoveryWindow,
|
||||
ChannelBackups: chanBackups,
|
||||
}
|
||||
|
@@ -122,8 +122,8 @@ func testChannelBackupRestore(net *lntest.NetworkHarness, t *harnessTest) {
|
||||
// obtained above.
|
||||
return func() (*lntest.HarnessNode, error) {
|
||||
return net.RestoreNodeWithSeed(
|
||||
"dave", nil, password,
|
||||
mnemonic, 1000, backupSnapshot,
|
||||
"dave", nil, password, mnemonic,
|
||||
"", 1000, backupSnapshot,
|
||||
copyPorts(oldNode),
|
||||
)
|
||||
}, nil
|
||||
@@ -159,8 +159,8 @@ func testChannelBackupRestore(net *lntest.NetworkHarness, t *harnessTest) {
|
||||
// restart it again using Unlock.
|
||||
return func() (*lntest.HarnessNode, error) {
|
||||
newNode, err := net.RestoreNodeWithSeed(
|
||||
"dave", nil, password,
|
||||
mnemonic, 1000, nil,
|
||||
"dave", nil, password, mnemonic,
|
||||
"", 1000, nil,
|
||||
copyPorts(oldNode),
|
||||
)
|
||||
if err != nil {
|
||||
@@ -208,7 +208,8 @@ func testChannelBackupRestore(net *lntest.NetworkHarness, t *harnessTest) {
|
||||
return func() (*lntest.HarnessNode, error) {
|
||||
newNode, err := net.RestoreNodeWithSeed(
|
||||
"dave", nil, password, mnemonic,
|
||||
1000, nil, copyPorts(oldNode),
|
||||
"", 1000, nil,
|
||||
copyPorts(oldNode),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to "+
|
||||
@@ -1309,7 +1310,7 @@ func chanRestoreViaRPC(net *lntest.NetworkHarness, password []byte,
|
||||
|
||||
return func() (*lntest.HarnessNode, error) {
|
||||
newNode, err := net.RestoreNodeWithSeed(
|
||||
"dave", nil, password, mnemonic, 1000, nil,
|
||||
"dave", nil, password, mnemonic, "", 1000, nil,
|
||||
copyPorts(oldNode),
|
||||
)
|
||||
if err != nil {
|
||||
|
@@ -5,9 +5,12 @@ import (
|
||||
"math"
|
||||
|
||||
"github.com/btcsuite/btcutil"
|
||||
"github.com/btcsuite/btcutil/hdkeychain"
|
||||
"github.com/lightningnetwork/lnd/aezeed"
|
||||
"github.com/lightningnetwork/lnd/lnrpc"
|
||||
"github.com/lightningnetwork/lnd/lntest"
|
||||
"github.com/lightningnetwork/lnd/lntest/wait"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// testGetRecoveryInfo checks whether lnd gives the right information about
|
||||
@@ -34,7 +37,8 @@ func testGetRecoveryInfo(net *lntest.NetworkHarness, t *harnessTest) {
|
||||
// Restore Carol, passing in the password, mnemonic, and
|
||||
// desired recovery window.
|
||||
node, err := net.RestoreNodeWithSeed(
|
||||
"Carol", nil, password, mnemonic, recoveryWindow, nil,
|
||||
"Carol", nil, password, mnemonic, "", recoveryWindow,
|
||||
nil,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to restore node: %v", err)
|
||||
@@ -124,11 +128,14 @@ func testOnchainFundRecovery(net *lntest.NetworkHarness, t *harnessTest) {
|
||||
carol, mnemonic, _, err := net.NewNodeWithSeed(
|
||||
"Carol", nil, password, false,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create node with seed; %v", err)
|
||||
}
|
||||
require.NoError(t.t, err)
|
||||
shutdownAndAssert(net, t, carol)
|
||||
|
||||
// As long as the mnemonic is non-nil and the extended key is empty, the
|
||||
// closure below will always restore the node from the seed. The tests
|
||||
// need to manually overwrite this value to change that behavior.
|
||||
rootKey := ""
|
||||
|
||||
// Create a closure for testing the recovery of Carol's wallet. This
|
||||
// method takes the expected value of Carol's balance when using the
|
||||
// given recovery window. Additionally, the caller can specify an action
|
||||
@@ -136,14 +143,15 @@ func testOnchainFundRecovery(net *lntest.NetworkHarness, t *harnessTest) {
|
||||
restoreCheckBalance := func(expAmount int64, expectedNumUTXOs uint32,
|
||||
recoveryWindow int32, fn func(*lntest.HarnessNode)) {
|
||||
|
||||
t.t.Helper()
|
||||
|
||||
// Restore Carol, passing in the password, mnemonic, and
|
||||
// desired recovery window.
|
||||
node, err := net.RestoreNodeWithSeed(
|
||||
"Carol", nil, password, mnemonic, recoveryWindow, nil,
|
||||
"Carol", nil, password, mnemonic, rootKey,
|
||||
recoveryWindow, nil,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to restore node: %v", err)
|
||||
}
|
||||
require.NoError(t.t, err)
|
||||
|
||||
// Query carol for her current wallet balance, and also that we
|
||||
// gain the expected number of UTXOs.
|
||||
@@ -155,10 +163,7 @@ func testOnchainFundRecovery(net *lntest.NetworkHarness, t *harnessTest) {
|
||||
req := &lnrpc.WalletBalanceRequest{}
|
||||
ctxt, _ := context.WithTimeout(ctxb, defaultTimeout)
|
||||
resp, err := node.WalletBalance(ctxt, req)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to query wallet balance: %v",
|
||||
err)
|
||||
}
|
||||
require.NoError(t.t, err)
|
||||
currBalance = resp.ConfirmedBalance
|
||||
|
||||
utxoReq := &lnrpc.ListUnspentRequest{
|
||||
@@ -166,9 +171,7 @@ func testOnchainFundRecovery(net *lntest.NetworkHarness, t *harnessTest) {
|
||||
}
|
||||
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
|
||||
utxoResp, err := node.ListUnspent(ctxt, utxoReq)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to query utxos: %v", err)
|
||||
}
|
||||
require.NoError(t.t, err)
|
||||
currNumUTXOs = uint32(len(utxoResp.Utxos))
|
||||
|
||||
// Verify that Carol's balance and number of UTXOs
|
||||
@@ -206,6 +209,8 @@ func testOnchainFundRecovery(net *lntest.NetworkHarness, t *harnessTest) {
|
||||
// behavior to both default P2WKH and NP2WKH scopes.
|
||||
skipAndSend := func(nskip int) func(*lntest.HarnessNode) {
|
||||
return func(node *lntest.HarnessNode) {
|
||||
t.t.Helper()
|
||||
|
||||
newP2WKHAddrReq := &lnrpc.NewAddressRequest{
|
||||
Type: AddrTypeWitnessPubkeyHash,
|
||||
}
|
||||
@@ -218,17 +223,11 @@ func testOnchainFundRecovery(net *lntest.NetworkHarness, t *harnessTest) {
|
||||
for i := 0; i < nskip; i++ {
|
||||
ctxt, _ := context.WithTimeout(ctxb, defaultTimeout)
|
||||
_, err = node.NewAddress(ctxt, newP2WKHAddrReq)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to generate new "+
|
||||
"p2wkh address: %v", err)
|
||||
}
|
||||
require.NoError(t.t, err)
|
||||
|
||||
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
|
||||
_, err = node.NewAddress(ctxt, newNP2WKHAddrReq)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to generate new "+
|
||||
"np2wkh address: %v", err)
|
||||
}
|
||||
require.NoError(t.t, err)
|
||||
}
|
||||
|
||||
// Send one BTC to the next P2WKH address.
|
||||
@@ -291,28 +290,22 @@ func testOnchainFundRecovery(net *lntest.NetworkHarness, t *harnessTest) {
|
||||
const minerAmt = 5 * btcutil.SatoshiPerBitcoin
|
||||
const finalBalance = 6 * btcutil.SatoshiPerBitcoin
|
||||
promptChangeAddr := func(node *lntest.HarnessNode) {
|
||||
t.t.Helper()
|
||||
|
||||
minerAddr, err := net.Miner.NewAddress()
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create new miner address: %v", err)
|
||||
}
|
||||
require.NoError(t.t, err)
|
||||
ctxt, _ := context.WithTimeout(ctxb, defaultTimeout)
|
||||
resp, err := node.SendCoins(ctxt, &lnrpc.SendCoinsRequest{
|
||||
Addr: minerAddr.String(),
|
||||
Amount: minerAmt,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("unable to send coins to miner: %v", err)
|
||||
}
|
||||
require.NoError(t.t, err)
|
||||
txid, err := waitForTxInMempool(
|
||||
net.Miner.Client, minerMempoolTimeout,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("transaction not found in mempool: %v", err)
|
||||
}
|
||||
if resp.Txid != txid.String() {
|
||||
t.Fatalf("txid mismatch: %v vs %v", resp.Txid,
|
||||
txid.String())
|
||||
}
|
||||
require.NoError(t.t, err)
|
||||
require.Equal(t.t, txid.String(), resp.Txid)
|
||||
|
||||
block := mineBlocks(t, net, 1, 1)[0]
|
||||
assertTxInBlock(t, block, txid)
|
||||
}
|
||||
@@ -323,4 +316,19 @@ func testOnchainFundRecovery(net *lntest.NetworkHarness, t *harnessTest) {
|
||||
// only have one UTXO present (the change output) of 6 - 5 - fee BTC.
|
||||
const fee = 27750
|
||||
restoreCheckBalance(finalBalance-minerAmt-fee, 1, 21, nil)
|
||||
|
||||
// Last of all, make sure we can also restore a node from the extended
|
||||
// master root key directly instead of the seed.
|
||||
var seedMnemonic aezeed.Mnemonic
|
||||
copy(seedMnemonic[:], mnemonic)
|
||||
cipherSeed, err := seedMnemonic.ToCipherSeed(password)
|
||||
require.NoError(t.t, err)
|
||||
extendedRootKey, err := hdkeychain.NewMaster(
|
||||
cipherSeed.Entropy[:], harnessNetParams,
|
||||
)
|
||||
require.NoError(t.t, err)
|
||||
rootKey = extendedRootKey.String()
|
||||
mnemonic = nil
|
||||
|
||||
restoreCheckBalance(finalBalance-minerAmt-fee, 1, 21, nil)
|
||||
}
|
||||
|
@@ -9,6 +9,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/btcsuite/btcd/chaincfg"
|
||||
"github.com/btcsuite/btcutil/hdkeychain"
|
||||
"github.com/btcsuite/btcwallet/wallet"
|
||||
"github.com/lightningnetwork/lnd/aezeed"
|
||||
"github.com/lightningnetwork/lnd/chanbackup"
|
||||
@@ -50,9 +51,19 @@ type WalletInitMsg struct {
|
||||
Passphrase []byte
|
||||
|
||||
// WalletSeed is the deciphered cipher seed that the wallet should use
|
||||
// to initialize itself.
|
||||
// to initialize itself. The seed might be nil if the wallet should be
|
||||
// created from an extended master root key instead.
|
||||
WalletSeed *aezeed.CipherSeed
|
||||
|
||||
// WalletExtendedKey is the wallet's extended master root key that
|
||||
// should be used instead of the seed, if non-nil. The extended key is
|
||||
// mutually exclusive to the wallet seed, but one of both is always set.
|
||||
WalletExtendedKey *hdkeychain.ExtendedKey
|
||||
|
||||
// ExtendedKeyBirthday is the birthday of a wallet that's being restored
|
||||
// through an extended key instead of an aezeed.
|
||||
ExtendedKeyBirthday time.Time
|
||||
|
||||
// RecoveryWindow is the address look-ahead used when restoring a seed
|
||||
// with existing funds. A recovery window zero indicates that no
|
||||
// recovery should be attempted, such as after the wallet's initial
|
||||
@@ -353,29 +364,104 @@ func (u *UnlockerService) InitWallet(ctx context.Context,
|
||||
return nil, fmt.Errorf("wallet already exists")
|
||||
}
|
||||
|
||||
// At this point, we know that the wallet doesn't already exist. So
|
||||
// we'll map the user provided aezeed and passphrase into a decoded
|
||||
// cipher seed instance.
|
||||
var mnemonic aezeed.Mnemonic
|
||||
copy(mnemonic[:], in.CipherSeedMnemonic[:])
|
||||
|
||||
// If we're unable to map it back into the ciphertext, then either the
|
||||
// mnemonic is wrong, or the passphrase is wrong.
|
||||
cipherSeed, err := mnemonic.ToCipherSeed(in.AezeedPassphrase)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// With the cipher seed deciphered, and the auth service created, we'll
|
||||
// now send over the wallet password and the seed. This will allow the
|
||||
// daemon to initialize itself and startup.
|
||||
// At this point, we know the wallet doesn't already exist so we can
|
||||
// prepare the message that we'll send over the channel later.
|
||||
initMsg := &WalletInitMsg{
|
||||
Passphrase: password,
|
||||
WalletSeed: cipherSeed,
|
||||
RecoveryWindow: uint32(recoveryWindow),
|
||||
StatelessInit: in.StatelessInit,
|
||||
}
|
||||
|
||||
// There are two supported ways to initialize the wallet. Either from
|
||||
// the aezeed or the final extended master key directly.
|
||||
switch {
|
||||
// Don't allow the user to specify both as that would be ambiguous.
|
||||
case len(in.CipherSeedMnemonic) > 0 && len(in.ExtendedMasterKey) > 0:
|
||||
return nil, fmt.Errorf("cannot specify both the cipher " +
|
||||
"seed mnemonic and the extended master key")
|
||||
|
||||
// The aezeed is the preferred and default way of initializing a wallet.
|
||||
case len(in.CipherSeedMnemonic) > 0:
|
||||
// We'll map the user provided aezeed and passphrase into a
|
||||
// decoded cipher seed instance.
|
||||
var mnemonic aezeed.Mnemonic
|
||||
copy(mnemonic[:], in.CipherSeedMnemonic)
|
||||
|
||||
// If we're unable to map it back into the ciphertext, then
|
||||
// either the mnemonic is wrong, or the passphrase is wrong.
|
||||
cipherSeed, err := mnemonic.ToCipherSeed(in.AezeedPassphrase)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
initMsg.WalletSeed = cipherSeed
|
||||
|
||||
// To support restoring a wallet where the seed isn't known or a wallet
|
||||
// created externally to lnd, we also allow the extended master key
|
||||
// (xprv) to be imported directly. This is what'll be stored in the
|
||||
// btcwallet database anyway.
|
||||
case len(in.ExtendedMasterKey) > 0:
|
||||
extendedKey, err := hdkeychain.NewKeyFromString(
|
||||
in.ExtendedMasterKey,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// The on-chain wallet of lnd is going to derive keys based on
|
||||
// the BIP49/84 key derivation paths from this root key. To make
|
||||
// sure we use default derivation paths, we want to avoid
|
||||
// deriving keys from something other than the master key (at
|
||||
// depth 0, denoted with "m/" in BIP32 notation).
|
||||
if extendedKey.Depth() != 0 {
|
||||
return nil, fmt.Errorf("extended master key must " +
|
||||
"be at depth 0 not a child key")
|
||||
}
|
||||
|
||||
// Because we need the master key (at depth 0), it must be an
|
||||
// extended private key as the first levels of BIP49/84
|
||||
// derivation paths are hardened, which isn't possible with
|
||||
// extended public keys.
|
||||
if !extendedKey.IsPrivate() {
|
||||
return nil, fmt.Errorf("extended master key must " +
|
||||
"contain private keys")
|
||||
}
|
||||
|
||||
// To avoid using the wrong master key, we check that it was
|
||||
// issued for the correct network. This will cause problems if
|
||||
// someone tries to import a "new" BIP84 zprv key because with
|
||||
// this we only support the "legacy" zprv prefix. But it is
|
||||
// trivial to convert between those formats, as long as the user
|
||||
// knows what they're doing.
|
||||
if !extendedKey.IsForNet(u.netParams) {
|
||||
return nil, fmt.Errorf("extended master key must be "+
|
||||
"for network %s", u.netParams.Name)
|
||||
}
|
||||
|
||||
// When importing a wallet from its extended private key we
|
||||
// don't know the birthday as that information is not encoded in
|
||||
// that format. We therefore must set an arbitrary date to start
|
||||
// rescanning at if the user doesn't provide an explicit value
|
||||
// for it. Since lnd only uses SegWit addresses, we pick the
|
||||
// date of the first block that contained SegWit transactions
|
||||
// (481824).
|
||||
initMsg.ExtendedKeyBirthday = time.Date(
|
||||
2017, time.August, 24, 1, 57, 37, 0, time.UTC,
|
||||
)
|
||||
if in.ExtendedMasterKeyBirthdayTimestamp != 0 {
|
||||
initMsg.ExtendedKeyBirthday = time.Unix(
|
||||
int64(in.ExtendedMasterKeyBirthdayTimestamp), 0,
|
||||
)
|
||||
}
|
||||
|
||||
initMsg.WalletExtendedKey = extendedKey
|
||||
|
||||
// No key material was set, no wallet can be created.
|
||||
default:
|
||||
return nil, fmt.Errorf("must either specify cipher seed " +
|
||||
"mnemonic or the extended master key")
|
||||
}
|
||||
|
||||
// Before we return the unlock payload, we'll check if we can extract
|
||||
// any channel backups to pass up to the higher level sub-system.
|
||||
chansToRestore := extractChanBackups(in.ChannelBackups)
|
||||
|
Reference in New Issue
Block a user