Upgraded BCash address decoding/encoding

pull/7/head
Jakub Matys 2018-05-29 15:03:25 +02:00
parent ea3cfd2d6a
commit 79ba6abadd
7 changed files with 97 additions and 173 deletions

14
Gopkg.lock generated
View File

@ -109,6 +109,12 @@
revision = "3247c84500bff8d9fb6d579d800f20b3e091582c" revision = "3247c84500bff8d9fb6d579d800f20b3e091582c"
version = "v1.0.0" version = "v1.0.0"
[[projects]]
branch = "master"
name = "github.com/mr-tron/base58"
packages = ["base58"]
revision = "c1bdf7c52f59d6685ca597b9955a443ff95eeee6"
[[projects]] [[projects]]
branch = "master" branch = "master"
name = "github.com/pebbe/zmq4" name = "github.com/pebbe/zmq4"
@ -145,6 +151,12 @@
revision = "feef513b9575b32f84bafa580aad89b011259019" revision = "feef513b9575b32f84bafa580aad89b011259019"
version = "v1.3.0" version = "v1.3.0"
[[projects]]
name = "github.com/schancel/cashaddr-converter"
packages = ["address","baseconv","cashaddress","legacy"]
revision = "0a38f5822f795dc3727b4caacc298e02938d9eb1"
version = "v9"
[[projects]] [[projects]]
branch = "master" branch = "master"
name = "github.com/syndtr/goleveldb" name = "github.com/syndtr/goleveldb"
@ -190,6 +202,6 @@
[solve-meta] [solve-meta]
analyzer-name = "dep" analyzer-name = "dep"
analyzer-version = 1 analyzer-version = 1
inputs-digest = "3a5adb167369a6d82fec8664beb93b7e7c8109b31c9f19ecd8ce1142755d621f" inputs-digest = "97b5e11b3aa46e6b54a5c3fd7835c49f324f9821a1c641e5682f60ee6716d8c2"
solver-name = "gps-cdcl" solver-name = "gps-cdcl"
solver-version = 1 solver-version = 1

View File

@ -214,27 +214,16 @@ func (a baseAddress) String() string {
return a.addr return a.addr
} }
func (a baseAddress) EncodeAddress() (string, error) { func (a baseAddress) AreEqual(addr string) bool {
return a.addr, nil return a.String() == addr
} }
func (a baseAddress) AreEqual(addr string) (bool, error) { func (a baseAddress) InSlice(addrs []string) bool {
ea, err := a.EncodeAddress() ea := a.String()
if err != nil {
return false, err
}
return ea == addr, nil
}
func (a baseAddress) InSlice(addrs []string) (bool, error) {
ea, err := a.EncodeAddress()
if err != nil {
return false, err
}
for _, addr := range addrs { for _, addr := range addrs {
if ea == addr { if ea == addr {
return true, nil return true
} }
} }
return false, nil return false
} }

View File

@ -10,6 +10,7 @@ import (
"github.com/btcsuite/btcd/txscript" "github.com/btcsuite/btcd/txscript"
"github.com/btcsuite/btcutil" "github.com/btcsuite/btcutil"
"github.com/cpacia/bchutil" "github.com/cpacia/bchutil"
"github.com/schancel/cashaddr-converter/address"
) )
type AddressFormat = uint8 type AddressFormat = uint8
@ -28,7 +29,7 @@ type BCashParser struct {
} }
// NewBCashParser returns new BCashParser instance // NewBCashParser returns new BCashParser instance
func NewBCashParser(params *chaincfg.Params, c *btc.Configuration) *BCashParser { func NewBCashParser(params *chaincfg.Params, c *btc.Configuration) (*BCashParser, error) {
var format AddressFormat var format AddressFormat
switch c.AddressFormat { switch c.AddressFormat {
case "": case "":
@ -38,11 +39,9 @@ func NewBCashParser(params *chaincfg.Params, c *btc.Configuration) *BCashParser
case "legacy": case "legacy":
format = Legacy format = Legacy
default: default:
// XXX return nil, fmt.Errorf("Unknown address format: %s", c.AddressFormat)
e := fmt.Errorf("Unknown address format: %s", c.AddressFormat)
panic(e)
} }
return &BCashParser{ p := &BCashParser{
BitcoinParser: &btc.BitcoinParser{ BitcoinParser: &btc.BitcoinParser{
BaseParser: &bchain.BaseParser{ BaseParser: &bchain.BaseParser{
AddressFactory: func(addr string) (bchain.Address, error) { return newBCashAddress(addr, params, format) }, AddressFactory: func(addr string) (bchain.Address, error) { return newBCashAddress(addr, params, format) },
@ -52,6 +51,7 @@ func NewBCashParser(params *chaincfg.Params, c *btc.Configuration) *BCashParser
}, },
AddressFormat: format, AddressFormat: format,
} }
return p, nil
} }
// GetChainParams contains network parameters for the main Bitcoin Cash network, // GetChainParams contains network parameters for the main Bitcoin Cash network,
@ -137,96 +137,55 @@ func (p *BCashParser) UnpackTx(buf []byte) (tx *bchain.Tx, height uint32, err er
} }
type bcashAddress struct { type bcashAddress struct {
addr btcutil.Address addr string
net *chaincfg.Params
format AddressFormat
} }
func newBCashAddress(addr string, net *chaincfg.Params, format AddressFormat) (*bcashAddress, error) { func newBCashAddress(addr string, net *chaincfg.Params, format AddressFormat) (*bcashAddress, error) {
var ( da, err := address.NewFromString(addr)
da btcutil.Address if err != nil {
err error return nil, err
)
if isCashAddr(addr) {
// for cashaddr we need to convert it to the legacy form (i.e. to btcutil's Address)
// because bchutil doesn't allow later conversions
da, err = bchutil.DecodeAddress(addr, net)
if err != nil {
return nil, err
}
switch ca := da.(type) {
case *bchutil.CashAddressPubKeyHash:
da, err = btcutil.NewAddressPubKeyHash(ca.Hash160()[:], net)
case *bchutil.CashAddressScriptHash:
da, err = btcutil.NewAddressScriptHash(ca.Hash160()[:], net)
default:
err = fmt.Errorf("Unknown address type: %T", da)
}
if err != nil {
return nil, err
}
} else {
da, err = btcutil.DecodeAddress(addr, net)
if err != nil {
return nil, err
}
} }
var ea string
switch format { switch format {
case Legacy, CashAddr: case CashAddr:
if a, err := da.CashAddress(); err != nil {
return nil, err
} else {
ea, err = a.Encode()
if err != nil {
return nil, err
}
}
case Legacy:
if a, err := da.Legacy(); err != nil {
return nil, err
} else {
ea, err = a.Encode()
if err != nil {
return nil, err
}
}
default: default:
return nil, fmt.Errorf("Unknown address format: %d", format) return nil, fmt.Errorf("Unknown address format: %d", format)
} }
return &bcashAddress{addr: da, net: net, format: format}, nil return &bcashAddress{addr: ea}, nil
} }
func (a *bcashAddress) String() string { func (a *bcashAddress) String() string {
return a.addr.String() return a.addr
} }
func (a *bcashAddress) EncodeAddress() (string, error) { func (a *bcashAddress) AreEqual(addr string) bool {
switch a.format { return a.String() == addr
case Legacy:
return a.String(), nil
case CashAddr:
var (
ca btcutil.Address
err error
)
switch da := a.addr.(type) {
case *btcutil.AddressPubKeyHash:
ca, err = bchutil.NewCashAddressPubKeyHash(da.Hash160()[:], a.net)
case *btcutil.AddressScriptHash:
ca, err = bchutil.NewCashAddressScriptHash(da.Hash160()[:], a.net)
default:
err = fmt.Errorf("Unknown address type: %T", da)
}
if err != nil {
return "", err
}
return ca.String(), nil
default:
return "", fmt.Errorf("Unknown address format: %d", a.format)
}
} }
func (a *bcashAddress) AreEqual(addr string) (bool, error) { func (a *bcashAddress) InSlice(addrs []string) bool {
ea, err := a.EncodeAddress() ea := a.String()
if err != nil {
return false, err
}
return ea == addr, nil
}
func (a *bcashAddress) InSlice(addrs []string) (bool, error) {
ea, err := a.EncodeAddress()
if err != nil {
return false, err
}
for _, addr := range addrs { for _, addr := range addrs {
if ea == addr { if ea == addr {
return true, nil return true
} }
} }
return false, nil return false
} }

View File

@ -15,26 +15,18 @@ func TestBcashAddressEncodeAddress(t *testing.T) {
t.Errorf("newBCashAddress() error = %v", err) t.Errorf("newBCashAddress() error = %v", err)
return return
} }
got1, err := addr1.EncodeAddress() got1 := addr1.String()
if err != nil {
t.Errorf("EncodeAddress() error = %v", err)
return
}
if got1 != "13zMwGC5bxRn9ckJ1mgxf7UR8qbbNe2iji" { if got1 != "13zMwGC5bxRn9ckJ1mgxf7UR8qbbNe2iji" {
t.Errorf("EncodeAddress() got1 = %v, want %v", got1, "13zMwGC5bxRn9ckJ1mgxf7UR8qbbNe2iji") t.Errorf("String() got1 = %v, want %v", got1, "13zMwGC5bxRn9ckJ1mgxf7UR8qbbNe2iji")
} }
addr2, err := newBCashAddress("13zMwGC5bxRn9ckJ1mgxf7UR8qbbNe2iji", GetChainParams("main"), CashAddr) addr2, err := newBCashAddress("13zMwGC5bxRn9ckJ1mgxf7UR8qbbNe2iji", GetChainParams("main"), CashAddr)
if err != nil { if err != nil {
t.Errorf("newBCashAddress() error = %v", err) t.Errorf("newBCashAddress() error = %v", err)
return return
} }
got2, err := addr2.EncodeAddress() got2 := addr2.String()
if err != nil {
t.Errorf("EncodeAddress() error = %v", err)
return
}
if got2 != "bitcoincash:qqsvjuqqwgyzvz7zz9xcvxent0ul2xjs6y4d9qvsrf" { if got2 != "bitcoincash:qqsvjuqqwgyzvz7zz9xcvxent0ul2xjs6y4d9qvsrf" {
t.Errorf("EncodeAddress() got2 = %v, want %v", got2, "bitcoincash:qqsvjuqqwgyzvz7zz9xcvxent0ul2xjs6y4d9qvsrf") t.Errorf("String() got2 = %v, want %v", got2, "bitcoincash:qqsvjuqqwgyzvz7zz9xcvxent0ul2xjs6y4d9qvsrf")
} }
} }
@ -49,35 +41,19 @@ func TestBcashAddressAreEqual(t *testing.T) {
t.Errorf("newBCashAddress() error = %v", err) t.Errorf("newBCashAddress() error = %v", err)
return return
} }
got1, err := addr1.AreEqual("13zMwGC5bxRn9ckJ1mgxf7UR8qbbNe2iji") got1 := addr1.AreEqual("13zMwGC5bxRn9ckJ1mgxf7UR8qbbNe2iji")
if err != nil {
t.Errorf("AreEqual() error = %v", err)
return
}
if got1 != true { if got1 != true {
t.Errorf("AreEqual() got1 = %v, want %v", got1, true) t.Errorf("AreEqual() got1 = %v, want %v", got1, true)
} }
got2, err := addr2.AreEqual("bitcoincash:qqsvjuqqwgyzvz7zz9xcvxent0ul2xjs6y4d9qvsrf") got2 := addr2.AreEqual("bitcoincash:qqsvjuqqwgyzvz7zz9xcvxent0ul2xjs6y4d9qvsrf")
if err != nil {
t.Errorf("AreEqual() error = %v", err)
return
}
if got2 != true { if got2 != true {
t.Errorf("AreEqual() got2 = %v, want %v", got2, true) t.Errorf("AreEqual() got2 = %v, want %v", got2, true)
} }
got3, err := addr1.AreEqual("1HoKgKQh7ZNomWURmS9Tk3z8JM2MWm7S1w") got3 := addr1.AreEqual("1HoKgKQh7ZNomWURmS9Tk3z8JM2MWm7S1w")
if err != nil {
t.Errorf("AreEqual() error = %v", err)
return
}
if got3 != false { if got3 != false {
t.Errorf("AreEqual() got3 = %v, want %v", got3, false) t.Errorf("AreEqual() got3 = %v, want %v", got3, false)
} }
got4, err := addr2.AreEqual("bitcoincash:qzuyf0gpqj7q5wfck3nyghhklju7r0k3ksmq6d0vch") got4 := addr2.AreEqual("bitcoincash:qzuyf0gpqj7q5wfck3nyghhklju7r0k3ksmq6d0vch")
if err != nil {
t.Errorf("AreEqual() error = %v", err)
return
}
if got4 != false { if got4 != false {
t.Errorf("AreEqual() got4 = %v, want %v", got4, false) t.Errorf("AreEqual() got4 = %v, want %v", got4, false)
} }
@ -94,42 +70,30 @@ func TestBcashAddressInSlice(t *testing.T) {
t.Errorf("newBCashAddress() error = %v", err) t.Errorf("newBCashAddress() error = %v", err)
return return
} }
got1, err := addr1.InSlice([]string{"13zMwGC5bxRn9ckJ1mgxf7UR8qbbNe2iji", "1HoKgKQh7ZNomWURmS9Tk3z8JM2MWm7S1w"}) got1 := addr1.InSlice([]string{"13zMwGC5bxRn9ckJ1mgxf7UR8qbbNe2iji", "1HoKgKQh7ZNomWURmS9Tk3z8JM2MWm7S1w"})
if err != nil {
t.Errorf("InSlice() error = %v", err)
return
}
if got1 != true { if got1 != true {
t.Errorf("InSlice() got1 = %v, want %v", got1, true) t.Errorf("InSlice() got1 = %v, want %v", got1, true)
} }
got2, err := addr2.InSlice([]string{"bitcoincash:qzuyf0gpqj7q5wfck3nyghhklju7r0k3ksmq6d0vch", "bitcoincash:qqsvjuqqwgyzvz7zz9xcvxent0ul2xjs6y4d9qvsrf"}) got2 := addr2.InSlice([]string{"bitcoincash:qzuyf0gpqj7q5wfck3nyghhklju7r0k3ksmq6d0vch", "bitcoincash:qqsvjuqqwgyzvz7zz9xcvxent0ul2xjs6y4d9qvsrf"})
if err != nil {
t.Errorf("InSlice() error = %v", err)
return
}
if got2 != true { if got2 != true {
t.Errorf("InSlice() got2 = %v, want %v", got2, true) t.Errorf("InSlice() got2 = %v, want %v", got2, true)
} }
got3, err := addr1.InSlice([]string{"1HoKgKQh7ZNomWURmS9Tk3z8JM2MWm7S1w", "1E6Np6dUPYpBSdLMLuwBF8sRQ3cngdaRRY"}) got3 := addr1.InSlice([]string{"1HoKgKQh7ZNomWURmS9Tk3z8JM2MWm7S1w", "1E6Np6dUPYpBSdLMLuwBF8sRQ3cngdaRRY"})
if err != nil {
t.Errorf("InSlice() error = %v", err)
return
}
if got3 != false { if got3 != false {
t.Errorf("InSlice() got3 = %v, want %v", got3, false) t.Errorf("InSlice() got3 = %v, want %v", got3, false)
} }
got4, err := addr2.InSlice([]string{"bitcoincash:qzuyf0gpqj7q5wfck3nyghhklju7r0k3ksmq6d0vch", "bitcoincash:qz8emmpenqgeg7et8xsz8prvhy6cqcalyyjcamt7e9"}) got4 := addr2.InSlice([]string{"bitcoincash:qzuyf0gpqj7q5wfck3nyghhklju7r0k3ksmq6d0vch", "bitcoincash:qz8emmpenqgeg7et8xsz8prvhy6cqcalyyjcamt7e9"})
if err != nil {
t.Errorf("InSlice() error = %v", err)
return
}
if got4 != false { if got4 != false {
t.Errorf("InSlice() got4 = %v, want %v", got4, false) t.Errorf("InSlice() got4 = %v, want %v", got4, false)
} }
} }
func TestAddressToOutputScript(t *testing.T) { func TestAddressToOutputScript(t *testing.T) {
parser := NewBCashParser(GetChainParams("test"), &btc.Configuration{}) parser, err := NewBCashParser(GetChainParams("test"), &btc.Configuration{AddressFormat: "legacy"})
if err != nil {
t.Errorf("NewBCashParser() error = %v", err)
return
}
want, err := hex.DecodeString("76a9144fa927fd3bcf57d4e3c582c3d2eb2bd3df8df47c88ac") want, err := hex.DecodeString("76a9144fa927fd3bcf57d4e3c582c3d2eb2bd3df8df47c88ac")
if err != nil { if err != nil {
panic(err) panic(err)
@ -248,6 +212,17 @@ func init() {
} }
func Test_UnpackTx(t *testing.T) { func Test_UnpackTx(t *testing.T) {
parser1, err := NewBCashParser(GetChainParams("main"), &btc.Configuration{AddressFormat: "legacy"})
if err != nil {
t.Errorf("NewBCashParser() error = %v", err)
return
}
parser2, err := NewBCashParser(GetChainParams("test"), &btc.Configuration{AddressFormat: "legacy"})
if err != nil {
t.Errorf("NewBCashParser() error = %v", err)
return
}
type args struct { type args struct {
packedTx string packedTx string
parser *BCashParser parser *BCashParser
@ -263,7 +238,7 @@ func Test_UnpackTx(t *testing.T) {
name: "btc-1", name: "btc-1",
args: args{ args: args{
packedTx: testTxPacked1, packedTx: testTxPacked1,
parser: NewBCashParser(GetChainParams("main"), &btc.Configuration{AddressFormat: "legacy"}), parser: parser1,
}, },
want: &testTx1, want: &testTx1,
want1: 123456, want1: 123456,
@ -273,7 +248,7 @@ func Test_UnpackTx(t *testing.T) {
name: "testnet-1", name: "testnet-1",
args: args{ args: args{
packedTx: testTxPacked2, packedTx: testTxPacked2,
parser: NewBCashParser(GetChainParams("test"), &btc.Configuration{AddressFormat: "legacy"}), parser: parser2,
}, },
want: &testTx2, want: &testTx2,
want1: 510234, want1: 510234,

View File

@ -32,7 +32,6 @@ func NewBCashRPC(config json.RawMessage, pushHandler func(bchain.NotificationTyp
// Initialize initializes BCashRPC instance. // Initialize initializes BCashRPC instance.
func (b *BCashRPC) Initialize() error { func (b *BCashRPC) Initialize() error {
chainName, err := b.GetChainInfoAndInitializeMempool(b) chainName, err := b.GetChainInfoAndInitializeMempool(b)
if err != nil { if err != nil {
return err return err
@ -41,7 +40,11 @@ func (b *BCashRPC) Initialize() error {
params := GetChainParams(chainName) params := GetChainParams(chainName)
// always create parser // always create parser
b.Parser = NewBCashParser(params, b.ChainConfig) b.Parser, err = NewBCashParser(params, b.ChainConfig)
if err != nil {
return err
}
// parameters for getInfo request // parameters for getInfo request
if params.Net == bchutil.MainnetMagic { if params.Net == bchutil.MainnetMagic {

View File

@ -43,9 +43,8 @@ type ScriptPubKey struct {
type Address interface { type Address interface {
String() string String() string
EncodeAddress() (string, error) AreEqual(addr string) bool
AreEqual(addr string) (bool, error) InSlice(addrs []string) bool
InSlice(addrs []string) (bool, error)
} }
type Vout struct { type Vout struct {

View File

@ -478,16 +478,9 @@ func (s *SocketIoServer) getAddressHistory(addr []string, opts *addrOpts) (res r
Script: &aoh, Script: &aoh,
} }
if vout.Address != nil { if vout.Address != nil {
a, err := vout.Address.EncodeAddress() a := vout.Address.String()
if err != nil {
return res, err
}
ao.Address = &a ao.Address = &a
found, err := vout.Address.InSlice(addr) if vout.Address.InSlice(addr) {
if err != nil {
return res, err
}
if found {
hi, ok := ads[a] hi, ok := ads[a]
if ok { if ok {
hi.OutputIndexes = append(hi.OutputIndexes, int(vout.N)) hi.OutputIndexes = append(hi.OutputIndexes, int(vout.N))
@ -735,10 +728,7 @@ func (s *SocketIoServer) getDetailedTransaction(txid string) (res resultGetDetai
if len(otx.Vout) > int(vin.Vout) { if len(otx.Vout) > int(vin.Vout) {
vout := otx.Vout[vin.Vout] vout := otx.Vout[vin.Vout]
if vout.Address != nil { if vout.Address != nil {
a, err := vout.Address.EncodeAddress() a := vout.Address.String()
if err != nil {
return res, err
}
ai.Address = &a ai.Address = &a
} }
ai.Satoshis = int64(vout.Value*1E8 + 0.5) ai.Satoshis = int64(vout.Value*1E8 + 0.5)
@ -753,10 +743,7 @@ func (s *SocketIoServer) getDetailedTransaction(txid string) (res resultGetDetai
Script: &aos, Script: &aos,
} }
if vout.Address != nil { if vout.Address != nil {
a, err := vout.Address.EncodeAddress() a := vout.Address.String()
if err != nil {
return res, err
}
ao.Address = &a ao.Address = &a
} }
ho = append(ho, ao) ho = append(ho, ao)