chaincfg: Add IsBech32SegwitPrefix utility method.

The IsBech32SegwitPrefix method takes a string prefix and
determines if it is a valid prefix for a Bech32 encoded segwit
address for any of the default or registered networks.
This commit is contained in:
Johan T. Halseth 2017-07-07 23:31:11 +02:00
parent 9822ffad68
commit 47885ab870
2 changed files with 154 additions and 11 deletions

View file

@ -8,6 +8,7 @@ import (
"errors" "errors"
"math" "math"
"math/big" "math/big"
"strings"
"time" "time"
"github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/chaincfg/chainhash"
@ -195,8 +196,8 @@ type Params struct {
// Mempool parameters // Mempool parameters
RelayNonStdTxs bool RelayNonStdTxs bool
// Human-readable part for Bech32 encoded segwit addresses, as defined in // Human-readable part for Bech32 encoded segwit addresses, as defined
// BIP 173. // in BIP 173.
Bech32HRPSegwit string Bech32HRPSegwit string
// Address encoding magics // Address encoding magics
@ -547,10 +548,11 @@ var (
) )
var ( var (
registeredNets = make(map[wire.BitcoinNet]struct{}) registeredNets = make(map[wire.BitcoinNet]struct{})
pubKeyHashAddrIDs = make(map[byte]struct{}) pubKeyHashAddrIDs = make(map[byte]struct{})
scriptHashAddrIDs = make(map[byte]struct{}) scriptHashAddrIDs = make(map[byte]struct{})
hdPrivToPubKeyIDs = make(map[[4]byte][]byte) bech32SegwitPrefixes = make(map[string]struct{})
hdPrivToPubKeyIDs = make(map[[4]byte][]byte)
) )
// String returns the hostname of the DNS seed in human-readable form. // String returns the hostname of the DNS seed in human-readable form.
@ -575,6 +577,10 @@ func Register(params *Params) error {
pubKeyHashAddrIDs[params.PubKeyHashAddrID] = struct{}{} pubKeyHashAddrIDs[params.PubKeyHashAddrID] = struct{}{}
scriptHashAddrIDs[params.ScriptHashAddrID] = struct{}{} scriptHashAddrIDs[params.ScriptHashAddrID] = struct{}{}
hdPrivToPubKeyIDs[params.HDPrivateKeyID] = params.HDPublicKeyID[:] hdPrivToPubKeyIDs[params.HDPrivateKeyID] = params.HDPublicKeyID[:]
// A valid Bech32 encoded segwit address always has as prefix the
// human-readable part for the given net followed by '1'.
bech32SegwitPrefixes[params.Bech32HRPSegwit+"1"] = struct{}{}
return nil return nil
} }
@ -608,6 +614,15 @@ func IsScriptHashAddrID(id byte) bool {
return ok return ok
} }
// IsBech32SegwitPrefix returns whether the prefix is a known prefix for segwit
// addresses on any default or registered network. This is used when decoding
// an address string into a specific address type.
func IsBech32SegwitPrefix(prefix string) bool {
prefix = strings.ToLower(prefix)
_, ok := bech32SegwitPrefixes[prefix]
return ok
}
// HDPrivateKeyToPublicKeyID accepts a private hierarchical deterministic // HDPrivateKeyToPublicKeyID accepts a private hierarchical deterministic
// extended key id and returns the associated public key id. When the provided // extended key id and returns the associated public key id. When the provided
// id is not registered, the ErrUnknownHDKeyID error will be returned. // id is not registered, the ErrUnknownHDKeyID error will be returned.

View file

@ -3,6 +3,7 @@ package chaincfg_test
import ( import (
"bytes" "bytes"
"reflect" "reflect"
"strings"
"testing" "testing"
. "github.com/btcsuite/btcd/chaincfg" . "github.com/btcsuite/btcd/chaincfg"
@ -16,6 +17,7 @@ var mockNetParams = Params{
Net: 1<<32 - 1, Net: 1<<32 - 1,
PubKeyHashAddrID: 0x9f, PubKeyHashAddrID: 0x9f,
ScriptHashAddrID: 0xf9, ScriptHashAddrID: 0xf9,
Bech32HRPSegwit: "tc",
HDPrivateKeyID: [4]byte{0x01, 0x02, 0x03, 0x04}, HDPrivateKeyID: [4]byte{0x01, 0x02, 0x03, 0x04},
HDPublicKeyID: [4]byte{0x05, 0x06, 0x07, 0x08}, HDPublicKeyID: [4]byte{0x05, 0x06, 0x07, 0x08},
} }
@ -30,6 +32,10 @@ func TestRegister(t *testing.T) {
magic byte magic byte
valid bool valid bool
} }
type prefixTest struct {
prefix string
valid bool
}
type hdTest struct { type hdTest struct {
priv []byte priv []byte
want []byte want []byte
@ -37,11 +43,12 @@ func TestRegister(t *testing.T) {
} }
tests := []struct { tests := []struct {
name string name string
register []registerTest register []registerTest
p2pkhMagics []magicTest p2pkhMagics []magicTest
p2shMagics []magicTest p2shMagics []magicTest
hdMagics []hdTest segwitPrefixes []prefixTest
hdMagics []hdTest
}{ }{
{ {
name: "default networks", name: "default networks",
@ -119,6 +126,44 @@ func TestRegister(t *testing.T) {
valid: false, valid: false,
}, },
}, },
segwitPrefixes: []prefixTest{
{
prefix: MainNetParams.Bech32HRPSegwit + "1",
valid: true,
},
{
prefix: TestNet3Params.Bech32HRPSegwit + "1",
valid: true,
},
{
prefix: RegressionNetParams.Bech32HRPSegwit + "1",
valid: true,
},
{
prefix: SimNetParams.Bech32HRPSegwit + "1",
valid: true,
},
{
prefix: strings.ToUpper(MainNetParams.Bech32HRPSegwit + "1"),
valid: true,
},
{
prefix: mockNetParams.Bech32HRPSegwit + "1",
valid: false,
},
{
prefix: "abc1",
valid: false,
},
{
prefix: "1",
valid: false,
},
{
prefix: MainNetParams.Bech32HRPSegwit,
valid: false,
},
},
hdMagics: []hdTest{ hdMagics: []hdTest{
{ {
priv: MainNetParams.HDPrivateKeyID[:], priv: MainNetParams.HDPrivateKeyID[:],
@ -215,6 +260,44 @@ func TestRegister(t *testing.T) {
valid: false, valid: false,
}, },
}, },
segwitPrefixes: []prefixTest{
{
prefix: MainNetParams.Bech32HRPSegwit + "1",
valid: true,
},
{
prefix: TestNet3Params.Bech32HRPSegwit + "1",
valid: true,
},
{
prefix: RegressionNetParams.Bech32HRPSegwit + "1",
valid: true,
},
{
prefix: SimNetParams.Bech32HRPSegwit + "1",
valid: true,
},
{
prefix: strings.ToUpper(MainNetParams.Bech32HRPSegwit + "1"),
valid: true,
},
{
prefix: mockNetParams.Bech32HRPSegwit + "1",
valid: true,
},
{
prefix: "abc1",
valid: false,
},
{
prefix: "1",
valid: false,
},
{
prefix: MainNetParams.Bech32HRPSegwit,
valid: false,
},
},
hdMagics: []hdTest{ hdMagics: []hdTest{
{ {
priv: mockNetParams.HDPrivateKeyID[:], priv: mockNetParams.HDPrivateKeyID[:],
@ -304,6 +387,44 @@ func TestRegister(t *testing.T) {
valid: false, valid: false,
}, },
}, },
segwitPrefixes: []prefixTest{
{
prefix: MainNetParams.Bech32HRPSegwit + "1",
valid: true,
},
{
prefix: TestNet3Params.Bech32HRPSegwit + "1",
valid: true,
},
{
prefix: RegressionNetParams.Bech32HRPSegwit + "1",
valid: true,
},
{
prefix: SimNetParams.Bech32HRPSegwit + "1",
valid: true,
},
{
prefix: strings.ToUpper(MainNetParams.Bech32HRPSegwit + "1"),
valid: true,
},
{
prefix: mockNetParams.Bech32HRPSegwit + "1",
valid: true,
},
{
prefix: "abc1",
valid: false,
},
{
prefix: "1",
valid: false,
},
{
prefix: MainNetParams.Bech32HRPSegwit,
valid: false,
},
},
hdMagics: []hdTest{ hdMagics: []hdTest{
{ {
priv: MainNetParams.HDPrivateKeyID[:], priv: MainNetParams.HDPrivateKeyID[:],
@ -364,6 +485,13 @@ func TestRegister(t *testing.T) {
test.name, i, valid, magTest.valid) test.name, i, valid, magTest.valid)
} }
} }
for i, prxTest := range test.segwitPrefixes {
valid := IsBech32SegwitPrefix(prxTest.prefix)
if valid != prxTest.valid {
t.Errorf("%s: segwit prefix %s (%d) valid mismatch: got %v expected %v",
test.name, prxTest.prefix, i, valid, prxTest.valid)
}
}
for i, magTest := range test.hdMagics { for i, magTest := range test.hdMagics {
pubKey, err := HDPrivateKeyToPublicKeyID(magTest.priv[:]) pubKey, err := HDPrivateKeyToPublicKeyID(magTest.priv[:])
if !reflect.DeepEqual(err, magTest.err) { if !reflect.DeepEqual(err, magTest.err) {