Fixed empty Address field when transaction was unpacked from JSON

This commit is contained in:
Jakub Matys 2018-05-18 15:04:40 +02:00
parent c5895c466a
commit 3ecb380ef4
14 changed files with 101 additions and 42 deletions

View File

@ -2,6 +2,7 @@ package bchain
import ( import (
"encoding/hex" "encoding/hex"
"encoding/json"
"fmt" "fmt"
"github.com/gogo/protobuf/proto" "github.com/gogo/protobuf/proto"
@ -9,7 +10,9 @@ import (
) )
// BaseParser implements data parsing/handling functionality base for all other parsers // 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 // AddressToOutputScript converts address to ScriptPubKey - currently not implemented
func (p *BaseParser) AddressToOutputScript(address string) ([]byte, error) { 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") 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 // PackedTxidLen returns length in bytes of packed txid
func (p *BaseParser) PackedTxidLen() int { func (p *BaseParser) PackedTxidLen() int {
return 32 return 32
@ -159,7 +179,7 @@ func (p *BaseParser) UnpackTx(buf []byte) (*Tx, uint32, error) {
Value: pto.Value, Value: pto.Value,
} }
if len(pto.Addresses) == 1 { if len(pto.Addresses) == 1 {
vout[i].Address = NewBaseAddress(pto.Addresses[0]) vout[i].Address = p.AddressFactory(pto.Addresses[0])
} }
} }
tx := Tx{ tx := Tx{

View File

@ -19,6 +19,20 @@ type BCashParser struct {
*btc.BitcoinParser *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, // GetChainParams contains network parameters for the main Bitcoin Cash network,
// the regression test Bitcoin Cash network, the test Bitcoin Cash network and // the regression test Bitcoin Cash network, the test Bitcoin Cash network and
// the simulation test Bitcoin Cash network, in this order // the simulation test Bitcoin Cash network, in this order

View File

@ -2,7 +2,6 @@ package bch
import ( import (
"blockbook/bchain" "blockbook/bchain"
"blockbook/bchain/coins/btc"
"bytes" "bytes"
"encoding/hex" "encoding/hex"
"reflect" "reflect"
@ -102,7 +101,7 @@ func TestBcashAddressInSlice(t *testing.T) {
} }
func TestAddressToOutputScript(t *testing.T) { func TestAddressToOutputScript(t *testing.T) {
parser := BCashParser{&btc.BitcoinParser{Params: GetChainParams("test")}} parser := NewBCashParser(GetChainParams("test"))
want, err := hex.DecodeString("76a9144fa927fd3bcf57d4e3c582c3d2eb2bd3df8df47c88ac") want, err := hex.DecodeString("76a9144fa927fd3bcf57d4e3c582c3d2eb2bd3df8df47c88ac")
if err != nil { if err != nil {
panic(err) panic(err)
@ -214,7 +213,7 @@ func Test_UnpackTx(t *testing.T) {
name: "btc-1", name: "btc-1",
args: args{ args: args{
packedTx: testTxPacked1, packedTx: testTxPacked1,
parser: &BCashParser{&btc.BitcoinParser{Params: GetChainParams("main")}}, parser: NewBCashParser(GetChainParams("main")),
}, },
want: &testTx1, want: &testTx1,
want1: 123456, want1: 123456,
@ -224,7 +223,7 @@ func Test_UnpackTx(t *testing.T) {
name: "testnet-1", name: "testnet-1",
args: args{ args: args{
packedTx: testTxPacked2, packedTx: testTxPacked2,
parser: &BCashParser{&btc.BitcoinParser{Params: GetChainParams("test")}}, parser: NewBCashParser(GetChainParams("test")),
}, },
want: &testTx2, want: &testTx2,
want1: 510234, want1: 510234,

View File

@ -41,11 +41,7 @@ func (b *BCashRPC) Initialize() error {
params := GetChainParams(chainName) params := GetChainParams(chainName)
// always create parser // always create parser
b.Parser = &BCashParser{ b.Parser = NewBCashParser(params)
&btc.BitcoinParser{
Params: params,
},
}
// parameters for getInfo request // parameters for getInfo request
if params.Net == bchutil.MainnetMagic { if params.Net == bchutil.MainnetMagic {

View File

@ -20,6 +20,16 @@ type BitcoinParser struct {
Params *chaincfg.Params 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, // GetChainParams contains network parameters for the main Bitcoin network,
// the regression test Bitcoin network, the test Bitcoin network and // the regression test Bitcoin network, the test Bitcoin network and
// the simulation test Bitcoin network, in this order // 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 := p.txFromMsgTx(&t, true)
tx.Hex = hex.EncodeToString(b) 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 return &tx, nil
} }
@ -170,11 +187,5 @@ func (p *BitcoinParser) UnpackTx(buf []byte) (*bchain.Tx, uint32, error) {
} }
tx.Blocktime = bt 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 return tx, height, nil
} }

View File

@ -42,7 +42,7 @@ func TestAddressToOutputScript(t *testing.T) {
wantErr: false, wantErr: false,
}, },
} }
parser := &BitcoinParser{Params: GetChainParams("main")} parser := NewBitcoinParser(GetChainParams("main"))
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
@ -94,7 +94,7 @@ func TestOutputScriptToAddresses(t *testing.T) {
wantErr: false, wantErr: false,
}, },
} }
parser := &BitcoinParser{Params: GetChainParams("main")} parser := NewBitcoinParser(GetChainParams("main"))
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
b, _ := hex.DecodeString(tt.args.script) b, _ := hex.DecodeString(tt.args.script)
@ -202,7 +202,7 @@ func Test_PackTx(t *testing.T) {
tx: testTx1, tx: testTx1,
height: 123456, height: 123456,
blockTime: 1519053802, blockTime: 1519053802,
parser: &BitcoinParser{Params: GetChainParams("main")}, parser: NewBitcoinParser(GetChainParams("main")),
}, },
want: testTxPacked1, want: testTxPacked1,
wantErr: false, wantErr: false,
@ -213,7 +213,7 @@ func Test_PackTx(t *testing.T) {
tx: testTx2, tx: testTx2,
height: 510234, height: 510234,
blockTime: 1235678901, blockTime: 1235678901,
parser: &BitcoinParser{Params: GetChainParams("test")}, parser: NewBitcoinParser(GetChainParams("test")),
}, },
want: testTxPacked2, want: testTxPacked2,
wantErr: false, wantErr: false,
@ -250,7 +250,7 @@ func Test_UnpackTx(t *testing.T) {
name: "btc-1", name: "btc-1",
args: args{ args: args{
packedTx: testTxPacked1, packedTx: testTxPacked1,
parser: &BitcoinParser{Params: GetChainParams("main")}, parser: NewBitcoinParser(GetChainParams("main")),
}, },
want: &testTx1, want: &testTx1,
want1: 123456, want1: 123456,
@ -260,7 +260,7 @@ func Test_UnpackTx(t *testing.T) {
name: "testnet-1", name: "testnet-1",
args: args{ args: args{
packedTx: testTxPacked2, packedTx: testTxPacked2,
parser: &BitcoinParser{Params: GetChainParams("test")}, parser: NewBitcoinParser(GetChainParams("test")),
}, },
want: &testTx2, want: &testTx2,
want1: 510234, want1: 510234,

View File

@ -105,9 +105,7 @@ func (b *BitcoinRPC) Initialize() error {
params := GetChainParams(chainName) params := GetChainParams(chainName)
// always create parser // always create parser
b.Parser = &BitcoinParser{ b.Parser = NewBitcoinParser(params)
Params: params,
}
// parameters for getInfo request // parameters for getInfo request
if params.Net == wire.MainNet { if params.Net == wire.MainNet {
@ -260,7 +258,7 @@ type cmdGetRawTransaction struct {
type resGetRawTransaction struct { type resGetRawTransaction struct {
Error *bchain.RPCError `json:"error"` Error *bchain.RPCError `json:"error"`
Result bchain.Tx `json:"result"` Result json.RawMessage `json:"result"`
} }
type resGetRawTransactionNonverbose struct { type resGetRawTransactionNonverbose struct {
@ -612,7 +610,11 @@ func (b *BitcoinRPC) GetTransaction(txid string) (*bchain.Tx, error) {
if res.Error != nil { if res.Error != nil {
return nil, errors.Annotatef(res.Error, "txid %v", txid) 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. // ResyncMempool gets mempool transactions and maps output scripts to transactions.

View File

@ -20,6 +20,11 @@ type EthereumParser struct {
*bchain.BaseParser *bchain.BaseParser
} }
// NewEthereumParser returns new EthereumParser instance
func NewEthereumParser() *EthereumParser {
return &EthereumParser{&bchain.BaseParser{AddressFactory: bchain.NewBaseAddress}}
}
type rpcTransaction struct { type rpcTransaction struct {
AccountNonce string `json:"nonce" gencodec:"required"` AccountNonce string `json:"nonce" gencodec:"required"`
Price string `json:"gasPrice" 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) 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) txid := ethHashToHash(tx.Hash)
var fa, ta []string var fa, ta []string
var addr bchain.Address
if len(tx.From) > 2 { if len(tx.From) > 2 {
fa = []string{tx.From} fa = []string{tx.From}
} }
if len(tx.To) > 2 { if len(tx.To) > 2 {
ta = []string{tx.To} ta = []string{tx.To}
addr = p.AddressFactory(tx.To)
} }
// temporarily, the complete rpcTransaction without BlockHash is marshalled and hex encoded to bchain.Tx.Hex // temporarily, the complete rpcTransaction without BlockHash is marshalled and hex encoded to bchain.Tx.Hex
bh := tx.BlockHash bh := tx.BlockHash
@ -98,6 +105,7 @@ func ethTxToTx(tx *rpcTransaction, blocktime int64, confirmations uint32) (*bcha
// Hex // Hex
Addresses: ta, Addresses: ta,
}, },
Address: addr,
}, },
}, },
}, nil }, nil
@ -230,7 +238,7 @@ func (p *EthereumParser) UnpackTx(buf []byte) (*bchain.Tx, uint32, error) {
TransactionIndex: hexutil.EncodeUint64(uint64(pt.TransactionIndex)), TransactionIndex: hexutil.EncodeUint64(uint64(pt.TransactionIndex)),
Value: hexEncodeBig(pt.Value), Value: hexEncodeBig(pt.Value),
} }
tx, err := ethTxToTx(&r, int64(pt.BlockTime), 0) tx, err := p.ethTxToTx(&r, int64(pt.BlockTime), 0)
if err != nil { if err != nil {
return nil, 0, err return nil, 0, err
} }

View File

@ -47,7 +47,7 @@ func TestEthParser_GetAddrIDFromAddress(t *testing.T) {
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
p := &EthereumParser{} p := NewEthereumParser()
got, err := p.GetAddrIDFromAddress(tt.args.address) got, err := p.GetAddrIDFromAddress(tt.args.address)
if (err != nil) != tt.wantErr { if (err != nil) != tt.wantErr {
t.Errorf("EthParser.GetAddrIDFromAddress() error = %v, wantErr %v", err, 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 { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { 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) got, err := p.PackTx(tt.args.tx, tt.args.height, tt.args.blockTime)
if (err != nil) != tt.wantErr { if (err != nil) != tt.wantErr {
t.Errorf("EthereumParser.PackTx() error = %v, wantErr %v", err, 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{ ScriptPubKey: bchain.ScriptPubKey{
Addresses: []string{"0x682b7903a11098cf770c7aef4aa02a85b3f3601a"}, Addresses: []string{"0x682b7903a11098cf770c7aef4aa02a85b3f3601a"},
}, },
Address: bchain.NewBaseAddress("0x682b7903a11098cf770c7aef4aa02a85b3f3601a"),
}, },
}, },
}, },
@ -196,6 +197,7 @@ func TestEthereumParser_UnpackTx(t *testing.T) {
ScriptPubKey: bchain.ScriptPubKey{ ScriptPubKey: bchain.ScriptPubKey{
Addresses: []string{"0x555ee11fbddc0e49a9bab358a8941ad95ffdb48f"}, Addresses: []string{"0x555ee11fbddc0e49a9bab358a8941ad95ffdb48f"},
}, },
Address: bchain.NewBaseAddress("0x555ee11fbddc0e49a9bab358a8941ad95ffdb48f"),
}, },
}, },
}, },
@ -204,7 +206,7 @@ func TestEthereumParser_UnpackTx(t *testing.T) {
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
p := &EthereumParser{} p := NewEthereumParser()
b, err := hex.DecodeString(tt.args.hex) b, err := hex.DecodeString(tt.args.hex)
if err != nil { if err != nil {
panic(err) panic(err)

View File

@ -73,7 +73,7 @@ func NewEthereumRPC(config json.RawMessage, pushHandler func(bchain.Notification
} }
// always create parser // always create parser
s.Parser = &EthereumParser{} s.Parser = NewEthereumParser()
s.timeout = time.Duration(c.RPCTimeout) * time.Second s.timeout = time.Duration(c.RPCTimeout) * time.Second
// new blocks notifications handling // new blocks notifications handling
@ -375,7 +375,7 @@ func (b *EthereumRPC) GetBlock(hash string, height uint32) (*bchain.Block, error
bbh, err := b.ethHeaderToBlockHeader(head) bbh, err := b.ethHeaderToBlockHeader(head)
btxs := make([]bchain.Tx, len(body.Transactions)) btxs := make([]bchain.Tx, len(body.Transactions))
for i, tx := range 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 { if err != nil {
return nil, errors.Annotatef(err, "hash %v, height %v, txid %v", hash, height, tx.Hash.String()) 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 var btx *bchain.Tx
if tx.BlockNumber == "" { if tx.BlockNumber == "" {
// mempool tx // mempool tx
btx, err = ethTxToTx(tx, 0, 0) btx, err = b.Parser.ethTxToTx(tx, 0, 0)
if err != nil { if err != nil {
return nil, errors.Annotatef(err, "txid %v", txid) return nil, errors.Annotatef(err, "txid %v", txid)
} }
@ -428,7 +428,7 @@ func (b *EthereumRPC) GetTransaction(txid string) (*bchain.Tx, error) {
if err != nil { if err != nil {
return nil, errors.Annotatef(err, "txid %v", txid) 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 { if err != nil {
return nil, errors.Annotatef(err, "txid %v", txid) return nil, errors.Annotatef(err, "txid %v", txid)
} }

View File

@ -9,6 +9,11 @@ type ZCashParser struct {
*bchain.BaseParser *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 // GetAddrIDFromVout returns internal address representation of given transaction output
func (p *ZCashParser) GetAddrIDFromVout(output *bchain.Vout) ([]byte, error) { func (p *ZCashParser) GetAddrIDFromVout(output *bchain.Vout) ([]byte, error) {
if len(output.ScriptPubKey.Addresses) != 1 { if len(output.ScriptPubKey.Addresses) != 1 {

View File

@ -103,7 +103,7 @@ func TestPackTx(t *testing.T) {
tx: testTx1, tx: testTx1,
height: 292272, height: 292272,
blockTime: 1521645728, blockTime: 1521645728,
parser: &ZCashParser{}, parser: NewZCashParser(),
}, },
want: testTxPacked1, want: testTxPacked1,
wantErr: false, wantErr: false,
@ -114,7 +114,7 @@ func TestPackTx(t *testing.T) {
tx: testTx2, tx: testTx2,
height: 292217, height: 292217,
blockTime: 1521637604, blockTime: 1521637604,
parser: &ZCashParser{}, parser: NewZCashParser(),
}, },
want: testTxPacked2, want: testTxPacked2,
wantErr: false, wantErr: false,
@ -151,7 +151,7 @@ func TestUnpackTx(t *testing.T) {
name: "zec-1", name: "zec-1",
args: args{ args: args{
packedTx: testTxPacked1, packedTx: testTxPacked1,
parser: &ZCashParser{}, parser: NewZCashParser(),
}, },
want: &testTx1, want: &testTx1,
want1: 292272, want1: 292272,
@ -161,7 +161,7 @@ func TestUnpackTx(t *testing.T) {
name: "zec-2", name: "zec-2",
args: args{ args: args{
packedTx: testTxPacked2, packedTx: testTxPacked2,
parser: &ZCashParser{}, parser: NewZCashParser(),
}, },
want: &testTx2, want: &testTx2,
want1: 292217, want1: 292217,

View File

@ -31,7 +31,7 @@ func (z *ZCashRPC) Initialize() error {
return err return err
} }
z.Parser = &ZCashParser{} z.Parser = NewZCashParser()
z.Testnet = false z.Testnet = false
z.Network = "livenet" z.Network = "livenet"

View File

@ -1,6 +1,7 @@
package bchain package bchain
import ( import (
"encoding/json"
"errors" "errors"
"fmt" "fmt"
) )
@ -168,6 +169,7 @@ type BlockChainParser interface {
PackTxid(txid string) ([]byte, error) PackTxid(txid string) ([]byte, error)
UnpackTxid(buf []byte) (string, error) UnpackTxid(buf []byte) (string, error)
ParseTx(b []byte) (*Tx, error) ParseTx(b []byte) (*Tx, error)
ParseTxFromJson(json.RawMessage) (*Tx, error)
PackTx(tx *Tx, height uint32, blockTime int64) ([]byte, error) PackTx(tx *Tx, height uint32, blockTime int64) ([]byte, error)
UnpackTx(buf []byte) (*Tx, uint32, error) UnpackTx(buf []byte) (*Tx, uint32, error)
// blocks // blocks