Handle taproot xpub descriptors

This commit is contained in:
Martin Boehm 2021-10-10 22:32:53 +02:00
parent ba75e60950
commit c4128e5c9c
8 changed files with 201 additions and 53 deletions

View File

@ -24,6 +24,8 @@ const xpubCacheExpirationSeconds = 3600
var cachedXpubs map[string]xpubData
var cachedXpubsMux sync.Mutex
const xpubLogPrefix = 30
type xpubTxid struct {
txid string
height uint32
@ -547,7 +549,7 @@ func (w *Worker) GetXpubAddress(xpub string, page int, txsOnPage int, option Acc
Tokens: tokens,
XPubAddresses: xpubAddresses,
}
glog.Info("GetXpubAddress ", xpub[:16], ", cache ", inCache, ", ", len(data.addresses)+len(data.changeAddresses), " addresses, ", txCount, " txs, ", time.Since(start))
glog.Info("GetXpubAddress ", xpub[:xpubLogPrefix], ", cache ", inCache, ", ", len(data.addresses)+len(data.changeAddresses), " addresses, ", txCount, " txs, ", time.Since(start))
return &addr, nil
}
@ -588,7 +590,7 @@ func (w *Worker) GetXpubUtxo(xpub string, onlyConfirmed bool, gap int) (Utxos, e
}
}
sort.Stable(r)
glog.Info("GetXpubUtxo ", xpub[:16], ", cache ", inCache, ", ", len(r), " utxos, ", time.Since(start))
glog.Info("GetXpubUtxo ", xpub[:xpubLogPrefix], ", cache ", inCache, ", ", len(r), " utxos, ", time.Since(start))
return r, nil
}
@ -635,6 +637,6 @@ func (w *Worker) GetXpubBalanceHistory(xpub string, fromTimestamp, toTimestamp i
if err != nil {
return nil, err
}
glog.Info("GetUtxoBalanceHistory ", xpub[:16], ", cache ", inCache, ", blocks ", fromHeight, "-", toHeight, ", count ", len(bha), ", ", time.Since(start))
glog.Info("GetUtxoBalanceHistory ", xpub[:xpubLogPrefix], ", cache ", inCache, ", blocks ", fromHeight, "-", toHeight, ", count ", len(bha), ", ", time.Since(start))
return bha, nil
}

View File

@ -2,15 +2,18 @@ package btc
import (
"bytes"
"crypto/sha256"
"encoding/binary"
"encoding/hex"
"math/big"
"regexp"
"strconv"
"unicode/utf8"
vlq "github.com/bsm/go-vlq"
"github.com/juju/errors"
"github.com/martinboehm/btcd/blockchain"
"github.com/martinboehm/btcd/btcec"
"github.com/martinboehm/btcd/wire"
"github.com/martinboehm/btcutil"
"github.com/martinboehm/btcutil/chaincfg"
@ -315,23 +318,70 @@ func (p *BitcoinLikeParser) MinimumCoinbaseConfirmations() int {
return p.minimumCoinbaseConfirmations
}
func (p *BitcoinLikeParser) addrDescFromExtKey(extKey *hdkeychain.ExtendedKey) (bchain.AddressDescriptor, error) {
var tapTweakTagHash = sha256.Sum256([]byte("TapTweak"))
func tapTweakHash(msg []byte) []byte {
tagLen := len(tapTweakTagHash)
m := make([]byte, tagLen*2+len(msg))
copy(m[:tagLen], tapTweakTagHash[:])
copy(m[tagLen:tagLen*2], tapTweakTagHash[:])
copy(m[tagLen*2:], msg)
h := sha256.Sum256(m)
return h[:]
}
func (p *BitcoinLikeParser) taprootAddrFromExtKey(extKey *hdkeychain.ExtendedKey) (*btcutil.AddressWitnessTaproot, error) {
curve := btcec.S256()
t := new(big.Int)
// tweak the derived pubkey to the output pub key according to https://en.bitcoin.it/wiki/BIP_0341
// and https://github.com/bitcoin/bips/blob/master/bip-0086.mediawiki
derived_key := extKey.PubKeyBytes()[1:]
t.SetBytes(tapTweakHash(derived_key))
// Fail if t >=order of the base point
if t.Cmp(curve.N) >= 0 {
return nil, errors.New("greater than or equal to curve order")
}
// Q = point_add(lift_x(int_from_bytes(pubkey)), point_mul(G, t))
ipx, ipy, err := btcec.LiftX(derived_key)
if err != nil {
return nil, err
}
tGx, tGy := curve.ScalarBaseMult(t.Bytes())
output_pubkey, _ := curve.Add(ipx, ipy, tGx, tGy)
//
b := output_pubkey.Bytes()
// the x coordinate on the curve can be a number small enough that it does not need 32 bytes required for the output script
if len(b) < 32 {
b = make([]byte, 32)
output_pubkey.FillBytes(b)
}
return btcutil.NewAddressWitnessTaproot(b, p.Params)
}
func (p *BitcoinLikeParser) addrDescFromExtKey(extKey *hdkeychain.ExtendedKey, bip string) (bchain.AddressDescriptor, error) {
var a btcutil.Address
var err error
if extKey.Version() == p.XPubMagicSegwitP2sh {
// redeemScript <witness version: OP_0><len pubKeyHash: 20><20-byte-pubKeyHash>
pubKeyHash := btcutil.Hash160(extKey.PubKeyBytes())
redeemScript := make([]byte, len(pubKeyHash)+2)
redeemScript[0] = 0
redeemScript[1] = byte(len(pubKeyHash))
copy(redeemScript[2:], pubKeyHash)
hash := btcutil.Hash160(redeemScript)
a, err = btcutil.NewAddressScriptHashFromHash(hash, p.Params)
} else if extKey.Version() == p.XPubMagicSegwitNative {
a, err = btcutil.NewAddressWitnessPubKeyHash(btcutil.Hash160(extKey.PubKeyBytes()), p.Params)
if bip != "" {
a, err = p.taprootAddrFromExtKey(extKey)
} else {
// default to P2PKH address
a, err = extKey.Address(p.Params)
version := extKey.Version()
if version == p.XPubMagicSegwitP2sh {
// redeemScript <witness version: OP_0><len pubKeyHash: 20><20-byte-pubKeyHash>
pubKeyHash := btcutil.Hash160(extKey.PubKeyBytes())
redeemScript := make([]byte, len(pubKeyHash)+2)
redeemScript[0] = 0
redeemScript[1] = byte(len(pubKeyHash))
copy(redeemScript[2:], pubKeyHash)
hash := btcutil.Hash160(redeemScript)
a, err = btcutil.NewAddressScriptHashFromHash(hash, p.Params)
} else if version == p.XPubMagicSegwitNative {
a, err = btcutil.NewAddressWitnessPubKeyHash(btcutil.Hash160(extKey.PubKeyBytes()), p.Params)
} else {
// default to P2PKH address
a, err = extKey.Address(p.Params)
}
}
if err != nil {
return nil, err
@ -339,9 +389,19 @@ func (p *BitcoinLikeParser) addrDescFromExtKey(extKey *hdkeychain.ExtendedKey) (
return txscript.PayToAddrScript(a)
}
func parseXpub(xpub string) (string, string) {
r, _ := regexp.Compile(`tr\(\[\w+/(\d+)'/.*\](\w+)`)
match := r.FindStringSubmatch(xpub)
if len(match) == 3 {
return match[2], match[1]
}
return xpub, ""
}
// DeriveAddressDescriptors derives address descriptors from given xpub for listed indexes
func (p *BitcoinLikeParser) DeriveAddressDescriptors(xpub string, change uint32, indexes []uint32) ([]bchain.AddressDescriptor, error) {
extKey, err := hdkeychain.NewKeyFromString(xpub, p.Params.Base58CksumHasher)
parsedXpub, bip := parseXpub(xpub)
extKey, err := hdkeychain.NewKeyFromString(parsedXpub, p.Params.Base58CksumHasher)
if err != nil {
return nil, err
}
@ -355,7 +415,7 @@ func (p *BitcoinLikeParser) DeriveAddressDescriptors(xpub string, change uint32,
if err != nil {
return nil, err
}
ad[i], err = p.addrDescFromExtKey(indexExtKey)
ad[i], err = p.addrDescFromExtKey(indexExtKey, bip)
if err != nil {
return nil, err
}
@ -368,7 +428,8 @@ func (p *BitcoinLikeParser) DeriveAddressDescriptorsFromTo(xpub string, change u
if toIndex <= fromIndex {
return nil, errors.New("toIndex<=fromIndex")
}
extKey, err := hdkeychain.NewKeyFromString(xpub, p.Params.Base58CksumHasher)
parsedXpub, bip := parseXpub(xpub)
extKey, err := hdkeychain.NewKeyFromString(parsedXpub, p.Params.Base58CksumHasher)
if err != nil {
return nil, err
}
@ -382,7 +443,7 @@ func (p *BitcoinLikeParser) DeriveAddressDescriptorsFromTo(xpub string, change u
if err != nil {
return nil, err
}
ad[index-fromIndex], err = p.addrDescFromExtKey(indexExtKey)
ad[index-fromIndex], err = p.addrDescFromExtKey(indexExtKey, bip)
if err != nil {
return nil, err
}
@ -392,11 +453,12 @@ func (p *BitcoinLikeParser) DeriveAddressDescriptorsFromTo(xpub string, change u
// DerivationBasePath returns base path of xpub
func (p *BitcoinLikeParser) DerivationBasePath(xpub string) (string, error) {
extKey, err := hdkeychain.NewKeyFromString(xpub, p.Params.Base58CksumHasher)
parsedXpub, bip := parseXpub(xpub)
extKey, err := hdkeychain.NewKeyFromString(parsedXpub, p.Params.Base58CksumHasher)
if err != nil {
return "", err
}
var c, bip string
var c string
cn := extKey.ChildNum()
if cn >= 0x80000000 {
cn -= 0x80000000
@ -406,12 +468,14 @@ func (p *BitcoinLikeParser) DerivationBasePath(xpub string) (string, error) {
if extKey.Depth() != 3 {
return "unknown/" + c, nil
}
if extKey.Version() == p.XPubMagicSegwitP2sh {
bip = "49"
} else if extKey.Version() == p.XPubMagicSegwitNative {
bip = "84"
} else {
bip = "44"
if bip == "" {
if extKey.Version() == p.XPubMagicSegwitP2sh {
bip = "49"
} else if extKey.Version() == p.XPubMagicSegwitNative {
bip = "84"
} else {
bip = "44"
}
}
return "m/" + bip + "'/" + strconv.Itoa(int(p.Slip44)) + "'/" + c, nil
}

View File

@ -720,6 +720,7 @@ func TestUnpackTx(t *testing.T) {
func TestDeriveAddressDescriptors(t *testing.T) {
btcMainParser := NewBitcoinParser(GetChainParams("main"), &Configuration{XPubMagic: 76067358, XPubMagicSegwitP2sh: 77429938, XPubMagicSegwitNative: 78792518})
btcTestnetParser := NewBitcoinParser(GetChainParams("test"), &Configuration{XPubMagic: 70617039, XPubMagicSegwitP2sh: 71979618, XPubMagicSegwitNative: 73342198})
type args struct {
xpub string
change uint32
@ -732,6 +733,36 @@ func TestDeriveAddressDescriptors(t *testing.T) {
want []string
wantErr bool
}{
{
name: "m/86'/1'/0'",
args: args{
xpub: "tr([5c9e228d/86'/1'/0']tpubDC88gkaZi5HvJGxGDNLADkvtdpni3mLmx6vr2KnXmWMG8zfkBRggsxHVBkUpgcwPe2KKpkyvTJCdXHb1UHEWE64vczyyPQfHr1skBcsRedN/0/*)#4rqwxvej",
change: 0,
indexes: []uint32{0, 1, 10},
parser: btcTestnetParser,
},
want: []string{"tb1pswrqtykue8r89t9u4rprjs0gt4qzkdfuursfnvqaa3f2yql07zmq8s8a5u", "tb1p8tvmvsvhsee73rhym86wt435qrqm92psfsyhy6a3n5gw455znnpqm8wald", "tb1pqr4803xedptkvsr6ksed2m7fx780y3u8shnd0fqdupnc0w75262sl49kwz"},
},
{
name: "m/86'/0'/0'",
args: args{
xpub: "tr([5c9e228d/86'/0'/0']xpub6BgBgsespWvERF3LHQu6CnqdvfEvtMcQjYrcRzx53QJjSxarj2afYWcLteoGVky7D3UKDP9QyrLprQ3VCECoY49yfdDEHGCtMMj92pReUsQ/0/*)#d8jj22qr",
change: 0,
indexes: []uint32{0, 1},
parser: btcMainParser,
},
want: []string{"bc1p5cyxnuxmeuwuvkwfem96lqzszd02n6xdcjrs20cac6yqjjwudpxqkedrcr", "bc1p4qhjn9zdvkux4e44uhx8tc55attvtyu358kutcqkudyccelu0was9fqzwh"},
},
{
name: "m/86'/0'/0'/1",
args: args{
xpub: "tr([5c9e228d/86'/0'/0']xpub6BgBgsespWvERF3LHQu6CnqdvfEvtMcQjYrcRzx53QJjSxarj2afYWcLteoGVky7D3UKDP9QyrLprQ3VCECoY49yfdDEHGCtMMj92pReUsQ/0/*)#d8jj22qr",
change: 1,
indexes: []uint32{0},
parser: btcMainParser,
},
want: []string{"bc1p3qkhfews2uk44qtvauqyr2ttdsw7svhkl9nkm9s9c3x4ax5h60wqwruhk7"},
},
{
name: "m/44'/0'/0'",
args: args{
@ -788,7 +819,7 @@ func TestDeriveAddressDescriptors(t *testing.T) {
func TestDeriveAddressDescriptorsFromTo(t *testing.T) {
btcMainParser := NewBitcoinParser(GetChainParams("main"), &Configuration{XPubMagic: 76067358, XPubMagicSegwitP2sh: 77429938, XPubMagicSegwitNative: 78792518})
btcTestnetsParser := NewBitcoinParser(GetChainParams("test"), &Configuration{XPubMagic: 70617039, XPubMagicSegwitP2sh: 71979618, XPubMagicSegwitNative: 73342198})
btcTestnetParser := NewBitcoinParser(GetChainParams("test"), &Configuration{XPubMagic: 70617039, XPubMagicSegwitP2sh: 71979618, XPubMagicSegwitNative: 73342198})
type args struct {
xpub string
change uint32
@ -835,6 +866,17 @@ func TestDeriveAddressDescriptorsFromTo(t *testing.T) {
},
want: []string{"bc1qcr8te4kr609gcawutmrza0j4xv80jy8z306fyu"},
},
{
name: "m/86'/0'/0'",
args: args{
xpub: "tr([5c9e228d/86'/0'/0']xpub6BgBgsespWvERF3LHQu6CnqdvfEvtMcQjYrcRzx53QJjSxarj2afYWcLteoGVky7D3UKDP9QyrLprQ3VCECoY49yfdDEHGCtMMj92pReUsQ/0/*)#d8jj22qr",
change: 0,
fromIndex: 0,
toIndex: 1,
parser: btcMainParser,
},
want: []string{"bc1p5cyxnuxmeuwuvkwfem96lqzszd02n6xdcjrs20cac6yqjjwudpxqkedrcr"},
},
{
name: "m/49'/1'/0'",
args: args{
@ -842,7 +884,7 @@ func TestDeriveAddressDescriptorsFromTo(t *testing.T) {
change: 0,
fromIndex: 0,
toIndex: 10,
parser: btcTestnetsParser,
parser: btcTestnetParser,
},
want: []string{"2N4Q5FhU2497BryFfUgbqkAJE87aKHUhXMp", "2Mt7P2BAfE922zmfXrdcYTLyR7GUvbwSEns", "2N6aUMgQk8y1zvoq6FeWFyotyj75WY9BGsu", "2NA7tbZWM9BcRwBuebKSQe2xbhhF1paJwBM", "2N8RZMzvrUUnpLmvACX9ysmJ2MX3GK5jcQM", "2MvUUSiQZDSqyeSdofKX9KrSCio1nANPDTe", "2NBXaWu1HazjoUVgrXgcKNoBLhtkkD9Gmet", "2N791Ttf89tMVw2maj86E1Y3VgxD9Mc7PU7", "2NCJmwEq8GJm8t8GWWyBXAfpw7F2qZEVP5Y", "2NEgW71hWKer2XCSA8ZCC2VnWpB77L6bk68"},
},
@ -893,7 +935,7 @@ func BenchmarkDeriveAddressDescriptorsFromToZpub(b *testing.B) {
func TestBitcoinParser_DerivationBasePath(t *testing.T) {
btcMainParser := NewBitcoinParser(GetChainParams("main"), &Configuration{XPubMagic: 76067358, XPubMagicSegwitP2sh: 77429938, XPubMagicSegwitNative: 78792518, Slip44: 0})
btcTestnetsParser := NewBitcoinParser(GetChainParams("test"), &Configuration{XPubMagic: 70617039, XPubMagicSegwitP2sh: 71979618, XPubMagicSegwitNative: 73342198, Slip44: 1})
btcTestnetParser := NewBitcoinParser(GetChainParams("test"), &Configuration{XPubMagic: 70617039, XPubMagicSegwitP2sh: 71979618, XPubMagicSegwitNative: 73342198, Slip44: 1})
zecMainParser := NewBitcoinParser(GetChainParams("main"), &Configuration{XPubMagic: 76067358, Slip44: 133})
type args struct {
xpub string
@ -905,6 +947,22 @@ func TestBitcoinParser_DerivationBasePath(t *testing.T) {
want string
wantErr bool
}{
{
name: "m/86'/1'/0'",
args: args{
xpub: "tr([5c9e228d/86'/1'/0']tpubDC88gkaZi5HvJGxGDNLADkvtdpni3mLmx6vr2KnXmWMG8zfkBRggsxHVBkUpgcwPe2KKpkyvTJCdXHb1UHEWE64vczyyPQfHr1skBcsRedN/0/*)#4rqwxvej",
parser: btcTestnetParser,
},
want: "m/86'/1'/0'",
},
{
name: "m/86'/0'/0'",
args: args{
xpub: "tr([5c9e228d/86'/0'/0']xpub6BgBgsespWvERF3LHQu6CnqdvfEvtMcQjYrcRzx53QJjSxarj2afYWcLteoGVky7D3UKDP9QyrLprQ3VCECoY49yfdDEHGCtMMj92pReUsQ/0/*)#d8jj22qr",
parser: btcMainParser,
},
want: "m/86'/0'/0'",
},
{
name: "m/84'/0'/0'",
args: args{
@ -933,7 +991,7 @@ func TestBitcoinParser_DerivationBasePath(t *testing.T) {
name: "m/49'/1'/0'",
args: args{
xpub: "upub5DR1Mg5nykixzYjFXWW5GghAU7dDqoPVJ2jrqFbL8sJ7Hs7jn69MP7KBnnmxn88GeZtnH8PRKV9w5MMSFX8AdEAoXY8Qd8BJPoXtpMeHMxJ",
parser: btcTestnetsParser,
parser: btcTestnetParser,
},
want: "m/49'/1'/0'",
},

10
go.mod
View File

@ -5,7 +5,6 @@ go 1.17
require (
github.com/Groestlcoin/go-groestl-hash v0.0.0-20181012171753-790653ac190c // indirect
github.com/bsm/go-vlq v0.0.0-20150828105119-ec6e8d4f5f4e
github.com/btcsuite/btcd v0.21.0-beta // indirect
github.com/dchest/blake256 v1.0.0 // indirect
github.com/deckarep/golang-set v1.7.1
github.com/decred/dcrd/chaincfg/chainhash v1.0.2
@ -28,8 +27,8 @@ require (
github.com/juju/loggo v0.0.0-20190526231331-6e530bcce5d8 // indirect
github.com/juju/testing v0.0.0-20191001232224-ce9dec17d28b // indirect
github.com/martinboehm/bchutil v0.0.0-20190104112650-6373f11b6efe
github.com/martinboehm/btcd v0.0.0-20200313230603-83af86142d93
github.com/martinboehm/btcutil v0.0.0-20210922221517-e83b0c752949
github.com/martinboehm/btcd v0.0.0-20211010165247-d1f65b0f30fa
github.com/martinboehm/btcutil v0.0.0-20211010173611-6ef1889c1819
github.com/martinboehm/golang-socketio v0.0.0-20180414165752-f60b0a8befde
github.com/mr-tron/base58 v1.2.0 // indirect
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
@ -45,6 +44,7 @@ require (
github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6 // indirect
github.com/agl/ed25519 v0.0.0-20170116200512-5312a6153412 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/btcsuite/btcd v0.20.1-beta // indirect
github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f // indirect
github.com/cespare/xxhash/v2 v2.1.1 // indirect
github.com/dchest/siphash v1.2.1 // indirect
@ -68,3 +68,7 @@ require (
google.golang.org/protobuf v1.23.0 // indirect
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect
)
// replace github.com/martinboehm/btcutil => ../btcutil
// replace github.com/martinboehm/btcd => ../btcd

16
go.sum
View File

@ -85,13 +85,11 @@ github.com/bmizerany/pat v0.0.0-20170815010413-6226ea591a40/go.mod h1:8rLXio+Wji
github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps=
github.com/bsm/go-vlq v0.0.0-20150828105119-ec6e8d4f5f4e h1:D64GF/Xr5zSUnM3q1Jylzo4sK7szhP/ON+nb2DB5XJA=
github.com/bsm/go-vlq v0.0.0-20150828105119-ec6e8d4f5f4e/go.mod h1:N+BjUcTjSxc2mtRGSCPsat1kze3CUtvJN3/jTXlp29k=
github.com/btcsuite/btcd v0.20.1-beta h1:Ik4hyJqN8Jfyv3S4AGBOmyouMsYE3EdYODkMbQjwPGw=
github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ=
github.com/btcsuite/btcd v0.21.0-beta h1:At9hIZdJW0s9E/fAz28nrz6AmcNlSVucCH796ZteX1M=
github.com/btcsuite/btcd v0.21.0-beta/go.mod h1:ZSWyehm27aAuS9bvkATT+Xte3hjHZ+MRgMY/8NJ7K94=
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=
github.com/btcsuite/btcutil v1.0.2/go.mod h1:j9HUFwoQRsZL3V4n+qG+CUnEGHOarIxfC3Le2Yhbcts=
github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg=
github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY=
github.com/btcsuite/goleveldb v1.0.0 h1:Tvd0BfvqX9o823q1j2UZ/epQo09eJh6dTcRp79ilIN4=
@ -403,15 +401,12 @@ github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN
github.com/martinboehm/bchutil v0.0.0-20190104112650-6373f11b6efe h1:khZWpHuxJNh2EGzBbaS6EQ2d6KxgK31WeG0TnlTMUD4=
github.com/martinboehm/bchutil v0.0.0-20190104112650-6373f11b6efe/go.mod h1:0hw4tpGU+9slqN/DrevhjTMb0iR9esxzpCdx8I6/UzU=
github.com/martinboehm/btcd v0.0.0-20190104121910-8e7c0427fee5/go.mod h1:rKQj/jGwFruYjpM6vN+syReFoR0DsLQaajhyH/5mwUE=
github.com/martinboehm/btcd v0.0.0-20200313230603-83af86142d93 h1:SBXxF9UMEPAswVhAt3Y275Lx59Do8C/rpAmg6k73HYY=
github.com/martinboehm/btcd v0.0.0-20200313230603-83af86142d93/go.mod h1:rKQj/jGwFruYjpM6vN+syReFoR0DsLQaajhyH/5mwUE=
github.com/martinboehm/btcd v0.0.0-20211010165247-d1f65b0f30fa h1:n8hCPoGumR6jNmNTMAo/VqDOw1yxUf0UCXJVZwf+JLQ=
github.com/martinboehm/btcd v0.0.0-20211010165247-d1f65b0f30fa/go.mod h1:YGXD0z/xtFXFF5jFp1GaVnrKRlEADn4pD47Zu4xaLg0=
github.com/martinboehm/btcutil v0.0.0-20180706230648-ab6388e0c60a/go.mod h1:NIviPmxe43yBgIB4HGB4w4kv9/s5kaDa/pi+wZAAxQo=
github.com/martinboehm/btcutil v0.0.0-20210914231321-8ece5dcd9f5f h1:zDEDlafs4y1CjDqcowXFzcttj/wt6N2wV9U2NihjgcU=
github.com/martinboehm/btcutil v0.0.0-20210914231321-8ece5dcd9f5f/go.mod h1:8iJaVY/VHW6lnojpTXf5X4gF2dx81Xtj2R6lJp2colA=
github.com/martinboehm/btcutil v0.0.0-20210922133746-0042eb304b5b h1:xm/0cKQ6PffWGXfPyfanJaZ7skZo/6uT6pb+mtVEU1o=
github.com/martinboehm/btcutil v0.0.0-20210922133746-0042eb304b5b/go.mod h1:8iJaVY/VHW6lnojpTXf5X4gF2dx81Xtj2R6lJp2colA=
github.com/martinboehm/btcutil v0.0.0-20210922221517-e83b0c752949 h1:GdmOPmno0QNl9jVMJZOTj7ekgGA9zRG6gn1X9jutrkU=
github.com/martinboehm/btcutil v0.0.0-20210922221517-e83b0c752949/go.mod h1:8iJaVY/VHW6lnojpTXf5X4gF2dx81Xtj2R6lJp2colA=
github.com/martinboehm/btcutil v0.0.0-20211010173611-6ef1889c1819 h1:ra2UymMEDhR0CVxqz/0minCNXO8YMeZwxdnnFDpWVJ0=
github.com/martinboehm/btcutil v0.0.0-20211010173611-6ef1889c1819/go.mod h1:/Z9FhVDXTih0kZExhK2hRvM+z68XkmbqZhFDU3bU1jY=
github.com/martinboehm/golang-socketio v0.0.0-20180414165752-f60b0a8befde h1:Tz7WkXgQjeQVymqSQkEapbe/ZuzKCvb6GANFHnl0uAE=
github.com/martinboehm/golang-socketio v0.0.0-20180414165752-f60b0a8befde/go.mod h1:p35TWcm7GkAwvPcUCEq4H+yTm0gA8Aq7UvGnbK6olQk=
github.com/matryer/moq v0.0.0-20190312154309-6cfb0558e1bd/go.mod h1:9ELz6aaclSIGnZBoaSLZ3NAl1VTufbOrXBPvtcy6WiQ=
@ -635,7 +630,6 @@ golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190909091759-094676da4a83/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=

View File

@ -8,6 +8,7 @@ import (
"io/ioutil"
"math/big"
"net/http"
"net/url"
"path/filepath"
"reflect"
"regexp"
@ -711,9 +712,9 @@ func (s *PublicServer) explorerAddress(w http.ResponseWriter, r *http.Request) (
func (s *PublicServer) explorerXpub(w http.ResponseWriter, r *http.Request) (tpl, *TemplateData, error) {
var xpub string
i := strings.LastIndexByte(r.URL.Path, '/')
i := strings.LastIndex(r.URL.Path, "xpub/")
if i > 0 {
xpub = r.URL.Path[i+1:]
xpub = r.URL.Path[i+5:]
}
if len(xpub) == 0 {
return errorTpl, nil, api.NewAPIError("Missing xpub", true)
@ -804,7 +805,7 @@ func (s *PublicServer) explorerSearch(w http.ResponseWriter, r *http.Request) (t
if len(q) > 0 {
address, err = s.api.GetXpubAddress(q, 0, 1, api.AccountDetailsBasic, &api.AddressFilter{Vout: api.AddressFilterVoutOff}, 0)
if err == nil {
http.Redirect(w, r, joinURL("/xpub/", address.AddrStr), 302)
http.Redirect(w, r, joinURL("/xpub/", url.QueryEscape(address.AddrStr)), 302)
return noTpl, nil, nil
}
block, err = s.api.GetBlock(q, 0, 1)
@ -1024,9 +1025,9 @@ func (s *PublicServer) apiAddress(r *http.Request, apiVersion int) (interface{},
func (s *PublicServer) apiXpub(r *http.Request, apiVersion int) (interface{}, error) {
var xpub string
i := strings.LastIndexByte(r.URL.Path, '/')
i := strings.LastIndex(r.URL.Path, "xpub/")
if i > 0 {
xpub = r.URL.Path[i+1:]
xpub = r.URL.Path[i+5:]
}
if len(xpub) == 0 {
return nil, api.NewAPIError("Missing xpub", true)

File diff suppressed because one or more lines are too long

View File

@ -17,7 +17,8 @@ const (
TxidB2T3 = "05e2e48aeabdd9b75def7b48d756ba304713c2aba7b522bf9dbc893fc4231b07"
TxidB2T4 = "fdd824a780cbb718eeb766eb05d83fdefc793a27082cd5e67f856d69798cf7db"
Xpub = "upub5E1xjDmZ7Hhej6LPpS8duATdKXnRYui7bDYj6ehfFGzWDZtmCmQkZhc3Zb7kgRLtHWd16QFxyP86JKL3ShZEBFX88aciJ3xyocuyhZZ8g6q"
Xpub = "upub5E1xjDmZ7Hhej6LPpS8duATdKXnRYui7bDYj6ehfFGzWDZtmCmQkZhc3Zb7kgRLtHWd16QFxyP86JKL3ShZEBFX88aciJ3xyocuyhZZ8g6q"
TaprootDescriptor = "tr([5c9e228d/86'/1'/0']tpubDC88gkaZi5HvJGxGDNLADkvtdpni3mLmx6vr2KnXmWMG8zfkBRggsxHVBkUpgcwPe2KKpkyvTJCdXHb1UHEWE64vczyyPQfHr1skBcsRedN/0/*)#4rqwxvej"
Addr1 = "mfcWp7DB6NuaZsExybTTXpVgWz559Np4Ti" // 76a914010d39800f86122416e28f485029acf77507169288ac
Addr2 = "mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz" // 76a9148bdf0aa3c567aa5975c2e61321b8bebbe7293df688ac