From 9c9367491846ba1f9479ed967921232cbaf8a178 Mon Sep 17 00:00:00 2001 From: Jakub Matys Date: Tue, 24 Apr 2018 10:20:26 +0200 Subject: [PATCH] added support of bcash addresses to RPCs --- bchain/baseparser.go | 25 +++++++- bchain/coins/bch/bcashparser.go | 53 +++++++++++------ bchain/types.go | 11 +++- server/socketio.go | 102 ++++++++++++++++++++++---------- server/static/test.html | 39 +++++++++--- 5 files changed, 168 insertions(+), 62 deletions(-) diff --git a/bchain/baseparser.go b/bchain/baseparser.go index ba8bfb1a..4d6bc761 100644 --- a/bchain/baseparser.go +++ b/bchain/baseparser.go @@ -178,9 +178,30 @@ func (a baseAddress) String() string { return a.addr } -func (a baseAddress) EncodeAddress(format uint8) (string, error) { - if format != 0 { +func (a baseAddress) EncodeAddress(format AddressFormat) (string, error) { + if format != DefaultAddress { return "", fmt.Errorf("Unknown address format: %d", format) } return a.addr, nil } + +func (a baseAddress) AreEqual(addr string) (bool, error) { + ea, err := a.EncodeAddress(0) + if err != nil { + return false, err + } + return ea == addr, nil +} + +func (a baseAddress) InSlice(addrs []string) (bool, error) { + for _, addr := range addrs { + eq, err := a.AreEqual(addr) + if err != nil { + return false, err + } + if eq { + return true, nil + } + } + return false, nil +} diff --git a/bchain/coins/bch/bcashparser.go b/bchain/coins/bch/bcashparser.go index 8eb94292..2e1fe9ca 100644 --- a/bchain/coins/bch/bcashparser.go +++ b/bchain/coins/bch/bcashparser.go @@ -10,17 +10,9 @@ import ( "github.com/btcsuite/btcd/txscript" "github.com/btcsuite/btcutil" "github.com/cpacia/bchutil" - "github.com/golang/glog" ) -var prefixes []string - -func init() { - prefixes = make([]string, 0, len(bchutil.Prefixes)) - for _, prefix := range bchutil.Prefixes { - prefixes = append(prefixes, prefix) - } -} +var prefixes = []string{"bitcoincash", "bchtest", "bchreg"} // BCashParser handle type BCashParser struct { @@ -92,6 +84,9 @@ func isCashAddr(addr string) bool { func (p *BCashParser) UnpackTx(buf []byte) (tx *bchain.Tx, height uint32, err error) { tx, height, err = p.BitcoinParser.UnpackTx(buf) + if err != nil { + return + } for i, vout := range tx.Vout { if len(vout.ScriptPubKey.Addresses) == 1 { @@ -114,18 +109,11 @@ func (a *bcashAddress) String() string { return a.addr } -type AddressFormat = uint8 - -const ( - LegacyAddress AddressFormat = iota - CashAddress -) - -func (a *bcashAddress) EncodeAddress(format AddressFormat) (string, error) { +func (a *bcashAddress) EncodeAddress(format bchain.AddressFormat) (string, error) { switch format { - case LegacyAddress: + case bchain.DefaultAddress: return a.String(), nil - case CashAddress: + case bchain.BCashAddress: da, err := btcutil.DecodeAddress(a.addr, a.net) if err != nil { return "", err @@ -148,3 +136,30 @@ func (a *bcashAddress) EncodeAddress(format AddressFormat) (string, error) { return "", fmt.Errorf("Unknown address format: %d", format) } } + +func (a *bcashAddress) AreEqual(addr string) (bool, error) { + var format bchain.AddressFormat + if isCashAddr(addr) { + format = bchain.BCashAddress + } else { + format = bchain.DefaultAddress + } + ea, err := a.EncodeAddress(format) + if err != nil { + return false, err + } + return ea == addr, nil +} + +func (a *bcashAddress) InSlice(addrs []string) (bool, error) { + for _, addr := range addrs { + eq, err := a.AreEqual(addr) + if err != nil { + return false, err + } + if eq { + return true, nil + } + } + return false, nil +} diff --git a/bchain/types.go b/bchain/types.go index 59ce276e..ddc49f4b 100644 --- a/bchain/types.go +++ b/bchain/types.go @@ -37,9 +37,18 @@ type ScriptPubKey struct { Addresses []string `json:"addresses,omitempty"` } +type AddressFormat = uint8 + +const ( + DefaultAddress AddressFormat = iota + BCashAddress +) + type Address interface { String() string - EncodeAddress(format uint8) (string, error) + EncodeAddress(format AddressFormat) (string, error) + AreEqual(addr string) (bool, error) + InSlice(addrs []string) (bool, error) } type Vout struct { diff --git a/server/socketio.go b/server/socketio.go index dc84af64..f7cb7007 100644 --- a/server/socketio.go +++ b/server/socketio.go @@ -124,27 +124,32 @@ func (s *SocketIoServer) txRedirect(w http.ResponseWriter, r *http.Request) { } } -type reqRange struct { - Start int `json:"start"` - End int `json:"end"` - QueryMempol bool `json:"queryMempol"` - QueryMempoolOnly bool `json:"queryMempoolOnly"` - From int `json:"from"` - To int `json:"to"` +type addrOpts struct { + Start int `json:"start"` + End int `json:"end"` + QueryMempol bool `json:"queryMempol"` + QueryMempoolOnly bool `json:"queryMempoolOnly"` + From int `json:"from"` + To int `json:"to"` + AddressFormat uint8 `json:"addressFormat"` +} + +type txOpts struct { + AddressFormat uint8 `json:"addressFormat"` } var onMessageHandlers = map[string]func(*SocketIoServer, json.RawMessage) (interface{}, error){ "getAddressTxids": func(s *SocketIoServer, params json.RawMessage) (rv interface{}, err error) { - addr, rr, err := unmarshalGetAddressRequest(params) + addr, opts, err := unmarshalGetAddressRequest(params) if err == nil { - rv, err = s.getAddressTxids(addr, &rr) + rv, err = s.getAddressTxids(addr, &opts) } return }, "getAddressHistory": func(s *SocketIoServer, params json.RawMessage) (rv interface{}, err error) { - addr, rr, err := unmarshalGetAddressRequest(params) + addr, opts, err := unmarshalGetAddressRequest(params) if err == nil { - rv, err = s.getAddressHistory(addr, &rr) + rv, err = s.getAddressHistory(addr, &opts) } return }, @@ -173,9 +178,9 @@ var onMessageHandlers = map[string]func(*SocketIoServer, json.RawMessage) (inter return s.getInfo() }, "getDetailedTransaction": func(s *SocketIoServer, params json.RawMessage) (rv interface{}, err error) { - txid, err := unmarshalStringParameter(params) + txid, opts, err := unmarshalGetDetailedTransaction(params) if err == nil { - rv, err = s.getDetailedTransaction(txid) + rv, err = s.getDetailedTransaction(txid, opts) } return }, @@ -226,7 +231,7 @@ func (s *SocketIoServer) onMessage(c *gosocketio.Channel, req map[string]json.Ra return e } -func unmarshalGetAddressRequest(params []byte) (addr []string, rr reqRange, err error) { +func unmarshalGetAddressRequest(params []byte) (addr []string, opts addrOpts, err error) { var p []json.RawMessage err = json.Unmarshal(params, &p) if err != nil { @@ -240,7 +245,7 @@ func unmarshalGetAddressRequest(params []byte) (addr []string, rr reqRange, err if err != nil { return } - err = json.Unmarshal(p[1], &rr) + err = json.Unmarshal(p[1], &opts) return } @@ -261,14 +266,14 @@ type resultAddressTxids struct { Result []string `json:"result"` } -func (s *SocketIoServer) getAddressTxids(addr []string, rr *reqRange) (res resultAddressTxids, err error) { +func (s *SocketIoServer) getAddressTxids(addr []string, opts *addrOpts) (res resultAddressTxids, err error) { txids := make([]string, 0) - lower, higher := uint32(rr.To), uint32(rr.Start) + lower, higher := uint32(opts.To), uint32(opts.Start) for _, address := range addr { - if !rr.QueryMempoolOnly { + if !opts.QueryMempoolOnly { err = s.db.GetTransactions(address, lower, higher, func(txid string, vout uint32, isOutput bool) error { txids = append(txids, txid) - if isOutput && rr.QueryMempol { + if isOutput && opts.QueryMempol { input := s.chain.GetMempoolSpentOutput(txid, vout) if input != "" { txids = append(txids, txid) @@ -280,7 +285,7 @@ func (s *SocketIoServer) getAddressTxids(addr []string, rr *reqRange) (res resul return res, err } } - if rr.QueryMempoolOnly || rr.QueryMempol { + if opts.QueryMempoolOnly || opts.QueryMempol { mtxids, err := s.chain.GetMempoolTransactions(address) if err != nil { return res, err @@ -375,8 +380,8 @@ func txToResTx(tx *bchain.Tx, height int, hi []txInputs, ho []txOutputs) resTx { } } -func (s *SocketIoServer) getAddressHistory(addr []string, rr *reqRange) (res resultGetAddressHistory, err error) { - txr, err := s.getAddressTxids(addr, rr) +func (s *SocketIoServer) getAddressHistory(addr []string, opts *addrOpts) (res resultGetAddressHistory, err error) { + txr, err := s.getAddressTxids(addr, opts) if err != nil { return } @@ -388,7 +393,7 @@ func (s *SocketIoServer) getAddressHistory(addr []string, rr *reqRange) (res res res.Result.TotalCount = len(txids) res.Result.Items = make([]addressHistoryItem, 0) for i, txid := range txids { - if i >= rr.From && i < rr.To { + if i >= opts.From && i < opts.To { tx, height, err := s.txCache.GetTransaction(txid, bestheight) if err != nil { return res, err @@ -402,10 +407,17 @@ func (s *SocketIoServer) getAddressHistory(addr []string, rr *reqRange) (res res Script: &vout.ScriptPubKey.Hex, SpentIndex: int(vout.N), } - if len(vout.ScriptPubKey.Addresses) == 1 { - a := vout.ScriptPubKey.Addresses[0] + if vout.Address != nil { + a, err := vout.Address.EncodeAddress(opts.AddressFormat) + if err != nil { + return res, err + } ao.Address = &a - if stringInSlice(a, addr) { + found, err := vout.Address.InSlice(addr) + if err != nil { + return res, err + } + if found { hi, ok := ads[a] if ok { hi.OutputIndexes = append(hi.OutputIndexes, int(vout.N)) @@ -603,11 +615,31 @@ func unmarshalStringParameter(params []byte) (s string, err error) { return } +func unmarshalGetDetailedTransaction(params []byte) (txid string, opts txOpts, err error) { + var p []json.RawMessage + err = json.Unmarshal(params, &p) + if err != nil { + return + } + if len(p) < 1 || len(p) > 2 { + err = errors.New("incorrect number of parameters") + return + } + err = json.Unmarshal(p[0], &txid) + if err != nil { + return + } + if len(p) > 1 { + err = json.Unmarshal(p[1], &opts) + } + return +} + type resultGetDetailedTransaction struct { Result resTx `json:"result"` } -func (s *SocketIoServer) getDetailedTransaction(txid string) (res resultGetDetailedTransaction, err error) { +func (s *SocketIoServer) getDetailedTransaction(txid string, opts txOpts) (res resultGetDetailedTransaction, err error) { bestheight, _, err := s.db.GetBestBlock() if err != nil { return @@ -631,8 +663,12 @@ func (s *SocketIoServer) getDetailedTransaction(txid string) (res resultGetDetai } if len(otx.Vout) > int(vin.Vout) { vout := otx.Vout[vin.Vout] - if len(vout.ScriptPubKey.Addresses) == 1 { - ai.Address = &vout.ScriptPubKey.Addresses[0] + if vout.Address != nil { + a, err := vout.Address.EncodeAddress(opts.AddressFormat) + if err != nil { + return res, err + } + ai.Address = &a } ai.Satoshis = int64(vout.Value * 1E8) } @@ -645,8 +681,12 @@ func (s *SocketIoServer) getDetailedTransaction(txid string) (res resultGetDetai Script: &vout.ScriptPubKey.Hex, SpentIndex: int(vout.N), } - if len(vout.ScriptPubKey.Addresses) == 1 { - ao.Address = &vout.ScriptPubKey.Addresses[0] + if vout.Address != nil { + a, err := vout.Address.EncodeAddress(opts.AddressFormat) + if err != nil { + return res, err + } + ao.Address = &a } ho = append(ho, ao) } diff --git a/server/static/test.html b/server/static/test.html index 8073cf2a..99097265 100644 --- a/server/static/test.html +++ b/server/static/test.html @@ -56,16 +56,17 @@ var addresses = document.getElementById('getAddressHistoryAddresses').value.split(","); addresses = addresses.map(s => s.trim()); var mempool = document.getElementById("getAddressHistoryMempool").checked; - lookupAddressHistories(addresses, 0, 5, mempool, 20000000, 0, function (result) { + var format = document.getElementById("getAddressHistoryFormat").value; + lookupAddressHistories(addresses, 0, 5, mempool, 20000000, 0, format, function (result) { console.log('getAddressHistory sent successfully'); console.log(result); document.getElementById('getAddressHistoryResult').innerText = JSON.stringify(result).replace(/,/g, ", "); }); } - function lookupAddressHistories(addresses, from, to, mempool, start, end, f) { + function lookupAddressHistories(addresses, from, to, mempool, start, end, format, f) { const method = 'getAddressHistory'; - const rangeParam = mempool ? { + const opts = mempool ? { start, // needed for older bitcores (so we don't load all history if bitcore-node < 3.1.3) end, queryMempoolOnly: true, @@ -77,9 +78,10 @@ const params = [ addresses, { - ...rangeParam, + ...opts, from, to, + addressFormat: parseInt(format), }, ]; return socket.send({ method, params }, f); @@ -87,7 +89,7 @@ function lookupTransactionsIdsMempool(addresses, mempool, start, end, f) { const method = 'getAddressTxids'; - const rangeParam = mempool ? { + const opts = mempool ? { start, end, queryMempoolOnly: true, @@ -98,7 +100,7 @@ }; const params = [ addresses, - rangeParam, + opts, ]; return socket.send({ method, params }, f); } @@ -155,17 +157,21 @@ function getDetailedTransaction() { var hash = document.getElementById('getDetailedTransactionHash').value.trim(); - lookupDetailedTransaction(hash, function (result) { + var format = document.getElementById("getDetailedTransactionFormat").value; + lookupDetailedTransaction(hash, format, function (result) { console.log('getDetailedTransaction sent successfully'); console.log(result); document.getElementById('getDetailedTransactionResult').innerText = JSON.stringify(result).replace(/,/g, ", "); }); } - function lookupDetailedTransaction(hash, f) { + function lookupDetailedTransaction(hash, format, f) { const method = 'getDetailedTransaction'; const params = [ hash, + { + addressFormat: parseInt(format), + }, ]; return socket.send({ method, params }, f); } @@ -275,6 +281,14 @@   +
+
+   + +
@@ -324,7 +338,14 @@
-
+
+
+
+   +