mirror of https://github.com/guggero/chantools
Add genimportscript command
parent
aac8a2a728
commit
305a9dab94
@ -0,0 +1,159 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/btcsuite/btcd/chaincfg"
|
||||
"github.com/btcsuite/btcutil"
|
||||
"github.com/btcsuite/btcutil/hdkeychain"
|
||||
"github.com/guggero/chantools/btc"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultRecoveryWindow = 2500
|
||||
defaultRescanFrom = 500000
|
||||
)
|
||||
|
||||
type genImportScriptCommand struct {
|
||||
RootKey string `long:"rootkey" description:"BIP32 HD root key to use. Leave empty to prompt for lnd 24 word aezeed."`
|
||||
Format string `long:"format" description:"The format of the generated import script. Currently supported are: bitcoin-cli, bitcoin-cli-watchonly."`
|
||||
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)"`
|
||||
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)"`
|
||||
}
|
||||
|
||||
func (c *genImportScriptCommand) Execute(_ []string) error {
|
||||
var (
|
||||
extendedKey *hdkeychain.ExtendedKey
|
||||
err error
|
||||
birthday time.Time
|
||||
)
|
||||
|
||||
// Check that root key is valid or fall back to console input.
|
||||
switch {
|
||||
case c.RootKey != "":
|
||||
extendedKey, err = hdkeychain.NewKeyFromString(c.RootKey)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error reading root key: %v", err)
|
||||
}
|
||||
|
||||
default:
|
||||
extendedKey, birthday, err = rootKeyFromConsole()
|
||||
if err != nil {
|
||||
return fmt.Errorf("error reading root key: %v", err)
|
||||
}
|
||||
// The btcwallet gives the birthday a slack of 48 hours, let's
|
||||
// do the same.
|
||||
c.RescanFrom = seedBirthdayToBlock(birthday.Add(-48 * time.Hour))
|
||||
}
|
||||
|
||||
// Set default values.
|
||||
if c.RecoveryWindow == 0 {
|
||||
c.RecoveryWindow = defaultRecoveryWindow
|
||||
}
|
||||
if c.RescanFrom == 0 {
|
||||
c.RescanFrom = defaultRescanFrom
|
||||
}
|
||||
|
||||
// Determine the format.
|
||||
printFn := printBitcoinCli
|
||||
if c.Format == "bitcoin-cli-watchonly" {
|
||||
printFn = printBitcoinCliWatchOnly
|
||||
}
|
||||
|
||||
fmt.Println("# Paste the following lines into a command line window.")
|
||||
|
||||
// External branch first (m/84'/<coinType>'/0'/0/x).
|
||||
for i := uint32(0); i < c.RecoveryWindow; i++ {
|
||||
derivedKey, err := btc.DeriveChildren(extendedKey, []uint32{
|
||||
btc.HardenedKeyStart + uint32(84),
|
||||
btc.HardenedKeyStart + chainParams.HDCoinType,
|
||||
btc.HardenedKeyStart + uint32(0),
|
||||
0,
|
||||
i,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = printFn(derivedKey, 0, i)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Now the internal branch (m/84'/<coinType>'/0'/1/x).
|
||||
for i := uint32(0); i < c.RecoveryWindow; i++ {
|
||||
derivedKey, err := btc.DeriveChildren(extendedKey, []uint32{
|
||||
btc.HardenedKeyStart + uint32(84),
|
||||
btc.HardenedKeyStart + chainParams.HDCoinType,
|
||||
btc.HardenedKeyStart + uint32(0),
|
||||
1,
|
||||
i,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = printFn(derivedKey, 1, i)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Printf("bitcoin-cli rescanblockchain %d\n", c.RescanFrom)
|
||||
return nil
|
||||
}
|
||||
|
||||
func printBitcoinCli(derivedKey *hdkeychain.ExtendedKey, branch,
|
||||
index uint32) error {
|
||||
|
||||
privKey, err := derivedKey.ECPrivKey()
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not derive private key: %v",
|
||||
err)
|
||||
}
|
||||
wif, err := btcutil.NewWIF(privKey, chainParams, true)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not encode WIF: %v", err)
|
||||
}
|
||||
|
||||
fmt.Printf("bitcoin-cli importprivkey %s \"m/84'/%d'/0'/%d/%d/"+
|
||||
"\" false\n", wif.String(), chainParams.HDCoinType, branch,
|
||||
index)
|
||||
return nil
|
||||
}
|
||||
|
||||
func printBitcoinCliWatchOnly(derivedKey *hdkeychain.ExtendedKey, branch,
|
||||
index uint32) error {
|
||||
|
||||
pubKey, err := derivedKey.ECPubKey()
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not derive private key: %v",
|
||||
err)
|
||||
}
|
||||
|
||||
fmt.Printf("bitcoin-cli importpubkey %x \"m/84'/%d'/0'/%d/%d/"+
|
||||
"\" false\n", pubKey.SerializeCompressed(),
|
||||
chainParams.HDCoinType, branch, index)
|
||||
return nil
|
||||
}
|
||||
|
||||
func seedBirthdayToBlock(birthdayTimestamp time.Time) uint32 {
|
||||
var genesisTimestamp time.Time
|
||||
switch chainParams.Name {
|
||||
case "mainnet":
|
||||
genesisTimestamp =
|
||||
chaincfg.MainNetParams.GenesisBlock.Header.Timestamp
|
||||
|
||||
case "testnet":
|
||||
genesisTimestamp =
|
||||
chaincfg.TestNet3Params.GenesisBlock.Header.Timestamp
|
||||
|
||||
default:
|
||||
panic(fmt.Errorf("unimplemented network %v", chainParams.Name))
|
||||
}
|
||||
|
||||
// With the timestamps retrieved, we can estimate a block height by
|
||||
// taking the difference between them and dividing by the average block
|
||||
// time (10 minutes).
|
||||
return uint32(birthdayTimestamp.Sub(genesisTimestamp).Seconds() / 600)
|
||||
}
|
Loading…
Reference in New Issue