Skip to content

Commit 41bc4e2

Browse files
genimportscript: adds flag for custom derivation path
1 parent a399c2c commit 41bc4e2

File tree

1 file changed

+53
-32
lines changed

1 file changed

+53
-32
lines changed

cmd/chantools/genimportscript.go

Lines changed: 53 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ package main
22

33
import (
44
"fmt"
5+
"strconv"
6+
"strings"
57
"time"
68

79
"github.com/btcsuite/btcd/chaincfg"
@@ -13,11 +15,13 @@ import (
1315
const (
1416
defaultRecoveryWindow = 2500
1517
defaultRescanFrom = 500000
18+
defaultDerivationPath = "m/84'/0'/0'"
1619
)
1720

1821
type genImportScriptCommand struct {
1922
RootKey string `long:"rootkey" description:"BIP32 HD root key to use. Leave empty to prompt for lnd 24 word aezeed."`
2023
Format string `long:"format" description:"The format of the generated import script. Currently supported are: bitcoin-cli, bitcoin-cli-watchonly, bitcoin-importwallet."`
24+
DerivationPath string `long:"derivationpath" description:"The first levels of the derivation path before any internal/external branch. (default m/84'/0'/0')"`
2125
RecoveryWindow uint32 `long:"recoverywindow" description:"The number of keys to scan per internal/external branch. The output will consist of double this amount of keys. (default 2500)"`
2226
RescanFrom uint32 `long:"rescanfrom" description:"The block number to rescan from. Will be set automatically from the wallet birthday if the lnd 24 word aezeed is entered. (default 500000)"`
2327
}
@@ -56,12 +60,39 @@ func (c *genImportScriptCommand) Execute(_ []string) error {
5660
if c.RescanFrom == 0 {
5761
c.RescanFrom = defaultRescanFrom
5862
}
63+
if c.DerivationPath == "" {
64+
c.DerivationPath = defaultDerivationPath
65+
}
66+
67+
// Process derivation path
68+
levels := strings.Split(c.DerivationPath, "/")
69+
if len(levels) == 0 || levels[0] != "m" {
70+
return fmt.Errorf("error reading derivationpath: path \"%s\" not in "+
71+
"correct format, e.g. \"m/purpose'/coin_type'/account'\"", c.DerivationPath)
72+
}
73+
levels = levels[1:] // removes masterseed purposed "m"
74+
75+
derivationPath := make([]uint32, len(levels))
76+
for i := range levels {
77+
unHardened := strings.TrimSuffix(levels[i], "'")
78+
d, err := strconv.Atoi(unHardened)
79+
if err != nil {
80+
return fmt.Errorf("error reading derivationpath: <%s> is not a valid "+
81+
"derivation", unHardened)
82+
}
83+
84+
if levels[i] == unHardened {
85+
derivationPath[i] = uint32(d)
86+
} else {
87+
derivationPath[i] = lnd.HardenedKeyStart + uint32(d)
88+
}
89+
}
5990

6091
fmt.Printf("# Wallet dump created by chantools on %s\n",
6192
time.Now().UTC())
6293

6394
// Determine the format.
64-
var printFn func(*hdkeychain.ExtendedKey, uint32, uint32) error
95+
var printFn func(*hdkeychain.ExtendedKey, string, uint32, uint32) error
6596
switch c.Format {
6697
default:
6798
fallthrough
@@ -82,37 +113,27 @@ func (c *genImportScriptCommand) Execute(_ []string) error {
82113
"importwallet command of bitcoin core.")
83114
}
84115

85-
// External branch first (m/84'/<coinType>'/0'/0/x).
116+
// External branch first (<DerivationPath>/0/i).
86117
for i := uint32(0); i < c.RecoveryWindow; i++ {
87-
derivedKey, err := lnd.DeriveChildren(extendedKey, []uint32{
88-
lnd.HardenedKeyStart + uint32(84),
89-
lnd.HardenedKeyStart + chainParams.HDCoinType,
90-
lnd.HardenedKeyStart + uint32(0),
91-
0,
92-
i,
93-
})
118+
path := append(derivationPath, []uint32{0, i}...)
119+
derivedKey, err := lnd.DeriveChildren(extendedKey, path)
94120
if err != nil {
95121
return err
96122
}
97-
err = printFn(derivedKey, 0, i)
123+
err = printFn(derivedKey, c.DerivationPath, 0, i)
98124
if err != nil {
99125
return err
100126
}
101127
}
102128

103-
// Now the internal branch (m/84'/<coinType>'/0'/1/x).
129+
// Now the internal branch (<DerivationPath>/1/i).
104130
for i := uint32(0); i < c.RecoveryWindow; i++ {
105-
derivedKey, err := lnd.DeriveChildren(extendedKey, []uint32{
106-
lnd.HardenedKeyStart + uint32(84),
107-
lnd.HardenedKeyStart + chainParams.HDCoinType,
108-
lnd.HardenedKeyStart + uint32(0),
109-
1,
110-
i,
111-
})
131+
path := append(derivationPath, []uint32{1, i}...)
132+
derivedKey, err := lnd.DeriveChildren(extendedKey, path)
112133
if err != nil {
113134
return err
114135
}
115-
err = printFn(derivedKey, 1, i)
136+
err = printFn(derivedKey, c.DerivationPath, 1, i)
116137
if err != nil {
117138
return err
118139
}
@@ -122,8 +143,8 @@ func (c *genImportScriptCommand) Execute(_ []string) error {
122143
return nil
123144
}
124145

125-
func printBitcoinCli(hdKey *hdkeychain.ExtendedKey, branch,
126-
index uint32) error {
146+
func printBitcoinCli(hdKey *hdkeychain.ExtendedKey, path string,
147+
branch, index uint32) error {
127148

128149
privKey, err := hdKey.ECPrivKey()
129150
if err != nil {
@@ -134,28 +155,28 @@ func printBitcoinCli(hdKey *hdkeychain.ExtendedKey, branch,
134155
if err != nil {
135156
return fmt.Errorf("could not encode WIF: %v", err)
136157
}
137-
fmt.Printf("bitcoin-cli importprivkey %s \"m/84'/%d'/0'/%d/%d/"+
138-
"\" false\n", wif.String(), chainParams.HDCoinType, branch,
158+
fmt.Printf("bitcoin-cli importprivkey %s \"%s/%d/%d/"+
159+
"\" false\n", wif.String(), path, branch,
139160
index)
140161
return nil
141162
}
142163

143-
func printBitcoinCliWatchOnly(hdKey *hdkeychain.ExtendedKey, branch,
144-
index uint32) error {
164+
func printBitcoinCliWatchOnly(hdKey *hdkeychain.ExtendedKey, path string,
165+
branch, index uint32) error {
145166

146167
pubKey, err := hdKey.ECPubKey()
147168
if err != nil {
148169
return fmt.Errorf("could not derive private key: %v",
149170
err)
150171
}
151-
fmt.Printf("bitcoin-cli importpubkey %x \"m/84'/%d'/0'/%d/%d/"+
172+
fmt.Printf("bitcoin-cli importpubkey %x \"%s/%d/%d/"+
152173
"\" false\n", pubKey.SerializeCompressed(),
153-
chainParams.HDCoinType, branch, index)
174+
path, branch, index)
154175
return nil
155176
}
156177

157-
func printBitcoinImportWallet(hdKey *hdkeychain.ExtendedKey, branch,
158-
index uint32) error {
178+
func printBitcoinImportWallet(hdKey *hdkeychain.ExtendedKey, path string,
179+
branch, index uint32) error {
159180

160181
privKey, err := hdKey.ECPrivKey()
161182
if err != nil {
@@ -179,9 +200,9 @@ func printBitcoinImportWallet(hdKey *hdkeychain.ExtendedKey, branch,
179200
}
180201
addr := addrPubkey.AddressPubKeyHash()
181202

182-
fmt.Printf("%s 1970-01-01T00:00:01Z label=m/84'/%d'/0'/%d/%d/ "+
183-
"# addr=%s", wif.String(), chainParams.HDCoinType, branch,
184-
index, addr.EncodeAddress(),
203+
fmt.Printf("%s 1970-01-01T00:00:01Z label=%s/%d/%d/ "+
204+
"# addr=%s\n", wif.String(), path, branch, index,
205+
addr.EncodeAddress(),
185206
)
186207
return nil
187208
}

0 commit comments

Comments
 (0)