From 3ecb380ef437f952b3eb701571633498b165c876 Mon Sep 17 00:00:00 2001 From: Jakub Matys Date: Fri, 18 May 2018 15:04:40 +0200 Subject: [PATCH] Fixed empty Address field when transaction was unpacked from JSON --- bchain/baseparser.go | 24 ++++++++++++++++++++++-- bchain/coins/bch/bcashparser.go | 14 ++++++++++++++ bchain/coins/bch/bcashparser_test.go | 7 +++---- bchain/coins/bch/bcashrpc.go | 6 +----- bchain/coins/btc/bitcoinparser.go | 23 +++++++++++++++++------ bchain/coins/btc/bitcoinparser_test.go | 12 ++++++------ bchain/coins/btc/bitcoinrpc.go | 12 +++++++----- bchain/coins/eth/ethparser.go | 12 ++++++++++-- bchain/coins/eth/ethparser_test.go | 8 +++++--- bchain/coins/eth/ethrpc.go | 8 ++++---- bchain/coins/zec/zcashparser.go | 5 +++++ bchain/coins/zec/zcashparser_test.go | 8 ++++---- bchain/coins/zec/zcashrpc.go | 2 +- bchain/types.go | 2 ++ 14 files changed, 101 insertions(+), 42 deletions(-) diff --git a/bchain/baseparser.go b/bchain/baseparser.go index a5373807..de444460 100644 --- a/bchain/baseparser.go +++ b/bchain/baseparser.go @@ -2,6 +2,7 @@ package bchain import ( "encoding/hex" + "encoding/json" "fmt" "github.com/gogo/protobuf/proto" @@ -9,7 +10,9 @@ import ( ) // BaseParser implements data parsing/handling functionality base for all other parsers -type BaseParser struct{} +type BaseParser struct { + AddressFactory func(string) Address +} // AddressToOutputScript converts address to ScriptPubKey - currently not implemented func (p *BaseParser) AddressToOutputScript(address string) ([]byte, error) { @@ -31,6 +34,23 @@ func (p *BaseParser) ParseTx(b []byte) (*Tx, error) { return nil, errors.New("ParseTx: not implemented") } +// ParseTxFromJson parses JSON message containing transaction and returs Tx struct +func (p *BaseParser) ParseTxFromJson(msg json.RawMessage) (*Tx, error) { + var tx Tx + err := json.Unmarshal(msg, tx) + if err != nil { + return nil, err + } + + for i, vout := range tx.Vout { + if len(vout.ScriptPubKey.Addresses) == 1 { + tx.Vout[i].Address = p.AddressFactory(vout.ScriptPubKey.Addresses[0]) + } + } + + return &tx, nil +} + // PackedTxidLen returns length in bytes of packed txid func (p *BaseParser) PackedTxidLen() int { return 32 @@ -159,7 +179,7 @@ func (p *BaseParser) UnpackTx(buf []byte) (*Tx, uint32, error) { Value: pto.Value, } if len(pto.Addresses) == 1 { - vout[i].Address = NewBaseAddress(pto.Addresses[0]) + vout[i].Address = p.AddressFactory(pto.Addresses[0]) } } tx := Tx{ diff --git a/bchain/coins/bch/bcashparser.go b/bchain/coins/bch/bcashparser.go index 2e1fe9ca..49ab7acd 100644 --- a/bchain/coins/bch/bcashparser.go +++ b/bchain/coins/bch/bcashparser.go @@ -19,6 +19,20 @@ type BCashParser struct { *btc.BitcoinParser } +// NewBCashParser returns new BCashParser instance +func NewBCashParser(params *chaincfg.Params) *BCashParser { + return &BCashParser{ + &btc.BitcoinParser{ + &bchain.BaseParser{ + AddressFactory: func(addr string) bchain.Address { + return &bcashAddress{addr: addr, net: params} + }, + }, + params, + }, + } +} + // GetChainParams contains network parameters for the main Bitcoin Cash network, // the regression test Bitcoin Cash network, the test Bitcoin Cash network and // the simulation test Bitcoin Cash network, in this order diff --git a/bchain/coins/bch/bcashparser_test.go b/bchain/coins/bch/bcashparser_test.go index 0bad2eb8..9658b259 100644 --- a/bchain/coins/bch/bcashparser_test.go +++ b/bchain/coins/bch/bcashparser_test.go @@ -2,7 +2,6 @@ package bch import ( "blockbook/bchain" - "blockbook/bchain/coins/btc" "bytes" "encoding/hex" "reflect" @@ -102,7 +101,7 @@ func TestBcashAddressInSlice(t *testing.T) { } func TestAddressToOutputScript(t *testing.T) { - parser := BCashParser{&btc.BitcoinParser{Params: GetChainParams("test")}} + parser := NewBCashParser(GetChainParams("test")) want, err := hex.DecodeString("76a9144fa927fd3bcf57d4e3c582c3d2eb2bd3df8df47c88ac") if err != nil { panic(err) @@ -214,7 +213,7 @@ func Test_UnpackTx(t *testing.T) { name: "btc-1", args: args{ packedTx: testTxPacked1, - parser: &BCashParser{&btc.BitcoinParser{Params: GetChainParams("main")}}, + parser: NewBCashParser(GetChainParams("main")), }, want: &testTx1, want1: 123456, @@ -224,7 +223,7 @@ func Test_UnpackTx(t *testing.T) { name: "testnet-1", args: args{ packedTx: testTxPacked2, - parser: &BCashParser{&btc.BitcoinParser{Params: GetChainParams("test")}}, + parser: NewBCashParser(GetChainParams("test")), }, want: &testTx2, want1: 510234, diff --git a/bchain/coins/bch/bcashrpc.go b/bchain/coins/bch/bcashrpc.go index bb4292d1..0c1ac525 100644 --- a/bchain/coins/bch/bcashrpc.go +++ b/bchain/coins/bch/bcashrpc.go @@ -41,11 +41,7 @@ func (b *BCashRPC) Initialize() error { params := GetChainParams(chainName) // always create parser - b.Parser = &BCashParser{ - &btc.BitcoinParser{ - Params: params, - }, - } + b.Parser = NewBCashParser(params) // parameters for getInfo request if params.Net == bchutil.MainnetMagic { diff --git a/bchain/coins/btc/bitcoinparser.go b/bchain/coins/btc/bitcoinparser.go index 0c1f2fe7..4fc7d89a 100644 --- a/bchain/coins/btc/bitcoinparser.go +++ b/bchain/coins/btc/bitcoinparser.go @@ -20,6 +20,16 @@ type BitcoinParser struct { Params *chaincfg.Params } +// NewBitcoinParser returns new BitcoinParser instance +func NewBitcoinParser(params *chaincfg.Params) *BitcoinParser { + return &BitcoinParser{ + &bchain.BaseParser{ + AddressFactory: bchain.NewBaseAddress, + }, + params, + } +} + // GetChainParams contains network parameters for the main Bitcoin network, // the regression test Bitcoin network, the test Bitcoin network and // the simulation test Bitcoin network, in this order @@ -131,6 +141,13 @@ func (p *BitcoinParser) ParseTx(b []byte) (*bchain.Tx, error) { } tx := p.txFromMsgTx(&t, true) tx.Hex = hex.EncodeToString(b) + + for i, vout := range tx.Vout { + if len(vout.ScriptPubKey.Addresses) == 1 { + tx.Vout[i].Address = p.AddressFactory(vout.ScriptPubKey.Addresses[0]) + } + } + return &tx, nil } @@ -170,11 +187,5 @@ func (p *BitcoinParser) UnpackTx(buf []byte) (*bchain.Tx, uint32, error) { } tx.Blocktime = bt - for i, vout := range tx.Vout { - if len(vout.ScriptPubKey.Addresses) == 1 { - tx.Vout[i].Address = bchain.NewBaseAddress(vout.ScriptPubKey.Addresses[0]) - } - } - return tx, height, nil } diff --git a/bchain/coins/btc/bitcoinparser_test.go b/bchain/coins/btc/bitcoinparser_test.go index 6982128f..b45bf9cc 100644 --- a/bchain/coins/btc/bitcoinparser_test.go +++ b/bchain/coins/btc/bitcoinparser_test.go @@ -42,7 +42,7 @@ func TestAddressToOutputScript(t *testing.T) { wantErr: false, }, } - parser := &BitcoinParser{Params: GetChainParams("main")} + parser := NewBitcoinParser(GetChainParams("main")) for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -94,7 +94,7 @@ func TestOutputScriptToAddresses(t *testing.T) { wantErr: false, }, } - parser := &BitcoinParser{Params: GetChainParams("main")} + parser := NewBitcoinParser(GetChainParams("main")) for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { b, _ := hex.DecodeString(tt.args.script) @@ -202,7 +202,7 @@ func Test_PackTx(t *testing.T) { tx: testTx1, height: 123456, blockTime: 1519053802, - parser: &BitcoinParser{Params: GetChainParams("main")}, + parser: NewBitcoinParser(GetChainParams("main")), }, want: testTxPacked1, wantErr: false, @@ -213,7 +213,7 @@ func Test_PackTx(t *testing.T) { tx: testTx2, height: 510234, blockTime: 1235678901, - parser: &BitcoinParser{Params: GetChainParams("test")}, + parser: NewBitcoinParser(GetChainParams("test")), }, want: testTxPacked2, wantErr: false, @@ -250,7 +250,7 @@ func Test_UnpackTx(t *testing.T) { name: "btc-1", args: args{ packedTx: testTxPacked1, - parser: &BitcoinParser{Params: GetChainParams("main")}, + parser: NewBitcoinParser(GetChainParams("main")), }, want: &testTx1, want1: 123456, @@ -260,7 +260,7 @@ func Test_UnpackTx(t *testing.T) { name: "testnet-1", args: args{ packedTx: testTxPacked2, - parser: &BitcoinParser{Params: GetChainParams("test")}, + parser: NewBitcoinParser(GetChainParams("test")), }, want: &testTx2, want1: 510234, diff --git a/bchain/coins/btc/bitcoinrpc.go b/bchain/coins/btc/bitcoinrpc.go index 8a54927d..a41f2b50 100644 --- a/bchain/coins/btc/bitcoinrpc.go +++ b/bchain/coins/btc/bitcoinrpc.go @@ -105,9 +105,7 @@ func (b *BitcoinRPC) Initialize() error { params := GetChainParams(chainName) // always create parser - b.Parser = &BitcoinParser{ - Params: params, - } + b.Parser = NewBitcoinParser(params) // parameters for getInfo request if params.Net == wire.MainNet { @@ -260,7 +258,7 @@ type cmdGetRawTransaction struct { type resGetRawTransaction struct { Error *bchain.RPCError `json:"error"` - Result bchain.Tx `json:"result"` + Result json.RawMessage `json:"result"` } type resGetRawTransactionNonverbose struct { @@ -612,7 +610,11 @@ func (b *BitcoinRPC) GetTransaction(txid string) (*bchain.Tx, error) { if res.Error != nil { return nil, errors.Annotatef(res.Error, "txid %v", txid) } - return &res.Result, nil + tx, err := b.Parser.ParseTxFromJson(res.Result) + if err != nil { + return nil, errors.Annotatef(err, "txid %v", txid) + } + return tx, nil } // ResyncMempool gets mempool transactions and maps output scripts to transactions. diff --git a/bchain/coins/eth/ethparser.go b/bchain/coins/eth/ethparser.go index 5e3ed45f..1eabd03c 100644 --- a/bchain/coins/eth/ethparser.go +++ b/bchain/coins/eth/ethparser.go @@ -20,6 +20,11 @@ type EthereumParser struct { *bchain.BaseParser } +// NewEthereumParser returns new EthereumParser instance +func NewEthereumParser() *EthereumParser { + return &EthereumParser{&bchain.BaseParser{AddressFactory: bchain.NewBaseAddress}} +} + type rpcTransaction struct { AccountNonce string `json:"nonce" gencodec:"required"` Price string `json:"gasPrice" gencodec:"required"` @@ -55,14 +60,16 @@ func ethNumber(n string) (int64, error) { return 0, errors.Errorf("Not a number: '%v'", n) } -func ethTxToTx(tx *rpcTransaction, blocktime int64, confirmations uint32) (*bchain.Tx, error) { +func (p *EthereumParser) ethTxToTx(tx *rpcTransaction, blocktime int64, confirmations uint32) (*bchain.Tx, error) { txid := ethHashToHash(tx.Hash) var fa, ta []string + var addr bchain.Address if len(tx.From) > 2 { fa = []string{tx.From} } if len(tx.To) > 2 { ta = []string{tx.To} + addr = p.AddressFactory(tx.To) } // temporarily, the complete rpcTransaction without BlockHash is marshalled and hex encoded to bchain.Tx.Hex bh := tx.BlockHash @@ -98,6 +105,7 @@ func ethTxToTx(tx *rpcTransaction, blocktime int64, confirmations uint32) (*bcha // Hex Addresses: ta, }, + Address: addr, }, }, }, nil @@ -230,7 +238,7 @@ func (p *EthereumParser) UnpackTx(buf []byte) (*bchain.Tx, uint32, error) { TransactionIndex: hexutil.EncodeUint64(uint64(pt.TransactionIndex)), Value: hexEncodeBig(pt.Value), } - tx, err := ethTxToTx(&r, int64(pt.BlockTime), 0) + tx, err := p.ethTxToTx(&r, int64(pt.BlockTime), 0) if err != nil { return nil, 0, err } diff --git a/bchain/coins/eth/ethparser_test.go b/bchain/coins/eth/ethparser_test.go index 2f84afd9..16e6fd4b 100644 --- a/bchain/coins/eth/ethparser_test.go +++ b/bchain/coins/eth/ethparser_test.go @@ -47,7 +47,7 @@ func TestEthParser_GetAddrIDFromAddress(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - p := &EthereumParser{} + p := NewEthereumParser() got, err := p.GetAddrIDFromAddress(tt.args.address) if (err != nil) != tt.wantErr { t.Errorf("EthParser.GetAddrIDFromAddress() error = %v, wantErr %v", err, tt.wantErr) @@ -129,7 +129,7 @@ func TestEthereumParser_PackTx(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - p := &EthereumParser{} + p := NewEthereumParser() got, err := p.PackTx(tt.args.tx, tt.args.height, tt.args.blockTime) if (err != nil) != tt.wantErr { t.Errorf("EthereumParser.PackTx() error = %v, wantErr %v", err, tt.wantErr) @@ -173,6 +173,7 @@ func TestEthereumParser_UnpackTx(t *testing.T) { ScriptPubKey: bchain.ScriptPubKey{ Addresses: []string{"0x682b7903a11098cf770c7aef4aa02a85b3f3601a"}, }, + Address: bchain.NewBaseAddress("0x682b7903a11098cf770c7aef4aa02a85b3f3601a"), }, }, }, @@ -196,6 +197,7 @@ func TestEthereumParser_UnpackTx(t *testing.T) { ScriptPubKey: bchain.ScriptPubKey{ Addresses: []string{"0x555ee11fbddc0e49a9bab358a8941ad95ffdb48f"}, }, + Address: bchain.NewBaseAddress("0x555ee11fbddc0e49a9bab358a8941ad95ffdb48f"), }, }, }, @@ -204,7 +206,7 @@ func TestEthereumParser_UnpackTx(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - p := &EthereumParser{} + p := NewEthereumParser() b, err := hex.DecodeString(tt.args.hex) if err != nil { panic(err) diff --git a/bchain/coins/eth/ethrpc.go b/bchain/coins/eth/ethrpc.go index 249a7cb9..e25fd69c 100644 --- a/bchain/coins/eth/ethrpc.go +++ b/bchain/coins/eth/ethrpc.go @@ -73,7 +73,7 @@ func NewEthereumRPC(config json.RawMessage, pushHandler func(bchain.Notification } // always create parser - s.Parser = &EthereumParser{} + s.Parser = NewEthereumParser() s.timeout = time.Duration(c.RPCTimeout) * time.Second // new blocks notifications handling @@ -375,7 +375,7 @@ func (b *EthereumRPC) GetBlock(hash string, height uint32) (*bchain.Block, error bbh, err := b.ethHeaderToBlockHeader(head) btxs := make([]bchain.Tx, len(body.Transactions)) for i, tx := range body.Transactions { - btx, err := ethTxToTx(&tx, int64(head.Time.Uint64()), uint32(bbh.Confirmations)) + btx, err := b.Parser.ethTxToTx(&tx, int64(head.Time.Uint64()), uint32(bbh.Confirmations)) if err != nil { return nil, errors.Annotatef(err, "hash %v, height %v, txid %v", hash, height, tx.Hash.String()) } @@ -410,7 +410,7 @@ func (b *EthereumRPC) GetTransaction(txid string) (*bchain.Tx, error) { var btx *bchain.Tx if tx.BlockNumber == "" { // mempool tx - btx, err = ethTxToTx(tx, 0, 0) + btx, err = b.Parser.ethTxToTx(tx, 0, 0) if err != nil { return nil, errors.Annotatef(err, "txid %v", txid) } @@ -428,7 +428,7 @@ func (b *EthereumRPC) GetTransaction(txid string) (*bchain.Tx, error) { if err != nil { return nil, errors.Annotatef(err, "txid %v", txid) } - btx, err = ethTxToTx(tx, h.Time.Int64(), confirmations) + btx, err = b.Parser.ethTxToTx(tx, h.Time.Int64(), confirmations) if err != nil { return nil, errors.Annotatef(err, "txid %v", txid) } diff --git a/bchain/coins/zec/zcashparser.go b/bchain/coins/zec/zcashparser.go index 00ce5c36..039542ad 100644 --- a/bchain/coins/zec/zcashparser.go +++ b/bchain/coins/zec/zcashparser.go @@ -9,6 +9,11 @@ type ZCashParser struct { *bchain.BaseParser } +// NewZCAshParser returns new ZCAshParser instance +func NewZCashParser() *ZCashParser { + return &ZCashParser{&bchain.BaseParser{AddressFactory: bchain.NewBaseAddress}} +} + // GetAddrIDFromVout returns internal address representation of given transaction output func (p *ZCashParser) GetAddrIDFromVout(output *bchain.Vout) ([]byte, error) { if len(output.ScriptPubKey.Addresses) != 1 { diff --git a/bchain/coins/zec/zcashparser_test.go b/bchain/coins/zec/zcashparser_test.go index 2fdf9ed4..abcb1fb0 100644 --- a/bchain/coins/zec/zcashparser_test.go +++ b/bchain/coins/zec/zcashparser_test.go @@ -103,7 +103,7 @@ func TestPackTx(t *testing.T) { tx: testTx1, height: 292272, blockTime: 1521645728, - parser: &ZCashParser{}, + parser: NewZCashParser(), }, want: testTxPacked1, wantErr: false, @@ -114,7 +114,7 @@ func TestPackTx(t *testing.T) { tx: testTx2, height: 292217, blockTime: 1521637604, - parser: &ZCashParser{}, + parser: NewZCashParser(), }, want: testTxPacked2, wantErr: false, @@ -151,7 +151,7 @@ func TestUnpackTx(t *testing.T) { name: "zec-1", args: args{ packedTx: testTxPacked1, - parser: &ZCashParser{}, + parser: NewZCashParser(), }, want: &testTx1, want1: 292272, @@ -161,7 +161,7 @@ func TestUnpackTx(t *testing.T) { name: "zec-2", args: args{ packedTx: testTxPacked2, - parser: &ZCashParser{}, + parser: NewZCashParser(), }, want: &testTx2, want1: 292217, diff --git a/bchain/coins/zec/zcashrpc.go b/bchain/coins/zec/zcashrpc.go index 3d3010f1..e0029de8 100644 --- a/bchain/coins/zec/zcashrpc.go +++ b/bchain/coins/zec/zcashrpc.go @@ -31,7 +31,7 @@ func (z *ZCashRPC) Initialize() error { return err } - z.Parser = &ZCashParser{} + z.Parser = NewZCashParser() z.Testnet = false z.Network = "livenet" diff --git a/bchain/types.go b/bchain/types.go index 2a2abe8d..8e9dff2e 100644 --- a/bchain/types.go +++ b/bchain/types.go @@ -1,6 +1,7 @@ package bchain import ( + "encoding/json" "errors" "fmt" ) @@ -168,6 +169,7 @@ type BlockChainParser interface { PackTxid(txid string) ([]byte, error) UnpackTxid(buf []byte) (string, error) ParseTx(b []byte) (*Tx, error) + ParseTxFromJson(json.RawMessage) (*Tx, error) PackTx(tx *Tx, height uint32, blockTime int64) ([]byte, error) UnpackTx(buf []byte) (*Tx, uint32, error) // blocks