multi: add bitcoin-descriptors to genimportscript

pull/66/head
Oliver Gugger 1 year ago
parent f35a469949
commit 9fadec97a7
No known key found for this signature in database
GPG Key ID: 8E4256593F177720

@ -17,6 +17,7 @@ const (
FormatCli = "bitcoin-cli"
FormatCliWatchOnly = "bitcoin-cli-watchonly"
FormatImportwallet = "bitcoin-importwallet"
FormatDescriptors = "bitcoin-descriptors"
FormatElectrum = "electrum"
)
@ -40,6 +41,9 @@ func ParseFormat(format string) (KeyExporter, error) {
case FormatImportwallet:
return &ImportWallet{}, nil
case FormatDescriptors:
return &Descriptors{}, nil
case FormatElectrum:
return &Electrum{}, nil
@ -179,19 +183,14 @@ func (c *CliWatchOnly) Format(hdKey *hdkeychain.ExtendedKey,
if err != nil {
return "", fmt.Errorf("could not create address: %w", err)
}
addrP2TR, err := lnd.P2TRAddr(pubKey, params)
if err != nil {
return "", fmt.Errorf("could not create address: %w", err)
}
flags := ""
if params.Net == wire.TestNet || params.Net == wire.TestNet3 {
flags = " -testnet"
}
return fmt.Sprintf("bitcoin-cli%s importpubkey %x \"%s/%d/%d/\" "+
"false # addr=%s,%s,%s,%s", flags, pubKey.SerializeCompressed(),
path, branch, index, addrP2PKH, addrP2WKH, addrNP2WKH,
addrP2TR), nil
"false # addr=%s,%s,%s", flags, pubKey.SerializeCompressed(),
path, branch, index, addrP2PKH, addrP2WKH, addrNP2WKH), nil
}
func (c *CliWatchOnly) Trailer(birthdayBlock uint32) string {
@ -276,3 +275,40 @@ func (p *Electrum) Format(hdKey *hdkeychain.ExtendedKey,
func (p *Electrum) Trailer(_ uint32) string {
return ""
}
type Descriptors struct{}
func (d *Descriptors) Header() string {
return "# Paste the following lines into a command line window."
}
func (d *Descriptors) Format(hdKey *hdkeychain.ExtendedKey,
params *chaincfg.Params, path string, branch, index uint32) (string,
error) {
privKey, err := hdKey.ECPrivKey()
if err != nil {
return "", fmt.Errorf("could not derive private key: %w", err)
}
wif, err := btcutil.NewWIF(privKey, params, true)
if err != nil {
return "", fmt.Errorf("could not encode WIF: %w", err)
}
np2wkh := makeDescriptor("sh(wpkh(%s))", wif.String())
p2wkh := makeDescriptor("wpkh(%s)", wif.String())
p2tr := makeDescriptor("tr(%s)", wif.String())
return fmt.Sprintf("bitcoin-cli importdescriptors '[%s,%s,%s]'",
np2wkh, p2wkh, p2tr), nil
}
func (d *Descriptors) Trailer(birthdayBlock uint32) string {
return fmt.Sprintf("bitcoin-cli rescanblockchain %d\n", birthdayBlock)
}
func makeDescriptor(format, wif string) string {
descriptor := fmt.Sprintf(format, wif)
return fmt.Sprintf("{\"desc\":\"%s\",\"timestamp\":\"now\"}",
DescriptorSumCreate(descriptor))
}

@ -0,0 +1,83 @@
package btc
import (
"strings"
)
var (
inputCharset = "0123456789()[],'/*abcdefgh@:$%{}IJKLMNOPQRSTUVWXYZ" +
"&+-.;<=>?!^_|~ijklmnopqrstuvwxyzABCDEFGH`#\\\"\\\\ "
checksumCharset = "qpzry9x8gf2tvdw0s3jn54khce6mua7l"
generator = []uint64{
0xf5dee51989, 0xa9fdca3312, 0x1bab10e32d, 0x3706b1677a,
0x644d626ffd,
}
)
func descriptorSumPolymod(symbols []uint64) uint64 {
chk := uint64(1)
for _, value := range symbols {
top := chk >> 35
chk = (chk&0x7ffffffff)<<5 ^ value
for i := 0; i < 5; i++ {
if (top>>i)&1 != 0 {
chk ^= generator[i]
}
}
}
return chk
}
func descriptorSumExpand(s string) []uint64 {
groups := []uint64{}
symbols := []uint64{}
for _, c := range s {
v := strings.IndexRune(inputCharset, c)
if v < 0 {
return nil
}
symbols = append(symbols, uint64(v&31))
groups = append(groups, uint64(v>>5))
if len(groups) == 3 {
symbols = append(
symbols, groups[0]*9+groups[1]*3+groups[2],
)
groups = []uint64{}
}
}
if len(groups) == 1 {
symbols = append(symbols, groups[0])
} else if len(groups) == 2 {
symbols = append(symbols, groups[0]*3+groups[1])
}
return symbols
}
func DescriptorSumCreate(s string) string {
symbols := append(descriptorSumExpand(s), 0, 0, 0, 0, 0, 0, 0, 0)
checksum := descriptorSumPolymod(symbols) ^ 1
builder := strings.Builder{}
for i := 0; i < 8; i++ {
builder.WriteByte(checksumCharset[(checksum>>(5*(7-i)))&31])
}
return s + "#" + builder.String()
}
func DescriptorSumCheck(s string, require bool) bool {
if !strings.Contains(s, "#") {
return !require
}
if s[len(s)-9] != '#' {
return false
}
for _, c := range s[len(s)-8:] {
if !strings.ContainsRune(checksumCharset, c) {
return false
}
}
symbols := append(
descriptorSumExpand(s[:len(s)-9]),
uint64(strings.Index(checksumCharset, s[len(s)-8:])),
)
return descriptorSumPolymod(symbols) == 1
}

@ -0,0 +1,27 @@
package btc
import (
"testing"
"github.com/stretchr/testify/require"
)
var testCases = []struct {
descriptor string
expectedSum string
}{{
descriptor: "addr(mkmZxiEcEd8ZqjQWVZuC6so5dFMKEFpN2j)",
expectedSum: "#02wpgw69",
}, {
descriptor: "tr(cRhCT5vC5NdnSrQ2Jrah6NPCcth41uT8DWFmA6uD8R4x2ufucnYX)",
expectedSum: "#gwfmkgga",
}}
func TestDescriptorSum(t *testing.T) {
for _, tc := range testCases {
sum := DescriptorSumCreate(tc.descriptor)
require.Equal(t, tc.descriptor+tc.expectedSum, sum)
DescriptorSumCheck(sum, true)
}
}

@ -41,7 +41,9 @@ imported into other software like bitcoind.
The following script formats are currently supported:
* bitcoin-cli: Creates a list of bitcoin-cli importprivkey commands that can
be used in combination with a bitcoind full node to recover the funds locked
in those private keys.
in those private keys. NOTE: This will only work for legacy wallets and only
for legacy, p2sh-segwit and bech32 (p2pkh, np2wkh and p2wkh) addresses. Use
bitcoin-descriptors and a descriptor wallet for bech32m (p2tr).
* bitcoin-cli-watchonly: Does the same as bitcoin-cli but with the
bitcoin-cli importpubkey command. That means, only the public keys are
imported into bitcoind to watch the UTXOs of those keys. The funds cannot be
@ -49,7 +51,12 @@ The following script formats are currently supported:
* bitcoin-importwallet: Creates a text output that is compatible with
bitcoind's importwallet command.
* electrum: Creates a text output that contains one private key per line with
the address type as the prefix, the way Electrum expects them.`,
the address type as the prefix, the way Electrum expects them.
* bitcoin-descriptors: Create a list of bitcoin-cli importdescriptors commands
that can be used in combination with a bitcoind full node that has a
descriptor wallet to recover the funds locked in those private keys.
NOTE: This will only work for descriptor wallets and only for
p2sh-segwit, bech32 and bech32m (np2wkh, p2wkh and p2tr) addresses.`,
Example: `chantools genimportscript --format bitcoin-cli \
--recoverywindow 5000`,
RunE: cc.Execute,
@ -58,7 +65,8 @@ The following script formats are currently supported:
&cc.Format, "format", "bitcoin-importwallet", "format of the "+
"generated import script; currently supported are: "+
"bitcoin-importwallet, bitcoin-cli, "+
"bitcoin-cli-watchonly and electrum",
"bitcoin-cli-watchonly, bitcoin-descriptors and "+
"electrum",
)
cc.cmd.Flags().BoolVar(
&cc.LndPaths, "lndpaths", false, "use all derivation paths "+

@ -11,7 +11,9 @@ imported into other software like bitcoind.
The following script formats are currently supported:
* bitcoin-cli: Creates a list of bitcoin-cli importprivkey commands that can
be used in combination with a bitcoind full node to recover the funds locked
in those private keys.
in those private keys. NOTE: This will only work for legacy wallets and only
for legacy, p2sh-segwit and bech32 (p2pkh, np2wkh and p2wkh) addresses. Use
bitcoin-descriptors and a descriptor wallet for bech32m (p2tr).
* bitcoin-cli-watchonly: Does the same as bitcoin-cli but with the
bitcoin-cli importpubkey command. That means, only the public keys are
imported into bitcoind to watch the UTXOs of those keys. The funds cannot be
@ -20,6 +22,11 @@ The following script formats are currently supported:
bitcoind's importwallet command.
* electrum: Creates a text output that contains one private key per line with
the address type as the prefix, the way Electrum expects them.
* bitcoin-descriptors: Create a list of bitcoin-cli importdescriptors commands
that can be used in combination with a bitcoind full node that has a
descriptor wallet to recover the funds locked in those private keys.
NOTE: This will only work for descriptor wallets and only for
p2sh-segwit, bech32 and bech32m (np2wkh, p2wkh and p2tr) addresses.
```
chantools genimportscript [flags]
@ -37,7 +44,7 @@ chantools genimportscript --format bitcoin-cli \
```
--bip39 read a classic BIP39 seed and passphrase from the terminal instead of asking for lnd seed format or providing the --rootkey flag
--derivationpath string use one specific derivation path; specify the first levels of the derivation path before any internal/external branch; Cannot be used in conjunction with --lndpaths
--format string format of the generated import script; currently supported are: bitcoin-importwallet, bitcoin-cli, bitcoin-cli-watchonly and electrum (default "bitcoin-importwallet")
--format string format of the generated import script; currently supported are: bitcoin-importwallet, bitcoin-cli, bitcoin-cli-watchonly, bitcoin-descriptors and electrum (default "bitcoin-importwallet")
-h, --help help for genimportscript
--lndpaths use all derivation paths that lnd used; results in a large number of results; cannot be used in conjunction with --derivationpath
--recoverywindow uint32 number of keys to scan per internal/external branch; output will consist of double this amount of keys (default 2500)

Loading…
Cancel
Save