From 79ba6abaddf9dbc323a9c574fd7b844940f6a416 Mon Sep 17 00:00:00 2001 From: Jakub Matys Date: Tue, 29 May 2018 15:03:25 +0200 Subject: [PATCH] Upgraded BCash address decoding/encoding --- Gopkg.lock | 14 +++- bchain/baseparser.go | 23 ++---- bchain/coins/bch/bcashparser.go | 115 +++++++++------------------ bchain/coins/bch/bcashparser_test.go | 85 +++++++------------- bchain/coins/bch/bcashrpc.go | 7 +- bchain/types.go | 5 +- server/socketio.go | 21 +---- 7 files changed, 97 insertions(+), 173 deletions(-) diff --git a/Gopkg.lock b/Gopkg.lock index cfbfc254..d3306553 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -109,6 +109,12 @@ revision = "3247c84500bff8d9fb6d579d800f20b3e091582c" version = "v1.0.0" +[[projects]] + branch = "master" + name = "github.com/mr-tron/base58" + packages = ["base58"] + revision = "c1bdf7c52f59d6685ca597b9955a443ff95eeee6" + [[projects]] branch = "master" name = "github.com/pebbe/zmq4" @@ -145,6 +151,12 @@ revision = "feef513b9575b32f84bafa580aad89b011259019" version = "v1.3.0" +[[projects]] + name = "github.com/schancel/cashaddr-converter" + packages = ["address","baseconv","cashaddress","legacy"] + revision = "0a38f5822f795dc3727b4caacc298e02938d9eb1" + version = "v9" + [[projects]] branch = "master" name = "github.com/syndtr/goleveldb" @@ -190,6 +202,6 @@ [solve-meta] analyzer-name = "dep" analyzer-version = 1 - inputs-digest = "3a5adb167369a6d82fec8664beb93b7e7c8109b31c9f19ecd8ce1142755d621f" + inputs-digest = "97b5e11b3aa46e6b54a5c3fd7835c49f324f9821a1c641e5682f60ee6716d8c2" solver-name = "gps-cdcl" solver-version = 1 diff --git a/bchain/baseparser.go b/bchain/baseparser.go index 7fc52728..896de7e5 100644 --- a/bchain/baseparser.go +++ b/bchain/baseparser.go @@ -214,27 +214,16 @@ func (a baseAddress) String() string { return a.addr } -func (a baseAddress) EncodeAddress() (string, error) { - return a.addr, nil +func (a baseAddress) AreEqual(addr string) bool { + return a.String() == addr } -func (a baseAddress) AreEqual(addr string) (bool, error) { - ea, err := a.EncodeAddress() - 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 - } +func (a baseAddress) InSlice(addrs []string) bool { + ea := a.String() for _, addr := range addrs { if ea == addr { - return true, nil + return true } } - return false, nil + return false } diff --git a/bchain/coins/bch/bcashparser.go b/bchain/coins/bch/bcashparser.go index 3861e5e5..5fbdffb1 100644 --- a/bchain/coins/bch/bcashparser.go +++ b/bchain/coins/bch/bcashparser.go @@ -10,6 +10,7 @@ import ( "github.com/btcsuite/btcd/txscript" "github.com/btcsuite/btcutil" "github.com/cpacia/bchutil" + "github.com/schancel/cashaddr-converter/address" ) type AddressFormat = uint8 @@ -28,7 +29,7 @@ type BCashParser struct { } // 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 switch c.AddressFormat { case "": @@ -38,11 +39,9 @@ func NewBCashParser(params *chaincfg.Params, c *btc.Configuration) *BCashParser case "legacy": format = Legacy default: - // XXX - e := fmt.Errorf("Unknown address format: %s", c.AddressFormat) - panic(e) + return nil, fmt.Errorf("Unknown address format: %s", c.AddressFormat) } - return &BCashParser{ + p := &BCashParser{ BitcoinParser: &btc.BitcoinParser{ BaseParser: &bchain.BaseParser{ 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, } + return p, nil } // 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 { - addr btcutil.Address - net *chaincfg.Params - format AddressFormat + addr string } func newBCashAddress(addr string, net *chaincfg.Params, format AddressFormat) (*bcashAddress, error) { - var ( - da btcutil.Address - err error - ) - 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 - } + da, err := address.NewFromString(addr) + if err != nil { + return nil, err } + var ea string 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: 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 { - return a.addr.String() + return a.addr } -func (a *bcashAddress) EncodeAddress() (string, error) { - switch a.format { - 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 { + return a.String() == addr } -func (a *bcashAddress) AreEqual(addr string) (bool, error) { - ea, err := a.EncodeAddress() - 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 - } +func (a *bcashAddress) InSlice(addrs []string) bool { + ea := a.String() for _, addr := range addrs { if ea == addr { - return true, nil + return true } } - return false, nil + return false } diff --git a/bchain/coins/bch/bcashparser_test.go b/bchain/coins/bch/bcashparser_test.go index e918fe8b..aa9c1186 100644 --- a/bchain/coins/bch/bcashparser_test.go +++ b/bchain/coins/bch/bcashparser_test.go @@ -15,26 +15,18 @@ func TestBcashAddressEncodeAddress(t *testing.T) { t.Errorf("newBCashAddress() error = %v", err) return } - got1, err := addr1.EncodeAddress() - if err != nil { - t.Errorf("EncodeAddress() error = %v", err) - return - } + got1 := addr1.String() 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) if err != nil { t.Errorf("newBCashAddress() error = %v", err) return } - got2, err := addr2.EncodeAddress() - if err != nil { - t.Errorf("EncodeAddress() error = %v", err) - return - } + got2 := addr2.String() 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) return } - got1, err := addr1.AreEqual("13zMwGC5bxRn9ckJ1mgxf7UR8qbbNe2iji") - if err != nil { - t.Errorf("AreEqual() error = %v", err) - return - } + got1 := addr1.AreEqual("13zMwGC5bxRn9ckJ1mgxf7UR8qbbNe2iji") if got1 != true { t.Errorf("AreEqual() got1 = %v, want %v", got1, true) } - got2, err := addr2.AreEqual("bitcoincash:qqsvjuqqwgyzvz7zz9xcvxent0ul2xjs6y4d9qvsrf") - if err != nil { - t.Errorf("AreEqual() error = %v", err) - return - } + got2 := addr2.AreEqual("bitcoincash:qqsvjuqqwgyzvz7zz9xcvxent0ul2xjs6y4d9qvsrf") if got2 != true { t.Errorf("AreEqual() got2 = %v, want %v", got2, true) } - got3, err := addr1.AreEqual("1HoKgKQh7ZNomWURmS9Tk3z8JM2MWm7S1w") - if err != nil { - t.Errorf("AreEqual() error = %v", err) - return - } + got3 := addr1.AreEqual("1HoKgKQh7ZNomWURmS9Tk3z8JM2MWm7S1w") if got3 != false { t.Errorf("AreEqual() got3 = %v, want %v", got3, false) } - got4, err := addr2.AreEqual("bitcoincash:qzuyf0gpqj7q5wfck3nyghhklju7r0k3ksmq6d0vch") - if err != nil { - t.Errorf("AreEqual() error = %v", err) - return - } + got4 := addr2.AreEqual("bitcoincash:qzuyf0gpqj7q5wfck3nyghhklju7r0k3ksmq6d0vch") if 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) return } - got1, err := addr1.InSlice([]string{"13zMwGC5bxRn9ckJ1mgxf7UR8qbbNe2iji", "1HoKgKQh7ZNomWURmS9Tk3z8JM2MWm7S1w"}) - if err != nil { - t.Errorf("InSlice() error = %v", err) - return - } + got1 := addr1.InSlice([]string{"13zMwGC5bxRn9ckJ1mgxf7UR8qbbNe2iji", "1HoKgKQh7ZNomWURmS9Tk3z8JM2MWm7S1w"}) if got1 != true { t.Errorf("InSlice() got1 = %v, want %v", got1, true) } - got2, err := addr2.InSlice([]string{"bitcoincash:qzuyf0gpqj7q5wfck3nyghhklju7r0k3ksmq6d0vch", "bitcoincash:qqsvjuqqwgyzvz7zz9xcvxent0ul2xjs6y4d9qvsrf"}) - if err != nil { - t.Errorf("InSlice() error = %v", err) - return - } + got2 := addr2.InSlice([]string{"bitcoincash:qzuyf0gpqj7q5wfck3nyghhklju7r0k3ksmq6d0vch", "bitcoincash:qqsvjuqqwgyzvz7zz9xcvxent0ul2xjs6y4d9qvsrf"}) if got2 != true { t.Errorf("InSlice() got2 = %v, want %v", got2, true) } - got3, err := addr1.InSlice([]string{"1HoKgKQh7ZNomWURmS9Tk3z8JM2MWm7S1w", "1E6Np6dUPYpBSdLMLuwBF8sRQ3cngdaRRY"}) - if err != nil { - t.Errorf("InSlice() error = %v", err) - return - } + got3 := addr1.InSlice([]string{"1HoKgKQh7ZNomWURmS9Tk3z8JM2MWm7S1w", "1E6Np6dUPYpBSdLMLuwBF8sRQ3cngdaRRY"}) if got3 != false { t.Errorf("InSlice() got3 = %v, want %v", got3, false) } - got4, err := addr2.InSlice([]string{"bitcoincash:qzuyf0gpqj7q5wfck3nyghhklju7r0k3ksmq6d0vch", "bitcoincash:qz8emmpenqgeg7et8xsz8prvhy6cqcalyyjcamt7e9"}) - if err != nil { - t.Errorf("InSlice() error = %v", err) - return - } + got4 := addr2.InSlice([]string{"bitcoincash:qzuyf0gpqj7q5wfck3nyghhklju7r0k3ksmq6d0vch", "bitcoincash:qz8emmpenqgeg7et8xsz8prvhy6cqcalyyjcamt7e9"}) if got4 != false { t.Errorf("InSlice() got4 = %v, want %v", got4, false) } } 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") if err != nil { panic(err) @@ -248,6 +212,17 @@ func init() { } 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 { packedTx string parser *BCashParser @@ -263,7 +238,7 @@ func Test_UnpackTx(t *testing.T) { name: "btc-1", args: args{ packedTx: testTxPacked1, - parser: NewBCashParser(GetChainParams("main"), &btc.Configuration{AddressFormat: "legacy"}), + parser: parser1, }, want: &testTx1, want1: 123456, @@ -273,7 +248,7 @@ func Test_UnpackTx(t *testing.T) { name: "testnet-1", args: args{ packedTx: testTxPacked2, - parser: NewBCashParser(GetChainParams("test"), &btc.Configuration{AddressFormat: "legacy"}), + parser: parser2, }, want: &testTx2, want1: 510234, diff --git a/bchain/coins/bch/bcashrpc.go b/bchain/coins/bch/bcashrpc.go index b148b019..4ed4f834 100644 --- a/bchain/coins/bch/bcashrpc.go +++ b/bchain/coins/bch/bcashrpc.go @@ -32,7 +32,6 @@ func NewBCashRPC(config json.RawMessage, pushHandler func(bchain.NotificationTyp // Initialize initializes BCashRPC instance. func (b *BCashRPC) Initialize() error { - chainName, err := b.GetChainInfoAndInitializeMempool(b) if err != nil { return err @@ -41,7 +40,11 @@ func (b *BCashRPC) Initialize() error { params := GetChainParams(chainName) // 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 if params.Net == bchutil.MainnetMagic { diff --git a/bchain/types.go b/bchain/types.go index de78ed19..7f9ba636 100644 --- a/bchain/types.go +++ b/bchain/types.go @@ -43,9 +43,8 @@ type ScriptPubKey struct { type Address interface { String() string - EncodeAddress() (string, error) - AreEqual(addr string) (bool, error) - InSlice(addrs []string) (bool, error) + AreEqual(addr string) bool + InSlice(addrs []string) bool } type Vout struct { diff --git a/server/socketio.go b/server/socketio.go index 899059b1..56c2a14f 100644 --- a/server/socketio.go +++ b/server/socketio.go @@ -478,16 +478,9 @@ func (s *SocketIoServer) getAddressHistory(addr []string, opts *addrOpts) (res r Script: &aoh, } if vout.Address != nil { - a, err := vout.Address.EncodeAddress() - if err != nil { - return res, err - } + a := vout.Address.String() ao.Address = &a - found, err := vout.Address.InSlice(addr) - if err != nil { - return res, err - } - if found { + if vout.Address.InSlice(addr) { hi, ok := ads[a] if ok { 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) { vout := otx.Vout[vin.Vout] if vout.Address != nil { - a, err := vout.Address.EncodeAddress() - if err != nil { - return res, err - } + a := vout.Address.String() ai.Address = &a } ai.Satoshis = int64(vout.Value*1E8 + 0.5) @@ -753,10 +743,7 @@ func (s *SocketIoServer) getDetailedTransaction(txid string) (res resultGetDetai Script: &aos, } if vout.Address != nil { - a, err := vout.Address.EncodeAddress() - if err != nil { - return res, err - } + a := vout.Address.String() ao.Address = &a } ho = append(ho, ao)