RPC integration test was generalized for all coins
This commit is contained in:
parent
5e4b7d0b03
commit
8ec0b7c590
@ -8,9 +8,11 @@ import (
|
|||||||
"github.com/juju/errors"
|
"github.com/juju/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type AddressFactoryFunc func(string) (Address, error)
|
||||||
|
|
||||||
// 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, error)
|
AddressFactory AddressFactoryFunc
|
||||||
BlockAddressesToKeep int
|
BlockAddressesToKeep int
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -19,6 +21,11 @@ func (p *BaseParser) AddressToOutputScript(address string) ([]byte, error) {
|
|||||||
return nil, errors.New("AddressToOutputScript: not implemented")
|
return nil, errors.New("AddressToOutputScript: not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// OutputScriptToAddresses converts ScriptPubKey to addresses - currently not implemented
|
||||||
|
func (p *BaseParser) OutputScriptToAddresses(script []byte) ([]string, error) {
|
||||||
|
return nil, errors.New("OutputScriptToAddresses: not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
// ParseBlock parses raw block to our Block struct - currently not implemented
|
// ParseBlock parses raw block to our Block struct - currently not implemented
|
||||||
func (p *BaseParser) ParseBlock(b []byte) (*Block, error) {
|
func (p *BaseParser) ParseBlock(b []byte) (*Block, error) {
|
||||||
return nil, errors.New("ParseBlock: not implemented")
|
return nil, errors.New("ParseBlock: not implemented")
|
||||||
|
|||||||
@ -50,8 +50,8 @@ func NewBCashParser(params *chaincfg.Params, c *btc.Configuration) (*BCashParser
|
|||||||
AddressFactory: func(addr string) (bchain.Address, error) { return newBCashAddress(addr, format) },
|
AddressFactory: func(addr string) (bchain.Address, error) { return newBCashAddress(addr, format) },
|
||||||
BlockAddressesToKeep: c.BlockAddressesToKeep,
|
BlockAddressesToKeep: c.BlockAddressesToKeep,
|
||||||
},
|
},
|
||||||
Params: params,
|
Params: params,
|
||||||
OutputScriptToAddresses: outputScriptToAddresses,
|
OutputScriptToAddressesFunc: outputScriptToAddresses,
|
||||||
},
|
},
|
||||||
AddressFormat: format,
|
AddressFormat: format,
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@ -20,8 +20,8 @@ type OutputScriptToAddressesFunc func(script []byte, params *chaincfg.Params) ([
|
|||||||
// BitcoinParser handle
|
// BitcoinParser handle
|
||||||
type BitcoinParser struct {
|
type BitcoinParser struct {
|
||||||
*bchain.BaseParser
|
*bchain.BaseParser
|
||||||
Params *chaincfg.Params
|
Params *chaincfg.Params
|
||||||
OutputScriptToAddresses OutputScriptToAddressesFunc
|
OutputScriptToAddressesFunc OutputScriptToAddressesFunc
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewBitcoinParser returns new BitcoinParser instance
|
// NewBitcoinParser returns new BitcoinParser instance
|
||||||
@ -72,6 +72,11 @@ func (p *BitcoinParser) AddressToOutputScript(address string) ([]byte, error) {
|
|||||||
return script, nil
|
return script, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// OutputScriptToAddresses converts ScriptPubKey to bitcoin addresses
|
||||||
|
func (p *BitcoinParser) OutputScriptToAddresses(script []byte) ([]string, error) {
|
||||||
|
return p.OutputScriptToAddressesFunc(script, p.Params)
|
||||||
|
}
|
||||||
|
|
||||||
// outputScriptToAddresses converts ScriptPubKey to bitcoin addresses
|
// outputScriptToAddresses converts ScriptPubKey to bitcoin addresses
|
||||||
func outputScriptToAddresses(script []byte, params *chaincfg.Params) ([]string, error) {
|
func outputScriptToAddresses(script []byte, params *chaincfg.Params) ([]string, error) {
|
||||||
_, addresses, _, err := txscript.ExtractPkScriptAddrs(script, params)
|
_, addresses, _, err := txscript.ExtractPkScriptAddrs(script, params)
|
||||||
@ -110,7 +115,7 @@ func (p *BitcoinParser) TxFromMsgTx(t *wire.MsgTx, parseAddresses bool) bchain.T
|
|||||||
for i, out := range t.TxOut {
|
for i, out := range t.TxOut {
|
||||||
addrs := []string{}
|
addrs := []string{}
|
||||||
if parseAddresses {
|
if parseAddresses {
|
||||||
addrs, _ = p.OutputScriptToAddresses(out.PkScript, p.Params)
|
addrs, _ = p.OutputScriptToAddresses(out.PkScript)
|
||||||
}
|
}
|
||||||
s := bchain.ScriptPubKey{
|
s := bchain.ScriptPubKey{
|
||||||
Hex: hex.EncodeToString(out.PkScript),
|
Hex: hex.EncodeToString(out.PkScript),
|
||||||
|
|||||||
27
bchain/tests/rpc/config.json
Normal file
27
bchain/tests/rpc/config.json
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
{
|
||||||
|
"bch": {
|
||||||
|
"url": "http://localhost:18031",
|
||||||
|
"user": "rpc",
|
||||||
|
"pass": "rpc"
|
||||||
|
},
|
||||||
|
"btc": {
|
||||||
|
"url": "http://localhost:18030",
|
||||||
|
"user": "rpc",
|
||||||
|
"pass": "rpc"
|
||||||
|
},
|
||||||
|
"dash": {
|
||||||
|
"url": "http://localhost:18033",
|
||||||
|
"user": "rpc",
|
||||||
|
"pass": "rpc"
|
||||||
|
},
|
||||||
|
"eth": {
|
||||||
|
"url": "ws://localhost:18036",
|
||||||
|
"user": null,
|
||||||
|
"pass": null
|
||||||
|
},
|
||||||
|
"zec": {
|
||||||
|
"url": "http://localhost:18032",
|
||||||
|
"user": "rpc",
|
||||||
|
"pass": "rpc"
|
||||||
|
}
|
||||||
|
}
|
||||||
96
bchain/tests/rpc/data.go
Normal file
96
bchain/tests/rpc/data.go
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
// +build integration
|
||||||
|
|
||||||
|
package rpc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func joinPathsWithCommonElement(p1, p2 string) (string, bool) {
|
||||||
|
idx := strings.IndexRune(p2, filepath.Separator)
|
||||||
|
if idx <= 0 {
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
p2root := p2[:idx]
|
||||||
|
idx = strings.LastIndex(p1, p2root)
|
||||||
|
if idx < 0 {
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
prefix := p1[:idx]
|
||||||
|
return filepath.Join(prefix, p2), true
|
||||||
|
}
|
||||||
|
|
||||||
|
func readDataFile(dir, relDir, filename string) ([]byte, error) {
|
||||||
|
var err error
|
||||||
|
dir, err = filepath.Abs(dir)
|
||||||
|
if err == nil {
|
||||||
|
dir, err = filepath.EvalSymlinks(dir)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
path, ok := joinPathsWithCommonElement(dir, relDir)
|
||||||
|
if !ok {
|
||||||
|
return nil, errors.New("Path not found")
|
||||||
|
}
|
||||||
|
path = filepath.Join(path, filename)
|
||||||
|
return ioutil.ReadFile(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
var testConfigRegistry map[string]*TestConfig
|
||||||
|
|
||||||
|
func LoadTestConfig(coin string) (*TestConfig, error) {
|
||||||
|
if testConfigRegistry == nil {
|
||||||
|
b, err := readDataFile(".", "bchain/tests/rpc", "config.json")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var v map[string]*TestConfig
|
||||||
|
err = json.Unmarshal(b, &v)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
testConfigRegistry = v
|
||||||
|
}
|
||||||
|
c, found := testConfigRegistry[coin]
|
||||||
|
if !found {
|
||||||
|
return nil, errors.New("Test config not found")
|
||||||
|
}
|
||||||
|
return c, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func LoadRPCConfig(coin string) (json.RawMessage, error) {
|
||||||
|
t := `{
|
||||||
|
"coin_name": "%s",
|
||||||
|
"rpcURL": "%s",
|
||||||
|
"rpcUser": "%s",
|
||||||
|
"rpcPass": "%s",
|
||||||
|
"rpcTimeout": 25,
|
||||||
|
"parse": true
|
||||||
|
}`
|
||||||
|
|
||||||
|
c, err := LoadTestConfig(coin)
|
||||||
|
if err != nil {
|
||||||
|
return json.RawMessage{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return json.RawMessage(fmt.Sprintf(t, coin, c.URL, c.User, c.Pass)), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func LoadTestData(coin string) (*TestData, error) {
|
||||||
|
b, err := readDataFile(".", "bchain/tests/rpc/testdata", coin+".json")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var v TestData
|
||||||
|
err = json.Unmarshal(b, &v)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &v, nil
|
||||||
|
}
|
||||||
126
bchain/tests/rpc/rpc.go
Normal file
126
bchain/tests/rpc/rpc.go
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
// +build integration
|
||||||
|
|
||||||
|
package rpc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"blockbook/bchain"
|
||||||
|
"encoding/json"
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
type TestConfig struct {
|
||||||
|
URL string `json:"url"`
|
||||||
|
User string `json:"user"`
|
||||||
|
Pass string `json:"pass"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type TestData struct {
|
||||||
|
BlockHeight uint32 `json:"blockHeight"`
|
||||||
|
BlockHash string `json:"blockHash"`
|
||||||
|
BlockHex string `json:"blockHex"`
|
||||||
|
BlockTxs []string `json:"blockTxs"`
|
||||||
|
TxDetails map[string]*bchain.Tx `json:"txDetails"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Test struct {
|
||||||
|
Client bchain.BlockChain
|
||||||
|
TestData *TestData
|
||||||
|
}
|
||||||
|
|
||||||
|
type TestChainFactoryFunc func(json.RawMessage) (bchain.BlockChain, error)
|
||||||
|
|
||||||
|
func NewTest(coin string, factory TestChainFactoryFunc) (*Test, error) {
|
||||||
|
cfg, err := LoadRPCConfig(coin)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
cli, err := factory(cfg)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
td, err := LoadTestData(coin)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if td.TxDetails != nil {
|
||||||
|
parser := cli.GetChainParser()
|
||||||
|
|
||||||
|
for _, tx := range td.TxDetails {
|
||||||
|
err := setTxAddresses(parser, tx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Test{Client: cli, TestData: td}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func setTxAddresses(parser bchain.BlockChainParser, tx *bchain.Tx) error {
|
||||||
|
// pack and unpack transaction in order to get addresses decoded - ugly but works
|
||||||
|
var tmp *bchain.Tx
|
||||||
|
b, err := parser.PackTx(tx, 0, 0)
|
||||||
|
if err == nil {
|
||||||
|
tmp, _, err = parser.UnpackTx(b)
|
||||||
|
if err == nil {
|
||||||
|
for i := 0; i < len(tx.Vout); i++ {
|
||||||
|
tx.Vout[i].ScriptPubKey.Addresses = tmp.Vout[i].ScriptPubKey.Addresses
|
||||||
|
tx.Vout[i].Address = tmp.Vout[i].Address
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
func (rt *Test) TestGetBlockHash(t *testing.T) {
|
||||||
|
hash, err := rt.Client.GetBlockHash(rt.TestData.BlockHeight)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if hash != rt.TestData.BlockHash {
|
||||||
|
t.Errorf("GetBlockHash() got %q, want %q", hash, rt.TestData.BlockHash)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rt *Test) TestGetBlock(t *testing.T) {
|
||||||
|
blk, err := rt.Client.GetBlock(rt.TestData.BlockHash, 0)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(blk.Txs) != len(rt.TestData.BlockTxs) {
|
||||||
|
t.Errorf("GetBlock() number of transactions: got %d, want %d", len(blk.Txs), len(rt.TestData.BlockTxs))
|
||||||
|
}
|
||||||
|
|
||||||
|
for ti, tx := range blk.Txs {
|
||||||
|
if tx.Txid != rt.TestData.BlockTxs[ti] {
|
||||||
|
t.Errorf("GetBlock() transaction %d: got %s, want %s", ti, tx.Txid, rt.TestData.BlockTxs[ti])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
func (rt *Test) TestGetTransaction(t *testing.T) {
|
||||||
|
for txid, want := range rt.TestData.TxDetails {
|
||||||
|
got, err := rt.Client.GetTransaction(txid)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Confirmations is variable field, we just check if is set and reset it
|
||||||
|
if got.Confirmations > 0 {
|
||||||
|
got.Confirmations = 0
|
||||||
|
} else {
|
||||||
|
t.Errorf("GetTransaction() has empty Confirmations field")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(got, want) {
|
||||||
|
t.Errorf("GetTransaction() got %v, want %v", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
146
bchain/tests/rpc/testdata/bch.json
vendored
Normal file
146
bchain/tests/rpc/testdata/bch.json
vendored
Normal file
File diff suppressed because one or more lines are too long
@ -158,6 +158,7 @@ type BlockChainParser interface {
|
|||||||
GetAddrIDFromAddress(address string) ([]byte, error)
|
GetAddrIDFromAddress(address string) ([]byte, error)
|
||||||
// address to output script conversions
|
// address to output script conversions
|
||||||
AddressToOutputScript(address string) ([]byte, error)
|
AddressToOutputScript(address string) ([]byte, error)
|
||||||
|
OutputScriptToAddresses(script []byte) ([]string, error)
|
||||||
// transactions
|
// transactions
|
||||||
PackedTxidLen() int
|
PackedTxidLen() int
|
||||||
PackTxid(txid string) ([]byte, error)
|
PackTxid(txid string) ([]byte, error)
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user