Merge branch 'v0.0.7'

A big update of Blockbook, with main features:
- new index format, incompatible with release 0.0.6
- computing of address balances
- internal blockchain explorer
pull/68/head
Martin Boehm 2018-10-01 13:32:57 +02:00
commit 6c4204e93f
125 changed files with 6596 additions and 4313 deletions

View File

@ -81,9 +81,8 @@ In section *blockbook* update information how to build and configure Blockbook s
Update *package_maintainer* and *package_maintainer_email* options in section *meta*.
Execute script *contrib/scripts/check-ports.go* that will check mandatory ports and uniquity of registered ports.
Execute script *contrib/scripts/generate-port-registry.go* that will update *docs/ports.md*.
Execute script *go run contrib/scripts/check-and-generate-port-registry.go -w* that checks mandatory ports and
uniqueness of ports and updates registry of ports *docs/ports.md*.
Now you can try generate package definitions as described above in order to check outputs.

26
Gopkg.lock generated
View File

@ -49,12 +49,6 @@
packages = ["ripemd160"]
revision = "53f62d9b43e87a6c56975cf862af7edf33a8d0df"
[[projects]]
branch = "master"
name = "github.com/cpacia/bchutil"
packages = ["."]
revision = "12e86f41eb040d3b85b5d8e3a3a4bed035517c52"
[[projects]]
name = "github.com/dchest/blake256"
packages = ["."]
@ -67,6 +61,12 @@
revision = "1d4478f51bed434f1dadf96dcd9b43aabac66795"
version = "v1.7"
[[projects]]
branch = "master"
name = "github.com/erikdubbelboer/gspt"
packages = ["."]
revision = "e39e726e09cc23d1ccf13b36ce10dbdb4a4510e0"
[[projects]]
name = "github.com/ethereum/go-ethereum"
packages = [".","common","common/hexutil","common/math","core/types","crypto","crypto/secp256k1","crypto/sha3","ethclient","ethdb","log","metrics","p2p/netutil","params","rlp","rpc","trie"]
@ -121,6 +121,18 @@
revision = "ea4d1f681babbce9545c9c5f3d5194a789c89f5b"
version = "v1.2.0"
[[projects]]
branch = "master"
name = "github.com/jakm/bchutil"
packages = ["."]
revision = "9e4fc13b082c87967b0befbcbad6fe5a5aa5ac1a"
[[projects]]
branch = "master"
name = "github.com/jakm/btcutil"
packages = [".","base58","bech32","chaincfg","txscript"]
revision = "a45c5a6a9cb32f0caecb14a4e5a8f82640be1f39"
[[projects]]
branch = "master"
name = "github.com/juju/errors"
@ -244,6 +256,6 @@
[solve-meta]
analyzer-name = "dep"
analyzer-version = 1
inputs-digest = "03817720a4b1b5011cc64018a1344ed3fb4d8228af41c86856069f57851b3819"
inputs-digest = "98fb9a6252acd86d1c0a4b09a42e34673c2ccd6327caf49ed85bcee573601d95"
solver-name = "gps-cdcl"
solver-version = 1

View File

@ -1,65 +1,138 @@
package api
import (
"blockbook/bchain"
"blockbook/common"
"blockbook/db"
"math/big"
"time"
)
const BlockbookAbout = "Blockbook - blockchain indexer for TREZOR wallet https://trezor.io/. Do not use for any other purpose."
type ApiError struct {
Text string
Public bool
}
func (e *ApiError) Error() string {
return e.Text
}
func NewApiError(s string, public bool) error {
return &ApiError{
Text: s,
Public: public,
}
}
type ScriptSig struct {
Hex string `json:"hex"`
Asm string `json:"asm,omitempty"`
}
type Vin struct {
Txid string `json:"txid"`
Vout uint32 `json:"vout"`
Sequence int64 `json:"sequence,omitempty"`
N int `json:"n"`
ScriptSig ScriptSig `json:"scriptSig"`
Addr string `json:"addr"`
ValueSat int64 `json:"valueSat"`
Value float64 `json:"value"`
Txid string `json:"txid"`
Vout uint32 `json:"vout"`
Sequence int64 `json:"sequence,omitempty"`
N int `json:"n"`
ScriptSig ScriptSig `json:"scriptSig"`
AddrDesc bchain.AddressDescriptor `json:"-"`
Addresses []string `json:"addresses"`
Searchable bool `json:"-"`
Value string `json:"value"`
ValueSat big.Int `json:"-"`
}
type ScriptPubKey struct {
Hex string `json:"hex"`
Asm string `json:"asm,omitempty"`
Addresses []string `json:"addresses"`
Type string `json:"type,omitempty"`
Hex string `json:"hex"`
Asm string `json:"asm,omitempty"`
AddrDesc bchain.AddressDescriptor `json:"-"`
Addresses []string `json:"addresses"`
Searchable bool `json:"-"`
Type string `json:"type,omitempty"`
}
type Vout struct {
Value float64 `json:"value"`
Value string `json:"value"`
ValueSat big.Int `json:"-"`
N int `json:"n"`
ScriptPubKey ScriptPubKey `json:"scriptPubKey"`
Spent bool `json:"-"`
SpentTxID string `json:"spentTxId,omitempty"`
SpentIndex int `json:"spentIndex,omitempty"`
SpentHeight int `json:"spentHeight,omitempty"`
}
type Tx struct {
Txid string `json:"txid"`
Version int32 `json:"version,omitempty"`
Locktime uint32 `json:"locktime,omitempty"`
Vin []Vin `json:"vin"`
Vout []Vout `json:"vout"`
Blockhash string `json:"blockhash,omitempty"`
Blockheight int `json:"blockheight"`
Confirmations uint32 `json:"confirmations"`
Time int64 `json:"time,omitempty"`
Blocktime int64 `json:"blocktime"`
ValueOut float64 `json:"valueOut"`
Size int `json:"size,omitempty"`
ValueIn float64 `json:"valueIn"`
Fees float64 `json:"fees"`
WithSpends bool `json:"withSpends,omitempty"`
Txid string `json:"txid"`
Version int32 `json:"version,omitempty"`
Locktime uint32 `json:"locktime,omitempty"`
Vin []Vin `json:"vin"`
Vout []Vout `json:"vout"`
Blockhash string `json:"blockhash,omitempty"`
Blockheight int `json:"blockheight"`
Confirmations uint32 `json:"confirmations"`
Time int64 `json:"time,omitempty"`
Blocktime int64 `json:"blocktime"`
ValueOut string `json:"valueOut"`
Size int `json:"size,omitempty"`
ValueIn string `json:"valueIn"`
Fees string `json:"fees"`
Hex string `json:"hex"`
}
type Paging struct {
Page int `json:"page"`
TotalPages int `json:"totalPages"`
ItemsOnPage int `json:"itemsOnPage"`
}
type Address struct {
AddrStr string `json:"addrStr"`
Balance float64 `json:"balance"`
BalanceSat int64 `json:"balanceSat"`
TotalReceived float64 `json:"totalReceived"`
TotalReceivedSat int64 `json:"totalReceivedSat"`
TotalSent float64 `json:"totalSent"`
TotalSentSat int64 `json:"totalSentSat"`
UnconfirmedBalance float64 `json:"unconfirmedBalance"`
UnconfirmedBalanceSat int64 `json:"unconfirmedBalanceSat"`
UnconfirmedTxApperances int `json:"unconfirmedTxApperances"`
TxApperances int `json:"txApperances"`
Transactions []*Tx `json:"transactions"`
Paging
AddrStr string `json:"addrStr"`
Balance string `json:"balance"`
TotalReceived string `json:"totalReceived"`
TotalSent string `json:"totalSent"`
UnconfirmedBalance string `json:"unconfirmedBalance"`
UnconfirmedTxApperances int `json:"unconfirmedTxApperances"`
TxApperances int `json:"txApperances"`
Transactions []*Tx `json:"txs,omitempty"`
Txids []string `json:"transactions,omitempty"`
}
type Blocks struct {
Paging
Blocks []db.BlockInfo `json:"blocks"`
}
type Block struct {
Paging
bchain.BlockInfo
TxCount int `json:"TxCount"`
Transactions []*Tx `json:"txs,omitempty"`
}
type BlockbookInfo struct {
Coin string `json:"coin"`
Host string `json:"host"`
Version string `json:"version"`
GitCommit string `json:"gitcommit"`
BuildTime string `json:"buildtime"`
SyncMode bool `json:"syncMode"`
InitialSync bool `json:"initialsync"`
InSync bool `json:"inSync"`
BestHeight uint32 `json:"bestHeight"`
LastBlockTime time.Time `json:"lastBlockTime"`
InSyncMempool bool `json:"inSyncMempool"`
LastMempoolTime time.Time `json:"lastMempoolTime"`
MempoolSize int `json:"mempoolSize"`
DbSize int64 `json:"dbSize"`
DbSizeFromColumns int64 `json:"dbSizeFromColumns,omitempty"`
DbColumns []common.InternalStateColumn `json:"dbColumns,omitempty"`
About string `json:"about"`
}
type SystemInfo struct {
Blockbook *BlockbookInfo `json:"blockbook"`
Backend *bchain.ChainInfo `json:"backend"`
}

View File

@ -4,12 +4,16 @@ import (
"blockbook/bchain"
"blockbook/common"
"blockbook/db"
"bytes"
"fmt"
"math/big"
"strconv"
"time"
"github.com/golang/glog"
"github.com/juju/errors"
)
const txsOnPage = 30
// Worker is handle to api worker
type Worker struct {
db *db.RocksDB
@ -31,20 +35,89 @@ func NewWorker(db *db.RocksDB, chain bchain.BlockChain, txCache *db.TxCache, is
return w, nil
}
func (w *Worker) getAddressesFromVout(vout *bchain.Vout) (bchain.AddressDescriptor, []string, bool, error) {
addrDesc, err := w.chainParser.GetAddrDescFromVout(vout)
if err != nil {
return nil, nil, false, err
}
a, s, err := w.chainParser.GetAddressesFromAddrDesc(addrDesc)
return addrDesc, a, s, err
}
// setSpendingTxToVout is helper function, that finds transaction that spent given output and sets it to the output
// there is not an index, it must be found using addresses -> txaddresses -> tx
func (w *Worker) setSpendingTxToVout(vout *Vout, txid string, height, bestheight uint32) error {
err := w.db.GetAddrDescTransactions(vout.ScriptPubKey.AddrDesc, height, ^uint32(0), func(t string, index uint32, isOutput bool) error {
if isOutput == false {
tsp, err := w.db.GetTxAddresses(t)
if err != nil {
glog.Warning("DB inconsistency: tx ", t, ": not found in txAddresses")
} else {
if len(tsp.Inputs) > int(index) {
if tsp.Inputs[index].ValueSat.Cmp(&vout.ValueSat) == 0 {
spentTx, spentHeight, err := w.txCache.GetTransaction(t, bestheight)
if err != nil {
glog.Warning("Tx ", t, ": not found")
} else {
if len(spentTx.Vin) > int(index) {
if spentTx.Vin[index].Txid == txid {
vout.SpentTxID = t
vout.SpentHeight = int(spentHeight)
vout.SpentIndex = int(index)
return &db.StopIteration{}
}
}
}
}
}
}
}
return nil
})
return err
}
// GetSpendingTxid returns transaction id of transaction that spent given output
func (w *Worker) GetSpendingTxid(txid string, n int) (string, error) {
start := time.Now()
bestheight, _, err := w.db.GetBestBlock()
if err != nil {
return "", err
}
tx, err := w.GetTransaction(txid, bestheight, false)
if err != nil {
return "", err
}
if n >= len(tx.Vout) || n < 0 {
return "", NewApiError(fmt.Sprintf("Passed incorrect vout index %v for tx %v, len vout %v", n, tx.Txid, len(tx.Vout)), false)
}
err = w.setSpendingTxToVout(&tx.Vout[n], tx.Txid, uint32(tx.Blockheight), bestheight)
if err != nil {
return "", err
}
glog.Info("GetSpendingTxid ", txid, " ", n, " finished in ", time.Since(start))
return tx.Vout[n].SpentTxID, nil
}
// GetTransaction reads transaction data from txid
func (w *Worker) GetTransaction(txid string, bestheight uint32, spendingTx bool) (*Tx, error) {
func (w *Worker) GetTransaction(txid string, bestheight uint32, spendingTxs bool) (*Tx, error) {
start := time.Now()
bchainTx, height, err := w.txCache.GetTransaction(txid, bestheight)
if err != nil {
return nil, err
return nil, NewApiError(fmt.Sprintf("Tx not found, %v", err), true)
}
ta, err := w.db.GetTxAddresses(txid)
if err != nil {
return nil, errors.Annotatef(err, "GetTxAddresses %v", txid)
}
var blockhash string
if bchainTx.Confirmations > 0 {
blockhash, err = w.db.GetBlockHash(height)
if err != nil {
return nil, err
return nil, errors.Annotatef(err, "GetBlockHash %v", height)
}
}
var valIn, valOut, fees float64
var valInSat, valOutSat, feesSat big.Int
vins := make([]Vin, len(bchainTx.Vin))
for i := range bchainTx.Vin {
bchainVin := &bchainTx.Vin[i]
@ -55,20 +128,43 @@ func (w *Worker) GetTransaction(txid string, bestheight uint32, spendingTx bool)
vin.ScriptSig.Hex = bchainVin.ScriptSig.Hex
// bchainVin.Txid=="" is coinbase transaction
if bchainVin.Txid != "" {
otx, _, err := w.txCache.GetTransaction(bchainVin.Txid, bestheight)
// load spending addresses from TxAddresses
tas, err := w.db.GetTxAddresses(bchainVin.Txid)
if err != nil {
return nil, err
return nil, errors.Annotatef(err, "GetTxAddresses %v", bchainVin.Txid)
}
if len(otx.Vout) > int(vin.Vout) {
vout := &otx.Vout[vin.Vout]
vin.Value = vout.Value
valIn += vout.Value
vin.ValueSat = int64(vout.Value*1E8 + 0.5)
if vout.Address != nil {
a := vout.Address.String()
vin.Addr = a
if tas == nil {
// mempool transactions are not in TxAddresses but confirmed should be there, log a problem
if bchainTx.Confirmations > 0 {
glog.Warning("DB inconsistency: tx ", bchainVin.Txid, ": not found in txAddresses")
}
// try to load from backend
otx, _, err := w.txCache.GetTransaction(bchainVin.Txid, bestheight)
if err != nil {
return nil, errors.Annotatef(err, "txCache.GetTransaction %v", bchainVin.Txid)
}
if len(otx.Vout) > int(vin.Vout) {
vout := &otx.Vout[vin.Vout]
vin.ValueSat = vout.ValueSat
vin.AddrDesc, vin.Addresses, vin.Searchable, err = w.getAddressesFromVout(vout)
if err != nil {
glog.Errorf("getAddressesFromVout error %v, vout %+v", err, vout)
}
}
} else {
if len(tas.Outputs) > int(vin.Vout) {
output := &tas.Outputs[vin.Vout]
vin.ValueSat = output.ValueSat
vin.Value = w.chainParser.AmountToDecimalString(&vin.ValueSat)
vin.AddrDesc = output.AddrDesc
vin.Addresses, vin.Searchable, err = output.Addresses(w.chainParser)
if err != nil {
glog.Errorf("output.Addresses error %v, tx %v, output %v", err, bchainVin.Txid, i)
}
}
}
vin.Value = w.chainParser.AmountToDecimalString(&vin.ValueSat)
valInSat.Add(&valInSat, &vin.ValueSat)
}
}
vouts := make([]Vout, len(bchainTx.Vout))
@ -76,18 +172,25 @@ func (w *Worker) GetTransaction(txid string, bestheight uint32, spendingTx bool)
bchainVout := &bchainTx.Vout[i]
vout := &vouts[i]
vout.N = i
vout.Value = bchainVout.Value
valOut += bchainVout.Value
vout.ValueSat = bchainVout.ValueSat
vout.Value = w.chainParser.AmountToDecimalString(&bchainVout.ValueSat)
valOutSat.Add(&valOutSat, &bchainVout.ValueSat)
vout.ScriptPubKey.Hex = bchainVout.ScriptPubKey.Hex
vout.ScriptPubKey.Addresses = bchainVout.ScriptPubKey.Addresses
if spendingTx {
// TODO
vout.ScriptPubKey.AddrDesc, vout.ScriptPubKey.Addresses, vout.ScriptPubKey.Searchable, err = w.getAddressesFromVout(bchainVout)
if ta != nil {
vout.Spent = ta.Outputs[i].Spent
if spendingTxs && vout.Spent {
err = w.setSpendingTxToVout(vout, bchainTx.Txid, height, bestheight)
if err != nil {
glog.Errorf("setSpendingTxToVout error %v, %v, output %v", err, vout.ScriptPubKey.AddrDesc, vout.N)
}
}
}
}
// for coinbase transactions valIn is 0
fees = valIn - valOut
if fees < 0 {
fees = 0
feesSat.Sub(&valInSat, &valOutSat)
if feesSat.Sign() == -1 {
feesSat.SetUint64(0)
}
// for now do not return size, we would have to compute vsize of segwit transactions
// size:=len(bchainTx.Hex) / 2
@ -96,25 +199,28 @@ func (w *Worker) GetTransaction(txid string, bestheight uint32, spendingTx bool)
Blockheight: int(height),
Blocktime: bchainTx.Blocktime,
Confirmations: bchainTx.Confirmations,
Fees: fees,
Fees: w.chainParser.AmountToDecimalString(&feesSat),
Locktime: bchainTx.LockTime,
WithSpends: spendingTx,
Time: bchainTx.Time,
Txid: bchainTx.Txid,
ValueIn: valIn,
ValueOut: valOut,
ValueIn: w.chainParser.AmountToDecimalString(&valInSat),
ValueOut: w.chainParser.AmountToDecimalString(&valOutSat),
Version: bchainTx.Version,
Hex: bchainTx.Hex,
Vin: vins,
Vout: vouts,
}
if spendingTxs {
glog.Info("GetTransaction ", txid, " finished in ", time.Since(start))
}
return r, nil
}
func (s *Worker) getAddressTxids(address string, mempool bool) ([]string, error) {
func (w *Worker) getAddressTxids(addrDesc bchain.AddressDescriptor, mempool bool) ([]string, error) {
var err error
txids := make([]string, 0)
if !mempool {
err = s.db.GetTransactions(address, 0, ^uint32(0), func(txid string, vout uint32, isOutput bool) error {
err = w.db.GetAddrDescTransactions(addrDesc, 0, ^uint32(0), func(txid string, vout uint32, isOutput bool) error {
txids = append(txids, txid)
return nil
})
@ -122,7 +228,7 @@ func (s *Worker) getAddressTxids(address string, mempool bool) ([]string, error)
return nil, err
}
} else {
m, err := s.chain.GetMempoolTransactions(address)
m, err := w.chain.GetMempoolTransactionsForAddrDesc(addrDesc)
if err != nil {
return nil, err
}
@ -131,26 +237,24 @@ func (s *Worker) getAddressTxids(address string, mempool bool) ([]string, error)
return txids, nil
}
func (t *Tx) getAddrVoutValue(addrID string) float64 {
var val float64
func (t *Tx) getAddrVoutValue(addrDesc bchain.AddressDescriptor) *big.Int {
var val big.Int
for _, vout := range t.Vout {
for _, a := range vout.ScriptPubKey.Addresses {
if a == addrID {
val += vout.Value
}
if bytes.Equal(vout.ScriptPubKey.AddrDesc, addrDesc) {
val.Add(&val, &vout.ValueSat)
}
}
return val
return &val
}
func (t *Tx) getAddrVinValue(addrID string) float64 {
var val float64
func (t *Tx) getAddrVinValue(addrDesc bchain.AddressDescriptor) *big.Int {
var val big.Int
for _, vin := range t.Vin {
if vin.Addr == addrID {
val += vin.Value
if bytes.Equal(vin.AddrDesc, addrDesc) {
val.Add(&val, &vin.ValueSat)
}
}
return val
return &val
}
// UniqueTxidsInReverse reverts the order of transactions (so that newest are first) and removes duplicate transactions
@ -169,75 +273,320 @@ func UniqueTxidsInReverse(txids []string) []string {
return ut[i:]
}
// GetAddress computes address value and gets transactions for given address
func (w *Worker) GetAddress(addrID string, page int) (*Address, error) {
glog.Info(addrID, " start")
txc, err := w.getAddressTxids(addrID, false)
txc = UniqueTxidsInReverse(txc)
if err != nil {
return nil, err
func (w *Worker) txFromTxAddress(txid string, ta *db.TxAddresses, bi *db.BlockInfo, bestheight uint32) *Tx {
var err error
var valInSat, valOutSat, feesSat big.Int
vins := make([]Vin, len(ta.Inputs))
for i := range ta.Inputs {
tai := &ta.Inputs[i]
vin := &vins[i]
vin.N = i
vin.ValueSat = tai.ValueSat
vin.Value = w.chainParser.AmountToDecimalString(&vin.ValueSat)
valInSat.Add(&valInSat, &vin.ValueSat)
vin.Addresses, vin.Searchable, err = tai.Addresses(w.chainParser)
if err != nil {
glog.Errorf("tai.Addresses error %v, tx %v, input %v, tai %+v", err, txid, i, tai)
}
}
txm, err := w.getAddressTxids(addrID, true)
vouts := make([]Vout, len(ta.Outputs))
for i := range ta.Outputs {
tao := &ta.Outputs[i]
vout := &vouts[i]
vout.N = i
vout.ValueSat = tao.ValueSat
vout.Value = w.chainParser.AmountToDecimalString(&vout.ValueSat)
valOutSat.Add(&valOutSat, &vout.ValueSat)
vout.ScriptPubKey.Addresses, vout.ScriptPubKey.Searchable, err = tao.Addresses(w.chainParser)
if err != nil {
glog.Errorf("tai.Addresses error %v, tx %v, output %v, tao %+v", err, txid, i, tao)
}
vout.Spent = tao.Spent
}
// for coinbase transactions valIn is 0
feesSat.Sub(&valInSat, &valOutSat)
if feesSat.Sign() == -1 {
feesSat.SetUint64(0)
}
r := &Tx{
Blockhash: bi.Hash,
Blockheight: int(ta.Height),
Blocktime: bi.Time,
Confirmations: bestheight - ta.Height + 1,
Fees: w.chainParser.AmountToDecimalString(&feesSat),
Time: bi.Time,
Txid: txid,
ValueIn: w.chainParser.AmountToDecimalString(&valInSat),
ValueOut: w.chainParser.AmountToDecimalString(&valOutSat),
Vin: vins,
Vout: vouts,
}
return r
}
func computePaging(count, page, itemsOnPage int) (Paging, int, int, int) {
from := page * itemsOnPage
totalPages := (count - 1) / itemsOnPage
if totalPages < 0 {
totalPages = 0
}
if from >= count {
page = totalPages
}
from = page * itemsOnPage
to := (page + 1) * itemsOnPage
if to > count {
to = count
}
return Paging{
ItemsOnPage: itemsOnPage,
Page: page + 1,
TotalPages: totalPages + 1,
}, from, to, page
}
// GetAddress computes address value and gets transactions for given address
func (w *Worker) GetAddress(address string, page int, txsOnPage int, onlyTxids bool) (*Address, error) {
start := time.Now()
page--
if page < 0 {
page = 0
}
addrDesc, err := w.chainParser.GetAddrDescFromAddress(address)
if err != nil {
return nil, err
return nil, NewApiError(fmt.Sprintf("Address not found, %v", err), true)
}
// ba can be nil if the address is only in mempool!
ba, err := w.db.GetAddrDescBalance(addrDesc)
if err != nil {
return nil, NewApiError(fmt.Sprintf("Address not found, %v", err), true)
}
// convert the address to the format defined by the parser
addresses, _, err := w.chainParser.GetAddressesFromAddrDesc(addrDesc)
if len(addresses) == 1 {
address = addresses[0]
}
txc, err := w.getAddressTxids(addrDesc, false)
if err != nil {
return nil, errors.Annotatef(err, "getAddressTxids %v false", address)
}
txc = UniqueTxidsInReverse(txc)
var txm []string
// if there are only unconfirmed transactions, ba is nil
if ba == nil {
ba = &db.AddrBalance{}
page = 0
}
txm, err = w.getAddressTxids(addrDesc, true)
if err != nil {
return nil, errors.Annotatef(err, "getAddressTxids %v true", address)
}
txm = UniqueTxidsInReverse(txm)
// check if the address exist
if len(txc)+len(txm) == 0 {
return nil, NewApiError("Address not found", true)
}
bestheight, _, err := w.db.GetBestBlock()
if err != nil {
return nil, err
return nil, errors.Annotatef(err, "GetBestBlock")
}
lc := len(txc)
if lc > txsOnPage {
lc = txsOnPage
pg, from, to, page := computePaging(len(txc), page, txsOnPage)
var txs []*Tx
var txids []string
if onlyTxids {
txids = make([]string, len(txm)+to-from)
} else {
txs = make([]*Tx, len(txm)+to-from)
}
txs := make([]*Tx, len(txm)+lc)
txi := 0
var uBal, bal, totRecv, totSent float64
// load mempool transactions
var uBalSat big.Int
for _, tx := range txm {
tx, err := w.GetTransaction(tx, bestheight, false)
// mempool transaction may fail
if err != nil {
glog.Error("GetTransaction ", tx, ": ", err)
glog.Error("GetTransaction in mempool ", tx, ": ", err)
} else {
uBal = tx.getAddrVoutValue(addrID) - tx.getAddrVinValue(addrID)
txs[txi] = tx
txi++
}
}
if page < 0 {
page = 0
}
from := page * txsOnPage
if from > len(txc) {
from = 0
}
to := from + txsOnPage
for i, tx := range txc {
tx, err := w.GetTransaction(tx, bestheight, false)
if err != nil {
return nil, err
} else {
totRecv += tx.getAddrVoutValue(addrID)
totSent += tx.getAddrVinValue(addrID)
if i >= from && i < to {
txs[txi] = tx
uBalSat.Add(&uBalSat, tx.getAddrVoutValue(addrDesc))
uBalSat.Sub(&uBalSat, tx.getAddrVinValue(addrDesc))
if page == 0 {
if onlyTxids {
txids[txi] = tx.Txid
} else {
txs[txi] = tx
}
txi++
}
}
}
bal = totRecv - totSent
r := &Address{
AddrStr: addrID,
Balance: bal,
BalanceSat: int64(bal*1E8 + 0.5),
TotalReceived: totRecv,
TotalReceivedSat: int64(totRecv*1E8 + 0.5),
TotalSent: totSent,
TotalSentSat: int64(totSent*1E8 + 0.5),
Transactions: txs[:txi],
TxApperances: len(txc),
UnconfirmedBalance: uBal,
UnconfirmedTxApperances: len(txm),
if len(txc) != int(ba.Txs) {
glog.Warning("DB inconsistency for address ", address, ": number of txs from column addresses ", len(txc), ", from addressBalance ", ba.Txs)
}
glog.Info(addrID, " finished")
for i := from; i < to; i++ {
txid := txc[i]
if onlyTxids {
txids[txi] = txid
} else {
ta, err := w.db.GetTxAddresses(txid)
if err != nil {
return nil, errors.Annotatef(err, "GetTxAddresses %v", txid)
}
if ta == nil {
glog.Warning("DB inconsistency: tx ", txid, ": not found in txAddresses")
continue
}
bi, err := w.db.GetBlockInfo(ta.Height)
if err != nil {
return nil, errors.Annotatef(err, "GetBlockInfo %v", ta.Height)
}
if bi == nil {
glog.Warning("DB inconsistency: block height ", ta.Height, ": not found in db")
continue
}
txs[txi] = w.txFromTxAddress(txid, ta, bi, bestheight)
}
txi++
}
if onlyTxids {
txids = txids[:txi]
} else {
txs = txs[:txi]
}
r := &Address{
Paging: pg,
AddrStr: address,
Balance: w.chainParser.AmountToDecimalString(&ba.BalanceSat),
TotalReceived: w.chainParser.AmountToDecimalString(ba.ReceivedSat()),
TotalSent: w.chainParser.AmountToDecimalString(&ba.SentSat),
TxApperances: len(txc),
UnconfirmedBalance: w.chainParser.AmountToDecimalString(&uBalSat),
UnconfirmedTxApperances: len(txm),
Transactions: txs,
Txids: txids,
}
glog.Info("GetAddress ", address, " finished in ", time.Since(start))
return r, nil
}
// GetBlocks returns BlockInfo for blocks on given page
func (w *Worker) GetBlocks(page int, blocksOnPage int) (*Blocks, error) {
start := time.Now()
page--
if page < 0 {
page = 0
}
b, _, err := w.db.GetBestBlock()
bestheight := int(b)
if err != nil {
return nil, errors.Annotatef(err, "GetBestBlock")
}
pg, from, to, page := computePaging(bestheight+1, page, blocksOnPage)
r := &Blocks{Paging: pg}
r.Blocks = make([]db.BlockInfo, to-from)
for i := from; i < to; i++ {
bi, err := w.db.GetBlockInfo(uint32(bestheight - i))
if err != nil {
return nil, err
}
r.Blocks[i-from] = *bi
}
glog.Info("GetBlocks page ", page, " finished in ", time.Since(start))
return r, nil
}
// GetBlock returns paged data about block
func (w *Worker) GetBlock(bid string, page int, txsOnPage int) (*Block, error) {
start := time.Now()
page--
if page < 0 {
page = 0
}
var hash string
height, err := strconv.Atoi(bid)
if err == nil && height < int(^uint32(0)) {
hash, err = w.db.GetBlockHash(uint32(height))
} else {
hash = bid
}
bi, err := w.chain.GetBlockInfo(hash)
if err != nil {
if err == bchain.ErrBlockNotFound {
return nil, NewApiError("Block not found", true)
}
return nil, NewApiError(fmt.Sprintf("Block not found, %v", err), true)
}
dbi := &db.BlockInfo{
Hash: bi.Hash,
Height: bi.Height,
Time: bi.Time,
}
txCount := len(bi.Txids)
bestheight, _, err := w.db.GetBestBlock()
if err != nil {
return nil, errors.Annotatef(err, "GetBestBlock")
}
pg, from, to, page := computePaging(txCount, page, txsOnPage)
glog.Info("GetBlock ", bid, ", page ", page, " finished in ", time.Since(start))
txs := make([]*Tx, to-from)
txi := 0
for i := from; i < to; i++ {
txid := bi.Txids[i]
ta, err := w.db.GetTxAddresses(txid)
if err != nil {
return nil, errors.Annotatef(err, "GetTxAddresses %v", txid)
}
if ta == nil {
glog.Warning("DB inconsistency: tx ", txid, ": not found in txAddresses")
continue
}
txs[txi] = w.txFromTxAddress(txid, ta, dbi, bestheight)
txi++
}
txs = txs[:txi]
bi.Txids = nil
return &Block{
Paging: pg,
BlockInfo: *bi,
TxCount: txCount,
Transactions: txs,
}, nil
}
// GetSystemInfo returns information about system
func (w *Worker) GetSystemInfo(internal bool) (*SystemInfo, error) {
start := time.Now()
ci, err := w.chain.GetChainInfo()
if err != nil {
return nil, errors.Annotatef(err, "GetChainInfo")
}
vi := common.GetVersionInfo()
ss, bh, st := w.is.GetSyncState()
ms, mt, msz := w.is.GetMempoolSyncState()
var dbc []common.InternalStateColumn
var dbs int64
if internal {
dbc = w.is.GetAllDBColumnStats()
dbs = w.is.DBSizeTotal()
}
bi := &BlockbookInfo{
Coin: w.is.Coin,
Host: w.is.Host,
Version: vi.Version,
GitCommit: vi.GitCommit,
BuildTime: vi.BuildTime,
SyncMode: w.is.SyncMode,
InitialSync: w.is.InitialSync,
InSync: ss,
BestHeight: bh,
LastBlockTime: st,
InSyncMempool: ms,
LastMempoolTime: mt,
MempoolSize: msz,
DbSize: w.db.DatabaseSizeOnDisk(),
DbSizeFromColumns: dbs,
DbColumns: dbc,
About: BlockbookAbout,
}
glog.Info("GetSystemInfo finished in ", time.Since(start))
return &SystemInfo{bi, ci}, nil
}

View File

@ -3,27 +3,17 @@ package bchain
import (
"encoding/hex"
"encoding/json"
"math/big"
"strings"
"github.com/gogo/protobuf/proto"
"github.com/juju/errors"
)
type AddressFactoryFunc func(string) (Address, error)
// BaseParser implements data parsing/handling functionality base for all other parsers
type BaseParser struct {
AddressFactory AddressFactoryFunc
BlockAddressesToKeep int
}
// AddressToOutputScript converts address to ScriptPubKey - currently not implemented
func (p *BaseParser) AddressToOutputScript(address string) ([]byte, error) {
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")
AmountDecimalPoint int
}
// ParseBlock parses raw block to our Block struct - currently not implemented
@ -36,7 +26,52 @@ 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
const zeros = "0000000000000000000000000000000000000000"
// AmountToBigInt converts amount in json.Number (string) to big.Int
// it uses string operations to avoid problems with rounding
func (p *BaseParser) AmountToBigInt(n json.Number) (big.Int, error) {
var r big.Int
s := string(n)
i := strings.IndexByte(s, '.')
if i == -1 {
s = s + zeros[:p.AmountDecimalPoint]
} else {
z := p.AmountDecimalPoint - len(s) + i + 1
if z > 0 {
s = s[:i] + s[i+1:] + zeros[:z]
} else {
s = s[:i] + s[i+1:len(s)+z]
}
}
if _, ok := r.SetString(s, 10); !ok {
return r, errors.New("AmountToBigInt: failed to convert")
}
return r, nil
}
// AmountToDecimalString converts amount in big.Int to string with decimal point in the correct place
func (p *BaseParser) AmountToDecimalString(a *big.Int) string {
n := a.String()
var s string
if n[0] == '-' {
n = n[1:]
s = "-"
}
if len(n) <= p.AmountDecimalPoint {
n = zeros[:p.AmountDecimalPoint-len(n)+1] + n
}
i := len(n) - p.AmountDecimalPoint
ad := strings.TrimRight(n[i:], "0")
if len(ad) > 0 {
n = n[:i] + "." + ad
} else {
n = n[:i]
}
return s + n
}
// ParseTxFromJson parses JSON message containing transaction and returns Tx struct
func (p *BaseParser) ParseTxFromJson(msg json.RawMessage) (*Tx, error) {
var tx Tx
err := json.Unmarshal(msg, &tx)
@ -44,14 +79,14 @@ func (p *BaseParser) ParseTxFromJson(msg json.RawMessage) (*Tx, error) {
return nil, err
}
for i, vout := range tx.Vout {
if len(vout.ScriptPubKey.Addresses) == 1 {
a, err := p.AddressFactory(vout.ScriptPubKey.Addresses[0])
if err != nil {
return nil, err
}
tx.Vout[i].Address = a
for i := range tx.Vout {
vout := &tx.Vout[i]
// convert vout.JsonValue to big.Int and clear it, it is only temporary value used for unmarshal
vout.ValueSat, err = p.AmountToBigInt(vout.JsonValue)
if err != nil {
return nil, err
}
vout.JsonValue = ""
}
return &tx, nil
@ -127,7 +162,7 @@ func (p *BaseParser) PackTx(tx *Tx, height uint32, blockTime int64) ([]byte, err
Addresses: vo.ScriptPubKey.Addresses,
N: vo.N,
ScriptPubKeyHex: hex,
Value: vo.Value,
ValueSat: vo.ValueSat.Bytes(),
}
}
pt := &ProtoTransaction{
@ -176,20 +211,15 @@ func (p *BaseParser) UnpackTx(buf []byte) (*Tx, uint32, error) {
}
vout := make([]Vout, len(pt.Vout))
for i, pto := range pt.Vout {
var vs big.Int
vs.SetBytes(pto.ValueSat)
vout[i] = Vout{
N: pto.N,
ScriptPubKey: ScriptPubKey{
Addresses: pto.Addresses,
Hex: hex.EncodeToString(pto.ScriptPubKeyHex),
},
Value: pto.Value,
}
if len(pto.Addresses) == 1 {
a, err := p.AddressFactory(pto.Addresses[0])
if err != nil {
return nil, 0, err
}
vout[i].Address = a
ValueSat: vs,
}
}
tx := Tx{
@ -203,29 +233,3 @@ func (p *BaseParser) UnpackTx(buf []byte) (*Tx, uint32, error) {
}
return &tx, pt.Height, nil
}
type baseAddress struct {
addr string
}
func NewBaseAddress(addr string) (Address, error) {
return &baseAddress{addr: addr}, nil
}
func (a baseAddress) String() string {
return a.addr
}
func (a baseAddress) AreEqual(addr string) bool {
return a.String() == addr
}
func (a baseAddress) InSlice(addrs []string) bool {
ea := a.String()
for _, addr := range addrs {
if ea == addr {
return true
}
}
return false
}

View File

@ -0,0 +1,56 @@
package bchain
import (
"encoding/json"
"math/big"
"testing"
)
func NewBaseParser(adp int) *BaseParser {
return &BaseParser{
AmountDecimalPoint: adp,
}
}
var amounts = []struct {
a *big.Int
s string
adp int
alternative string
}{
{big.NewInt(123456789), "1.23456789", 8, "!"},
{big.NewInt(2), "0.00000002", 8, "!"},
{big.NewInt(300000000), "3", 8, "!"},
{big.NewInt(498700000), "4.987", 8, "!"},
{big.NewInt(567890), "0.00000000000056789", 18, "!"},
{big.NewInt(-100000000), "-1", 8, "!"},
{big.NewInt(-8), "-0.00000008", 8, "!"},
{big.NewInt(-89012345678), "-890.12345678", 8, "!"},
{big.NewInt(-12345), "-0.00012345", 8, "!"},
{big.NewInt(12345678), "0.123456789012", 8, "0.12345678"}, // test of truncation of too many decimal places
}
func TestBaseParser_AmountToDecimalString(t *testing.T) {
for _, tt := range amounts {
t.Run(tt.s, func(t *testing.T) {
if got := NewBaseParser(tt.adp).AmountToDecimalString(tt.a); got != tt.s && got != tt.alternative {
t.Errorf("BaseParser.AmountToDecimalString() = %v, want %v", got, tt.s)
}
})
}
}
func TestBaseParser_AmountToBigInt(t *testing.T) {
for _, tt := range amounts {
t.Run(tt.s, func(t *testing.T) {
got, err := NewBaseParser(tt.adp).AmountToBigInt(json.Number(tt.s))
if err != nil {
t.Errorf("BaseParser.AmountToBigInt() error = %v", err)
return
}
if got.Cmp(tt.a) != 0 {
t.Errorf("BaseParser.AmountToBigInt() = %v, want %v", got, tt.a)
}
})
}
}

View File

@ -5,10 +5,10 @@ import (
"blockbook/bchain/coins/btc"
"fmt"
"github.com/btcsuite/btcd/chaincfg"
"github.com/btcsuite/btcd/txscript"
"github.com/btcsuite/btcutil"
"github.com/cpacia/bchutil"
"github.com/jakm/bchutil"
"github.com/jakm/btcutil"
"github.com/jakm/btcutil/chaincfg"
"github.com/jakm/btcutil/txscript"
"github.com/schancel/cashaddr-converter/address"
)
@ -47,14 +47,14 @@ func NewBCashParser(params *chaincfg.Params, c *btc.Configuration) (*BCashParser
p := &BCashParser{
BitcoinParser: &btc.BitcoinParser{
BaseParser: &bchain.BaseParser{
AddressFactory: func(addr string) (bchain.Address, error) { return newBCashAddress(addr, format) },
BlockAddressesToKeep: c.BlockAddressesToKeep,
AmountDecimalPoint: 8,
},
Params: params,
OutputScriptToAddressesFunc: outputScriptToAddresses,
},
AddressFormat: format,
}
p.OutputScriptToAddressesFunc = p.outputScriptToAddresses
return p, nil
}
@ -78,13 +78,13 @@ func GetChainParams(chain string) *chaincfg.Params {
return params
}
// GetAddrIDFromAddress returns internal address representation of given address
func (p *BCashParser) GetAddrIDFromAddress(address string) ([]byte, error) {
return p.AddressToOutputScript(address)
// GetAddrDescFromAddress returns internal address representation of given address
func (p *BCashParser) GetAddrDescFromAddress(address string) (bchain.AddressDescriptor, error) {
return p.addressToOutputScript(address)
}
// AddressToOutputScript converts bitcoin address to ScriptPubKey
func (p *BCashParser) AddressToOutputScript(address string) ([]byte, error) {
// addressToOutputScript converts bitcoin address to ScriptPubKey
func (p *BCashParser) addressToOutputScript(address string) ([]byte, error) {
if isCashAddr(address) {
da, err := bchutil.DecodeAddress(address, p.Params)
if err != nil {
@ -123,68 +123,49 @@ func isCashAddr(addr string) bool {
}
// outputScriptToAddresses converts ScriptPubKey to bitcoin addresses
func outputScriptToAddresses(script []byte, params *chaincfg.Params) ([]string, error) {
a, err := bchutil.ExtractPkScriptAddrs(script, params)
func (p *BCashParser) outputScriptToAddresses(script []byte) ([]string, bool, error) {
a, err := bchutil.ExtractPkScriptAddrs(script, p.Params)
if err != nil {
return nil, err
}
return []string{a.EncodeAddress()}, nil
}
type bcashAddress struct {
addr string
}
func newBCashAddress(addr string, format AddressFormat) (*bcashAddress, error) {
if isCashAddr(addr) && format == CashAddr {
return &bcashAddress{addr: addr}, nil
}
da, err := address.NewFromString(addr)
if err != nil {
return nil, err
}
var ea string
switch format {
case CashAddr:
if a, err := da.CashAddress(); err != nil {
return nil, err
} else {
ea, err = a.Encode()
if err != nil {
return nil, err
// do not return unknown script type error as error
if err.Error() == "unknown script type" {
// try bitcoin parser to parse P2PK scripts - kind of hack as bchutil does not support pay-to-pubkey
_, addresses, _, err := txscript.ExtractPkScriptAddrs(script, p.Params)
if err == nil && len(addresses) == 1 {
// convert the address back to output script and back to get from bitcoin to bcash
s, err := p.addressToOutputScript(addresses[0].EncodeAddress())
if err == nil {
a, err = bchutil.ExtractPkScriptAddrs(s, p.Params)
if err != nil {
return []string{}, false, nil
}
}
} else {
// try OP_RETURN script
or := btc.TryParseOPReturn(script)
if or != "" {
return []string{or}, false, nil
}
return []string{}, false, nil
}
}
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: ea}, nil
}
func (a *bcashAddress) String() string {
return a.addr
}
func (a *bcashAddress) AreEqual(addr string) bool {
return a.String() == addr
}
func (a *bcashAddress) InSlice(addrs []string) bool {
ea := a.String()
for _, addr := range addrs {
if ea == addr {
return true
return nil, false, err
}
}
return false
// EncodeAddress returns CashAddr address
addr := a.EncodeAddress()
if p.AddressFormat == Legacy {
da, err := address.NewFromString(addr)
if err != nil {
return nil, false, err
}
ca, err := da.Legacy()
if err != nil {
return nil, false, err
}
addr, err = ca.Encode()
if err != nil {
return nil, false, err
}
}
return []string{addr}, len(addr) > 0, nil
}

View File

@ -5,141 +5,218 @@ package bch
import (
"blockbook/bchain"
"blockbook/bchain/coins/btc"
"bytes"
"encoding/hex"
"math/big"
"reflect"
"testing"
)
func TestBcashAddressEncodeAddress(t *testing.T) {
addr1, err := newBCashAddress("13zMwGC5bxRn9ckJ1mgxf7UR8qbbNe2iji", Legacy)
if err != nil {
t.Errorf("newBCashAddress() error = %v", err)
return
func Test_GetAddrDescFromAddress(t *testing.T) {
mainParserCashAddr, mainParserLegacy, testParserCashAddr, _ := setupParsers(t)
tests := []struct {
name string
parser *BCashParser
addresses []string
hex string
wantErr bool
}{
{
name: "test-P2PKH-0",
parser: testParserCashAddr,
addresses: []string{"mnnAKPTSrWjgoi3uEYaQkHA1QEC5btFeBr"},
hex: "76a9144fa927fd3bcf57d4e3c582c3d2eb2bd3df8df47c88ac",
wantErr: false,
},
{
name: "test-P2PKH-1",
parser: testParserCashAddr,
addresses: []string{"bchtest:qp86jfla8084048rckpv85ht90falr050s03ejaesm"},
hex: "76a9144fa927fd3bcf57d4e3c582c3d2eb2bd3df8df47c88ac",
wantErr: false,
},
{
name: "main-P2PKH-0",
parser: mainParserLegacy,
addresses: []string{"129HiRqekqPVucKy2M8zsqvafGgKypciPp"},
hex: "76a9140c8967e6382c7a2ca64d8e850bfc99b7736e1a0d88ac",
wantErr: false,
},
{
name: "main-P2PKH-0",
parser: mainParserCashAddr,
addresses: []string{"bitcoincash:qqxgjelx8qk85t9xfk8g2zlunxmhxms6p55xarv2r5"},
hex: "76a9140c8967e6382c7a2ca64d8e850bfc99b7736e1a0d88ac",
wantErr: false,
},
{
name: "main-P2SH-0",
parser: mainParserCashAddr,
addresses: []string{"3EBEFWPtDYWCNszQ7etoqtWmmygccayLiH"},
hex: "a91488f772450c830a30eddfdc08a93d5f2ae1a30e1787",
wantErr: false,
},
{
name: "main-P2SH-1",
parser: mainParserLegacy,
addresses: []string{"bitcoincash:pzy0wuj9pjps5v8dmlwq32fatu4wrgcwzuayq5nfhh"},
hex: "a91488f772450c830a30eddfdc08a93d5f2ae1a30e1787",
wantErr: false,
},
}
got1 := addr1.String()
if got1 != "13zMwGC5bxRn9ckJ1mgxf7UR8qbbNe2iji" {
t.Errorf("String() got1 = %v, want %v", got1, "13zMwGC5bxRn9ckJ1mgxf7UR8qbbNe2iji")
}
addr2, err := newBCashAddress("13zMwGC5bxRn9ckJ1mgxf7UR8qbbNe2iji", CashAddr)
if err != nil {
t.Errorf("newBCashAddress() error = %v", err)
return
}
got2 := addr2.String()
if got2 != "bitcoincash:qqsvjuqqwgyzvz7zz9xcvxent0ul2xjs6y4d9qvsrf" {
t.Errorf("String() got2 = %v, want %v", got2, "bitcoincash:qqsvjuqqwgyzvz7zz9xcvxent0ul2xjs6y4d9qvsrf")
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := tt.parser.GetAddrDescFromAddress(tt.addresses[0])
if (err != nil) != tt.wantErr {
t.Errorf("GetAddrDescFromAddress() error = %v, wantErr %v", err, tt.wantErr)
return
}
h := hex.EncodeToString(got)
if !reflect.DeepEqual(h, tt.hex) {
t.Errorf("GetAddrDescFromAddress() = %v, want %v", h, tt.hex)
}
})
}
}
func TestBcashAddressAreEqual(t *testing.T) {
addr1, err := newBCashAddress("13zMwGC5bxRn9ckJ1mgxf7UR8qbbNe2iji", Legacy)
if err != nil {
t.Errorf("newBCashAddress() error = %v", err)
return
}
addr2, err := newBCashAddress("13zMwGC5bxRn9ckJ1mgxf7UR8qbbNe2iji", CashAddr)
if err != nil {
t.Errorf("newBCashAddress() error = %v", err)
return
}
got1 := addr1.AreEqual("13zMwGC5bxRn9ckJ1mgxf7UR8qbbNe2iji")
if got1 != true {
t.Errorf("AreEqual() got1 = %v, want %v", got1, true)
}
got2 := addr2.AreEqual("bitcoincash:qqsvjuqqwgyzvz7zz9xcvxent0ul2xjs6y4d9qvsrf")
if got2 != true {
t.Errorf("AreEqual() got2 = %v, want %v", got2, true)
}
got3 := addr1.AreEqual("1HoKgKQh7ZNomWURmS9Tk3z8JM2MWm7S1w")
if got3 != false {
t.Errorf("AreEqual() got3 = %v, want %v", got3, false)
}
got4 := addr2.AreEqual("bitcoincash:qzuyf0gpqj7q5wfck3nyghhklju7r0k3ksmq6d0vch")
if got4 != false {
t.Errorf("AreEqual() got4 = %v, want %v", got4, false)
}
}
func Test_GetAddressesFromAddrDesc(t *testing.T) {
mainParserCashAddr, mainParserLegacy, testParserCashAddr, testParserLegacy := setupParsers(t)
tests := []struct {
name string
parser *BCashParser
addresses []string
searchable bool
hex string
wantErr bool
}{
{
name: "test-P2PKH-0",
parser: testParserLegacy,
addresses: []string{"mnnAKPTSrWjgoi3uEYaQkHA1QEC5btFeBr"},
searchable: true,
hex: "76a9144fa927fd3bcf57d4e3c582c3d2eb2bd3df8df47c88ac",
func TestBcashAddressInSlice(t *testing.T) {
addr1, err := newBCashAddress("13zMwGC5bxRn9ckJ1mgxf7UR8qbbNe2iji", Legacy)
if err != nil {
t.Errorf("newBCashAddress() error = %v", err)
return
wantErr: false,
},
{
name: "test-P2PKH-1",
parser: testParserCashAddr,
addresses: []string{"bchtest:qp86jfla8084048rckpv85ht90falr050s03ejaesm"},
searchable: true,
hex: "76a9144fa927fd3bcf57d4e3c582c3d2eb2bd3df8df47c88ac",
wantErr: false,
},
{
name: "main-P2PKH-0",
parser: mainParserLegacy,
addresses: []string{"129HiRqekqPVucKy2M8zsqvafGgKypciPp"},
searchable: true,
hex: "76a9140c8967e6382c7a2ca64d8e850bfc99b7736e1a0d88ac",
wantErr: false,
},
{
name: "main-P2PKH-0",
parser: mainParserCashAddr,
addresses: []string{"bitcoincash:qqxgjelx8qk85t9xfk8g2zlunxmhxms6p55xarv2r5"},
searchable: true,
hex: "76a9140c8967e6382c7a2ca64d8e850bfc99b7736e1a0d88ac",
wantErr: false,
},
{
name: "main-P2SH-0",
parser: mainParserLegacy,
addresses: []string{"3EBEFWPtDYWCNszQ7etoqtWmmygccayLiH"},
searchable: true,
hex: "a91488f772450c830a30eddfdc08a93d5f2ae1a30e1787",
wantErr: false,
},
{
name: "main-P2SH-1",
parser: mainParserCashAddr,
addresses: []string{"bitcoincash:pzy0wuj9pjps5v8dmlwq32fatu4wrgcwzuayq5nfhh"},
searchable: true,
hex: "a91488f772450c830a30eddfdc08a93d5f2ae1a30e1787",
wantErr: false,
},
{
name: "main-P2PK",
parser: mainParserCashAddr,
addresses: []string{"bitcoincash:qqr95pwp0w5jqnh9vcjl4qm4x45atr0er57n49pq75"},
searchable: true,
hex: "2103db3c3977c5165058bf38c46f72d32f4e872112dbafc13083a948676165cd1603ac",
wantErr: false,
},
{
name: "OP_RETURN ascii",
parser: mainParserCashAddr,
addresses: []string{"OP_RETURN (ahoj)"},
searchable: false,
hex: "6a0461686f6a",
wantErr: false,
},
{
name: "OP_RETURN hex",
parser: mainParserCashAddr,
addresses: []string{"OP_RETURN 2020f1686f6a20"},
searchable: false,
hex: "6a072020f1686f6a20",
wantErr: false,
},
{
name: "empty",
parser: mainParserCashAddr,
addresses: []string{},
searchable: false,
hex: "",
wantErr: false,
},
}
addr2, err := newBCashAddress("13zMwGC5bxRn9ckJ1mgxf7UR8qbbNe2iji", CashAddr)
if err != nil {
t.Errorf("newBCashAddress() error = %v", err)
return
}
got1 := addr1.InSlice([]string{"13zMwGC5bxRn9ckJ1mgxf7UR8qbbNe2iji", "1HoKgKQh7ZNomWURmS9Tk3z8JM2MWm7S1w"})
if got1 != true {
t.Errorf("InSlice() got1 = %v, want %v", got1, true)
}
got2 := addr2.InSlice([]string{"bitcoincash:qzuyf0gpqj7q5wfck3nyghhklju7r0k3ksmq6d0vch", "bitcoincash:qqsvjuqqwgyzvz7zz9xcvxent0ul2xjs6y4d9qvsrf"})
if got2 != true {
t.Errorf("InSlice() got2 = %v, want %v", got2, true)
}
got3 := addr1.InSlice([]string{"1HoKgKQh7ZNomWURmS9Tk3z8JM2MWm7S1w", "1E6Np6dUPYpBSdLMLuwBF8sRQ3cngdaRRY"})
if got3 != false {
t.Errorf("InSlice() got3 = %v, want %v", got3, false)
}
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, 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)
}
got1, err := parser.AddressToOutputScript("mnnAKPTSrWjgoi3uEYaQkHA1QEC5btFeBr")
if err != nil {
t.Errorf("AddressToOutputScript() error = %v", err)
return
}
if !bytes.Equal(got1, want) {
t.Errorf("AddressToOutputScript() got1 = %v, want %v", got1, want)
}
got2, err := parser.AddressToOutputScript("bchtest:qp86jfla8084048rckpv85ht90falr050s03ejaesm")
if err != nil {
t.Errorf("AddressToOutputScript() error = %v", err)
return
}
if !bytes.Equal(got2, want) {
t.Errorf("AddressToOutputScript() got2 = %v, want %v", got2, want)
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
b, _ := hex.DecodeString(tt.hex)
got, got2, err := tt.parser.GetAddressesFromAddrDesc(b)
if (err != nil) != tt.wantErr {
t.Errorf("GetAddressesFromAddrDesc() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.addresses) {
t.Errorf("GetAddressesFromAddrDesc() = %v, want %v", got, tt.addresses)
}
if !reflect.DeepEqual(got2, tt.searchable) {
t.Errorf("GetAddressesFromAddrDesc() = %v, want %v", got2, tt.searchable)
}
})
}
}
var (
testTx1, testTx2 bchain.Tx
testTxPacked1 = "0001e2408ba8d7af5401000000017f9a22c9cbf54bd902400df746f138f37bcf5b4d93eb755820e974ba43ed5f42040000006a4730440220037f4ed5427cde81d55b9b6a2fd08c8a25090c2c2fff3a75c1a57625ca8a7118022076c702fe55969fa08137f71afd4851c48e31082dd3c40c919c92cdbc826758d30121029f6da5623c9f9b68a9baf9c1bc7511df88fa34c6c2f71f7c62f2f03ff48dca80feffffff019c9700000000000017a9146144d57c8aff48492c9dfb914e120b20bad72d6f8773d00700"
testTxPacked2 = "0007c91a899ab7da6a010000000001019d64f0c72a0d206001decbffaa722eb1044534c74eee7a5df8318e42a4323ec10000000017160014550da1f5d25a9dae2eafd6902b4194c4c6500af6ffffffff02809698000000000017a914cd668d781ece600efa4b2404dc91fd26b8b8aed8870553d7360000000017a914246655bdbd54c7e477d0ea2375e86e0db2b8f80a8702473044022076aba4ad559616905fa51d4ddd357fc1fdb428d40cb388e042cdd1da4a1b7357022011916f90c712ead9a66d5f058252efd280439ad8956a967e95d437d246710bc9012102a80a5964c5612bb769ef73147b2cf3c149bc0fd4ecb02f8097629c94ab013ffd00000000"
testTxPacked1 = "0001e2408ba8d7af5401000000017f9a22c9cbf54bd902400df746f138f37bcf5b4d93eb755820e974ba43ed5f42040000006a4730440220037f4ed5427cde81d55b9b6a2fd08c8a25090c2c2fff3a75c1a57625ca8a7118022076c702fe55969fa08137f71afd4851c48e31082dd3c40c919c92cdbc826758d30121029f6da5623c9f9b68a9baf9c1bc7511df88fa34c6c2f71f7c62f2f03ff48dca80feffffff019c9700000000000017a9146144d57c8aff48492c9dfb914e120b20bad72d6f8773d00700"
testTxPacked2 = "0007c91a899ab7da6a010000000001019d64f0c72a0d206001decbffaa722eb1044534c74eee7a5df8318e42a4323ec10000000017160014550da1f5d25a9dae2eafd6902b4194c4c6500af6ffffffff02809698000000000017a914cd668d781ece600efa4b2404dc91fd26b8b8aed8870553d7360000000017a914246655bdbd54c7e477d0ea2375e86e0db2b8f80a8702473044022076aba4ad559616905fa51d4ddd357fc1fdb428d40cb388e042cdd1da4a1b7357022011916f90c712ead9a66d5f058252efd280439ad8956a967e95d437d246710bc9012102a80a5964c5612bb769ef73147b2cf3c149bc0fd4ecb02f8097629c94ab013ffd00000000"
)
func init() {
var (
addr1, addr2, addr3 bchain.Address
err error
)
addr1, err = newBCashAddress("3AZKvpKhSh1o8t1QrX3UeXG9d2BhCRnbcK", Legacy)
if err == nil {
addr2, err = newBCashAddress("2NByHN6A8QYkBATzxf4pRGbCSHD5CEN2TRu", Legacy)
}
if err == nil {
addr3, err = newBCashAddress("2MvZguYaGjM7JihBgNqgLF2Ca2Enb76Hj9D", Legacy)
}
func setupParsers(t *testing.T) (mainParserCashAddr, mainParserLegacy, testParserCashAddr, testParserLegacy *BCashParser) {
parser1, err := NewBCashParser(GetChainParams("main"), &btc.Configuration{AddressFormat: "cashaddr"})
if err != nil {
panic(err)
t.Fatalf("NewBCashParser() error = %v", err)
}
parser2, err := NewBCashParser(GetChainParams("main"), &btc.Configuration{AddressFormat: "legacy"})
if err != nil {
t.Fatalf("NewBCashParser() error = %v", err)
}
parser3, err := NewBCashParser(GetChainParams("test"), &btc.Configuration{AddressFormat: "cashaddr"})
if err != nil {
t.Fatalf("NewBCashParser() error = %v", err)
}
parser4, err := NewBCashParser(GetChainParams("test"), &btc.Configuration{AddressFormat: "legacy"})
if err != nil {
t.Fatalf("NewBCashParser() error = %v", err)
}
return parser1, parser2, parser3, parser4
}
func init() {
testTx1 = bchain.Tx{
Hex: "01000000017f9a22c9cbf54bd902400df746f138f37bcf5b4d93eb755820e974ba43ed5f42040000006a4730440220037f4ed5427cde81d55b9b6a2fd08c8a25090c2c2fff3a75c1a57625ca8a7118022076c702fe55969fa08137f71afd4851c48e31082dd3c40c919c92cdbc826758d30121029f6da5623c9f9b68a9baf9c1bc7511df88fa34c6c2f71f7c62f2f03ff48dca80feffffff019c9700000000000017a9146144d57c8aff48492c9dfb914e120b20bad72d6f8773d00700",
@ -159,15 +236,14 @@ func init() {
},
Vout: []bchain.Vout{
{
Value: 0.00038812,
N: 0,
ValueSat: *big.NewInt(38812),
N: 0,
ScriptPubKey: bchain.ScriptPubKey{
Hex: "a9146144d57c8aff48492c9dfb914e120b20bad72d6f87",
Addresses: []string{
"bitcoincash:pps5f4tu3tl5sjfvnhaeznsjpvst44eddugfcnqpy9",
},
},
Address: addr1,
},
},
}
@ -190,42 +266,31 @@ func init() {
},
Vout: []bchain.Vout{
{
Value: .1,
N: 0,
ValueSat: *big.NewInt(10000000),
N: 0,
ScriptPubKey: bchain.ScriptPubKey{
Hex: "a914cd668d781ece600efa4b2404dc91fd26b8b8aed887",
Addresses: []string{
"bchtest:prxkdrtcrm8xqrh6fvjqfhy3l5nt3w9wmq9fmsvkmz",
},
},
Address: addr2,
},
{
Value: 9.20081157,
N: 1,
ValueSat: *big.NewInt(920081157),
N: 1,
ScriptPubKey: bchain.ScriptPubKey{
Hex: "a914246655bdbd54c7e477d0ea2375e86e0db2b8f80a87",
Addresses: []string{
"bchtest:pqjxv4dah42v0erh6r4zxa0gdcxm9w8cpg0qw8tqf6",
},
},
Address: addr3,
},
},
}
}
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
}
mainParser, _, testParser, _ := setupParsers(t)
type args struct {
packedTx string
@ -239,10 +304,10 @@ func Test_UnpackTx(t *testing.T) {
wantErr bool
}{
{
name: "btc-1",
name: "bcash-1",
args: args{
packedTx: testTxPacked1,
parser: parser1,
parser: mainParser,
},
want: &testTx1,
want1: 123456,
@ -252,7 +317,7 @@ func Test_UnpackTx(t *testing.T) {
name: "testnet-1",
args: args{
packedTx: testTxPacked2,
parser: parser2,
parser: testParser,
},
want: &testTx2,
want1: 510234,

View File

@ -5,9 +5,10 @@ import (
"blockbook/bchain/coins/btc"
"encoding/hex"
"encoding/json"
"math/big"
"github.com/cpacia/bchutil"
"github.com/golang/glog"
"github.com/jakm/bchutil"
"github.com/juju/errors"
)
@ -100,7 +101,10 @@ func (b *BCashRPC) GetBlock(hash string, height uint32) (*bchain.Block, error) {
if err != nil {
return nil, errors.Annotatef(err, "hash %v", hash)
}
// size is not returned by GetBlockHeader and would be overwritten
size := block.Size
block.BlockHeader = *header
block.Size = size
return block, nil
}
@ -126,13 +130,35 @@ func (b *BCashRPC) GetBlockRaw(hash string) ([]byte, error) {
return hex.DecodeString(res.Result)
}
// GetBlockInfo returns extended header (more info than in bchain.BlockHeader) with a list of txids
func (b *BCashRPC) GetBlockInfo(hash string) (*bchain.BlockInfo, error) {
glog.V(1).Info("rpc: getblock (verbosity=1) ", hash)
res := btc.ResGetBlockInfo{}
req := cmdGetBlock{Method: "getblock"}
req.Params.BlockHash = hash
req.Params.Verbose = true
err := b.Call(&req, &res)
if err != nil {
return nil, errors.Annotatef(err, "hash %v", hash)
}
if res.Error != nil {
if isErrBlockNotFound(res.Error) {
return nil, bchain.ErrBlockNotFound
}
return nil, errors.Annotatef(res.Error, "hash %v", hash)
}
return &res.Result, nil
}
// GetBlockFull returns block with given hash.
func (b *BCashRPC) GetBlockFull(hash string) (*bchain.Block, error) {
return nil, errors.New("Not implemented")
}
// EstimateSmartFee returns fee estimation.
func (b *BCashRPC) EstimateSmartFee(blocks int, conservative bool) (float64, error) {
func (b *BCashRPC) EstimateSmartFee(blocks int, conservative bool) (big.Int, error) {
glog.V(1).Info("rpc: estimatesmartfee ", blocks)
res := btc.ResEstimateSmartFee{}
@ -141,13 +167,18 @@ func (b *BCashRPC) EstimateSmartFee(blocks int, conservative bool) (float64, err
// conservative param is omitted
err := b.Call(&req, &res)
var r big.Int
if err != nil {
return 0, err
return r, err
}
if res.Error != nil {
return 0, res.Error
return r, res.Error
}
return res.Result.Feerate, nil
r, err = b.Parser.AmountToBigInt(res.Result.Feerate)
if err != nil {
return r, err
}
return r, nil
}
func isErrBlockNotFound(err *bchain.RPCError) bool {

View File

@ -1,141 +0,0 @@
// +build integration
package bch
import (
"blockbook/bchain"
"blockbook/bchain/tests/rpc"
"encoding/json"
"flag"
"os"
"testing"
)
func getRPCClient(chain string) func(json.RawMessage) (bchain.BlockChain, error) {
return func(cfg json.RawMessage) (bchain.BlockChain, error) {
c, err := NewBCashRPC(cfg, nil)
if err != nil {
return nil, err
}
cli := c.(*BCashRPC)
cli.Parser, err = NewBCashParser(GetChainParams(chain), cli.ChainConfig)
if err != nil {
return nil, err
}
cli.Mempool = bchain.NewUTXOMempool(cli, cli.ChainConfig.MempoolWorkers, cli.ChainConfig.MempoolSubWorkers)
return cli, nil
}
}
var tests struct {
mainnet *rpc.Test
testnet *rpc.Test
}
func TestMain(m *testing.M) {
flag.Parse()
t, err := rpc.NewTest("Bcash", getRPCClient("main"))
if err != nil {
panic(err)
}
tests.mainnet = t
t, err = rpc.NewTest("Bcash Testnet", getRPCClient("test"))
if err != nil {
panic(err)
}
tests.testnet = t
os.Exit(m.Run())
}
func TestBCashRPC_GetBlockHash(t *testing.T) {
tests.mainnet.TestGetBlockHash(t)
}
func TestBCashRPC_GetBlock(t *testing.T) {
tests.mainnet.TestGetBlock(t)
}
func TestBCashRPC_GetTransaction(t *testing.T) {
tests.mainnet.TestGetTransaction(t)
}
func TestBCashRPC_GetTransactionForMempool(t *testing.T) {
tests.mainnet.TestGetTransactionForMempool(t)
}
func TestBCashRPC_MempoolSync(t *testing.T) {
tests.mainnet.TestMempoolSync(t)
}
func TestBCashRPC_GetMempoolEntry(t *testing.T) {
tests.mainnet.TestGetMempoolEntry(t)
}
func TestBCashRPC_EstimateSmartFee(t *testing.T) {
tests.mainnet.TestEstimateSmartFee(t)
}
func TestBCashRPC_EstimateFee(t *testing.T) {
tests.mainnet.TestEstimateFee(t)
}
func TestBCashRPC_GetBestBlockHash(t *testing.T) {
tests.mainnet.TestGetBestBlockHash(t)
}
func TestBCashRPC_GetBestBlockHeight(t *testing.T) {
tests.mainnet.TestGetBestBlockHeight(t)
}
func TestBCashRPC_GetBlockHeader(t *testing.T) {
tests.mainnet.TestGetBlockHeader(t)
}
func TestBCashTestnetRPC_GetBlockHash(t *testing.T) {
tests.testnet.TestGetBlockHash(t)
}
func TestBCashTestnetRPC_GetBlock(t *testing.T) {
tests.testnet.TestGetBlock(t)
}
func TestBCashTestnetRPC_GetTransaction(t *testing.T) {
tests.testnet.TestGetTransaction(t)
}
func TestBCashTestnetRPC_GetTransactionForMempool(t *testing.T) {
tests.testnet.TestGetTransactionForMempool(t)
}
func TestBCashTestnetRPC_MempoolSync(t *testing.T) {
tests.testnet.TestMempoolSync(t)
}
func TestBCashTestnetRPC_GetMempoolEntry(t *testing.T) {
tests.testnet.TestGetMempoolEntry(t)
}
func TestBCashTestnetRPC_EstimateSmartFee(t *testing.T) {
tests.testnet.TestEstimateSmartFee(t)
}
func TestBCashTestnetRPC_EstimateFee(t *testing.T) {
tests.testnet.TestEstimateFee(t)
}
func TestBCashTestnetRPC_GetBestBlockHash(t *testing.T) {
tests.testnet.TestGetBestBlockHash(t)
}
func TestBCashTestnetRPC_GetBestBlockHeight(t *testing.T) {
tests.testnet.TestGetBestBlockHeight(t)
}
func TestBCashTestnetRPC_GetBlockHeader(t *testing.T) {
tests.testnet.TestGetBlockHeader(t)
}

View File

@ -18,6 +18,7 @@ import (
"encoding/json"
"fmt"
"io/ioutil"
"math/big"
"reflect"
"time"
@ -26,29 +27,29 @@ import (
type blockChainFactory func(config json.RawMessage, pushHandler func(bchain.NotificationType)) (bchain.BlockChain, error)
var blockChainFactories = make(map[string]blockChainFactory)
var BlockChainFactories = make(map[string]blockChainFactory)
func init() {
blockChainFactories["Bitcoin"] = btc.NewBitcoinRPC
blockChainFactories["Testnet"] = btc.NewBitcoinRPC
blockChainFactories["Zcash"] = zec.NewZCashRPC
blockChainFactories["Zcash Testnet"] = zec.NewZCashRPC
blockChainFactories["Ethereum"] = eth.NewEthereumRPC
blockChainFactories["Ethereum Classic"] = eth.NewEthereumRPC
blockChainFactories["Ethereum Testnet Ropsten"] = eth.NewEthereumRPC
blockChainFactories["Bcash"] = bch.NewBCashRPC
blockChainFactories["Bcash Testnet"] = bch.NewBCashRPC
blockChainFactories["Bgold"] = btg.NewBGoldRPC
blockChainFactories["Dash"] = dash.NewDashRPC
blockChainFactories["Dash Testnet"] = dash.NewDashRPC
blockChainFactories["Litecoin"] = litecoin.NewLitecoinRPC
blockChainFactories["Litecoin Testnet"] = litecoin.NewLitecoinRPC
blockChainFactories["Dogecoin"] = dogecoin.NewDogecoinRPC
blockChainFactories["Vertcoin"] = vertcoin.NewVertcoinRPC
blockChainFactories["Vertcoin Testnet"] = vertcoin.NewVertcoinRPC
blockChainFactories["Namecoin"] = namecoin.NewNamecoinRPC
blockChainFactories["Monacoin"] = monacoin.NewMonacoinRPC
blockChainFactories["Monacoin Testnet"] = monacoin.NewMonacoinRPC
BlockChainFactories["Bitcoin"] = btc.NewBitcoinRPC
BlockChainFactories["Testnet"] = btc.NewBitcoinRPC
BlockChainFactories["Zcash"] = zec.NewZCashRPC
BlockChainFactories["Zcash Testnet"] = zec.NewZCashRPC
BlockChainFactories["Ethereum"] = eth.NewEthereumRPC
BlockChainFactories["Ethereum Classic"] = eth.NewEthereumRPC
BlockChainFactories["Ethereum Testnet Ropsten"] = eth.NewEthereumRPC
BlockChainFactories["Bcash"] = bch.NewBCashRPC
BlockChainFactories["Bcash Testnet"] = bch.NewBCashRPC
BlockChainFactories["Bgold"] = btg.NewBGoldRPC
BlockChainFactories["Dash"] = dash.NewDashRPC
BlockChainFactories["Dash Testnet"] = dash.NewDashRPC
BlockChainFactories["Litecoin"] = litecoin.NewLitecoinRPC
BlockChainFactories["Litecoin Testnet"] = litecoin.NewLitecoinRPC
BlockChainFactories["Dogecoin"] = dogecoin.NewDogecoinRPC
BlockChainFactories["Vertcoin"] = vertcoin.NewVertcoinRPC
BlockChainFactories["Vertcoin Testnet"] = vertcoin.NewVertcoinRPC
BlockChainFactories["Namecoin"] = namecoin.NewNamecoinRPC
BlockChainFactories["Monacoin"] = monacoin.NewMonacoinRPC
BlockChainFactories["Monacoin Testnet"] = monacoin.NewMonacoinRPC
}
// GetCoinNameFromConfig gets coin name and coin shortcut from config file
@ -79,9 +80,9 @@ func NewBlockChain(coin string, configfile string, pushHandler func(bchain.Notif
if err != nil {
return nil, errors.Annotatef(err, "Error parsing file %v", configfile)
}
bcf, ok := blockChainFactories[coin]
bcf, ok := BlockChainFactories[coin]
if !ok {
return nil, errors.New(fmt.Sprint("Unsupported coin '", coin, "'. Must be one of ", reflect.ValueOf(blockChainFactories).MapKeys()))
return nil, errors.New(fmt.Sprint("Unsupported coin '", coin, "'. Must be one of ", reflect.ValueOf(BlockChainFactories).MapKeys()))
}
bc, err := bcf(config, pushHandler)
if err != nil {
@ -131,9 +132,9 @@ func (c *blockChainWithMetrics) GetSubversion() string {
return c.b.GetSubversion()
}
func (c *blockChainWithMetrics) GetBlockChainInfo() (v string, err error) {
defer func(s time.Time) { c.observeRPCLatency("GetBlockChainInfo", s, err) }(time.Now())
return c.b.GetBlockChainInfo()
func (c *blockChainWithMetrics) GetChainInfo() (v *bchain.ChainInfo, err error) {
defer func(s time.Time) { c.observeRPCLatency("GetChainInfo", s, err) }(time.Now())
return c.b.GetChainInfo()
}
func (c *blockChainWithMetrics) GetBestBlockHash() (v string, err error) {
@ -161,6 +162,11 @@ func (c *blockChainWithMetrics) GetBlock(hash string, height uint32) (v *bchain.
return c.b.GetBlock(hash, height)
}
func (c *blockChainWithMetrics) GetBlockInfo(hash string) (v *bchain.BlockInfo, err error) {
defer func(s time.Time) { c.observeRPCLatency("GetBlockInfo", s, err) }(time.Now())
return c.b.GetBlockInfo(hash)
}
func (c *blockChainWithMetrics) GetMempool() (v []string, err error) {
defer func(s time.Time) { c.observeRPCLatency("GetMempool", s, err) }(time.Now())
return c.b.GetMempool()
@ -176,12 +182,12 @@ func (c *blockChainWithMetrics) GetTransactionForMempool(txid string) (v *bchain
return c.b.GetTransactionForMempool(txid)
}
func (c *blockChainWithMetrics) EstimateSmartFee(blocks int, conservative bool) (v float64, err error) {
func (c *blockChainWithMetrics) EstimateSmartFee(blocks int, conservative bool) (v big.Int, err error) {
defer func(s time.Time) { c.observeRPCLatency("EstimateSmartFee", s, err) }(time.Now())
return c.b.EstimateSmartFee(blocks, conservative)
}
func (c *blockChainWithMetrics) EstimateFee(blocks int) (v float64, err error) {
func (c *blockChainWithMetrics) EstimateFee(blocks int) (v big.Int, err error) {
defer func(s time.Time) { c.observeRPCLatency("EstimateFee", s, err) }(time.Now())
return c.b.EstimateFee(blocks)
}
@ -205,6 +211,11 @@ func (c *blockChainWithMetrics) GetMempoolTransactions(address string) (v []stri
return c.b.GetMempoolTransactions(address)
}
func (c *blockChainWithMetrics) GetMempoolTransactionsForAddrDesc(addrDesc bchain.AddressDescriptor) (v []string, err error) {
defer func(s time.Time) { c.observeRPCLatency("GetMempoolTransactionsForAddrDesc", s, err) }(time.Now())
return c.b.GetMempoolTransactionsForAddrDesc(addrDesc)
}
func (c *blockChainWithMetrics) GetMempoolEntry(txid string) (v *bchain.MempoolEntry, err error) {
defer func(s time.Time) { c.observeRPCLatency("GetMempoolEntry", s, err) }(time.Now())
return c.b.GetMempoolEntry(txid)

View File

@ -5,17 +5,18 @@ import (
"bytes"
"encoding/binary"
"encoding/hex"
"math/big"
vlq "github.com/bsm/go-vlq"
"github.com/btcsuite/btcd/blockchain"
"github.com/btcsuite/btcd/chaincfg"
"github.com/btcsuite/btcd/txscript"
"github.com/btcsuite/btcd/wire"
"github.com/btcsuite/btcutil"
"github.com/jakm/btcutil"
"github.com/jakm/btcutil/chaincfg"
"github.com/jakm/btcutil/txscript"
)
// OutputScriptToAddressesFunc converts ScriptPubKey to bitcoin addresses
type OutputScriptToAddressesFunc func(script []byte, params *chaincfg.Params) ([]string, error)
type OutputScriptToAddressesFunc func(script []byte) ([]string, bool, error)
// BitcoinParser handle
type BitcoinParser struct {
@ -26,14 +27,15 @@ type BitcoinParser struct {
// NewBitcoinParser returns new BitcoinParser instance
func NewBitcoinParser(params *chaincfg.Params, c *Configuration) *BitcoinParser {
return &BitcoinParser{
&bchain.BaseParser{
AddressFactory: bchain.NewBaseAddress,
p := &BitcoinParser{
BaseParser: &bchain.BaseParser{
BlockAddressesToKeep: c.BlockAddressesToKeep,
AmountDecimalPoint: 8,
},
params,
outputScriptToAddresses,
Params: params,
}
p.OutputScriptToAddressesFunc = p.outputScriptToAddresses
return p
}
// GetChainParams contains network parameters for the main Bitcoin network,
@ -49,18 +51,28 @@ func GetChainParams(chain string) *chaincfg.Params {
return &chaincfg.MainNetParams
}
// GetAddrIDFromVout returns internal address representation of given transaction output
func (p *BitcoinParser) GetAddrIDFromVout(output *bchain.Vout) ([]byte, error) {
// GetAddrDescFromVout returns internal address representation (descriptor) of given transaction output
func (p *BitcoinParser) GetAddrDescFromVout(output *bchain.Vout) (bchain.AddressDescriptor, error) {
return hex.DecodeString(output.ScriptPubKey.Hex)
}
// GetAddrIDFromAddress returns internal address representation of given address
func (p *BitcoinParser) GetAddrIDFromAddress(address string) ([]byte, error) {
return p.AddressToOutputScript(address)
// GetAddrDescFromAddress returns internal address representation (descriptor) of given address
func (p *BitcoinParser) GetAddrDescFromAddress(address string) (bchain.AddressDescriptor, error) {
return p.addressToOutputScript(address)
}
// AddressToOutputScript converts bitcoin address to ScriptPubKey
func (p *BitcoinParser) AddressToOutputScript(address string) ([]byte, error) {
// GetAddressesFromAddrDesc returns addresses for given address descriptor with flag if the addresses are searchable
func (p *BitcoinParser) GetAddressesFromAddrDesc(addrDesc bchain.AddressDescriptor) ([]string, bool, error) {
return p.OutputScriptToAddressesFunc(addrDesc)
}
// GetScriptFromAddrDesc returns output script for given address descriptor
func (p *BitcoinParser) GetScriptFromAddrDesc(addrDesc bchain.AddressDescriptor) ([]byte, error) {
return addrDesc, nil
}
// addressToOutputScript converts bitcoin address to ScriptPubKey
func (p *BitcoinParser) addressToOutputScript(address string) ([]byte, error) {
da, err := btcutil.DecodeAddress(address, p.Params)
if err != nil {
return nil, err
@ -72,22 +84,65 @@ func (p *BitcoinParser) AddressToOutputScript(address string) ([]byte, error) {
return script, nil
}
// OutputScriptToAddresses converts ScriptPubKey to bitcoin addresses
func (p *BitcoinParser) OutputScriptToAddresses(script []byte) ([]string, error) {
return p.OutputScriptToAddressesFunc(script, p.Params)
// TryParseOPReturn tries to process OP_RETURN script and return its string representation
func TryParseOPReturn(script []byte) string {
if len(script) > 1 && script[0] == txscript.OP_RETURN {
// trying 2 variants of OP_RETURN data
// 1) OP_RETURN OP_PUSHDATA1 <datalen> <data>
// 2) OP_RETURN <datalen> <data>
var data []byte
var l int
if script[1] == txscript.OP_PUSHDATA1 && len(script) > 2 {
l = int(script[2])
data = script[3:]
if l != len(data) {
l = int(script[1])
data = script[2:]
}
} else {
l = int(script[1])
data = script[2:]
}
if l == len(data) {
isASCII := true
for _, c := range data {
if c < 32 || c > 127 {
isASCII = false
break
}
}
var ed string
if isASCII {
ed = "(" + string(data) + ")"
} else {
ed = hex.EncodeToString(data)
}
return "OP_RETURN " + ed
}
}
return ""
}
// outputScriptToAddresses converts ScriptPubKey to bitcoin addresses
func outputScriptToAddresses(script []byte, params *chaincfg.Params) ([]string, error) {
_, addresses, _, err := txscript.ExtractPkScriptAddrs(script, params)
func (p *BitcoinParser) outputScriptToAddresses(script []byte) ([]string, bool, error) {
sc, addresses, _, err := txscript.ExtractPkScriptAddrs(script, p.Params)
if err != nil {
return nil, err
return nil, false, err
}
rv := make([]string, len(addresses))
for i, a := range addresses {
rv[i] = a.EncodeAddress()
}
return rv, nil
var s bool
if sc != txscript.NonStandardTy && sc != txscript.NullDataTy {
s = true
} else if len(rv) == 0 {
or := TryParseOPReturn(script)
if or != "" {
rv = []string{or}
}
}
return rv, s, nil
}
func (p *BitcoinParser) TxFromMsgTx(t *wire.MsgTx, parseAddresses bool) bchain.Tx {
@ -115,7 +170,7 @@ func (p *BitcoinParser) TxFromMsgTx(t *wire.MsgTx, parseAddresses bool) bchain.T
for i, out := range t.TxOut {
addrs := []string{}
if parseAddresses {
addrs, _ = p.OutputScriptToAddresses(out.PkScript)
addrs, _, _ = p.OutputScriptToAddressesFunc(out.PkScript)
}
s := bchain.ScriptPubKey{
Hex: hex.EncodeToString(out.PkScript),
@ -123,8 +178,10 @@ func (p *BitcoinParser) TxFromMsgTx(t *wire.MsgTx, parseAddresses bool) bchain.T
// missing: Asm,
// missing: Type,
}
var vs big.Int
vs.SetInt64(out.Value)
vout[i] = bchain.Vout{
Value: float64(out.Value) / 1E8,
ValueSat: vs,
N: uint32(i),
ScriptPubKey: s,
}
@ -152,17 +209,6 @@ 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 {
a, err := p.AddressFactory(vout.ScriptPubKey.Addresses[0])
if err != nil {
return nil, err
}
tx.Vout[i].Address = a
}
}
return &tx, nil
}
@ -180,7 +226,13 @@ func (p *BitcoinParser) ParseBlock(b []byte) (*bchain.Block, error) {
txs[ti] = p.TxFromMsgTx(t, false)
}
return &bchain.Block{Txs: txs}, nil
return &bchain.Block{
BlockHeader: bchain.BlockHeader{
Size: len(b),
Time: w.Header.Timestamp.Unix(),
},
Txs: txs,
}, nil
}
// PackTx packs transaction to byte array

View File

@ -5,11 +5,12 @@ package btc
import (
"blockbook/bchain"
"encoding/hex"
"math/big"
"reflect"
"testing"
)
func TestAddressToOutputScript(t *testing.T) {
func Test_GetAddrDescFromAddress(t *testing.T) {
type args struct {
address string
}
@ -48,20 +49,20 @@ func TestAddressToOutputScript(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := parser.AddressToOutputScript(tt.args.address)
got, err := parser.GetAddrDescFromAddress(tt.args.address)
if (err != nil) != tt.wantErr {
t.Errorf("AddressToOutputScript() error = %v, wantErr %v", err, tt.wantErr)
t.Errorf("GetAddrDescFromAddress() error = %v, wantErr %v", err, tt.wantErr)
return
}
h := hex.EncodeToString(got)
if !reflect.DeepEqual(h, tt.want) {
t.Errorf("AddressToOutputScript() = %v, want %v", h, tt.want)
t.Errorf("GetAddrDescFromAddress() = %v, want %v", h, tt.want)
}
})
}
}
func TestOutputScriptToAddresses(t *testing.T) {
func Test_GetAddressesFromAddrDesc(t *testing.T) {
type args struct {
script string
}
@ -69,43 +70,75 @@ func TestOutputScriptToAddresses(t *testing.T) {
name string
args args
want []string
want2 bool
wantErr bool
}{
{
name: "P2PKH",
args: args{script: "76a914be027bf3eac907bd4ac8cb9c5293b6f37662722088ac"},
want: []string{"1JKgN43B9SyLuZH19H5ECvr4KcfrbVHzZ6"},
want2: true,
wantErr: false,
},
{
name: "P2SH",
args: args{script: "a9140394b3cf9a44782c10105b93962daa8dba304d7f87"},
want: []string{"321x69Cb9HZLWwAWGiUBT1U81r1zPLnEjL"},
want2: true,
wantErr: false,
},
{
name: "P2WPKH",
args: args{script: "00141c12afc6b2602607fdbc209f2a053c54ecd2c673"},
want: []string{"bc1qrsf2l34jvqnq0lduyz0j5pfu2nkd93nnq0qggn"},
want2: true,
wantErr: false,
},
{
name: "P2WSH",
args: args{script: "002003973a40ec94c0d10f6f6f0e7a62ba2044b7d19db6ff2bf60651e17fb29d8d29"},
want: []string{"bc1qqwtn5s8vjnqdzrm0du885c46ypzt05vakmljhasx28shlv5a355sw5exgr"},
want2: true,
wantErr: false,
},
{
name: "OP_RETURN ascii",
args: args{script: "6a0461686f6a"},
want: []string{"OP_RETURN (ahoj)"},
want2: false,
wantErr: false,
},
{
name: "OP_RETURN OP_PUSHDATA1 ascii",
args: args{script: "6a4c0b446c6f7568792074657874"},
want: []string{"OP_RETURN (Dlouhy text)"},
want2: false,
wantErr: false,
},
{
name: "OP_RETURN hex",
args: args{script: "6a072020f1686f6a20"},
want: []string{"OP_RETURN 2020f1686f6a20"},
want2: false,
wantErr: false,
},
}
parser := NewBitcoinParser(GetChainParams("main"), &Configuration{})
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
b, _ := hex.DecodeString(tt.args.script)
got, err := outputScriptToAddresses(b, GetChainParams("main"))
got, got2, err := parser.GetAddressesFromAddrDesc(b)
if (err != nil) != tt.wantErr {
t.Errorf("outputScriptToAddresses() error = %v, wantErr %v", err, tt.wantErr)
t.Errorf("GetAddressesFromAddrDesc() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("outputScriptToAddresses() = %v, want %v", got, tt.want)
t.Errorf("GetAddressesFromAddrDesc() = %v, want %v", got, tt.want)
}
if !reflect.DeepEqual(got2, tt.want2) {
t.Errorf("GetAddressesFromAddrDesc() = %v, want %v", got2, tt.want2)
}
})
}
@ -119,21 +152,6 @@ var (
)
func init() {
var (
addr1, addr2, addr3 bchain.Address
err error
)
addr1, err = bchain.NewBaseAddress("3AZKvpKhSh1o8t1QrX3UeXG9d2BhCRnbcK")
if err == nil {
addr2, err = bchain.NewBaseAddress("2NByHN6A8QYkBATzxf4pRGbCSHD5CEN2TRu")
}
if err == nil {
addr3, err = bchain.NewBaseAddress("2MvZguYaGjM7JihBgNqgLF2Ca2Enb76Hj9D")
}
if err != nil {
panic(err)
}
testTx1 = bchain.Tx{
Hex: "01000000017f9a22c9cbf54bd902400df746f138f37bcf5b4d93eb755820e974ba43ed5f42040000006a4730440220037f4ed5427cde81d55b9b6a2fd08c8a25090c2c2fff3a75c1a57625ca8a7118022076c702fe55969fa08137f71afd4851c48e31082dd3c40c919c92cdbc826758d30121029f6da5623c9f9b68a9baf9c1bc7511df88fa34c6c2f71f7c62f2f03ff48dca80feffffff019c9700000000000017a9146144d57c8aff48492c9dfb914e120b20bad72d6f8773d00700",
Blocktime: 1519053802,
@ -152,15 +170,14 @@ func init() {
},
Vout: []bchain.Vout{
{
Value: 0.00038812,
N: 0,
ValueSat: *big.NewInt(38812),
N: 0,
ScriptPubKey: bchain.ScriptPubKey{
Hex: "a9146144d57c8aff48492c9dfb914e120b20bad72d6f87",
Addresses: []string{
"3AZKvpKhSh1o8t1QrX3UeXG9d2BhCRnbcK",
},
},
Address: addr1,
},
},
}
@ -183,26 +200,24 @@ func init() {
},
Vout: []bchain.Vout{
{
Value: .1,
N: 0,
ValueSat: *big.NewInt(10000000),
N: 0,
ScriptPubKey: bchain.ScriptPubKey{
Hex: "a914cd668d781ece600efa4b2404dc91fd26b8b8aed887",
Addresses: []string{
"2NByHN6A8QYkBATzxf4pRGbCSHD5CEN2TRu",
},
},
Address: addr2,
},
{
Value: 9.20081157,
N: 1,
ValueSat: *big.NewInt(920081157),
N: 1,
ScriptPubKey: bchain.ScriptPubKey{
Hex: "a914246655bdbd54c7e477d0ea2375e86e0db2b8f80a87",
Addresses: []string{
"2MvZguYaGjM7JihBgNqgLF2Ca2Enb76Hj9D",
},
},
Address: addr3,
},
},
}

View File

@ -8,6 +8,7 @@ import (
"encoding/json"
"io"
"io/ioutil"
"math/big"
"net"
"net/http"
"time"
@ -36,19 +37,21 @@ type BitcoinRPC struct {
}
type Configuration struct {
CoinName string `json:"coin_name"`
CoinShortcut string `json:"coin_shortcut"`
RPCURL string `json:"rpc_url"`
RPCUser string `json:"rpc_user"`
RPCPass string `json:"rpc_pass"`
RPCTimeout int `json:"rpc_timeout"`
Parse bool `json:"parse"`
MessageQueueBinding string `json:"message_queue_binding"`
Subversion string `json:"subversion"`
BlockAddressesToKeep int `json:"block_addresses_to_keep"`
MempoolWorkers int `json:"mempool_workers"`
MempoolSubWorkers int `json:"mempool_sub_workers"`
AddressFormat string `json:"address_format"`
CoinName string `json:"coin_name"`
CoinShortcut string `json:"coin_shortcut"`
RPCURL string `json:"rpc_url"`
RPCUser string `json:"rpc_user"`
RPCPass string `json:"rpc_pass"`
RPCTimeout int `json:"rpc_timeout"`
Parse bool `json:"parse"`
MessageQueueBinding string `json:"message_queue_binding"`
Subversion string `json:"subversion"`
BlockAddressesToKeep int `json:"block_addresses_to_keep"`
MempoolWorkers int `json:"mempool_workers"`
MempoolSubWorkers int `json:"mempool_sub_workers"`
AddressFormat string `json:"address_format"`
SupportsEstimateFee bool `json:"supports_estimate_fee"`
SupportsEstimateSmartFee bool `json:"supports_estimate_smart_fee"`
}
// NewBitcoinRPC returns new BitcoinRPC instance.
@ -70,6 +73,9 @@ func NewBitcoinRPC(config json.RawMessage, pushHandler func(bchain.NotificationT
if c.MempoolSubWorkers < 1 {
c.MempoolSubWorkers = 1
}
// btc supports both calls, other coins overriding BitcoinRPC can change this
c.SupportsEstimateFee = true
c.SupportsEstimateSmartFee = true
transport := &http.Transport{
Dial: (&net.Dialer{KeepAlive: 600 * time.Second}).Dial,
@ -96,10 +102,11 @@ func NewBitcoinRPC(config json.RawMessage, pushHandler func(bchain.NotificationT
// and if successful it connects to ZeroMQ and creates mempool handler
func (b *BitcoinRPC) GetChainInfoAndInitializeMempool(bc bchain.BlockChain) (string, error) {
// try to connect to block chain and get some info
chainName, err := bc.GetBlockChainInfo()
ci, err := bc.GetChainInfo()
if err != nil {
return "", err
}
chainName := ci.Chain
mq, err := bchain.NewMQ(b.ChainConfig.MessageQueueBinding, b.pushHandler)
if err != nil {
@ -211,10 +218,30 @@ type CmdGetBlockChainInfo struct {
type ResGetBlockChainInfo struct {
Error *bchain.RPCError `json:"error"`
Result struct {
Chain string `json:"chain"`
Blocks int `json:"blocks"`
Headers int `json:"headers"`
Bestblockhash string `json:"bestblockhash"`
Chain string `json:"chain"`
Blocks int `json:"blocks"`
Headers int `json:"headers"`
Bestblockhash string `json:"bestblockhash"`
Difficulty json.Number `json:"difficulty"`
SizeOnDisk int64 `json:"size_on_disk"`
Warnings string `json:"warnings"`
} `json:"result"`
}
// getnetworkinfo
type CmdGetNetworkInfo struct {
Method string `json:"method"`
}
type ResGetNetworkInfo struct {
Error *bchain.RPCError `json:"error"`
Result struct {
Version json.Number `json:"version"`
Subversion json.Number `json:"subversion"`
ProtocolVersion json.Number `json:"protocolversion"`
Timeoffset float64 `json:"timeoffset"`
Warnings string `json:"warnings"`
} `json:"result"`
}
@ -259,9 +286,14 @@ type ResGetBlockRaw struct {
Result string `json:"result"`
}
type BlockThin struct {
bchain.BlockHeader
Txids []string `json:"tx"`
}
type ResGetBlockThin struct {
Error *bchain.RPCError `json:"error"`
Result bchain.ThinBlock `json:"result"`
Result BlockThin `json:"result"`
}
type ResGetBlockFull struct {
@ -269,6 +301,11 @@ type ResGetBlockFull struct {
Result bchain.Block `json:"result"`
}
type ResGetBlockInfo struct {
Error *bchain.RPCError `json:"error"`
Result bchain.BlockInfo `json:"result"`
}
// getrawtransaction
type CmdGetRawTransaction struct {
@ -302,8 +339,8 @@ type CmdEstimateSmartFee struct {
type ResEstimateSmartFee struct {
Error *bchain.RPCError `json:"error"`
Result struct {
Feerate float64 `json:"feerate"`
Blocks int `json:"blocks"`
Feerate json.Number `json:"feerate"`
Blocks int `json:"blocks"`
} `json:"result"`
}
@ -318,7 +355,7 @@ type CmdEstimateFee struct {
type ResEstimateFee struct {
Error *bchain.RPCError `json:"error"`
Result float64 `json:"result"`
Result json.Number `json:"result"`
}
// sendrawtransaction
@ -380,21 +417,48 @@ func (b *BitcoinRPC) GetBestBlockHeight() (uint32, error) {
return res.Result, nil
}
// GetBlockChainInfo returns the name of the block chain: main/test/regtest.
func (b *BitcoinRPC) GetBlockChainInfo() (string, error) {
// GetChainInfo returns information about the connected backend
func (b *BitcoinRPC) GetChainInfo() (*bchain.ChainInfo, error) {
glog.V(1).Info("rpc: getblockchaininfo")
res := ResGetBlockChainInfo{}
req := CmdGetBlockChainInfo{Method: "getblockchaininfo"}
err := b.Call(&req, &res)
resCi := ResGetBlockChainInfo{}
err := b.Call(&CmdGetBlockChainInfo{Method: "getblockchaininfo"}, &resCi)
if err != nil {
return "", err
return nil, err
}
if res.Error != nil {
return "", res.Error
if resCi.Error != nil {
return nil, resCi.Error
}
return res.Result.Chain, nil
glog.V(1).Info("rpc: getnetworkinfo")
resNi := ResGetNetworkInfo{}
err = b.Call(&CmdGetNetworkInfo{Method: "getnetworkinfo"}, &resNi)
if err != nil {
return nil, err
}
if resNi.Error != nil {
return nil, resNi.Error
}
rv := &bchain.ChainInfo{
Bestblockhash: resCi.Result.Bestblockhash,
Blocks: resCi.Result.Blocks,
Chain: resCi.Result.Chain,
Difficulty: string(resCi.Result.Difficulty),
Headers: resCi.Result.Headers,
SizeOnDisk: resCi.Result.SizeOnDisk,
Subversion: string(resNi.Result.Subversion),
Timeoffset: resNi.Result.Timeoffset,
}
rv.Version = string(resNi.Result.Version)
rv.ProtocolVersion = string(resNi.Result.ProtocolVersion)
if len(resCi.Result.Warnings) > 0 {
rv.Warnings = resCi.Result.Warnings + " "
}
if resCi.Result.Warnings != resNi.Result.Warnings {
rv.Warnings += resNi.Result.Warnings
}
return rv, nil
}
func isErrBlockNotFound(err *bchain.RPCError) bool {
@ -448,7 +512,7 @@ func (b *BitcoinRPC) GetBlockHeader(hash string) (*bchain.BlockHeader, error) {
// GetBlock returns block with given hash.
func (b *BitcoinRPC) GetBlock(hash string, height uint32) (*bchain.Block, error) {
var err error
if hash == "" && height > 0 {
if hash == "" {
hash, err = b.GetBlockHash(height)
if err != nil {
return nil, err
@ -477,7 +541,29 @@ func (b *BitcoinRPC) GetBlock(hash string, height uint32) (*bchain.Block, error)
return block, nil
}
// getBlockWithoutHeader is an optimization - it does not call GetBlockHeader to get prev, next hashes
// GetBlockInfo returns extended header (more info than in bchain.BlockHeader) with a list of txids
func (b *BitcoinRPC) GetBlockInfo(hash string) (*bchain.BlockInfo, error) {
glog.V(1).Info("rpc: getblock (verbosity=1) ", hash)
res := ResGetBlockInfo{}
req := CmdGetBlock{Method: "getblock"}
req.Params.BlockHash = hash
req.Params.Verbosity = 1
err := b.Call(&req, &res)
if err != nil {
return nil, errors.Annotatef(err, "hash %v", hash)
}
if res.Error != nil {
if isErrBlockNotFound(res.Error) {
return nil, bchain.ErrBlockNotFound
}
return nil, errors.Annotatef(res.Error, "hash %v", hash)
}
return &res.Result, nil
}
// GetBlockWithoutHeader is an optimization - it does not call GetBlockHeader to get prev, next hashes
// instead it sets to header only block hash and height passed in parameters
func (b *BitcoinRPC) GetBlockWithoutHeader(hash string, height uint32) (*bchain.Block, error) {
data, err := b.GetBlockRaw(hash)
@ -611,13 +697,23 @@ func (b *BitcoinRPC) ResyncMempool(onNewTxAddr bchain.OnNewTxAddrFunc) (int, err
return b.Mempool.Resync(onNewTxAddr)
}
// GetMempoolTransactions returns slice of mempool transactions for given address.
// GetMempoolTransactions returns slice of mempool transactions for given address
func (b *BitcoinRPC) GetMempoolTransactions(address string) ([]string, error) {
return b.Mempool.GetTransactions(address)
}
// EstimateSmartFee returns fee estimation.
func (b *BitcoinRPC) EstimateSmartFee(blocks int, conservative bool) (float64, error) {
// GetMempoolTransactionsForAddrDesc returns slice of mempool transactions for given address descriptor
func (b *BitcoinRPC) GetMempoolTransactionsForAddrDesc(addrDesc bchain.AddressDescriptor) ([]string, error) {
return b.Mempool.GetAddrDescTransactions(addrDesc)
}
// EstimateSmartFee returns fee estimation
func (b *BitcoinRPC) EstimateSmartFee(blocks int, conservative bool) (big.Int, error) {
// use EstimateFee if EstimateSmartFee is not supported
if !b.ChainConfig.SupportsEstimateSmartFee && b.ChainConfig.SupportsEstimateFee {
return b.EstimateFee(blocks)
}
glog.V(1).Info("rpc: estimatesmartfee ", blocks)
res := ResEstimateSmartFee{}
@ -630,17 +726,27 @@ func (b *BitcoinRPC) EstimateSmartFee(blocks int, conservative bool) (float64, e
}
err := b.Call(&req, &res)
var r big.Int
if err != nil {
return 0, err
return r, err
}
if res.Error != nil {
return 0, res.Error
return r, res.Error
}
return res.Result.Feerate, nil
r, err = b.Parser.AmountToBigInt(res.Result.Feerate)
if err != nil {
return r, err
}
return r, nil
}
// EstimateFee returns fee estimation.
func (b *BitcoinRPC) EstimateFee(blocks int) (float64, error) {
func (b *BitcoinRPC) EstimateFee(blocks int) (big.Int, error) {
// use EstimateSmartFee if EstimateFee is not supported
if !b.ChainConfig.SupportsEstimateFee && b.ChainConfig.SupportsEstimateSmartFee {
return b.EstimateSmartFee(blocks, true)
}
glog.V(1).Info("rpc: estimatefee ", blocks)
res := ResEstimateFee{}
@ -648,13 +754,18 @@ func (b *BitcoinRPC) EstimateFee(blocks int) (float64, error) {
req.Params.Blocks = blocks
err := b.Call(&req, &res)
var r big.Int
if err != nil {
return 0, err
return r, err
}
if res.Error != nil {
return 0, res.Error
return r, res.Error
}
return res.Result, nil
r, err = b.Parser.AmountToBigInt(res.Result)
if err != nil {
return r, err
}
return r, nil
}
// SendRawTransaction sends raw transaction.
@ -685,13 +796,20 @@ func (b *BitcoinRPC) GetMempoolEntry(txid string) (*bchain.MempoolEntry, error)
Params: []string{txid},
}
err := b.Call(&req, &res)
if err != nil {
return nil, err
}
if res.Error != nil {
return nil, res.Error
}
res.Result.FeeSat, err = b.Parser.AmountToBigInt(res.Result.Fee)
if err != nil {
return nil, err
}
res.Result.ModifiedFeeSat, err = b.Parser.AmountToBigInt(res.Result.ModifiedFee)
if err != nil {
return nil, err
}
return res.Result, nil
}

View File

@ -1,142 +0,0 @@
// +build integration
package btc
import (
"blockbook/bchain"
"blockbook/bchain/tests/rpc"
"encoding/json"
"flag"
"os"
"testing"
)
func getRPCClient(chain string) func(json.RawMessage) (bchain.BlockChain, error) {
return func(cfg json.RawMessage) (bchain.BlockChain, error) {
c, err := NewBitcoinRPC(cfg, nil)
if err != nil {
return nil, err
}
cli := c.(*BitcoinRPC)
cli.Parser = NewBitcoinParser(GetChainParams(chain), cli.ChainConfig)
if err != nil {
return nil, err
}
cli.Mempool = bchain.NewUTXOMempool(cli, cli.ChainConfig.MempoolWorkers, cli.ChainConfig.MempoolSubWorkers)
return cli, nil
}
}
var tests struct {
mainnet *rpc.Test
testnet *rpc.Test
}
func TestMain(m *testing.M) {
flag.Parse()
t, err := rpc.NewTest("Bitcoin", getRPCClient("main"))
if err != nil {
panic(err)
}
tests.mainnet = t
t, err = rpc.NewTest("Bitcoin Testnet", getRPCClient("test"))
if err != nil {
panic(err)
}
tests.testnet = t
os.Exit(m.Run())
}
func TestBitcoinRPC_GetBlockHash(t *testing.T) {
tests.mainnet.TestGetBlockHash(t)
}
func TestBitcoinRPC_GetBlock(t *testing.T) {
tests.mainnet.TestGetBlock(t)
}
func TestBitcoinRPC_GetTransaction(t *testing.T) {
tests.mainnet.TestGetTransaction(t)
}
func TestBitcoinRPC_GetTransactionForMempool(t *testing.T) {
tests.mainnet.TestGetTransactionForMempool(t)
}
// FIXME
func TestBitcoinRPC_MempoolSync(t *testing.T) {
t.Skip("skipping test, run too long")
// tests.mainnet.TestMempoolSync(t)
}
func TestBitcoinRPC_GetMempoolEntry(t *testing.T) {
tests.mainnet.TestGetMempoolEntry(t)
}
func TestBitcoinRPC_EstimateSmartFee(t *testing.T) {
tests.mainnet.TestEstimateSmartFee(t)
}
func TestBitcoinRPC_EstimateFee(t *testing.T) {
tests.mainnet.TestEstimateFee(t)
}
func TestBitcoinRPC_GetBestBlockHash(t *testing.T) {
tests.mainnet.TestGetBestBlockHash(t)
}
func TestBitcoinRPC_GetBestBlockHeight(t *testing.T) {
tests.mainnet.TestGetBestBlockHeight(t)
}
func TestBitcoinRPC_GetBlockHeader(t *testing.T) {
tests.mainnet.TestGetBlockHeader(t)
}
func TestBitcoinTestnetRPC_GetBlockHash(t *testing.T) {
tests.testnet.TestGetBlockHash(t)
}
func TestBitcoinTestnetRPC_GetBlock(t *testing.T) {
tests.testnet.TestGetBlock(t)
}
func TestBitcoinTestnetRPC_GetTransaction(t *testing.T) {
tests.testnet.TestGetTransaction(t)
}
func TestBitcoinTestnetRPC_GetTransactionForMempool(t *testing.T) {
tests.testnet.TestGetTransactionForMempool(t)
}
func TestBitcoinTestnetRPC_MempoolSync(t *testing.T) {
tests.testnet.TestMempoolSync(t)
}
func TestBitcoinTestnetRPC_GetMempoolEntry(t *testing.T) {
tests.testnet.TestGetMempoolEntry(t)
}
func TestBitcoinTestnetRPC_EstimateSmartFee(t *testing.T) {
tests.testnet.TestEstimateSmartFee(t)
}
func TestBitcoinTestnetRPC_EstimateFee(t *testing.T) {
tests.testnet.TestEstimateFee(t)
}
func TestBitcoinTestnetRPC_GetBestBlockHash(t *testing.T) {
tests.testnet.TestGetBestBlockHash(t)
}
func TestBitcoinTestnetRPC_GetBestBlockHeight(t *testing.T) {
tests.testnet.TestGetBestBlockHeight(t)
}
func TestBitcoinTestnetRPC_GetBlockHeader(t *testing.T) {
tests.testnet.TestGetBlockHeader(t)
}

View File

@ -5,11 +5,12 @@ import (
"blockbook/bchain/coins/btc"
"blockbook/bchain/coins/utils"
"bytes"
"encoding/binary"
"io"
"github.com/btcsuite/btcd/chaincfg"
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/wire"
"github.com/jakm/btcutil/chaincfg"
)
const (
@ -22,13 +23,13 @@ var (
TestNetParams chaincfg.Params
)
func init() {
func initParams() {
MainNetParams = chaincfg.MainNetParams
MainNetParams.Net = MainnetMagic
// Address encoding magics
MainNetParams.PubKeyHashAddrID = 38 // base58 prefix: G
MainNetParams.ScriptHashAddrID = 23 // base58 prefix: A
MainNetParams.PubKeyHashAddrID = []byte{38} // base58 prefix: G
MainNetParams.ScriptHashAddrID = []byte{23} // base58 prefix: A
TestNetParams = chaincfg.TestNet3Params
TestNetParams.Net = TestnetMagic
@ -62,6 +63,9 @@ func NewBGoldParser(params *chaincfg.Params, c *btc.Configuration) *BGoldParser
// the regression test Bitcoin Cash network, the test Bitcoin Cash network and
// the simulation test Bitcoin Cash network, in this order
func GetChainParams(chain string) *chaincfg.Params {
if MainNetParams.Name == "" {
initParams()
}
switch chain {
case "test":
return &TestNetParams
@ -75,11 +79,13 @@ func GetChainParams(chain string) *chaincfg.Params {
// headerFixedLength is the length of fixed fields of a block (i.e. without solution)
// see https://github.com/BTCGPU/BTCGPU/wiki/Technical-Spec#block-header
const headerFixedLength = 44 + (chainhash.HashSize * 3)
const timestampOffset = 100
const timestampLength = 4
// ParseBlock parses raw block to our Block struct
func (p *BGoldParser) ParseBlock(b []byte) (*bchain.Block, error) {
r := bytes.NewReader(b)
err := skipHeader(r, 0)
time, err := getTimestampAndSkipHeader(r, 0)
if err != nil {
return nil, err
}
@ -95,24 +101,41 @@ func (p *BGoldParser) ParseBlock(b []byte) (*bchain.Block, error) {
txs[ti] = p.TxFromMsgTx(t, false)
}
return &bchain.Block{Txs: txs}, nil
return &bchain.Block{
BlockHeader: bchain.BlockHeader{
Size: len(b),
Time: time,
},
Txs: txs,
}, nil
}
func skipHeader(r io.ReadSeeker, pver uint32) error {
_, err := r.Seek(headerFixedLength, io.SeekStart)
func getTimestampAndSkipHeader(r io.ReadSeeker, pver uint32) (int64, error) {
_, err := r.Seek(timestampOffset, io.SeekStart)
if err != nil {
return err
return 0, err
}
buf := make([]byte, timestampLength)
if _, err = io.ReadFull(r, buf); err != nil {
return 0, err
}
time := binary.LittleEndian.Uint32(buf)
_, err = r.Seek(headerFixedLength-timestampOffset-timestampLength, io.SeekCurrent)
if err != nil {
return 0, err
}
size, err := wire.ReadVarInt(r, pver)
if err != nil {
return err
return 0, err
}
_, err = r.Seek(int64(size), io.SeekCurrent)
if err != nil {
return err
return 0, err
}
return nil
return int64(time), nil
}

View File

@ -12,72 +12,86 @@ import (
"testing"
)
var testParseBlockTxs = map[int][]string{
104000: []string{
"331d4ef64118e9e5be75f0f51f1a4c5057550c3320e22ff7206f3e1101f113d0",
"1f4817d8e91c21d8c8d163dabccdd1875f760fd2dc34a1c2b7b8fa204e103597",
"268163b1a1092aa0996d118a6027b0b6f1076627e02addc4e66ae30239936818",
"27277a1049fafa2a46368ad02961d37da633416b014bcd42a1f1391753cbf559",
"276d2d331585d0968762d9788f71ae71524ccba3494f638b2328ac51e52edd3d",
"28d9f85089834c20507cc5e4ec54aaaf5b79feab80cad24a48b8296b6d327a43",
"2a2d66d3d9e8b6f154958a388377f681abd82ce98785a5bbf2e27d0ca454da3f",
"39c9e995a12b638b541d21ed9f9e555716709add6a97c8ba63fe26e4d26bdc3f",
"4a768efc6cc0716932800115989376c2ce3e5e17668b08bd2f43031105a6ac6e",
"4bc41fa0188d988d853a176eb847a541c5adf35348eab21d128904aab36e8574",
"4cad1bd38cc7be880f3a968af6f13a3aeb5dbdb51a774b7d1ae3d6d6bfd114e4",
"6bc558801583bfdb656106889c4b8bd783168133784666338c57e5b2a675a922",
"75eb5c1aa89b18ce71f363f95147942b46645ca9b1e472784fcb8c9a096f4f5c",
"91755365cff22c4eed09a57a8fb7b2faa5c1caa5fa750c89f830a2bb56fa4c68",
"9417d34969891f2a0b9aa3a1226505edf3b429fa1acd21a140d358a217d11d55",
"950fbb5f413af9f7c6e5dabfacf68b715c9851b5cf6ab6806960f7cc7cad2f9d",
"99b134dae55ddfab1f5d5243c2e60030a9ed969ba5915f98840b877f8af28ce0",
"9d7b15eaaccce66e25efe7e2979454ae4968b61281d50f32be9872d2d256c244",
"a54df5296f1d1a6101cee0869ffda03502e417fc72475b7902a6dfd5b9329399",
"adba400f14b476f0c2b11340ee1fa440208b49fd99c1072136198b5c43664826",
"bd7d8fee068bd45b06b4c17ccdf577b4bc2b21c9c4e0cee8453409b0e63b4f5d",
"beabd2d68b66a9b47b6aff23b569c1b59e429074f06bdd4993e9d3c2cb69c992",
"bfa81174d549eb7ed15be3f965686aff3084f22523c52fbed03c3fc3e18b7cda",
"e42472099cb039b1c2248b611ce212580338550977e02bd77accdf29bfd86e96",
"f056e02b12d99377f724a4987cde68ecf6f234fd7e2bdf4324172c03d015ba25",
"f1815cfb1ef4cfe13ff5ec2c15b5bc55fde043db85daca1bb34cc1b491193308",
"f225abce64f75383686fa08abe47242e59e97809a31c8fd7a88acce1e6cbcd27",
"f93f1b125bfa2da5ccaaf30ff96635b905b657d48a5962c24be93884a82ef354",
"fef75d015f2e9926d1d4bf82e567b91e51af66a6e636d03a072f203dd3062ae5",
"051b60a6accead85da54b8d18f4b2360ea946da948d3a27836306d2927fed13e",
"28e47b050ec4064cdbd3364f3be9445d52635e9730691ef71ed1db0f0147afd6",
"446ebde2457102bcbc2c86cac6ff582c595b00346fd0b27ea5a645240020504b",
"46c8fafe2b7bb1646aeefa229b18fa7ffe20fee0a30c4a9ef3e63c36c808f6f7",
"550d96cf82fbe91dcc9b96e7aa404f392ee47400c22a98a7631d29eee43fceaa",
"59b6b78a72cc33cd29741894b3007b1330fc7f7945bdc0a7a4044ed1dd289c19",
"5a3aa07474338cf193c1d7aacdc37f3311c971857ba8cfd308e8fabf5e473882",
"82e014b1a9c6cb7729274653ce748c66953de6abb3d1411471515b41b727cf75",
"8d70af4f135696da396c9aa9f936b54195bfbe6ff0e08b3b210ca0b52bc167d2",
"9949c2f2f3b96a557ef6e14004cbd239a0744c056faca34bdff01e125b4380e8",
"d09a8c83123ce1cb6ff837e7670aab5f50c5155d9706dd26f7e0761fd20c5536",
"f601482efc5b2dd3d0031e318e840cd06f7cab0ffff8cc37a5bf471b515ddfb7",
"f88d3c0ebe8b294f11e70d2ae6d2f0048437bfb20dae7e69d545a4c72d3432dd",
"2b9e574b90556250b20a79d9c94ceaff3dfd062291c34c3fa79c7ca8d85a3500",
"b9484ef8e38ceafe8d2b09ecf59562d262b15d185844b2d00db362718d52b2c2",
"07a4af0a81b55313a1c16da7d808829d689436fd078fa9559b6d1603dd72474e",
"3393bdcc3a7232b37d0fb6b56d603a2b9b0419e461bf989f1c375859a5d0156a",
"33ad36d79d63b575c7532c516f16b19541f5c637caf7073beb7ddf604c3f39cc",
type testBlock struct {
size int
time int64
txs []string
}
var testParseBlockTxs = map[int]testBlock{
104000: testBlock{
size: 15776,
time: 1295705889,
txs: []string{
"331d4ef64118e9e5be75f0f51f1a4c5057550c3320e22ff7206f3e1101f113d0",
"1f4817d8e91c21d8c8d163dabccdd1875f760fd2dc34a1c2b7b8fa204e103597",
"268163b1a1092aa0996d118a6027b0b6f1076627e02addc4e66ae30239936818",
"27277a1049fafa2a46368ad02961d37da633416b014bcd42a1f1391753cbf559",
"276d2d331585d0968762d9788f71ae71524ccba3494f638b2328ac51e52edd3d",
"28d9f85089834c20507cc5e4ec54aaaf5b79feab80cad24a48b8296b6d327a43",
"2a2d66d3d9e8b6f154958a388377f681abd82ce98785a5bbf2e27d0ca454da3f",
"39c9e995a12b638b541d21ed9f9e555716709add6a97c8ba63fe26e4d26bdc3f",
"4a768efc6cc0716932800115989376c2ce3e5e17668b08bd2f43031105a6ac6e",
"4bc41fa0188d988d853a176eb847a541c5adf35348eab21d128904aab36e8574",
"4cad1bd38cc7be880f3a968af6f13a3aeb5dbdb51a774b7d1ae3d6d6bfd114e4",
"6bc558801583bfdb656106889c4b8bd783168133784666338c57e5b2a675a922",
"75eb5c1aa89b18ce71f363f95147942b46645ca9b1e472784fcb8c9a096f4f5c",
"91755365cff22c4eed09a57a8fb7b2faa5c1caa5fa750c89f830a2bb56fa4c68",
"9417d34969891f2a0b9aa3a1226505edf3b429fa1acd21a140d358a217d11d55",
"950fbb5f413af9f7c6e5dabfacf68b715c9851b5cf6ab6806960f7cc7cad2f9d",
"99b134dae55ddfab1f5d5243c2e60030a9ed969ba5915f98840b877f8af28ce0",
"9d7b15eaaccce66e25efe7e2979454ae4968b61281d50f32be9872d2d256c244",
"a54df5296f1d1a6101cee0869ffda03502e417fc72475b7902a6dfd5b9329399",
"adba400f14b476f0c2b11340ee1fa440208b49fd99c1072136198b5c43664826",
"bd7d8fee068bd45b06b4c17ccdf577b4bc2b21c9c4e0cee8453409b0e63b4f5d",
"beabd2d68b66a9b47b6aff23b569c1b59e429074f06bdd4993e9d3c2cb69c992",
"bfa81174d549eb7ed15be3f965686aff3084f22523c52fbed03c3fc3e18b7cda",
"e42472099cb039b1c2248b611ce212580338550977e02bd77accdf29bfd86e96",
"f056e02b12d99377f724a4987cde68ecf6f234fd7e2bdf4324172c03d015ba25",
"f1815cfb1ef4cfe13ff5ec2c15b5bc55fde043db85daca1bb34cc1b491193308",
"f225abce64f75383686fa08abe47242e59e97809a31c8fd7a88acce1e6cbcd27",
"f93f1b125bfa2da5ccaaf30ff96635b905b657d48a5962c24be93884a82ef354",
"fef75d015f2e9926d1d4bf82e567b91e51af66a6e636d03a072f203dd3062ae5",
"051b60a6accead85da54b8d18f4b2360ea946da948d3a27836306d2927fed13e",
"28e47b050ec4064cdbd3364f3be9445d52635e9730691ef71ed1db0f0147afd6",
"446ebde2457102bcbc2c86cac6ff582c595b00346fd0b27ea5a645240020504b",
"46c8fafe2b7bb1646aeefa229b18fa7ffe20fee0a30c4a9ef3e63c36c808f6f7",
"550d96cf82fbe91dcc9b96e7aa404f392ee47400c22a98a7631d29eee43fceaa",
"59b6b78a72cc33cd29741894b3007b1330fc7f7945bdc0a7a4044ed1dd289c19",
"5a3aa07474338cf193c1d7aacdc37f3311c971857ba8cfd308e8fabf5e473882",
"82e014b1a9c6cb7729274653ce748c66953de6abb3d1411471515b41b727cf75",
"8d70af4f135696da396c9aa9f936b54195bfbe6ff0e08b3b210ca0b52bc167d2",
"9949c2f2f3b96a557ef6e14004cbd239a0744c056faca34bdff01e125b4380e8",
"d09a8c83123ce1cb6ff837e7670aab5f50c5155d9706dd26f7e0761fd20c5536",
"f601482efc5b2dd3d0031e318e840cd06f7cab0ffff8cc37a5bf471b515ddfb7",
"f88d3c0ebe8b294f11e70d2ae6d2f0048437bfb20dae7e69d545a4c72d3432dd",
"2b9e574b90556250b20a79d9c94ceaff3dfd062291c34c3fa79c7ca8d85a3500",
"b9484ef8e38ceafe8d2b09ecf59562d262b15d185844b2d00db362718d52b2c2",
"07a4af0a81b55313a1c16da7d808829d689436fd078fa9559b6d1603dd72474e",
"3393bdcc3a7232b37d0fb6b56d603a2b9b0419e461bf989f1c375859a5d0156a",
"33ad36d79d63b575c7532c516f16b19541f5c637caf7073beb7ddf604c3f39cc",
},
},
532144: []string{
"574348e23301cc89535408b6927bf75f2ac88fadf8fdfb181c17941a5de02fe0",
"9f048446401e7fac84963964df045b1f3992eda330a87b02871e422ff0a3fd28",
"9516c320745a227edb07c98087b1febea01c3ba85122a34387896fc82ba965e4",
"9d37e1ab5a28c49ce5e7ece4a2b9df740fb4c3a84bdec93b3023148cf20f0de7",
"a3cd0481b983ba402fed8805ef9daf5063d6d4e5085b82eca5b4781c9e362d6a",
"7f2c2567e8de0321744817cfeb751922d7e8d82ef71aa01164c84fb74a463a53",
"cd064315e3f5d07920b3d159160c218d0bb5b7b4be606265767b208ae7e256eb",
"a9523400f341aa425b0fcc00656ec1fa5421bf3545433bff98a8614fc9a99d1f",
"ec766daacbb05a8f48a3205e5c6494a7c817bd35eefff9aaf55e0dd47fe6e8fc",
"0837a4116872abf52caa52d1ff7608674ba5b09a239a1f43f3a25ba4052e4c77",
"a3e23a0344fe6ba7083fc6afb940517cdb666dce00389cbd8598bd599199cdda",
"048d951cef84d35d68f0bc3b575662caf23fee692e8035bd5efe38ab67e0d6c2",
"11307491b24d42ddd7ea27fc795d444b65c3936be31b904a97da68fabc85e5b8",
"84ad99dc0884e03fc71f163eebf515a1eb79d00b1aad7a1126b22747960a8275",
"728c8d0858e506d4a1a9b506f7b974b335e6c54047af9f40d4cb1a0561f783e1",
532144: testBlock{
size: 12198,
time: 1528372417,
txs: []string{
"574348e23301cc89535408b6927bf75f2ac88fadf8fdfb181c17941a5de02fe0",
"9f048446401e7fac84963964df045b1f3992eda330a87b02871e422ff0a3fd28",
"9516c320745a227edb07c98087b1febea01c3ba85122a34387896fc82ba965e4",
"9d37e1ab5a28c49ce5e7ece4a2b9df740fb4c3a84bdec93b3023148cf20f0de7",
"a3cd0481b983ba402fed8805ef9daf5063d6d4e5085b82eca5b4781c9e362d6a",
"7f2c2567e8de0321744817cfeb751922d7e8d82ef71aa01164c84fb74a463a53",
"cd064315e3f5d07920b3d159160c218d0bb5b7b4be606265767b208ae7e256eb",
"a9523400f341aa425b0fcc00656ec1fa5421bf3545433bff98a8614fc9a99d1f",
"ec766daacbb05a8f48a3205e5c6494a7c817bd35eefff9aaf55e0dd47fe6e8fc",
"0837a4116872abf52caa52d1ff7608674ba5b09a239a1f43f3a25ba4052e4c77",
"a3e23a0344fe6ba7083fc6afb940517cdb666dce00389cbd8598bd599199cdda",
"048d951cef84d35d68f0bc3b575662caf23fee692e8035bd5efe38ab67e0d6c2",
"11307491b24d42ddd7ea27fc795d444b65c3936be31b904a97da68fabc85e5b8",
"84ad99dc0884e03fc71f163eebf515a1eb79d00b1aad7a1126b22747960a8275",
"728c8d0858e506d4a1a9b506f7b974b335e6c54047af9f40d4cb1a0561f783e1",
},
},
}
@ -104,7 +118,7 @@ func helperLoadBlock(t *testing.T, height int) []byte {
func TestParseBlock(t *testing.T) {
p := NewBGoldParser(GetChainParams("main"), &btc.Configuration{})
for height, txs := range testParseBlockTxs {
for height, tb := range testParseBlockTxs {
b := helperLoadBlock(t, height)
blk, err := p.ParseBlock(b)
@ -112,11 +126,19 @@ func TestParseBlock(t *testing.T) {
t.Fatal(err)
}
if len(blk.Txs) != len(txs) {
t.Errorf("ParseBlock() number of transactions: got %d, want %d", len(blk.Txs), len(txs))
if blk.Size != tb.size {
t.Errorf("ParseBlock() block size: got %d, want %d", blk.Size, tb.size)
}
for ti, tx := range txs {
if blk.Time != tb.time {
t.Errorf("ParseBlock() block time: got %d, want %d", blk.Time, tb.time)
}
if len(blk.Txs) != len(tb.txs) {
t.Errorf("ParseBlock() number of transactions: got %d, want %d", len(blk.Txs), len(tb.txs))
}
for ti, tx := range tb.txs {
if blk.Txs[ti].Txid != tx {
t.Errorf("ParseBlock() transaction %d: got %s, want %s", ti, blk.Txs[ti].Txid, tx)
}

View File

@ -3,8 +3,8 @@ package dash
import (
"blockbook/bchain/coins/btc"
"github.com/btcsuite/btcd/chaincfg"
"github.com/btcsuite/btcd/wire"
"github.com/jakm/btcutil/chaincfg"
)
const (
@ -19,27 +19,27 @@ var (
RegtestParams chaincfg.Params
)
func init() {
func initParams() {
MainNetParams = chaincfg.MainNetParams
MainNetParams.Net = MainnetMagic
// Address encoding magics
MainNetParams.PubKeyHashAddrID = 76 // base58 prefix: X
MainNetParams.ScriptHashAddrID = 16 // base58 prefix: 7
MainNetParams.PubKeyHashAddrID = []byte{76} // base58 prefix: X
MainNetParams.ScriptHashAddrID = []byte{16} // base58 prefix: 7
TestNetParams = chaincfg.TestNet3Params
TestNetParams.Net = TestnetMagic
// Address encoding magics
TestNetParams.PubKeyHashAddrID = 140 // base58 prefix: y
TestNetParams.ScriptHashAddrID = 19 // base58 prefix: 8 or 9
TestNetParams.PubKeyHashAddrID = []byte{140} // base58 prefix: y
TestNetParams.ScriptHashAddrID = []byte{19} // base58 prefix: 8 or 9
RegtestParams = chaincfg.RegressionNetParams
RegtestParams.Net = RegtestMagic
// Address encoding magics
RegtestParams.PubKeyHashAddrID = 140 // base58 prefix: y
RegtestParams.ScriptHashAddrID = 19 // base58 prefix: 8 or 9
RegtestParams.PubKeyHashAddrID = []byte{140} // base58 prefix: y
RegtestParams.ScriptHashAddrID = []byte{19} // base58 prefix: 8 or 9
err := chaincfg.Register(&MainNetParams)
if err == nil {
@ -67,6 +67,9 @@ func NewDashParser(params *chaincfg.Params, c *btc.Configuration) *DashParser {
// the regression test Dash network, the test Dash network and
// the simulation test Dash network, in this order
func GetChainParams(chain string) *chaincfg.Params {
if MainNetParams.Name == "" {
initParams()
}
switch chain {
case "test":
return &TestNetParams

View File

@ -24,6 +24,7 @@ func NewDashRPC(config json.RawMessage, pushHandler func(bchain.NotificationType
b.(*btc.BitcoinRPC),
}
s.RPCMarshaler = btc.JSONMarshalerV1{}
s.ChainConfig.SupportsEstimateSmartFee = false
return s, nil
}
@ -53,8 +54,3 @@ func (b *DashRPC) Initialize() error {
return nil
}
// EstimateSmartFee returns fee estimation.
func (b *DashRPC) EstimateSmartFee(blocks int, conservative bool) (float64, error) {
return b.EstimateFee(blocks)
}

View File

@ -1,129 +0,0 @@
// +build integration
package dash
import (
"blockbook/bchain"
"blockbook/bchain/tests/rpc"
"encoding/json"
"flag"
"os"
"testing"
)
func getRPCClient(chain string) func(json.RawMessage) (bchain.BlockChain, error) {
return func(cfg json.RawMessage) (bchain.BlockChain, error) {
c, err := NewDashRPC(cfg, nil)
if err != nil {
return nil, err
}
cli := c.(*DashRPC)
cli.Parser = NewDashParser(GetChainParams(chain), cli.ChainConfig)
cli.Mempool = bchain.NewUTXOMempool(cli, cli.ChainConfig.MempoolWorkers, cli.ChainConfig.MempoolSubWorkers)
return cli, nil
}
}
var tests struct {
mainnet *rpc.Test
testnet *rpc.Test
}
func TestMain(m *testing.M) {
flag.Parse()
t, err := rpc.NewTest("Dash", getRPCClient("main"))
if err != nil {
panic(err)
}
tests.mainnet = t
t, err = rpc.NewTest("Dash Testnet", getRPCClient("test"))
if err != nil {
panic(err)
}
tests.testnet = t
os.Exit(m.Run())
}
func TestDashRPC_GetBlockHash(t *testing.T) {
tests.mainnet.TestGetBlockHash(t)
}
func TestDashRPC_GetBlock(t *testing.T) {
tests.mainnet.TestGetBlock(t)
}
func TestDashRPC_GetTransaction(t *testing.T) {
tests.mainnet.TestGetTransaction(t)
}
func TestDashRPC_GetTransactionForMempool(t *testing.T) {
tests.mainnet.TestGetTransactionForMempool(t)
}
func TestDashRPC_MempoolSync(t *testing.T) {
tests.mainnet.TestMempoolSync(t)
}
func TestDashRPC_EstimateSmartFee(t *testing.T) {
tests.mainnet.TestEstimateSmartFee(t)
}
func TestDashRPC_EstimateFee(t *testing.T) {
tests.mainnet.TestEstimateFee(t)
}
func TestDashRPC_GetBestBlockHash(t *testing.T) {
tests.mainnet.TestGetBestBlockHash(t)
}
func TestDashRPC_GetBestBlockHeight(t *testing.T) {
tests.mainnet.TestGetBestBlockHeight(t)
}
func TestDashRPC_GetBlockHeader(t *testing.T) {
tests.mainnet.TestGetBlockHeader(t)
}
func TestDashTestnetRPC_GetBlockHash(t *testing.T) {
tests.testnet.TestGetBlockHash(t)
}
func TestDashTestnetRPC_GetBlock(t *testing.T) {
tests.testnet.TestGetBlock(t)
}
func TestDashTestnetRPC_GetTransaction(t *testing.T) {
tests.testnet.TestGetTransaction(t)
}
func TestDashTestnetRPC_GetTransactionForMempool(t *testing.T) {
tests.testnet.TestGetTransactionForMempool(t)
}
func TestDashTestnetRPC_MempoolSync(t *testing.T) {
tests.testnet.TestMempoolSync(t)
}
func TestDashTestnetRPC_EstimateSmartFee(t *testing.T) {
tests.testnet.TestEstimateSmartFee(t)
}
func TestDashTestnetRPC_EstimateFee(t *testing.T) {
tests.testnet.TestEstimateFee(t)
}
func TestDashTestnetRPC_GetBestBlockHash(t *testing.T) {
tests.testnet.TestGetBestBlockHash(t)
}
func TestDashTestnetRPC_GetBestBlockHeight(t *testing.T) {
tests.testnet.TestGetBestBlockHeight(t)
}
func TestDashTestnetRPC_GetBlockHeader(t *testing.T) {
tests.testnet.TestGetBlockHeader(t)
}

View File

@ -6,8 +6,8 @@ import (
"blockbook/bchain/coins/utils"
"bytes"
"github.com/btcsuite/btcd/chaincfg"
"github.com/btcsuite/btcd/wire"
"github.com/jakm/btcutil/chaincfg"
)
const (
@ -18,11 +18,11 @@ var (
MainNetParams chaincfg.Params
)
func init() {
func initParams() {
MainNetParams = chaincfg.MainNetParams
MainNetParams.Net = MainnetMagic
MainNetParams.PubKeyHashAddrID = 30
MainNetParams.ScriptHashAddrID = 22
MainNetParams.PubKeyHashAddrID = []byte{30}
MainNetParams.ScriptHashAddrID = []byte{22}
err := chaincfg.Register(&MainNetParams)
if err != nil {
@ -43,6 +43,9 @@ func NewDogecoinParser(params *chaincfg.Params, c *btc.Configuration) *DogecoinP
// GetChainParams contains network parameters for the main Dogecoin network,
// and the test Dogecoin network
func GetChainParams(chain string) *chaincfg.Params {
if MainNetParams.Name == "" {
initParams()
}
switch chain {
default:
return &MainNetParams
@ -75,5 +78,11 @@ func (p *DogecoinParser) ParseBlock(b []byte) (*bchain.Block, error) {
txs[ti] = p.TxFromMsgTx(t, false)
}
return &bchain.Block{Txs: txs}, nil
return &bchain.Block{
BlockHeader: bchain.BlockHeader{
Size: len(b),
Time: h.Timestamp.Unix(),
},
Txs: txs,
}, nil
}

View File

@ -9,12 +9,13 @@ import (
"encoding/hex"
"fmt"
"io/ioutil"
"math/big"
"path/filepath"
"reflect"
"testing"
)
func TestAddressToOutputScript_Mainnet(t *testing.T) {
func Test_GetAddrDescFromAddress_Mainnet(t *testing.T) {
type args struct {
address string
}
@ -53,14 +54,14 @@ func TestAddressToOutputScript_Mainnet(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := parser.AddressToOutputScript(tt.args.address)
got, err := parser.GetAddrDescFromAddress(tt.args.address)
if (err != nil) != tt.wantErr {
t.Errorf("AddressToOutputScript() error = %v, wantErr %v", err, tt.wantErr)
t.Errorf("GetAddrDescFromAddress() error = %v, wantErr %v", err, tt.wantErr)
return
}
h := hex.EncodeToString(got)
if !reflect.DeepEqual(h, tt.want) {
t.Errorf("AddressToOutputScript() = %v, want %v", h, tt.want)
t.Errorf("GetAddrDescFromAddress() = %v, want %v", h, tt.want)
}
})
}
@ -75,24 +76,6 @@ var (
)
func init() {
var (
addr1, addr2, addr3, addr4 bchain.Address
err error
)
addr1, err = bchain.NewBaseAddress("DSvXNiqvG42wdteLqh3i6inxgDTs8Y9w2i")
if err == nil {
addr2, err = bchain.NewBaseAddress("DRemF3ZcqJ1PFeM7e7sXzzwQJKR8GNUtwK")
}
if err == nil {
addr3, err = bchain.NewBaseAddress("DJa8bWDrZKu4HgsYRYWuJrvxt6iTYuvXJ6")
}
if err == nil {
addr4, err = bchain.NewBaseAddress("DDTtqnuZ5kfRT5qh2c7sNtqrJmV3iXYdGG")
}
if err != nil {
panic(err)
}
testTx1 = bchain.Tx{
Hex: "01000000016b3c0c53267964120acf7f7e72217e3f463e52ce622f89659f6a6bb8e69a4d91000000006c493046022100a96454237e3a020994534583e28c04757881374bceac89f933ea9ff00b4db259022100fbb757ff7ea4f02c4e42556b2834c61eba1f1af605db089d836a0614d90a3b46012103cebdde6d1046e285df4f48497bc50dc20a4a258ca5b7308cb0a929c9fdadcd9dffffffff0217e823ca7f0200001976a914eef21768a546590993e313c7f3dfadf6a6efa1e888acaddf4cba010000001976a914e0fee2ea29dd9c6c759d8341bd0da4c4f738cced88ac00000000",
Blocktime: 1519053456,
@ -111,26 +94,24 @@ func init() {
},
Vout: []bchain.Vout{
{
Value: 27478.75452951,
N: 0,
ValueSat: *big.NewInt(2747875452951),
N: 0,
ScriptPubKey: bchain.ScriptPubKey{
Hex: "76a914eef21768a546590993e313c7f3dfadf6a6efa1e888ac",
Addresses: []string{
"DSvXNiqvG42wdteLqh3i6inxgDTs8Y9w2i",
},
},
Address: addr1,
},
{
Value: 74.20567469,
N: 1,
ValueSat: *big.NewInt(7420567469),
N: 1,
ScriptPubKey: bchain.ScriptPubKey{
Hex: "76a914e0fee2ea29dd9c6c759d8341bd0da4c4f738cced88ac",
Addresses: []string{
"DRemF3ZcqJ1PFeM7e7sXzzwQJKR8GNUtwK",
},
},
Address: addr2,
},
},
}
@ -153,26 +134,24 @@ func init() {
},
Vout: []bchain.Vout{
{
Value: 59890867.89818935,
N: 0,
ValueSat: *big.NewInt(5989086789818935),
N: 0,
ScriptPubKey: bchain.ScriptPubKey{
Hex: "76a9149355c01ed20057eac9fe0bbf8b07d87e62fe712d88ac",
Addresses: []string{
"DJa8bWDrZKu4HgsYRYWuJrvxt6iTYuvXJ6",
},
},
Address: addr3,
},
{
Value: 9999998.90000000,
N: 1,
ValueSat: *big.NewInt(999999890000000),
N: 1,
ScriptPubKey: bchain.ScriptPubKey{
Hex: "76a9145b4f2511c94e4fcaa8f8835b2458f8cb6542ca7688ac",
Addresses: []string{
"DDTtqnuZ5kfRT5qh2c7sNtqrJmV3iXYdGG",
},
},
Address: addr4,
},
},
}
@ -280,89 +259,111 @@ func Test_UnpackTx(t *testing.T) {
}
}
var testParseBlockTxs = map[int][]string{
type testBlock struct {
size int
time int64
txs []string
}
var testParseBlockTxs = map[int]testBlock{
// block without auxpow
12345: []string{
"9d1662dcc1443af9999c4fd1d6921b91027b5e2d0d3ebfaa41d84163cb99cad5",
"8284292cedeb0c9c509f9baa235802d52a546e1e9990040d35d018b97ad11cfa",
"3299d93aae5c3d37c795c07150ceaf008aefa5aad3205ea2519f94a35adbbe10",
"3f03016f32b63db48fdc0b17443c2d917ba5e307dcc2fc803feeb21c7219ee1b",
"a889449e9bc618c131c01f564cd309d2217ba1c5731480314795e44f1e02609b",
"29f79d91c10bc311ff5b69fe7ba57101969f68b6391cf0ca67d5f37ca1f0601b",
"b794ebc7c0176c35b125cd8b84a980257cf3dd9cefe2ed47da4ed1d73ee568f3",
"0ec479ba3c954dd422d75c4c5488a6edc3c588deb10ebdbfa8bd8edb7afcfea0",
"f357b6e667dfa456e7988bfa474377df25d0e0bfe07e5f97fc97ea3a0155f031",
"4ff189766f0455721a93d6be27a91eafa750383c800cb053fad2f86c434122d2",
"446d164e2ec4c9f2ac6c499c110735606d949a3625fb849274ac627c033eddbc",
"c489edebd8a2e17fd08f2801f528b95663aaafe15c897d56686423dd430e2d1f",
"3f42a7f1a356897da324d41eed94169c79438212bb9874eea58e9cbaf07481df",
"62c88fdd0fb111676844fcbaebc9e2211a0c990aa7e7529539cb25947a307a1b",
"522c47e315bc1949826339c535d419eb206aec4a332f91dfbd25c206f3c9527b",
"18ea78346e7e34cbdf2d2b6ba1630f8b15f9ef9a940114a3e6ee92d26f96691e",
"43dc0fbd1b9b87bcfc9a51c89457a7b3274855c01d429193aff1181791225f3c",
"d78cdfaadbe5b6b591529cb5c6869866a4cabe46ef82aa835fd2432056b4a383",
"d181759c7a3900ccaf4958f1f25a44949163ceefc306006502efc7a1de6f579e",
"8610b9230188854c7871258163cd1c2db353443d631c5512bff17224a24e95bf",
"e82f40a6bea32122f1d568d427c92708dcb684bdb3035ff3905617230e5ae5b8",
"c50ae6c127f8c346c60e7438fbd10c44c3629f3fe426646db77a2250fb2939f9",
"585202c03894ecaf25188ba4e5447dadd413f2010c2dc2a65c37598dbc6ad907",
"8bd766fde8c65e2f724dad581944dde4e23e4dbb4f7f7faf55bc348923f4d5ee",
"2d2fa25691088181569e508dd8f683b21f2b80ceefb5ccbd6714ebe2a697139f",
"5954622ffc602bec177d61da6c26a68990c42c1886627b218c3ab0e9e3491f4a",
"01b634bc53334df1cd9f04522729a34d811c418c2535144c3ed156cbc319e43e",
"c429a6c8265482b2d824af03afe1c090b233a856f243791485cb4269f2729649",
"dbe79231b916b6fb47a91ef874f35150270eb571af60c2d640ded92b41749940",
"1c396493a8dfd59557052b6e8643123405894b64f48b2eb6eb7a003159034077",
"2e2816ffb7bf1378f11acf5ba30d498efc8fd219d4b67a725e8254ce61b1b7ee",
12345: testBlock{
size: 8582,
time: 1387104223,
txs: []string{
"9d1662dcc1443af9999c4fd1d6921b91027b5e2d0d3ebfaa41d84163cb99cad5",
"8284292cedeb0c9c509f9baa235802d52a546e1e9990040d35d018b97ad11cfa",
"3299d93aae5c3d37c795c07150ceaf008aefa5aad3205ea2519f94a35adbbe10",
"3f03016f32b63db48fdc0b17443c2d917ba5e307dcc2fc803feeb21c7219ee1b",
"a889449e9bc618c131c01f564cd309d2217ba1c5731480314795e44f1e02609b",
"29f79d91c10bc311ff5b69fe7ba57101969f68b6391cf0ca67d5f37ca1f0601b",
"b794ebc7c0176c35b125cd8b84a980257cf3dd9cefe2ed47da4ed1d73ee568f3",
"0ec479ba3c954dd422d75c4c5488a6edc3c588deb10ebdbfa8bd8edb7afcfea0",
"f357b6e667dfa456e7988bfa474377df25d0e0bfe07e5f97fc97ea3a0155f031",
"4ff189766f0455721a93d6be27a91eafa750383c800cb053fad2f86c434122d2",
"446d164e2ec4c9f2ac6c499c110735606d949a3625fb849274ac627c033eddbc",
"c489edebd8a2e17fd08f2801f528b95663aaafe15c897d56686423dd430e2d1f",
"3f42a7f1a356897da324d41eed94169c79438212bb9874eea58e9cbaf07481df",
"62c88fdd0fb111676844fcbaebc9e2211a0c990aa7e7529539cb25947a307a1b",
"522c47e315bc1949826339c535d419eb206aec4a332f91dfbd25c206f3c9527b",
"18ea78346e7e34cbdf2d2b6ba1630f8b15f9ef9a940114a3e6ee92d26f96691e",
"43dc0fbd1b9b87bcfc9a51c89457a7b3274855c01d429193aff1181791225f3c",
"d78cdfaadbe5b6b591529cb5c6869866a4cabe46ef82aa835fd2432056b4a383",
"d181759c7a3900ccaf4958f1f25a44949163ceefc306006502efc7a1de6f579e",
"8610b9230188854c7871258163cd1c2db353443d631c5512bff17224a24e95bf",
"e82f40a6bea32122f1d568d427c92708dcb684bdb3035ff3905617230e5ae5b8",
"c50ae6c127f8c346c60e7438fbd10c44c3629f3fe426646db77a2250fb2939f9",
"585202c03894ecaf25188ba4e5447dadd413f2010c2dc2a65c37598dbc6ad907",
"8bd766fde8c65e2f724dad581944dde4e23e4dbb4f7f7faf55bc348923f4d5ee",
"2d2fa25691088181569e508dd8f683b21f2b80ceefb5ccbd6714ebe2a697139f",
"5954622ffc602bec177d61da6c26a68990c42c1886627b218c3ab0e9e3491f4a",
"01b634bc53334df1cd9f04522729a34d811c418c2535144c3ed156cbc319e43e",
"c429a6c8265482b2d824af03afe1c090b233a856f243791485cb4269f2729649",
"dbe79231b916b6fb47a91ef874f35150270eb571af60c2d640ded92b41749940",
"1c396493a8dfd59557052b6e8643123405894b64f48b2eb6eb7a003159034077",
"2e2816ffb7bf1378f11acf5ba30d498efc8fd219d4b67a725e8254ce61b1b7ee",
},
},
// 1st block with auxpow
371337: []string{
"4547b14bc16db4184fa9f141d645627430dd3dfa662d0e6f418fba497091da75",
"a965dba2ed06827ed9a24f0568ec05b73c431bc7f0fb6913b144e62db7faa519",
"5e3ab18cb7ba3abc44e62fb3a43d4c8168d00cf0a2e0f8dbeb2636bb9a212d12",
"f022935ac7c4c734bd2c9c6a780f8e7280352de8bd358d760d0645b7fe734a93",
"ec063cc8025f9f30a6ed40fc8b1fe63b0cbd2ea2c62664eb26b365e6243828ca",
"02c16e3389320da3e77686d39773dda65a1ecdf98a2ef9cfb938c9f4b58f7a40",
371337: testBlock{
size: 1704,
time: 1410464577,
txs: []string{
"4547b14bc16db4184fa9f141d645627430dd3dfa662d0e6f418fba497091da75",
"a965dba2ed06827ed9a24f0568ec05b73c431bc7f0fb6913b144e62db7faa519",
"5e3ab18cb7ba3abc44e62fb3a43d4c8168d00cf0a2e0f8dbeb2636bb9a212d12",
"f022935ac7c4c734bd2c9c6a780f8e7280352de8bd358d760d0645b7fe734a93",
"ec063cc8025f9f30a6ed40fc8b1fe63b0cbd2ea2c62664eb26b365e6243828ca",
"02c16e3389320da3e77686d39773dda65a1ecdf98a2ef9cfb938c9f4b58f7a40",
},
},
// block with auxpow
567890: []string{
"db20feea53be1f60848a66604d5bca63df62de4f6c66220f9c84436d788625a8",
"cf7e9e27c0f56f0b100eaf5c776ce106025e3412bd5927c6e1ce575500e24eaa",
"af84e010c1cf0bd927740d08e5e8163db45397b70f00df07aea5339c14d5f3aa",
"7362e25e8131255d101e5d874e6b6bb2faa7a821356cb041f1843d0901dffdbd",
"3b875344302e8893f6d5c9e7269d806ed27217ec67944940ae9048fc619bdae9",
"e3b95e269b7c251d87e8e241ea2a08a66ec14d12a1012762be368b3db55471e3",
"6ba3f95a37bcab5d0cb5b8bd2fe48040db0a6ae390f320d6dcc8162cc096ff8f",
"3211ccc66d05b10959fa6e56d1955c12368ea52b40303558b254d7dc22570382",
"54c1b279e78b924dfa15857c80131c3ddf835ab02f513dc03aa514f87b680493",
567890: testBlock{
size: 3833,
time: 1422855443,
txs: []string{
"db20feea53be1f60848a66604d5bca63df62de4f6c66220f9c84436d788625a8",
"cf7e9e27c0f56f0b100eaf5c776ce106025e3412bd5927c6e1ce575500e24eaa",
"af84e010c1cf0bd927740d08e5e8163db45397b70f00df07aea5339c14d5f3aa",
"7362e25e8131255d101e5d874e6b6bb2faa7a821356cb041f1843d0901dffdbd",
"3b875344302e8893f6d5c9e7269d806ed27217ec67944940ae9048fc619bdae9",
"e3b95e269b7c251d87e8e241ea2a08a66ec14d12a1012762be368b3db55471e3",
"6ba3f95a37bcab5d0cb5b8bd2fe48040db0a6ae390f320d6dcc8162cc096ff8f",
"3211ccc66d05b10959fa6e56d1955c12368ea52b40303558b254d7dc22570382",
"54c1b279e78b924dfa15857c80131c3ddf835ab02f513dc03aa514f87b680493",
},
},
// recent block
2264125: []string{
"76f0126562c99e020b5fba41b68dd8141a4f21eef62012b76a1e0635092045e9",
"7bb6688bec16de94014574e3e1d3f6f5fb956530d6b179b28db367f1fd8ae099",
"d7e2ee30c3d179ac896651fc09c1396333f41d952d008af8d5d6665cbea377bf",
"8e4783878df782003c43d014fcbb9c57d2034dfd1d9fcd7319bb1a9f501dbbb7",
"8d2a4ae226b6f23eea545957be5d71c68cd08674d96a3502d4ca21ffadacb5a9",
"a0da2b49de881133655c54b1b5c23af443a71c2b937e2d9bbdf3f498247e6b7b",
"c780a19b9cf46ed70b53c5d5722e8d33951211a4051cb165b25fb0c22a4ae1ff",
"ce29c2644d642bb4fedd09d0840ed98c9945bf292967fede8fcc6b26054b4058",
"a360b0566f68c329e2757918f67ee6421d3d76f70f1b452cdd32266805986119",
"17e85bd33cc5fb5035e489c5188979f45e75e92d14221eca937e14f5f7d7b074",
"3973eb930fd2d0726abbd81912eae645384268cd3500b9ec84d806fdd65a426a",
"b91cc1c98e5c77e80eec9bf93e86af27f810b00dfbce3ee2646758797a28d5f2",
"1a8c7bd3389dcbbc1133ee600898ed9e082f7a9c75f9eb52f33940ed7c2247ef",
"9b1782449bbd3fc3014c363167777f7bdf41f5ef6db192fbda784b29603911b0",
"afab4bcdc1a32891d638579c3029ae49ee72be3303425c6d62e1f8eaebe0ce18",
"5f839f9cd5293c02ff4f7cf5589c53dec52adb42a077599dc7a2c5842a156ca9",
"756d2dfd1d2872ba2531fae3b8984008506871bec41d19cb299f5e0f216cfb9b",
"6aa82514ab7a9cc624fabf3d06ccbd46ecb4009b3c784768e6243d7840d4bf93",
"d1430b3f7ecf147534796c39ba631ea22ac03530e25b9428367c0dc381b10863",
"2aeb69b1eb9eef8039da6b97d7851e46f57325851e6998ef5a84fc9a826c2c74",
"fc61d13eef806af8da693cfa621fe92110694f1514567b186a35c54e7ef4a188",
"a02dd44e60ba62fa00c83a67116f8079bf71062939b207bee0808cb98b30cf22",
"279f97cfc606fe62777b44614ff28675ce661687904e068e3ec79f619c4fdae7",
"d515d271849717b091a9c46bf11c47efb9d975e72b668c137786a208cf0a9739",
"a800da44e6eed944043561fe22ee0a6e11341e6bc1a8ec2789b83930cc9b170e",
2264125: testBlock{
size: 8531,
time: 1529099968,
txs: []string{
"76f0126562c99e020b5fba41b68dd8141a4f21eef62012b76a1e0635092045e9",
"7bb6688bec16de94014574e3e1d3f6f5fb956530d6b179b28db367f1fd8ae099",
"d7e2ee30c3d179ac896651fc09c1396333f41d952d008af8d5d6665cbea377bf",
"8e4783878df782003c43d014fcbb9c57d2034dfd1d9fcd7319bb1a9f501dbbb7",
"8d2a4ae226b6f23eea545957be5d71c68cd08674d96a3502d4ca21ffadacb5a9",
"a0da2b49de881133655c54b1b5c23af443a71c2b937e2d9bbdf3f498247e6b7b",
"c780a19b9cf46ed70b53c5d5722e8d33951211a4051cb165b25fb0c22a4ae1ff",
"ce29c2644d642bb4fedd09d0840ed98c9945bf292967fede8fcc6b26054b4058",
"a360b0566f68c329e2757918f67ee6421d3d76f70f1b452cdd32266805986119",
"17e85bd33cc5fb5035e489c5188979f45e75e92d14221eca937e14f5f7d7b074",
"3973eb930fd2d0726abbd81912eae645384268cd3500b9ec84d806fdd65a426a",
"b91cc1c98e5c77e80eec9bf93e86af27f810b00dfbce3ee2646758797a28d5f2",
"1a8c7bd3389dcbbc1133ee600898ed9e082f7a9c75f9eb52f33940ed7c2247ef",
"9b1782449bbd3fc3014c363167777f7bdf41f5ef6db192fbda784b29603911b0",
"afab4bcdc1a32891d638579c3029ae49ee72be3303425c6d62e1f8eaebe0ce18",
"5f839f9cd5293c02ff4f7cf5589c53dec52adb42a077599dc7a2c5842a156ca9",
"756d2dfd1d2872ba2531fae3b8984008506871bec41d19cb299f5e0f216cfb9b",
"6aa82514ab7a9cc624fabf3d06ccbd46ecb4009b3c784768e6243d7840d4bf93",
"d1430b3f7ecf147534796c39ba631ea22ac03530e25b9428367c0dc381b10863",
"2aeb69b1eb9eef8039da6b97d7851e46f57325851e6998ef5a84fc9a826c2c74",
"fc61d13eef806af8da693cfa621fe92110694f1514567b186a35c54e7ef4a188",
"a02dd44e60ba62fa00c83a67116f8079bf71062939b207bee0808cb98b30cf22",
"279f97cfc606fe62777b44614ff28675ce661687904e068e3ec79f619c4fdae7",
"d515d271849717b091a9c46bf11c47efb9d975e72b668c137786a208cf0a9739",
"a800da44e6eed944043561fe22ee0a6e11341e6bc1a8ec2789b83930cc9b170e",
},
},
}
@ -389,7 +390,7 @@ func helperLoadBlock(t *testing.T, height int) []byte {
func TestParseBlock(t *testing.T) {
p := NewDogecoinParser(GetChainParams("main"), &btc.Configuration{})
for height, txs := range testParseBlockTxs {
for height, tb := range testParseBlockTxs {
b := helperLoadBlock(t, height)
blk, err := p.ParseBlock(b)
@ -397,11 +398,19 @@ func TestParseBlock(t *testing.T) {
t.Fatal(err)
}
if len(blk.Txs) != len(txs) {
t.Errorf("ParseBlock() number of transactions: got %d, want %d", len(blk.Txs), len(txs))
if blk.Size != tb.size {
t.Errorf("ParseBlock() block size: got %d, want %d", blk.Size, tb.size)
}
for ti, tx := range txs {
if blk.Time != tb.time {
t.Errorf("ParseBlock() block time: got %d, want %d", blk.Time, tb.time)
}
if len(blk.Txs) != len(tb.txs) {
t.Errorf("ParseBlock() number of transactions: got %d, want %d", len(blk.Txs), len(tb.txs))
}
for ti, tx := range tb.txs {
if blk.Txs[ti].Txid != tx {
t.Errorf("ParseBlock() transaction %d: got %s, want %s", ti, blk.Txs[ti].Txid, tx)
}

View File

@ -24,6 +24,7 @@ func NewDogecoinRPC(config json.RawMessage, pushHandler func(bchain.Notification
b.(*btc.BitcoinRPC),
}
s.RPCMarshaler = btc.JSONMarshalerV1{}
s.ChainConfig.SupportsEstimateSmartFee = false
return s, nil
}
@ -69,8 +70,3 @@ func (b *DogecoinRPC) GetBlock(hash string, height uint32) (*bchain.Block, error
}
return b.GetBlockWithoutHeader(hash, height)
}
// EstimateSmartFee returns fee estimation.
func (b *DogecoinRPC) EstimateSmartFee(blocks int, conservative bool) (float64, error) {
return b.EstimateFee(blocks)
}

View File

@ -22,7 +22,10 @@ type EthereumParser struct {
// NewEthereumParser returns new EthereumParser instance
func NewEthereumParser() *EthereumParser {
return &EthereumParser{&bchain.BaseParser{AddressFactory: bchain.NewBaseAddress}}
return &EthereumParser{&bchain.BaseParser{
BlockAddressesToKeep: 0,
AmountDecimalPoint: 18,
}}
}
type rpcTransaction struct {
@ -64,7 +67,6 @@ func (p *EthereumParser) ethTxToTx(tx *rpcTransaction, blocktime int64, confirma
txid := ethHashToHash(tx.Hash)
var (
fa, ta []string
addr bchain.Address
err error
)
if len(tx.From) > 2 {
@ -72,10 +74,6 @@ func (p *EthereumParser) ethTxToTx(tx *rpcTransaction, blocktime int64, confirma
}
if len(tx.To) > 2 {
ta = []string{tx.To}
addr, err = p.AddressFactory(tx.To)
if err != nil {
return nil, err
}
}
// temporarily, the complete rpcTransaction without BlockHash is marshalled and hex encoded to bchain.Tx.Hex
bh := tx.BlockHash
@ -86,6 +84,10 @@ func (p *EthereumParser) ethTxToTx(tx *rpcTransaction, blocktime int64, confirma
}
tx.BlockHash = bh
h := hex.EncodeToString(b)
vs, err := hexutil.DecodeBig(tx.Value)
if err != nil {
return nil, err
}
return &bchain.Tx{
Blocktime: blocktime,
Confirmations: confirmations,
@ -105,32 +107,31 @@ func (p *EthereumParser) ethTxToTx(tx *rpcTransaction, blocktime int64, confirma
},
Vout: []bchain.Vout{
{
N: 0, // there is always up to one To address
// Value - cannot be set, it does not fit precisely to float64
N: 0, // there is always up to one To address
ValueSat: *vs,
ScriptPubKey: bchain.ScriptPubKey{
// Hex
Addresses: ta,
},
Address: addr,
},
},
}, nil
}
// GetAddrIDFromVout returns internal address representation of given transaction output
func (p *EthereumParser) GetAddrIDFromVout(output *bchain.Vout) ([]byte, error) {
// GetAddrDescFromVout returns internal address representation of given transaction output
func (p *EthereumParser) GetAddrDescFromVout(output *bchain.Vout) (bchain.AddressDescriptor, error) {
if len(output.ScriptPubKey.Addresses) != 1 {
return nil, bchain.ErrAddressMissing
}
return p.GetAddrIDFromAddress(output.ScriptPubKey.Addresses[0])
return p.GetAddrDescFromAddress(output.ScriptPubKey.Addresses[0])
}
func has0xPrefix(s string) bool {
return len(s) >= 2 && s[0] == '0' && (s[1]|32) == 'x'
}
// GetAddrIDFromAddress returns internal address representation of given address
func (p *EthereumParser) GetAddrIDFromAddress(address string) ([]byte, error) {
// GetAddrDescFromAddress returns internal address representation of given address
func (p *EthereumParser) GetAddrDescFromAddress(address string) (bchain.AddressDescriptor, error) {
// github.com/ethereum/go-ethereum/common.HexToAddress does not handle address errors, using own decoding
if has0xPrefix(address) {
address = address[2:]
@ -144,6 +145,16 @@ func (p *EthereumParser) GetAddrIDFromAddress(address string) ([]byte, error) {
return hex.DecodeString(address)
}
// GetAddressesFromAddrDesc returns addresses for given address descriptor with flag if the addresses are searchable
func (p *EthereumParser) GetAddressesFromAddrDesc(addrDesc bchain.AddressDescriptor) ([]string, bool, error) {
return []string{hexutil.Encode(addrDesc)}, true, nil
}
// GetScriptFromAddrDesc returns output script for given address descriptor
func (p *EthereumParser) GetScriptFromAddrDesc(addrDesc bchain.AddressDescriptor) ([]byte, error) {
return addrDesc, nil
}
func hexDecode(s string) ([]byte, error) {
b, err := hexutil.Decode(s)
if err != nil && err != hexutil.ErrEmptyString {
@ -286,9 +297,3 @@ func (p *EthereumParser) UnpackBlockHash(buf []byte) (string, error) {
func (p *EthereumParser) IsUTXOChain() bool {
return false
}
// KeepBlockAddresses returns number of blocks which are to be kept in blockaddresses column
// do not use the blockaddresses for eth
func (p *EthereumParser) KeepBlockAddresses() int {
return 0
}

View File

@ -5,11 +5,12 @@ package eth
import (
"blockbook/bchain"
"encoding/hex"
"math/big"
"reflect"
"testing"
)
func TestEthParser_GetAddrIDFromAddress(t *testing.T) {
func TestEthParser_GetAddrDescFromAddress(t *testing.T) {
type args struct {
address string
}
@ -50,19 +51,68 @@ func TestEthParser_GetAddrIDFromAddress(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
p := NewEthereumParser()
got, err := p.GetAddrIDFromAddress(tt.args.address)
got, err := p.GetAddrDescFromAddress(tt.args.address)
if (err != nil) != tt.wantErr {
t.Errorf("EthParser.GetAddrIDFromAddress() error = %v, wantErr %v", err, tt.wantErr)
t.Errorf("EthParser.GetAddrDescFromAddress() error = %v, wantErr %v", err, tt.wantErr)
return
}
h := hex.EncodeToString(got)
if !reflect.DeepEqual(h, tt.want) {
t.Errorf("EthParser.GetAddrIDFromAddress() = %v, want %v", h, tt.want)
t.Errorf("EthParser.GetAddrDescFromAddress() = %v, want %v", h, tt.want)
}
})
}
}
var (
testTx1, testTx2 bchain.Tx
testTxPacked1 = "08aebf0a1205012a05f20018a0f73622081234567890abcdef2a24f025caaf00000000000000000000000000000000000000000000000000000000000002253220e6b168d6bb3d8ed78e03dbf828b6bfd1fb613f6e129cba624964984553724c5d38f095af014092f4c1d5054a14682b7903a11098cf770c7aef4aa02a85b3f3601a5214dacc9c61754a0c4616fc5323dc946e89eb272302580162011b6a201bd40a31122c03918df6d166d740a6a3a22f08a25934ceb1688c62977661c80c7220607fbc15c1f7995a4258f5a9bccc63b040362d1991d5efe1361c56222e4ca89f"
testTxPacked2 = "08ece40212050430e234001888a4012201213220cd647151552b5132b2aef7c9be00dc6f73afc5901dde157aab131335baaa853b38889eaf0140fa83c3d5054a14555ee11fbddc0e49a9bab358a8941ad95ffdb48f52143e3a3d69dc66ba10737f531ed088954a9ec89d97580a6201296a20f7161c170d43573ad9c8d701cdaf714ff2a548a562b0dc639230d17889fcd40572203c4977fc90385a27efa0032e17b49fd575b2826cb56e3d1ecf21524f2a94f915"
)
func init() {
testTx1 = bchain.Tx{
Blocktime: 1521515026,
Hex: "7b226e6f6e6365223a2230783239666165222c226761735072696365223a223078313261303566323030222c22676173223a2230786462626130222c22746f223a22307836383262373930336131313039386366373730633761656634616130326138356233663336303161222c2276616c7565223a22307831323334353637383930616263646566222c22696e707574223a223078663032356361616630303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030323235222c2268617368223a22307865366231363864366262336438656437386530336462663832386236626664316662363133663665313239636261363234393634393834353533373234633564222c22626c6f636b4e756d626572223a223078326263616630222c2266726f6d223a22307864616363396336313735346130633436313666633533323364633934366538396562323732333032222c227472616e73616374696f6e496e646578223a22307831222c2276223a2230783162222c2272223a22307831626434306133313132326330333931386466366431363664373430613661336132326630386132353933346365623136383863363239373736363163383063222c2273223a22307836303766626331356331663739393561343235386635613962636363363362303430333632643139393164356566653133363163353632323265346361383966227d",
Time: 1521515026,
Txid: "0xe6b168d6bb3d8ed78e03dbf828b6bfd1fb613f6e129cba624964984553724c5d",
Vin: []bchain.Vin{
{
Addresses: []string{"0xdacc9c61754a0c4616fc5323dc946e89eb272302"},
},
},
Vout: []bchain.Vout{
{
ValueSat: *big.NewInt(1311768467294899695),
ScriptPubKey: bchain.ScriptPubKey{
Addresses: []string{"0x682b7903a11098cf770c7aef4aa02a85b3f3601a"},
},
},
},
}
testTx2 = bchain.Tx{
Blocktime: 1521533434,
Hex: "7b226e6f6e6365223a22307862323663222c226761735072696365223a223078343330653233343030222c22676173223a22307835323038222c22746f223a22307835353565653131666264646330653439613962616233353861383934316164393566666462343866222c2276616c7565223a2230783231222c22696e707574223a223078222c2268617368223a22307863643634373135313535326235313332623261656637633962653030646336663733616663353930316464653135376161623133313333356261616138353362222c22626c6f636b4e756d626572223a223078326263663038222c2266726f6d223a22307833653361336436396463363662613130373337663533316564303838393534613965633839643937222c227472616e73616374696f6e496e646578223a22307861222c2276223a2230783239222c2272223a22307866373136316331373064343335373361643963386437303163646166373134666632613534386135363262306463363339323330643137383839666364343035222c2273223a22307833633439373766633930333835613237656661303033326531376234396664353735623238323663623536653364316563663231353234663261393466393135227d",
Time: 1521533434,
Txid: "0xcd647151552b5132b2aef7c9be00dc6f73afc5901dde157aab131335baaa853b",
Vin: []bchain.Vin{
{
Addresses: []string{"0x3e3a3d69dc66ba10737f531ed088954a9ec89d97"},
},
},
Vout: []bchain.Vout{
{
ValueSat: *big.NewInt(33),
ScriptPubKey: bchain.ScriptPubKey{
Addresses: []string{"0x555ee11fbddc0e49a9bab358a8941ad95ffdb48f"},
},
},
},
}
}
func TestEthereumParser_PackTx(t *testing.T) {
type args struct {
tx *bchain.Tx
@ -77,61 +127,27 @@ func TestEthereumParser_PackTx(t *testing.T) {
wantErr bool
}{
{
name: "with 0x prefix",
name: "1",
args: args{
tx: &bchain.Tx{
Blocktime: 1521515026,
Hex: "7b226e6f6e6365223a2230783239666165222c226761735072696365223a223078313261303566323030222c22676173223a2230786462626130222c22746f223a22307836383262373930336131313039386366373730633761656634616130326138356233663336303161222c2276616c7565223a22307830222c22696e707574223a223078663032356361616630303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030323235222c2268617368223a22307865366231363864366262336438656437386530336462663832386236626664316662363133663665313239636261363234393634393834353533373234633564222c22626c6f636b4e756d626572223a223078326263616630222c2266726f6d223a22307864616363396336313735346130633436313666633533323364633934366538396562323732333032222c227472616e73616374696f6e496e646578223a22307831222c2276223a2230783162222c2272223a22307831626434306133313132326330333931386466366431363664373430613661336132326630386132353933346365623136383863363239373736363163383063222c2273223a22307836303766626331356331663739393561343235386635613962636363363362303430333632643139393164356566653133363163353632323265346361383966227d",
Time: 1521515026,
Txid: "0xe6b168d6bb3d8ed78e03dbf828b6bfd1fb613f6e129cba624964984553724c5d",
Vin: []bchain.Vin{
{
Addresses: []string{"0xdacc9c61754a0c4616fc5323dc946e89eb272302"},
},
},
Vout: []bchain.Vout{
{
ScriptPubKey: bchain.ScriptPubKey{
Addresses: []string{"0x682b7903a11098cf770c7aef4aa02a85b3f3601a"},
},
},
},
},
tx: &testTx1,
height: 2870000,
blockTime: 1521515026,
},
want: "08aebf0a1205012a05f20018a0f7362a24f025caaf00000000000000000000000000000000000000000000000000000000000002253220e6b168d6bb3d8ed78e03dbf828b6bfd1fb613f6e129cba624964984553724c5d38f095af014092f4c1d5054a14682b7903a11098cf770c7aef4aa02a85b3f3601a5214dacc9c61754a0c4616fc5323dc946e89eb272302580162011b6a201bd40a31122c03918df6d166d740a6a3a22f08a25934ceb1688c62977661c80c7220607fbc15c1f7995a4258f5a9bccc63b040362d1991d5efe1361c56222e4ca89f",
want: testTxPacked1,
},
{
name: "without 0x prefix",
name: "2",
args: args{
tx: &bchain.Tx{
Blocktime: 1521533434,
Hex: "7b226e6f6e6365223a22307862323663222c226761735072696365223a223078343330653233343030222c22676173223a22307835323038222c22746f223a22307835353565653131666264646330653439613962616233353861383934316164393566666462343866222c2276616c7565223a22307831626330313539643533306536303030222c22696e707574223a223078222c2268617368223a22307863643634373135313535326235313332623261656637633962653030646336663733616663353930316464653135376161623133313333356261616138353362222c22626c6f636b4e756d626572223a223078326263663038222c2266726f6d223a22307833653361336436396463363662613130373337663533316564303838393534613965633839643937222c227472616e73616374696f6e496e646578223a22307861222c2276223a2230783239222c2272223a22307866373136316331373064343335373361643963386437303163646166373134666632613534386135363262306463363339323330643137383839666364343035222c2273223a22307833633439373766633930333835613237656661303033326531376234396664353735623238323663623536653364316563663231353234663261393466393135227d",
Time: 1521533434,
Txid: "cd647151552b5132b2aef7c9be00dc6f73afc5901dde157aab131335baaa853b",
Vin: []bchain.Vin{
{
Addresses: []string{"3e3a3d69dc66ba10737f531ed088954a9ec89d97"},
},
},
Vout: []bchain.Vout{
{
ScriptPubKey: bchain.ScriptPubKey{
Addresses: []string{"555ee11fbddc0e49a9bab358a8941ad95ffdb48f"},
},
},
},
},
tx: &testTx2,
height: 2871048,
blockTime: 1521533434,
},
want: "08ece40212050430e234001888a40122081bc0159d530e60003220cd647151552b5132b2aef7c9be00dc6f73afc5901dde157aab131335baaa853b38889eaf0140fa83c3d5054a14555ee11fbddc0e49a9bab358a8941ad95ffdb48f52143e3a3d69dc66ba10737f531ed088954a9ec89d97580a6201296a20f7161c170d43573ad9c8d701cdaf714ff2a548a562b0dc639230d17889fcd40572203c4977fc90385a27efa0032e17b49fd575b2826cb56e3d1ecf21524f2a94f915",
want: testTxPacked2,
},
}
p := NewEthereumParser()
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
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)
@ -146,18 +162,6 @@ func TestEthereumParser_PackTx(t *testing.T) {
}
func TestEthereumParser_UnpackTx(t *testing.T) {
var (
addr1, addr2 bchain.Address
err error
)
addr1, err = bchain.NewBaseAddress("0x682b7903a11098cf770c7aef4aa02a85b3f3601a")
if err == nil {
addr2, err = bchain.NewBaseAddress("0x555ee11fbddc0e49a9bab358a8941ad95ffdb48f")
}
if err != nil {
panic(err)
}
type args struct {
hex string
}
@ -170,57 +174,21 @@ func TestEthereumParser_UnpackTx(t *testing.T) {
wantErr bool
}{
{
name: "1",
args: args{hex: "08aebf0a1205012a05f20018a0f7362a24f025caaf00000000000000000000000000000000000000000000000000000000000002253220e6b168d6bb3d8ed78e03dbf828b6bfd1fb613f6e129cba624964984553724c5d38f095af014092f4c1d5054a14682b7903a11098cf770c7aef4aa02a85b3f3601a5214dacc9c61754a0c4616fc5323dc946e89eb272302580162011b6a201bd40a31122c03918df6d166d740a6a3a22f08a25934ceb1688c62977661c80c7220607fbc15c1f7995a4258f5a9bccc63b040362d1991d5efe1361c56222e4ca89f"},
want: &bchain.Tx{
Blocktime: 1521515026,
Hex: "7b226e6f6e6365223a2230783239666165222c226761735072696365223a223078313261303566323030222c22676173223a2230786462626130222c22746f223a22307836383262373930336131313039386366373730633761656634616130326138356233663336303161222c2276616c7565223a22307830222c22696e707574223a223078663032356361616630303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030323235222c2268617368223a22307865366231363864366262336438656437386530336462663832386236626664316662363133663665313239636261363234393634393834353533373234633564222c22626c6f636b4e756d626572223a223078326263616630222c2266726f6d223a22307864616363396336313735346130633436313666633533323364633934366538396562323732333032222c227472616e73616374696f6e496e646578223a22307831222c2276223a2230783162222c2272223a22307831626434306133313132326330333931386466366431363664373430613661336132326630386132353933346365623136383863363239373736363163383063222c2273223a22307836303766626331356331663739393561343235386635613962636363363362303430333632643139393164356566653133363163353632323265346361383966227d",
Time: 1521515026,
Txid: "0xe6b168d6bb3d8ed78e03dbf828b6bfd1fb613f6e129cba624964984553724c5d",
Vin: []bchain.Vin{
{
Addresses: []string{"0xdacc9c61754a0c4616fc5323dc946e89eb272302"},
},
},
Vout: []bchain.Vout{
{
ScriptPubKey: bchain.ScriptPubKey{
Addresses: []string{"0x682b7903a11098cf770c7aef4aa02a85b3f3601a"},
},
Address: addr1,
},
},
},
name: "1",
args: args{hex: testTxPacked1},
want: &testTx1,
want1: 2870000,
},
{
name: "1",
args: args{hex: "08ece40212050430e234001888a40122081bc0159d530e60003220cd647151552b5132b2aef7c9be00dc6f73afc5901dde157aab131335baaa853b38889eaf0140fa83c3d5054a14555ee11fbddc0e49a9bab358a8941ad95ffdb48f52143e3a3d69dc66ba10737f531ed088954a9ec89d97580a6201296a20f7161c170d43573ad9c8d701cdaf714ff2a548a562b0dc639230d17889fcd40572203c4977fc90385a27efa0032e17b49fd575b2826cb56e3d1ecf21524f2a94f915"},
want: &bchain.Tx{
Blocktime: 1521533434,
Hex: "7b226e6f6e6365223a22307862323663222c226761735072696365223a223078343330653233343030222c22676173223a22307835323038222c22746f223a22307835353565653131666264646330653439613962616233353861383934316164393566666462343866222c2276616c7565223a22307831626330313539643533306536303030222c22696e707574223a223078222c2268617368223a22307863643634373135313535326235313332623261656637633962653030646336663733616663353930316464653135376161623133313333356261616138353362222c22626c6f636b4e756d626572223a223078326263663038222c2266726f6d223a22307833653361336436396463363662613130373337663533316564303838393534613965633839643937222c227472616e73616374696f6e496e646578223a22307861222c2276223a2230783239222c2272223a22307866373136316331373064343335373361643963386437303163646166373134666632613534386135363262306463363339323330643137383839666364343035222c2273223a22307833633439373766633930333835613237656661303033326531376234396664353735623238323663623536653364316563663231353234663261393466393135227d",
Time: 1521533434,
Txid: "0xcd647151552b5132b2aef7c9be00dc6f73afc5901dde157aab131335baaa853b",
Vin: []bchain.Vin{
{
Addresses: []string{"0x3e3a3d69dc66ba10737f531ed088954a9ec89d97"},
},
},
Vout: []bchain.Vout{
{
ScriptPubKey: bchain.ScriptPubKey{
Addresses: []string{"0x555ee11fbddc0e49a9bab358a8941ad95ffdb48f"},
},
Address: addr2,
},
},
},
name: "2",
args: args{hex: testTxPacked2},
want: &testTx2,
want1: 2871048,
},
}
p := NewEthereumParser()
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
p := NewEthereumParser()
b, err := hex.DecodeString(tt.args.hex)
if err != nil {
panic(err)

View File

@ -6,6 +6,7 @@ import (
"encoding/json"
"fmt"
"math/big"
"strconv"
"sync"
"time"
@ -260,16 +261,23 @@ func (b *EthereumRPC) GetSubversion() string {
return ""
}
// GetBlockChainInfo returns the NetworkID of the ethereum network
func (b *EthereumRPC) GetBlockChainInfo() (string, error) {
// GetChainInfo returns information about the connected backend
func (b *EthereumRPC) GetChainInfo() (*bchain.ChainInfo, error) {
ctx, cancel := context.WithTimeout(context.Background(), b.timeout)
defer cancel()
id, err := b.client.NetworkID(ctx)
if err != nil {
return "", err
return nil, err
}
return id.String(), nil
rv := &bchain.ChainInfo{}
idi := int(id.Uint64())
if idi == 1 {
rv.Chain = "mainnet"
} else {
rv.Chain = "testnet " + strconv.Itoa(idi)
}
// TODO - return more information about the chain
return rv, nil
}
func (b *EthereumRPC) getBestHeader() (*ethtypes.Header, error) {
@ -336,9 +344,9 @@ func (b *EthereumRPC) ethHeaderToBlockHeader(h *ethtypes.Header) (*bchain.BlockH
Hash: ethHashToHash(h.Hash()),
Height: uint32(hn),
Confirmations: int(c),
Time: int64(h.Time.Uint64()),
// Next
// Prev
}, nil
}
@ -408,6 +416,8 @@ func (b *EthereumRPC) GetBlock(hash string, height uint32) (*bchain.Block, error
return nil, errors.Annotatef(fmt.Errorf("server returned empty transaction list but block header indicates transactions"), "hash %v, height %v", hash, height)
}
bbh, err := b.ethHeaderToBlockHeader(head)
// TODO - this is probably not the correct size
bbh.Size = len(raw)
btxs := make([]bchain.Tx, len(body.Transactions))
for i, tx := range body.Transactions {
btx, err := b.Parser.ethTxToTx(&tx, int64(head.Time.Uint64()), uint32(bbh.Confirmations))
@ -423,6 +433,12 @@ func (b *EthereumRPC) GetBlock(hash string, height uint32) (*bchain.Block, error
return &bbk, nil
}
// GetBlockInfo returns extended header (more info than in bchain.BlockHeader) with a list of txids
func (b *EthereumRPC) GetBlockInfo(hash string) (*bchain.BlockInfo, error) {
// TODO - implement
return nil, errors.New("Not implemented yet")
}
// GetTransactionForMempool returns a transaction by the transaction ID.
// It could be optimized for mempool, i.e. without block time and confirmations
func (b *EthereumRPC) GetTransactionForMempool(txid string) (*bchain.Tx, error) {
@ -497,13 +513,13 @@ func (b *EthereumRPC) GetMempool() ([]string, error) {
return body.Transactions, nil
}
// EstimateFee returns fee estimation.
func (b *EthereumRPC) EstimateFee(blocks int) (float64, error) {
// EstimateFee returns fee estimation
func (b *EthereumRPC) EstimateFee(blocks int) (big.Int, error) {
return b.EstimateSmartFee(blocks, true)
}
// EstimateSmartFee returns fee estimation.
func (b *EthereumRPC) EstimateSmartFee(blocks int, conservative bool) (float64, error) {
// EstimateSmartFee returns fee estimation
func (b *EthereumRPC) EstimateSmartFee(blocks int, conservative bool) (big.Int, error) {
ctx, cancel := context.WithTimeout(context.Background(), b.timeout)
defer cancel()
// TODO - what parameters of msg to use to get better estimate, maybe more data from the wallet are needed
@ -512,10 +528,12 @@ func (b *EthereumRPC) EstimateSmartFee(blocks int, conservative bool) (float64,
To: &a,
}
g, err := b.client.EstimateGas(ctx, msg)
var r big.Int
if err != nil {
return 0, err
return r, err
}
return float64(g), nil
r.SetUint64(g)
return r, nil
}
// SendRawTransaction sends raw transaction.
@ -543,10 +561,16 @@ func (b *EthereumRPC) ResyncMempool(onNewTxAddr bchain.OnNewTxAddrFunc) (int, er
return b.Mempool.Resync(onNewTxAddr)
}
// GetMempoolTransactions returns slice of mempool transactions for given address
func (b *EthereumRPC) GetMempoolTransactions(address string) ([]string, error) {
return b.Mempool.GetTransactions(address)
}
// GetMempoolTransactionsForAddrDesc returns slice of mempool transactions for given address descriptor
func (b *EthereumRPC) GetMempoolTransactionsForAddrDesc(addrDesc bchain.AddressDescriptor) ([]string, error) {
return b.Mempool.GetAddrDescTransactions(addrDesc)
}
func (b *EthereumRPC) GetMempoolEntry(txid string) (*bchain.MempoolEntry, error) {
return nil, errors.New("GetMempoolEntry: not implemented")
}

View File

@ -1,62 +0,0 @@
// +build integration
package eth
import (
"blockbook/bchain"
"blockbook/bchain/tests/rpc"
"encoding/json"
"flag"
"os"
"testing"
)
func getRPCClient(cfg json.RawMessage) (bchain.BlockChain, error) {
c, err := NewEthereumRPC(cfg, nil)
if err != nil {
return nil, err
}
return c, nil
}
var rpcTest *rpc.Test
func TestMain(m *testing.M) {
flag.Parse()
t, err := rpc.NewTest("Ethereum Testnet", getRPCClient)
if err != nil {
panic(err)
}
rpcTest = t
os.Exit(m.Run())
}
func TestEthRPC_GetBlockHash(t *testing.T) {
rpcTest.TestGetBlockHash(t)
}
func TestEthRPC_GetBlock(t *testing.T) {
rpcTest.TestGetBlock(t)
}
func TestEthRPC_GetTransaction(t *testing.T) {
rpcTest.TestGetTransaction(t)
}
func TestEthRPC_GetBestBlockHash(t *testing.T) {
rpcTest.TestGetBestBlockHash(t)
}
func TestEthRPC_GetBestBlockHeight(t *testing.T) {
rpcTest.TestGetBestBlockHeight(t)
}
func TestEthRPC_GetBlockHeader(t *testing.T) {
rpcTest.TestGetBlockHeader(t)
}
func TestEthRPC_EstimateFee(t *testing.T) {
rpcTest.TestEstimateFee(t)
}

View File

@ -3,8 +3,8 @@ package litecoin
import (
"blockbook/bchain/coins/btc"
"github.com/btcsuite/btcd/chaincfg"
"github.com/btcsuite/btcd/wire"
"github.com/jakm/btcutil/chaincfg"
)
const (
@ -18,17 +18,17 @@ var (
TestNetParams chaincfg.Params
)
func init() {
func initParams() {
MainNetParams = chaincfg.MainNetParams
MainNetParams.Net = MainnetMagic
MainNetParams.PubKeyHashAddrID = 48
MainNetParams.ScriptHashAddrID = 50
MainNetParams.PubKeyHashAddrID = []byte{48}
MainNetParams.ScriptHashAddrID = []byte{50}
MainNetParams.Bech32HRPSegwit = "ltc"
TestNetParams = chaincfg.TestNet3Params
TestNetParams.Net = TestnetMagic
TestNetParams.PubKeyHashAddrID = 111
TestNetParams.ScriptHashAddrID = 58
TestNetParams.PubKeyHashAddrID = []byte{111}
TestNetParams.ScriptHashAddrID = []byte{58}
TestNetParams.Bech32HRPSegwit = "tltc"
err := chaincfg.Register(&MainNetParams)
@ -53,6 +53,9 @@ func NewLitecoinParser(params *chaincfg.Params, c *btc.Configuration) *LitecoinP
// GetChainParams contains network parameters for the main Litecoin network,
// and the test Litecoin network
func GetChainParams(chain string) *chaincfg.Params {
if MainNetParams.Name == "" {
initParams()
}
switch chain {
case "test":
return &TestNetParams

View File

@ -6,11 +6,12 @@ import (
"blockbook/bchain"
"blockbook/bchain/coins/btc"
"encoding/hex"
"math/big"
"reflect"
"testing"
)
func TestAddressToOutputScript_Testnet(t *testing.T) {
func Test_GetAddrDescFromAddress_Testnet(t *testing.T) {
type args struct {
address string
}
@ -43,20 +44,20 @@ func TestAddressToOutputScript_Testnet(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := parser.AddressToOutputScript(tt.args.address)
got, err := parser.GetAddrDescFromAddress(tt.args.address)
if (err != nil) != tt.wantErr {
t.Errorf("AddressToOutputScript() error = %v, wantErr %v", err, tt.wantErr)
t.Errorf("GetAddrDescFromAddress() error = %v, wantErr %v", err, tt.wantErr)
return
}
h := hex.EncodeToString(got)
if !reflect.DeepEqual(h, tt.want) {
t.Errorf("AddressToOutputScript() = %v, want %v", h, tt.want)
t.Errorf("GetAddrDescFromAddress() = %v, want %v", h, tt.want)
}
})
}
}
func TestAddressToOutputScript_Mainnet(t *testing.T) {
func Test_GetAddrDescFromAddress_Mainnet(t *testing.T) {
type args struct {
address string
}
@ -107,14 +108,14 @@ func TestAddressToOutputScript_Mainnet(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := parser.AddressToOutputScript(tt.args.address)
got, err := parser.GetAddrDescFromAddress(tt.args.address)
if (err != nil) != tt.wantErr {
t.Errorf("AddressToOutputScript() error = %v, wantErr %v", err, tt.wantErr)
t.Errorf("GetAddrDescFromAddress() error = %v, wantErr %v", err, tt.wantErr)
return
}
h := hex.EncodeToString(got)
if !reflect.DeepEqual(h, tt.want) {
t.Errorf("AddressToOutputScript() = %v, want %v", h, tt.want)
t.Errorf("GetAddrDescFromAddress() = %v, want %v", h, tt.want)
}
})
}
@ -127,18 +128,6 @@ var (
)
func init() {
var (
addr1, addr2 bchain.Address
err error
)
addr1, err = bchain.NewBaseAddress("LMgENNXzzuPxp7vfMjDrCU44bsmrEMgqvc")
if err == nil {
addr2, err = bchain.NewBaseAddress("LV1ByjbJNFTHyFQqwqwdJXKJznYDzXzg4B")
}
if err != nil {
panic(err)
}
testTx1 = bchain.Tx{
Hex: "02000000031e1977dc524bec5929e95d8d0946812944b7b5bda12f5b99fdf557773f2ee65e0100000000ffffffff8a398e44546dce0245452b90130e86832b21fd68f26662bc33aeb7c6c115d23c1900000000ffffffffb807ab93a7fcdff7af6d24581a4a18aa7c1db1ebecba2617a6805b009513940f0c00000000ffffffff020001a04a000000001976a9141ae882e788091732da6910595314447c9e38bd8d88ac27440f00000000001976a9146b474cbf0f6004329b630bdd4798f2c23d1751b688ac00000000",
Blocktime: 1519053456,
@ -173,26 +162,24 @@ func init() {
},
Vout: []bchain.Vout{
{
Value: 12.52000000,
N: 0,
ValueSat: *big.NewInt(1252000000),
N: 0,
ScriptPubKey: bchain.ScriptPubKey{
Hex: "76a9141ae882e788091732da6910595314447c9e38bd8d88ac",
Addresses: []string{
"LMgENNXzzuPxp7vfMjDrCU44bsmrEMgqvc",
},
},
Address: addr1,
},
{
Value: 0.01000487,
N: 1,
ValueSat: *big.NewInt(1000487),
N: 1,
ScriptPubKey: bchain.ScriptPubKey{
Hex: "76a9146b474cbf0f6004329b630bdd4798f2c23d1751b688ac",
Addresses: []string{
"LV1ByjbJNFTHyFQqwqwdJXKJznYDzXzg4B",
},
},
Address: addr2,
},
},
}

View File

@ -24,6 +24,7 @@ func NewLitecoinRPC(config json.RawMessage, pushHandler func(bchain.Notification
b.(*btc.BitcoinRPC),
}
s.RPCMarshaler = btc.JSONMarshalerV2{}
s.ChainConfig.SupportsEstimateFee = false
return s, nil
}
@ -54,8 +55,3 @@ func (b *LitecoinRPC) Initialize() error {
return nil
}
// EstimateFee returns fee estimation.
func (b *LitecoinRPC) EstimateFee(blocks int) (float64, error) {
return b.EstimateSmartFee(blocks, true)
}

View File

@ -1,10 +1,11 @@
package monacoin
import (
"blockbook/bchain"
"blockbook/bchain/coins/btc"
"github.com/btcsuite/btcd/chaincfg"
"github.com/btcsuite/btcd/wire"
"github.com/jakm/btcutil/chaincfg"
monacoinCfg "github.com/wakiyamap/monad/chaincfg"
"github.com/wakiyamap/monad/txscript"
monacoinWire "github.com/wakiyamap/monad/wire"
@ -25,11 +26,11 @@ var (
MonaTestParams monacoinCfg.Params
)
func init() {
func initParams() {
MainNetParams = chaincfg.MainNetParams
MainNetParams.Net = MainnetMagic
MainNetParams.PubKeyHashAddrID = 50
MainNetParams.ScriptHashAddrID = 55
MainNetParams.PubKeyHashAddrID = []byte{50}
MainNetParams.ScriptHashAddrID = []byte{55}
MainNetParams.Bech32HRPSegwit = "mona"
MonaMainParams = monacoinCfg.MainNetParams
MonaMainParams.Net = MonaMainMagic
@ -39,8 +40,8 @@ func init() {
TestNetParams = chaincfg.TestNet3Params
TestNetParams.Net = TestnetMagic
TestNetParams.PubKeyHashAddrID = 111
TestNetParams.ScriptHashAddrID = 117
TestNetParams.PubKeyHashAddrID = []byte{111}
TestNetParams.ScriptHashAddrID = []byte{117}
TestNetParams.Bech32HRPSegwit = "tmona"
MonaTestParams = monacoinCfg.TestNet4Params
MonaTestParams.Net = MonaTestMagic
@ -70,6 +71,9 @@ func NewMonacoinParser(params *chaincfg.Params, c *btc.Configuration) *MonacoinP
// GetChainParams contains network parameters for the main Monacoin network,
// and the test Monacoin network
func GetChainParams(chain string) *chaincfg.Params {
if MainNetParams.Name == "" {
initParams()
}
switch chain {
case "test":
return &TestNetParams
@ -89,13 +93,13 @@ func GetMonaChainParams(chain string) *monacoinCfg.Params {
}
}
// GetAddrIDFromAddress returns internal address representation of given address
func (p *MonacoinParser) GetAddrIDFromAddress(address string) ([]byte, error) {
return p.AddressToOutputScript(address)
// GetAddrDescFromAddress returns internal address representation (descriptor) of given address
func (p *MonacoinParser) GetAddrDescFromAddress(address string) (bchain.AddressDescriptor, error) {
return p.addressToOutputScript(address)
}
// AddressToOutputScript converts monacoin address to ScriptPubKey
func (p *MonacoinParser) AddressToOutputScript(address string) ([]byte, error) {
// addressToOutputScript converts monacoin address to ScriptPubKey
func (p *MonacoinParser) addressToOutputScript(address string) ([]byte, error) {
switch p.Params.Net {
case MainnetMagic:
da, err := monautil.DecodeAddress(address, &MonaMainParams)

View File

@ -1,4 +1,4 @@
// build unittest
// +build unittest
package monacoin
@ -6,11 +6,12 @@ import (
"blockbook/bchain"
"blockbook/bchain/coins/btc"
"encoding/hex"
"math/big"
"reflect"
"testing"
)
func TestAddressToOutputScript_Testnet(t *testing.T) {
func Test_GetAddrDescFromAddress_Testnet(t *testing.T) {
type args struct {
address string
}
@ -43,20 +44,20 @@ func TestAddressToOutputScript_Testnet(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := parser.AddressToOutputScript(tt.args.address)
got, err := parser.GetAddrDescFromAddress(tt.args.address)
if (err != nil) != tt.wantErr {
t.Errorf("AddressToOutputScript() error = %v, wantErr %v", err, tt.wantErr)
t.Errorf("GetAddrDescFromAddress() error = %v, wantErr %v", err, tt.wantErr)
return
}
h := hex.EncodeToString(got)
if !reflect.DeepEqual(h, tt.want) {
t.Errorf("AddressToOutputScript() = %v, want %v", h, tt.want)
t.Errorf("GetAddrDescFromAddress() = %v, want %v", h, tt.want)
}
})
}
}
func TestAddressToOutputScript_Mainnet(t *testing.T) {
func Test_GetAddrDescFromAddress_Mainnet(t *testing.T) {
type args struct {
address string
}
@ -107,14 +108,89 @@ func TestAddressToOutputScript_Mainnet(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := parser.AddressToOutputScript(tt.args.address)
got, err := parser.GetAddrDescFromAddress(tt.args.address)
if (err != nil) != tt.wantErr {
t.Errorf("AddressToOutputScript() error = %v, wantErr %v", err, tt.wantErr)
t.Errorf("GetAddrDescFromAddress() error = %v, wantErr %v", err, tt.wantErr)
return
}
h := hex.EncodeToString(got)
if !reflect.DeepEqual(h, tt.want) {
t.Errorf("AddressToOutputScript() = %v, want %v", h, tt.want)
t.Errorf("GetAddrDescFromAddress() = %v, want %v", h, tt.want)
}
})
}
}
func Test_GetAddressesFromAddrDesc(t *testing.T) {
type args struct {
script string
}
tests := []struct {
name string
args args
want []string
want2 bool
wantErr bool
}{
{
name: "P2PKH",
args: args{script: "76a91451dadacc7021440cbe4ca148a5db563b329b4c0388ac"},
want: []string{"MFMy9FwJsV6HiN5eZDqDETw4pw52q3UGrb"},
want2: true,
wantErr: false,
},
{
name: "P2SH",
args: args{script: "a9146449f568c9cd2378138f2636e1567112a184a9e887"},
want: []string{"PHjTKtgYLTJ9D2Bzw2f6xBB41KBm2HeGfg"},
want2: true,
wantErr: false,
},
{
name: "P2WPKH",
args: args{script: "0014a96d3cef194f469b33801f868ec9bc89a8831c22"},
want: []string{"mona1q49knemcefarfkvuqr7rgajdu3x5gx8pzdnurgq"},
want2: true,
wantErr: false,
},
{
name: "P2WSH",
args: args{script: "002009d27aa88e70cb7a0da620908c9bc08ac6c633bd1a61036312e514396aeb4893"},
want: []string{"mona1qp8f842ywwr9h5rdxyzggex7q3trvvvaarfssxccju52rj6htfzfsqr79j2"},
want2: true,
wantErr: false,
},
{
name: "OP_RETURN ascii",
args: args{script: "6a0461686f6a"},
want: []string{"OP_RETURN (ahoj)"},
want2: false,
wantErr: false,
},
{
name: "OP_RETURN hex",
args: args{script: "6a072020f1686f6a20"},
want: []string{"OP_RETURN 2020f1686f6a20"},
want2: false,
wantErr: false,
},
}
parser := NewMonacoinParser(GetChainParams("main"), &btc.Configuration{})
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
b, _ := hex.DecodeString(tt.args.script)
got, got2, err := parser.GetAddressesFromAddrDesc(b)
if (err != nil) != tt.wantErr {
t.Errorf("outputScriptToAddresses() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("GetAddressesFromAddrDesc() = %v, want %v", got, tt.want)
}
if !reflect.DeepEqual(got2, tt.want2) {
t.Errorf("GetAddressesFromAddrDesc() = %v, want %v", got2, tt.want2)
}
})
}
@ -127,18 +203,6 @@ var (
)
func init() {
var (
addr1, addr2 bchain.Address
err error
)
addr1, err = bchain.NewBaseAddress("MWpWpANNQRskQHcuY5ZQpN4BVynQxmSxRb")
if err == nil {
addr2, err = bchain.NewBaseAddress("MGtFpCVyKEHNtpVNesxPMxYuQayoEBX5yZ")
}
if err != nil {
panic(err)
}
testTx1 = bchain.Tx{
Hex: "0200000003e44ef4e5fe2e4345f1e1340afe396c780773e3834a5bffb153a2faf510e2845e000000006a47304402205ebd735621eaaf512441998727a37e99be94e5ecded54601ea3eebac9282bc2502207d48da44e1c883579c6cd8c2b8ccfb5380e5ac71affe70b475d2b558e0f7bd4b01210391f72b34c04855ce16b97dd79b0ba78fc4b26f40abce853c33788e348cb79c3bfeffffff0ad690a74c43c0df9527c516d26e31fa47e15471a2ead65757b672522888e920010000006b48304502210091a473124bf506edbb095951aa1a32c76bea7eba4020ae2858314961b1a83de602205c3818e517cf830a95a1208fc84aa343faaeeaaa96eab76238379769598ab2d40121038c217e5de8e375ed6cf648e96ec6bfb9e0fbcf5ae3945a5ea60d16919d9c8b68feffffffb9aa4aed4ad4c4b95419e132a43db34aa03a7ec35ef0beecdd627f9ca07bda03010000006a47304402204906d973ac9b4786403f8f8fc2b2ad2e6745ea01a93336b4b67af1d7d1b625cc022016820be905ffd6e11949da79e7a1c7eb97939421a04e0645c8caef8fc585f7ca012102b5f647c4eb677e952913c0b6934c12b29dc50afba8b558b1677ffd2d78c84a88feffffff02f6da4601000000001976a914fb69fe6dcfe88557dc0ce0ea65bd7cf02f5e4f5b88ac8bfd8c57000000001976a914628d603ac50d656e3311ff0cd5490b4c5cdd92ea88ac25fd1400",
Blocktime: 1530902705,
@ -173,26 +237,24 @@ func init() {
},
Vout: []bchain.Vout{
{
Value: 0.21420790,
N: 0,
ValueSat: *big.NewInt(21420790),
N: 0,
ScriptPubKey: bchain.ScriptPubKey{
Hex: "76a914fb69fe6dcfe88557dc0ce0ea65bd7cf02f5e4f5b88ac",
Addresses: []string{
"MWpWpANNQRskQHcuY5ZQpN4BVynQxmSxRb",
},
},
Address: addr1,
},
{
Value: 14.68857739,
N: 1,
ValueSat: *big.NewInt(1468857739),
N: 1,
ScriptPubKey: bchain.ScriptPubKey{
Hex: "76a914628d603ac50d656e3311ff0cd5490b4c5cdd92ea88ac",
Addresses: []string{
"MGtFpCVyKEHNtpVNesxPMxYuQayoEBX5yZ",
},
},
Address: addr2,
},
},
}

View File

@ -24,6 +24,7 @@ func NewMonacoinRPC(config json.RawMessage, pushHandler func(bchain.Notification
b.(*btc.BitcoinRPC),
}
s.RPCMarshaler = btc.JSONMarshalerV2{}
s.ChainConfig.SupportsEstimateFee = false
return s, nil
}
@ -54,8 +55,3 @@ func (b *MonacoinRPC) Initialize() error {
return nil
}
// EstimateFee returns fee estimation.
func (b *MonacoinRPC) EstimateFee(blocks int) (float64, error) {
return b.EstimateSmartFee(blocks, true)
}

View File

@ -1,72 +0,0 @@
// +build integration
package monacoin
import (
"blockbook/bchain"
"blockbook/bchain/tests/rpc"
"encoding/json"
"flag"
"os"
"testing"
)
func getRPCClient(cfg json.RawMessage) (bchain.BlockChain, error) {
c, err := NewMonacoinRPC(cfg, nil)
if err != nil {
return nil, err
}
cli := c.(*MonacoinRPC)
cli.Parser = NewMonacoinParser(GetChainParams("main"), cli.ChainConfig)
if err != nil {
return nil, err
}
cli.Mempool = bchain.NewUTXOMempool(cli, cli.ChainConfig.MempoolWorkers, cli.ChainConfig.MempoolSubWorkers)
return cli, nil
}
var rpcTest *rpc.Test
func TestMain(m *testing.M) {
flag.Parse()
t, err := rpc.NewTest("Monacoin", getRPCClient)
if err != nil {
panic(err)
}
rpcTest = t
os.Exit(m.Run())
}
func TestMonacoinRPC_GetBlockHash(t *testing.T) {
rpcTest.TestGetBlockHash(t)
}
func TestMonacoinRPC_GetBlock(t *testing.T) {
rpcTest.TestGetBlock(t)
}
func TestMonacoinRPC_GetTransaction(t *testing.T) {
rpcTest.TestGetTransaction(t)
}
func TestMonacoinRPC_GetTransactionForMempool(t *testing.T) {
rpcTest.TestGetTransactionForMempool(t)
}
func TestMonacoinRPC_MempoolSync(t *testing.T) {
rpcTest.TestMempoolSync(t)
}
func TestMonacoinRPC_GetMempoolEntry(t *testing.T) {
rpcTest.TestGetMempoolEntry(t)
}
func TestMonacoinRPC_EstimateSmartFee(t *testing.T) {
rpcTest.TestEstimateSmartFee(t)
}
func TestMonacoinRPC_EstimateFee(t *testing.T) {
rpcTest.TestEstimateFee(t)
}

View File

@ -6,8 +6,8 @@ import (
"blockbook/bchain/coins/utils"
"bytes"
"github.com/btcsuite/btcd/chaincfg"
"github.com/btcsuite/btcd/wire"
"github.com/jakm/btcutil/chaincfg"
)
const (
@ -18,11 +18,11 @@ var (
MainNetParams chaincfg.Params
)
func init() {
func initParams() {
MainNetParams = chaincfg.MainNetParams
MainNetParams.Net = MainnetMagic
MainNetParams.PubKeyHashAddrID = 52
MainNetParams.ScriptHashAddrID = 13
MainNetParams.PubKeyHashAddrID = []byte{52}
MainNetParams.ScriptHashAddrID = []byte{13}
err := chaincfg.Register(&MainNetParams)
if err != nil {
@ -43,6 +43,9 @@ func NewNamecoinParser(params *chaincfg.Params, c *btc.Configuration) *NamecoinP
// GetChainParams contains network parameters for the main Namecoin network,
// and the test Namecoin network
func GetChainParams(chain string) *chaincfg.Params {
if MainNetParams.Name == "" {
initParams()
}
switch chain {
default:
return &MainNetParams
@ -75,5 +78,11 @@ func (p *NamecoinParser) ParseBlock(b []byte) (*bchain.Block, error) {
txs[ti] = p.TxFromMsgTx(t, false)
}
return &bchain.Block{Txs: txs}, nil
return &bchain.Block{
BlockHeader: bchain.BlockHeader{
Size: len(b),
Time: h.Timestamp.Unix(),
},
Txs: txs,
}, nil
}

View File

@ -13,7 +13,7 @@ import (
"testing"
)
func TestAddressToOutputScript_Mainnet(t *testing.T) {
func Test_GetAddrDescFromAddress_Mainnet(t *testing.T) {
type args struct {
address string
}
@ -40,24 +40,34 @@ func TestAddressToOutputScript_Mainnet(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := parser.AddressToOutputScript(tt.args.address)
got, err := parser.GetAddrDescFromAddress(tt.args.address)
if (err != nil) != tt.wantErr {
t.Errorf("AddressToOutputScript() error = %v, wantErr %v", err, tt.wantErr)
t.Errorf("GetAddrDescFromAddress() error = %v, wantErr %v", err, tt.wantErr)
return
}
h := hex.EncodeToString(got)
if !reflect.DeepEqual(h, tt.want) {
t.Errorf("AddressToOutputScript() = %v, want %v", h, tt.want)
t.Errorf("GetAddrDescFromAddress() = %v, want %v", h, tt.want)
}
})
}
}
var testParseBlockTxs = map[int][]string{
40000: []string{
"e193a821393b20b99f4a4e05a481368ef8a8cfd43d0c45bdad7f53bc9535e844",
"ddcbf95797e81dd04127885bd001e96695b717e11c52721f6e8ee53f6dea8a6f",
"31ff728f24200f59fa4958e6c26de03d172b320e6eef2b8abecf6f94d01dd4ae",
type testBlock struct {
size int
time int64
txs []string
}
var testParseBlockTxs = map[int]testBlock{
40000: testBlock{
size: 1385,
time: 1327728573,
txs: []string{
"e193a821393b20b99f4a4e05a481368ef8a8cfd43d0c45bdad7f53bc9535e844",
"ddcbf95797e81dd04127885bd001e96695b717e11c52721f6e8ee53f6dea8a6f",
"31ff728f24200f59fa4958e6c26de03d172b320e6eef2b8abecf6f94d01dd4ae",
},
},
}
@ -84,7 +94,7 @@ func helperLoadBlock(t *testing.T, height int) []byte {
func TestParseBlock(t *testing.T) {
p := NewNamecoinParser(GetChainParams("main"), &btc.Configuration{})
for height, txs := range testParseBlockTxs {
for height, tb := range testParseBlockTxs {
b := helperLoadBlock(t, height)
blk, err := p.ParseBlock(b)
@ -92,11 +102,19 @@ func TestParseBlock(t *testing.T) {
t.Fatal(err)
}
if len(blk.Txs) != len(txs) {
t.Errorf("ParseBlock() number of transactions: got %d, want %d", len(blk.Txs), len(txs))
if blk.Size != tb.size {
t.Errorf("ParseBlock() block size: got %d, want %d", blk.Size, tb.size)
}
for ti, tx := range txs {
if blk.Time != tb.time {
t.Errorf("ParseBlock() block time: got %d, want %d", blk.Time, tb.time)
}
if len(blk.Txs) != len(tb.txs) {
t.Errorf("ParseBlock() number of transactions: got %d, want %d", len(blk.Txs), len(tb.txs))
}
for ti, tx := range tb.txs {
if blk.Txs[ti].Txid != tx {
t.Errorf("ParseBlock() transaction %d: got %s, want %s", ti, blk.Txs[ti].Txid, tx)
}

View File

@ -24,6 +24,7 @@ func NewNamecoinRPC(config json.RawMessage, pushHandler func(bchain.Notification
b.(*btc.BitcoinRPC),
}
s.RPCMarshaler = btc.JSONMarshalerV1{}
s.ChainConfig.SupportsEstimateSmartFee = false
return s, nil
}
@ -69,8 +70,3 @@ func (b *NamecoinRPC) GetBlock(hash string, height uint32) (*bchain.Block, error
}
return b.GetBlockWithoutHeader(hash, height)
}
// EstimateSmartFee returns fee estimation.
func (b *NamecoinRPC) EstimateSmartFee(blocks int, conservative bool) (float64, error) {
return b.EstimateFee(blocks)
}

View File

@ -1,86 +0,0 @@
// +build integration
package namecoin
import (
"blockbook/bchain"
"blockbook/bchain/tests/rpc"
"encoding/json"
"flag"
"os"
"testing"
)
func getRPCClient(cfg json.RawMessage) (bchain.BlockChain, error) {
c, err := NewNamecoinRPC(cfg, nil)
if err != nil {
return nil, err
}
cli := c.(*NamecoinRPC)
cli.Parser = NewNamecoinParser(GetChainParams("main"), cli.ChainConfig)
if err != nil {
return nil, err
}
cli.Mempool = bchain.NewUTXOMempool(cli, cli.ChainConfig.MempoolWorkers, cli.ChainConfig.MempoolSubWorkers)
return cli, nil
}
var rpcTest *rpc.Test
func TestMain(m *testing.M) {
flag.Parse()
t, err := rpc.NewTest("Namecoin", getRPCClient)
if err != nil {
panic(err)
}
rpcTest = t
os.Exit(m.Run())
}
func TestNamecoinRPC_GetBlockHash(t *testing.T) {
rpcTest.TestGetBlockHash(t)
}
func TestNamecoinRPC_GetBlock(t *testing.T) {
rpcTest.TestGetBlock(t)
}
func TestNamecoinRPC_GetTransaction(t *testing.T) {
rpcTest.TestGetTransaction(t)
}
func TestNamecoinRPC_GetTransactionForMempool(t *testing.T) {
// extra opcodes (name_new, name_firstupdate, name_update) aren't supported, so some transactions
// in mempool can't be parsed correctly
t.Skipf("Skipped because of instability")
}
func TestNamecoinRPC_MempoolSync(t *testing.T) {
rpcTest.TestMempoolSync(t)
}
func TestNamecoinRPC_GetMempoolEntry(t *testing.T) {
rpcTest.TestGetMempoolEntry(t)
}
func TestNamecoinRPC_EstimateSmartFee(t *testing.T) {
rpcTest.TestEstimateSmartFee(t)
}
func TestNamecoinRPC_EstimateFee(t *testing.T) {
rpcTest.TestEstimateFee(t)
}
func TestNamecoinRPC_GetBestBlockHash(t *testing.T) {
rpcTest.TestGetBestBlockHash(t)
}
func TestNamecoinRPC_GetBestBlockHeight(t *testing.T) {
rpcTest.TestGetBestBlockHeight(t)
}
func TestNamecoinRPC_GetBlockHeader(t *testing.T) {
rpcTest.TestGetBlockHeader(t)
}

View File

@ -1,42 +0,0 @@
package utils
import (
"crypto/sha256"
"errors"
"github.com/btcsuite/btcutil/base58"
)
var (
// ErrChecksumMismatch describes an error where decoding failed due
// to a bad checksum.
ErrChecksumMismatch = errors.New("checksum mismatch")
// ErrInvalidFormat describes an error where decoding failed due to invalid version
ErrInvalidFormat = errors.New("invalid format: version and/or checksum bytes missing")
)
// checksum: first four bytes of sha256^2
func checksum(input []byte) (cksum [4]byte) {
h := sha256.Sum256(input)
h2 := sha256.Sum256(h[:])
copy(cksum[:], h2[:4])
return
}
// CheckDecode decodes a string that was encoded with CheckEncode and verifies the checksum.
func CheckDecode(input string) (result []byte, version []byte, err error) {
decoded := base58.Decode(input)
if len(decoded) < 5 {
return nil, nil, ErrInvalidFormat
}
version = append(version, decoded[0:2]...)
var cksum [4]byte
copy(cksum[:], decoded[len(decoded)-4:])
if checksum(decoded[:len(decoded)-4]) != cksum {
return nil, nil, ErrChecksumMismatch
}
payload := decoded[2 : len(decoded)-4]
result = append(result, payload...)
return
}

View File

@ -33,7 +33,7 @@ func DecodeTransactions(r io.Reader, pver uint32, enc wire.MessageEncoding, blk
if txCount > maxTxPerBlock {
str := fmt.Sprintf("too many transactions to fit into a block "+
"[count %d, max %d]", txCount, maxTxPerBlock)
return &wire.MessageError{Func: "btg.decodeTransactions", Description: str}
return &wire.MessageError{Func: "utils.decodeTransactions", Description: str}
}
blk.Transactions = make([]*wire.MsgTx, 0, txCount)

View File

@ -3,8 +3,8 @@ package vertcoin
import (
"blockbook/bchain/coins/btc"
"github.com/btcsuite/btcd/chaincfg"
"github.com/btcsuite/btcd/wire"
"github.com/jakm/btcutil/chaincfg"
)
const (
@ -18,17 +18,17 @@ var (
TestNetParams chaincfg.Params
)
func init() {
func initParams() {
MainNetParams = chaincfg.MainNetParams
MainNetParams.Net = MainnetMagic
MainNetParams.PubKeyHashAddrID = 71
MainNetParams.ScriptHashAddrID = 5
MainNetParams.PubKeyHashAddrID = []byte{71}
MainNetParams.ScriptHashAddrID = []byte{5}
MainNetParams.Bech32HRPSegwit = "vtc"
TestNetParams = chaincfg.TestNet3Params
TestNetParams.Net = TestnetMagic
TestNetParams.PubKeyHashAddrID = 74
TestNetParams.ScriptHashAddrID = 196
TestNetParams.PubKeyHashAddrID = []byte{74}
TestNetParams.ScriptHashAddrID = []byte{196}
TestNetParams.Bech32HRPSegwit = "tvtc"
err := chaincfg.Register(&MainNetParams)
@ -53,6 +53,9 @@ func NewVertcoinParser(params *chaincfg.Params, c *btc.Configuration) *VertcoinP
// GetChainParams contains network parameters for the main Vertcoin network,
// and the test Vertcoin network
func GetChainParams(chain string) *chaincfg.Params {
if MainNetParams.Name == "" {
initParams()
}
switch chain {
case "test":
return &TestNetParams

View File

@ -6,11 +6,12 @@ import (
"blockbook/bchain"
"blockbook/bchain/coins/btc"
"encoding/hex"
"math/big"
"reflect"
"testing"
)
func TestAddressToOutputScript_Mainnet(t *testing.T) {
func Test_GetAddrDescFromAddress_Mainnet(t *testing.T) {
type args struct {
address string
}
@ -55,14 +56,14 @@ func TestAddressToOutputScript_Mainnet(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := parser.AddressToOutputScript(tt.args.address)
got, err := parser.GetAddrDescFromAddress(tt.args.address)
if (err != nil) != tt.wantErr {
t.Errorf("AddressToOutputScript() error = %v, wantErr %v", err, tt.wantErr)
t.Errorf("GetAddrDescFromAddress() error = %v, wantErr %v", err, tt.wantErr)
return
}
h := hex.EncodeToString(got)
if !reflect.DeepEqual(h, tt.want) {
t.Errorf("AddressToOutputScript() = %v, want %v", h, tt.want)
t.Errorf("GetAddrDescFromAddress() = %v, want %v", h, tt.want)
}
})
}
@ -75,18 +76,6 @@ var (
)
func init() {
var (
addr1, addr2 bchain.Address
err error
)
addr1, err = bchain.NewBaseAddress("Vp1UqzsmVecaexfbWFGSFFL5x1g2XQnrGR")
if err == nil {
addr2, err = bchain.NewBaseAddress("38A1RNvbA5c9wNRfyLVn1FCH5TPKJVG8YR")
}
if err != nil {
panic(err)
}
testTx1 = bchain.Tx{
Hex: "010000000146fd781834a34e0399ccda1edf9ec47d715e17d904ad0958d533a240b3605ad6000000006a473044022026b352a0c35c232342339e2b50ec9f04587b990d5213174e368cc76dc82686f002207d0787461ad846825872a50d3d6fc748d5a836575c1daf6ad0ca602f9c4a8826012103d36b6b829c571ed7caa565eca9bdc2aa36519b7ab8551ace5edb0356d477ad3cfdffffff020882a400000000001976a91499b16da88a7e29b913b6131df2644d6d06cb331b88ac80f0fa020000000017a91446eb90e002f137f05385896c882fe000cc2e967f8774870e00",
Blocktime: 1529925180,
@ -105,26 +94,24 @@ func init() {
},
Vout: []bchain.Vout{
{
Value: 0.10781192,
N: 0,
ValueSat: *big.NewInt(10781192),
N: 0,
ScriptPubKey: bchain.ScriptPubKey{
Hex: "76a91499b16da88a7e29b913b6131df2644d6d06cb331b88ac",
Addresses: []string{
"Vp1UqzsmVecaexfbWFGSFFL5x1g2XQnrGR",
},
},
Address: addr1,
},
{
Value: 0.5000000,
N: 1,
ValueSat: *big.NewInt(50000000),
N: 1,
ScriptPubKey: bchain.ScriptPubKey{
Hex: "a91446eb90e002f137f05385896c882fe000cc2e967f87",
Addresses: []string{
"38A1RNvbA5c9wNRfyLVn1FCH5TPKJVG8YR",
},
},
Address: addr2,
},
},
}

View File

@ -24,6 +24,7 @@ func NewVertcoinRPC(config json.RawMessage, pushHandler func(bchain.Notification
b.(*btc.BitcoinRPC),
}
s.RPCMarshaler = btc.JSONMarshalerV2{}
s.ChainConfig.SupportsEstimateFee = false
return s, nil
}
@ -54,8 +55,3 @@ func (b *VertcoinRPC) Initialize() error {
return nil
}
// EstimateFee returns fee estimation.
func (b *VertcoinRPC) EstimateFee(blocks int) (float64, error) {
return b.EstimateSmartFee(blocks, true)
}

View File

@ -1,84 +0,0 @@
// +build integration
package vertcoin
import (
"blockbook/bchain"
"blockbook/bchain/tests/rpc"
"encoding/json"
"flag"
"os"
"testing"
)
func getRPCClient(cfg json.RawMessage) (bchain.BlockChain, error) {
c, err := NewVertcoinRPC(cfg, nil)
if err != nil {
return nil, err
}
cli := c.(*VertcoinRPC)
cli.Parser = NewVertcoinParser(GetChainParams("main"), cli.ChainConfig)
if err != nil {
return nil, err
}
cli.Mempool = bchain.NewUTXOMempool(cli, cli.ChainConfig.MempoolWorkers, cli.ChainConfig.MempoolSubWorkers)
return cli, nil
}
var rpcTest *rpc.Test
func TestMain(m *testing.M) {
flag.Parse()
t, err := rpc.NewTest("Vertcoin", getRPCClient)
if err != nil {
panic(err)
}
rpcTest = t
os.Exit(m.Run())
}
func TestVertcoinRPC_GetBlockHash(t *testing.T) {
rpcTest.TestGetBlockHash(t)
}
func TestVertcoinRPC_GetBlock(t *testing.T) {
rpcTest.TestGetBlock(t)
}
func TestVertcoinRPC_GetTransaction(t *testing.T) {
rpcTest.TestGetTransaction(t)
}
func TestVertcoinRPC_GetTransactionForMempool(t *testing.T) {
rpcTest.TestGetTransactionForMempool(t)
}
func TestVertcoinRPC_MempoolSync(t *testing.T) {
rpcTest.TestMempoolSync(t)
}
func TestVertcoinRPC_GetMempoolEntry(t *testing.T) {
rpcTest.TestGetMempoolEntry(t)
}
func TestVertcoinRPC_EstimateSmartFee(t *testing.T) {
rpcTest.TestEstimateSmartFee(t)
}
func TestVertcoinRPC_EstimateFee(t *testing.T) {
rpcTest.TestEstimateFee(t)
}
func TestVertcoinRPC_GetBestBlockHash(t *testing.T) {
rpcTest.TestGetBestBlockHash(t)
}
func TestVertcoinRPC_GetBestBlockHeight(t *testing.T) {
rpcTest.TestGetBestBlockHeight(t)
}
func TestVertcoinRPC_GetBlockHeader(t *testing.T) {
rpcTest.TestGetBlockHeader(t)
}

View File

@ -3,10 +3,9 @@ package zec
import (
"blockbook/bchain"
"blockbook/bchain/coins/btc"
"blockbook/bchain/coins/utils"
"github.com/btcsuite/btcd/chaincfg"
"github.com/btcsuite/btcd/wire"
"github.com/jakm/btcutil/chaincfg"
)
const (
@ -15,18 +14,48 @@ const (
RegtestMagic wire.BitcoinNet = 0x5f3fe8aa
)
// ZCashParser handle
type ZCashParser struct {
*bchain.BaseParser
var (
MainNetParams chaincfg.Params
TestNetParams chaincfg.Params
)
func initParams() {
MainNetParams = chaincfg.MainNetParams
MainNetParams.Net = MainnetMagic
// Address encoding magics
MainNetParams.AddressMagicLen = 2
MainNetParams.PubKeyHashAddrID = []byte{0x1C, 0xB8} // base58 prefix: t1
MainNetParams.ScriptHashAddrID = []byte{0x1C, 0xBD} // base58 prefix: t3
TestNetParams = chaincfg.TestNet3Params
TestNetParams.Net = TestnetMagic
// Address encoding magics
TestNetParams.AddressMagicLen = 2
TestNetParams.PubKeyHashAddrID = []byte{0x1D, 0x25} // base58 prefix: tm
TestNetParams.ScriptHashAddrID = []byte{0x1C, 0xBA} // base58 prefix: t2
err := chaincfg.Register(&MainNetParams)
if err == nil {
err = chaincfg.Register(&TestNetParams)
}
if err != nil {
panic(err)
}
}
// NewZCAshParser returns new ZCAshParser instance
func NewZCashParser(c *btc.Configuration) *ZCashParser {
// ZCashParser handle
type ZCashParser struct {
*btc.BitcoinParser
baseparser *bchain.BaseParser
}
// NewZCashParser returns new ZCashParser instance
func NewZCashParser(params *chaincfg.Params, c *btc.Configuration) *ZCashParser {
return &ZCashParser{
&bchain.BaseParser{
AddressFactory: bchain.NewBaseAddress,
BlockAddressesToKeep: c.BlockAddressesToKeep,
},
BitcoinParser: btc.NewBitcoinParser(params, c),
baseparser: &bchain.BaseParser{},
}
}
@ -34,33 +63,29 @@ func NewZCashParser(c *btc.Configuration) *ZCashParser {
// the regression test ZCash network, the test ZCash network and
// the simulation test ZCash network, in this order
func GetChainParams(chain string) *chaincfg.Params {
if MainNetParams.Name == "" {
initParams()
}
var params *chaincfg.Params
switch chain {
case "test":
params = &chaincfg.TestNet3Params
params.Net = TestnetMagic
return &TestNetParams
case "regtest":
params = &chaincfg.RegressionNetParams
params.Net = RegtestMagic
default:
params = &chaincfg.MainNetParams
params.Net = MainnetMagic
return &MainNetParams
}
return params
}
// GetAddrIDFromVout returns internal address representation of given transaction output
func (p *ZCashParser) GetAddrIDFromVout(output *bchain.Vout) ([]byte, error) {
if len(output.ScriptPubKey.Addresses) != 1 {
return nil, nil
}
hash, _, err := utils.CheckDecode(output.ScriptPubKey.Addresses[0])
return hash, err
// PackTx packs transaction to byte array using protobuf
func (p *ZCashParser) PackTx(tx *bchain.Tx, height uint32, blockTime int64) ([]byte, error) {
return p.baseparser.PackTx(tx, height, blockTime)
}
// GetAddrIDFromAddress returns internal address representation of given address
func (p *ZCashParser) GetAddrIDFromAddress(address string) ([]byte, error) {
hash, _, err := utils.CheckDecode(address)
return hash, err
// UnpackTx unpacks transaction from protobuf byte array
func (p *ZCashParser) UnpackTx(buf []byte) (*bchain.Tx, uint32, error) {
return p.baseparser.UnpackTx(buf)
}

View File

@ -5,7 +5,9 @@ package zec
import (
"blockbook/bchain"
"blockbook/bchain/coins/btc"
"bytes"
"encoding/hex"
"math/big"
"reflect"
"testing"
)
@ -13,26 +15,11 @@ import (
var (
testTx1, testTx2 bchain.Tx
testTxPacked1 = "0a20e64aac0c211ad210c90934f06b1cc932327329e41a9f70c6eb76f79ef798b7b812ab1002000000019c012650c99d0ef761e863dbb966babf2cb7a7a2b5d90b1461c09521c473d23d000000006b483045022100f220f48c5267ef92a1e7a4d3b44fe9d97cce76eeba2785d45a0e2620b70e8d7302205640bc39e197ce19d95a98a3239af0f208ca289c067f80c97d8e411e61da5dee0121021721e83315fb5282f1d9d2a11892322df589bccd9cef45517b5fb3cfd3055c83ffffffff018eec1a3c040000001976a9149bb8229741305d8316ba3ca6a8d20740ce33c24188ac000000000162b4fc6b0000000000000000000000006ffa88c89b74f0f82e24744296845a0d0113b132ff5dfc2af34e6418eb15206af53078c4dd475cf143cd9a427983f5993622464b53e3a37d2519a946492c3977e30f0866550b9097222993a439a39260ac5e7d36aef38c7fdd1df3035a2d5817a9c20526e38f52f822d4db9d2f0156c4119d786d6e3a060ca871df7fae9a5c3a9c921b38ddc6414b13d16aa807389c68016e54bd6a9eb3b23a6bc7bf152e6dba15e9ec36f95dab15ad8f4a92a9d0309bbd930ef24bb7247bf534065c1e2f5b42e2c80eb59f48b4da6ec522319e065f8c4e463f95cc7fcad8d7ee91608e3c0ffcaa44129ba2d2da45d9a413919eca41af29faaf806a3eeb823e5a6c51afb1ec709505d812c0306bd76061a0a62d207355ad44d1ffce2b9e1dfd0818f79bd0f8e4031116b71fee2488484f17818b80532865773166cd389929e8409bb94e3948bd2e0215ef96d4e29d094590fda0de50715c11ff47c03380bb1d31b14e5b4ad8a372ca0b03364ef85f086b8a8eb5c56c3b1aee33e2cfbf1b2be1a3fb41b14b2c432b5d04d54c058fa87a96ae1d65d61b79360d09acc1e25a883fd7ae9a2a734a03362903021401c243173e1050b5cdb459b9ffc07c95e920f026618952d3a800b2e47e03b902084aed7ee8466a65d34abdbbd292781564dcd9b7440029d48c2640ebc196d4b40217f2872c1d0c1c9c2abf1147d6a5a9501895bc92960bfa182ceeb76a658224f1022bc53c4c1cd6888d72a152dc1aec5ba8a1d750fb7e498bee844d3481e4b4cd210227f94f775744185c9f24571b7df0c1c694cb2d3e4e9b955ed0b1caad2b02b5702139c4fbba03f0e422b2f3e4fc822b4f58baf32e7cd217cdbdec8540cb13d6496f271959b72a05e130eeffbe5b9a7fcd2793347cd9c0ea695265669844c363190f690c52a600cf413c3f00bdc5e9d1539e0cc63f4ec2945e0d86e6304a6deb5651e73eac21add5a641dfc95ab56200ed40d81f76755aee4659334c17ed3841ca5a5ab22f923956be1d264be2b485a0de55404510ece5c73d6626798be688f9dc18b69846acfe897a357cc4afe31f57fea32896717f124290e68f36f849fa6ecf76e02087f8c19dbc566135d7fa2daca2d843b9cc5bc3897d35f1de7d174f6407658f4a3706c12cea53d880b4d8c4d45b3f0d210214f815be49a664021a4a44b4a63e06a41d76b46f9aa6bad248e8d1a974ae7bbae5ea8ac269447db91637a19346729083cad5aebd5ff43ea13d04783068e9136da321b1152c666d2995d0ca06b26541deac62f4ef91f0e4af445b18a5c2a17c96eada0b27f85bb26dfb8f16515114c6b9f88037e2b85b3b84b65822eb99c992d99d12dcf9c71e5b46a586016faf5758483a716566db95b42187c101df68ca0554824e1c23cf0302bea03ad0a146af57e91794a268b8c82d78211718c8b5fea286f5de72fc7dfffecddcc02413525c472cb26022641d4bec2b8b7e71a7beb9ee18b82632799498eeee9a351cb9431a8d1906d5164acdf351bd538c3e9d1da8a211fe1cd18c44e72d8cdf16ce3fc9551552c05d52846ea7ef619232102588395cc2bcce509a4e7f150262a76c15475496c923dfce6bfc05871467ee7c213b39ea365c010083e0b1ba8926d3a9e586d8b11c9bab2a47d888bc7cb1a226c0086a1530e295d0047547006f4c8f1c24cdd8e16bb3845749895dec95f03fcda97d3224f6875b1b7b1c819d2fd35dd30968a3c82bc480d10082caf9d9dda8f9ec649c136c7fa07978099d97eaf4abfdc9854c266979d3cfc868f60689b6e3098b6c52a21796fe7c259d9a0dadf1b6efa59297d4c8c902febe7acf826eed30d40d2ac5119be91b51f4839d94599872c9a93c3e2691294914034001d3a278cb4a84d4ae048c0201a97e4cf1341ee663a162f5b586355018b9e5e30624ccdbeacf7d0382afacaf45f08e84d30c50bcd4e55c3138377261deb4e8c2931cd3c51cee94a048ae4839517b6e6537a5c0148d3830a33fea719ef9b4fa437e4d5fecdb646397c19ee56a0973c362a81803895cdc67246352dc566689cb203f9ebda900a5537bbb75aa25ddf3d4ab87b88737a58d760e1d271f08265daae1fe056e71971a8b826e5b215a05b71f99315b167dd2ec78874189657acafac2b5eeb9a901913f55f7ab69e1f9b203504448d414e71098b932a2309db57257eb3fef9de2f2a5a69aa46747d7b827df838345d38b95772bdab8c178c45777b92e8773864964b8e12ae29dbc1b21bf6527589f6bec71ff1cbb9928477409811c2e8150c79c3f21027ee954863b716875d3e9adfc6fdb18cd57a49bb395ca5c42da56f3beb78aad3a7a487de34a870bca61f3cdec422061328c83c910ab32ea7403c354915b7ebee29e1fea5a75158197e4a68e103f017fd7de5a70148ee7ce59356b1a74f83492e14faaa6cd4870bcc004e6eb0114d3429b74ea98fe2851b4553467a7660074e69b040aa31220d0e405d9166dbaf15e3ae2d8ec3b049ed99d17e0743bb6a1a7c3890bbdb7117f7374ad7a59aa1ab47d10445b28f4bc033794a71f88a8bf024189e9d27f9dc5859a4296437585b215656f807aca9dad35747494a43b8a1cf38be2b18a13de32a262ab29f9ba271c4fbce1a470a8243ebf9e7fd37b09262314afbb9a7e180218a0f1c9d505200028b0eb113299010a0012203dd273c42195c061140bd9b5a2a7b72cbfba66b9db63e861f70e9dc95026019c1800226b483045022100f220f48c5267ef92a1e7a4d3b44fe9d97cce76eeba2785d45a0e2620b70e8d7302205640bc39e197ce19d95a98a3239af0f208ca289c067f80c97d8e411e61da5dee0121021721e83315fb5282f1d9d2a11892322df589bccd9cef45517b5fb3cfd3055c8328ffffffff0f3a4b091e6c90cd3ebc664010001a1976a9149bb8229741305d8316ba3ca6a8d20740ce33c24188ac222374315934794c31344143486141626a656d6b647057376e594e48576e76317951624441"
testTxPacked2 = "0a20bb47a9dd926de63e9d4f8dac58c3f63f4a079569ed3b80e932274a80f60e58b512e20101000000019cafb5c287980e6e5afb47339f6c1c81136d8255f5bd5226b36b01288494c46f000000006b483045022100c92b2f3c54918fa26288530c63a58197ea4974e5b6d92db792dd9717e6d9183c02204e577254213675466a6adad3ae6e9384cf8269fb2dd9943b86fac0c0ad8e3f98012102c99dab469e63b232488b3e7acb9cfcab7e5755f61aad318d9e06b38e5ea22880feffffff0223a7a784010000001976a914826f87806ddd4643730be99b41c98acc379e83db88ac80969800000000001976a914e395634b7684289285926d4c64db395b783720ec88ac6e75040018e4b1c9d50520eeea1128f9ea113299010a0012206fc4948428016bb32652bdf555826d13811c6c9f3347fb5a6e0e9887c2b5af9c1800226b483045022100c92b2f3c54918fa26288530c63a58197ea4974e5b6d92db792dd9717e6d9183c02204e577254213675466a6adad3ae6e9384cf8269fb2dd9943b86fac0c0ad8e3f98012102c99dab469e63b232488b3e7acb9cfcab7e5755f61aad318d9e06b38e5ea2288028feffffff0f3a4b09257b2170264d504010001a1976a914826f87806ddd4643730be99b41c98acc379e83db88ac22237431566d4854547770457477766f6a786f644e32435351714c596931687a59336341713a4b099a9999999999b93f10011a1976a914e395634b7684289285926d4c64db395b783720ec88ac222374316563784d587070685554525158474c586e56684a367563714433445a6970646467"
testTxPacked1 = "0a20e64aac0c211ad210c90934f06b1cc932327329e41a9f70c6eb76f79ef798b7b812ab1002000000019c012650c99d0ef761e863dbb966babf2cb7a7a2b5d90b1461c09521c473d23d000000006b483045022100f220f48c5267ef92a1e7a4d3b44fe9d97cce76eeba2785d45a0e2620b70e8d7302205640bc39e197ce19d95a98a3239af0f208ca289c067f80c97d8e411e61da5dee0121021721e83315fb5282f1d9d2a11892322df589bccd9cef45517b5fb3cfd3055c83ffffffff018eec1a3c040000001976a9149bb8229741305d8316ba3ca6a8d20740ce33c24188ac000000000162b4fc6b0000000000000000000000006ffa88c89b74f0f82e24744296845a0d0113b132ff5dfc2af34e6418eb15206af53078c4dd475cf143cd9a427983f5993622464b53e3a37d2519a946492c3977e30f0866550b9097222993a439a39260ac5e7d36aef38c7fdd1df3035a2d5817a9c20526e38f52f822d4db9d2f0156c4119d786d6e3a060ca871df7fae9a5c3a9c921b38ddc6414b13d16aa807389c68016e54bd6a9eb3b23a6bc7bf152e6dba15e9ec36f95dab15ad8f4a92a9d0309bbd930ef24bb7247bf534065c1e2f5b42e2c80eb59f48b4da6ec522319e065f8c4e463f95cc7fcad8d7ee91608e3c0ffcaa44129ba2d2da45d9a413919eca41af29faaf806a3eeb823e5a6c51afb1ec709505d812c0306bd76061a0a62d207355ad44d1ffce2b9e1dfd0818f79bd0f8e4031116b71fee2488484f17818b80532865773166cd389929e8409bb94e3948bd2e0215ef96d4e29d094590fda0de50715c11ff47c03380bb1d31b14e5b4ad8a372ca0b03364ef85f086b8a8eb5c56c3b1aee33e2cfbf1b2be1a3fb41b14b2c432b5d04d54c058fa87a96ae1d65d61b79360d09acc1e25a883fd7ae9a2a734a03362903021401c243173e1050b5cdb459b9ffc07c95e920f026618952d3a800b2e47e03b902084aed7ee8466a65d34abdbbd292781564dcd9b7440029d48c2640ebc196d4b40217f2872c1d0c1c9c2abf1147d6a5a9501895bc92960bfa182ceeb76a658224f1022bc53c4c1cd6888d72a152dc1aec5ba8a1d750fb7e498bee844d3481e4b4cd210227f94f775744185c9f24571b7df0c1c694cb2d3e4e9b955ed0b1caad2b02b5702139c4fbba03f0e422b2f3e4fc822b4f58baf32e7cd217cdbdec8540cb13d6496f271959b72a05e130eeffbe5b9a7fcd2793347cd9c0ea695265669844c363190f690c52a600cf413c3f00bdc5e9d1539e0cc63f4ec2945e0d86e6304a6deb5651e73eac21add5a641dfc95ab56200ed40d81f76755aee4659334c17ed3841ca5a5ab22f923956be1d264be2b485a0de55404510ece5c73d6626798be688f9dc18b69846acfe897a357cc4afe31f57fea32896717f124290e68f36f849fa6ecf76e02087f8c19dbc566135d7fa2daca2d843b9cc5bc3897d35f1de7d174f6407658f4a3706c12cea53d880b4d8c4d45b3f0d210214f815be49a664021a4a44b4a63e06a41d76b46f9aa6bad248e8d1a974ae7bbae5ea8ac269447db91637a19346729083cad5aebd5ff43ea13d04783068e9136da321b1152c666d2995d0ca06b26541deac62f4ef91f0e4af445b18a5c2a17c96eada0b27f85bb26dfb8f16515114c6b9f88037e2b85b3b84b65822eb99c992d99d12dcf9c71e5b46a586016faf5758483a716566db95b42187c101df68ca0554824e1c23cf0302bea03ad0a146af57e91794a268b8c82d78211718c8b5fea286f5de72fc7dfffecddcc02413525c472cb26022641d4bec2b8b7e71a7beb9ee18b82632799498eeee9a351cb9431a8d1906d5164acdf351bd538c3e9d1da8a211fe1cd18c44e72d8cdf16ce3fc9551552c05d52846ea7ef619232102588395cc2bcce509a4e7f150262a76c15475496c923dfce6bfc05871467ee7c213b39ea365c010083e0b1ba8926d3a9e586d8b11c9bab2a47d888bc7cb1a226c0086a1530e295d0047547006f4c8f1c24cdd8e16bb3845749895dec95f03fcda97d3224f6875b1b7b1c819d2fd35dd30968a3c82bc480d10082caf9d9dda8f9ec649c136c7fa07978099d97eaf4abfdc9854c266979d3cfc868f60689b6e3098b6c52a21796fe7c259d9a0dadf1b6efa59297d4c8c902febe7acf826eed30d40d2ac5119be91b51f4839d94599872c9a93c3e2691294914034001d3a278cb4a84d4ae048c0201a97e4cf1341ee663a162f5b586355018b9e5e30624ccdbeacf7d0382afacaf45f08e84d30c50bcd4e55c3138377261deb4e8c2931cd3c51cee94a048ae4839517b6e6537a5c0148d3830a33fea719ef9b4fa437e4d5fecdb646397c19ee56a0973c362a81803895cdc67246352dc566689cb203f9ebda900a5537bbb75aa25ddf3d4ab87b88737a58d760e1d271f08265daae1fe056e71971a8b826e5b215a05b71f99315b167dd2ec78874189657acafac2b5eeb9a901913f55f7ab69e1f9b203504448d414e71098b932a2309db57257eb3fef9de2f2a5a69aa46747d7b827df838345d38b95772bdab8c178c45777b92e8773864964b8e12ae29dbc1b21bf6527589f6bec71ff1cbb9928477409811c2e8150c79c3f21027ee954863b716875d3e9adfc6fdb18cd57a49bb395ca5c42da56f3beb78aad3a7a487de34a870bca61f3cdec422061328c83c910ab32ea7403c354915b7ebee29e1fea5a75158197e4a68e103f017fd7de5a70148ee7ce59356b1a74f83492e14faaa6cd4870bcc004e6eb0114d3429b74ea98fe2851b4553467a7660074e69b040aa31220d0e405d9166dbaf15e3ae2d8ec3b049ed99d17e0743bb6a1a7c3890bbdb7117f7374ad7a59aa1ab47d10445b28f4bc033794a71f88a8bf024189e9d27f9dc5859a4296437585b215656f807aca9dad35747494a43b8a1cf38be2b18a13de32a262ab29f9ba271c4fbce1a470a8243ebf9e7fd37b09262314afbb9a7e180218a0f1c9d505200028b0eb113299010a0012203dd273c42195c061140bd9b5a2a7b72cbfba66b9db63e861f70e9dc95026019c1800226b483045022100f220f48c5267ef92a1e7a4d3b44fe9d97cce76eeba2785d45a0e2620b70e8d7302205640bc39e197ce19d95a98a3239af0f208ca289c067f80c97d8e411e61da5dee0121021721e83315fb5282f1d9d2a11892322df589bccd9cef45517b5fb3cfd3055c8328ffffffff0f3a490a05043c1aec8e10001a1976a9149bb8229741305d8316ba3ca6a8d20740ce33c24188ac222374315934794c31344143486141626a656d6b647057376e594e48576e76317951624441"
testTxPacked2 = "0a20bb47a9dd926de63e9d4f8dac58c3f63f4a079569ed3b80e932274a80f60e58b512e20101000000019cafb5c287980e6e5afb47339f6c1c81136d8255f5bd5226b36b01288494c46f000000006b483045022100c92b2f3c54918fa26288530c63a58197ea4974e5b6d92db792dd9717e6d9183c02204e577254213675466a6adad3ae6e9384cf8269fb2dd9943b86fac0c0ad8e3f98012102c99dab469e63b232488b3e7acb9cfcab7e5755f61aad318d9e06b38e5ea22880feffffff0223a7a784010000001976a914826f87806ddd4643730be99b41c98acc379e83db88ac80969800000000001976a914e395634b7684289285926d4c64db395b783720ec88ac6e75040018e4b1c9d50520eeea1128f9ea113299010a0012206fc4948428016bb32652bdf555826d13811c6c9f3347fb5a6e0e9887c2b5af9c1800226b483045022100c92b2f3c54918fa26288530c63a58197ea4974e5b6d92db792dd9717e6d9183c02204e577254213675466a6adad3ae6e9384cf8269fb2dd9943b86fac0c0ad8e3f98012102c99dab469e63b232488b3e7acb9cfcab7e5755f61aad318d9e06b38e5ea2288028feffffff0f3a490a050184a7a72310001a1976a914826f87806ddd4643730be99b41c98acc379e83db88ac22237431566d4854547770457477766f6a786f644e32435351714c596931687a59336341713a470a0398968010011a1976a914e395634b7684289285926d4c64db395b783720ec88ac222374316563784d587070685554525158474c586e56684a367563714433445a6970646467"
)
func init() {
var (
addr1, addr2, addr3 bchain.Address
err error
)
addr1, err = bchain.NewBaseAddress("t1Y4yL14ACHaAbjemkdpW7nYNHWnv1yQbDA")
if err == nil {
addr2, err = bchain.NewBaseAddress("t1VmHTTwpEtwvojxodN2CSQqLYi1hzY3cAq")
}
if err == nil {
addr3, err = bchain.NewBaseAddress("t1ecxMXpphUTRQXGLXnVhJ6ucqD3DZipddg")
}
if err != nil {
panic(err)
}
testTx1 = bchain.Tx{
Hex: "02000000019c012650c99d0ef761e863dbb966babf2cb7a7a2b5d90b1461c09521c473d23d000000006b483045022100f220f48c5267ef92a1e7a4d3b44fe9d97cce76eeba2785d45a0e2620b70e8d7302205640bc39e197ce19d95a98a3239af0f208ca289c067f80c97d8e411e61da5dee0121021721e83315fb5282f1d9d2a11892322df589bccd9cef45517b5fb3cfd3055c83ffffffff018eec1a3c040000001976a9149bb8229741305d8316ba3ca6a8d20740ce33c24188ac000000000162b4fc6b0000000000000000000000006ffa88c89b74f0f82e24744296845a0d0113b132ff5dfc2af34e6418eb15206af53078c4dd475cf143cd9a427983f5993622464b53e3a37d2519a946492c3977e30f0866550b9097222993a439a39260ac5e7d36aef38c7fdd1df3035a2d5817a9c20526e38f52f822d4db9d2f0156c4119d786d6e3a060ca871df7fae9a5c3a9c921b38ddc6414b13d16aa807389c68016e54bd6a9eb3b23a6bc7bf152e6dba15e9ec36f95dab15ad8f4a92a9d0309bbd930ef24bb7247bf534065c1e2f5b42e2c80eb59f48b4da6ec522319e065f8c4e463f95cc7fcad8d7ee91608e3c0ffcaa44129ba2d2da45d9a413919eca41af29faaf806a3eeb823e5a6c51afb1ec709505d812c0306bd76061a0a62d207355ad44d1ffce2b9e1dfd0818f79bd0f8e4031116b71fee2488484f17818b80532865773166cd389929e8409bb94e3948bd2e0215ef96d4e29d094590fda0de50715c11ff47c03380bb1d31b14e5b4ad8a372ca0b03364ef85f086b8a8eb5c56c3b1aee33e2cfbf1b2be1a3fb41b14b2c432b5d04d54c058fa87a96ae1d65d61b79360d09acc1e25a883fd7ae9a2a734a03362903021401c243173e1050b5cdb459b9ffc07c95e920f026618952d3a800b2e47e03b902084aed7ee8466a65d34abdbbd292781564dcd9b7440029d48c2640ebc196d4b40217f2872c1d0c1c9c2abf1147d6a5a9501895bc92960bfa182ceeb76a658224f1022bc53c4c1cd6888d72a152dc1aec5ba8a1d750fb7e498bee844d3481e4b4cd210227f94f775744185c9f24571b7df0c1c694cb2d3e4e9b955ed0b1caad2b02b5702139c4fbba03f0e422b2f3e4fc822b4f58baf32e7cd217cdbdec8540cb13d6496f271959b72a05e130eeffbe5b9a7fcd2793347cd9c0ea695265669844c363190f690c52a600cf413c3f00bdc5e9d1539e0cc63f4ec2945e0d86e6304a6deb5651e73eac21add5a641dfc95ab56200ed40d81f76755aee4659334c17ed3841ca5a5ab22f923956be1d264be2b485a0de55404510ece5c73d6626798be688f9dc18b69846acfe897a357cc4afe31f57fea32896717f124290e68f36f849fa6ecf76e02087f8c19dbc566135d7fa2daca2d843b9cc5bc3897d35f1de7d174f6407658f4a3706c12cea53d880b4d8c4d45b3f0d210214f815be49a664021a4a44b4a63e06a41d76b46f9aa6bad248e8d1a974ae7bbae5ea8ac269447db91637a19346729083cad5aebd5ff43ea13d04783068e9136da321b1152c666d2995d0ca06b26541deac62f4ef91f0e4af445b18a5c2a17c96eada0b27f85bb26dfb8f16515114c6b9f88037e2b85b3b84b65822eb99c992d99d12dcf9c71e5b46a586016faf5758483a716566db95b42187c101df68ca0554824e1c23cf0302bea03ad0a146af57e91794a268b8c82d78211718c8b5fea286f5de72fc7dfffecddcc02413525c472cb26022641d4bec2b8b7e71a7beb9ee18b82632799498eeee9a351cb9431a8d1906d5164acdf351bd538c3e9d1da8a211fe1cd18c44e72d8cdf16ce3fc9551552c05d52846ea7ef619232102588395cc2bcce509a4e7f150262a76c15475496c923dfce6bfc05871467ee7c213b39ea365c010083e0b1ba8926d3a9e586d8b11c9bab2a47d888bc7cb1a226c0086a1530e295d0047547006f4c8f1c24cdd8e16bb3845749895dec95f03fcda97d3224f6875b1b7b1c819d2fd35dd30968a3c82bc480d10082caf9d9dda8f9ec649c136c7fa07978099d97eaf4abfdc9854c266979d3cfc868f60689b6e3098b6c52a21796fe7c259d9a0dadf1b6efa59297d4c8c902febe7acf826eed30d40d2ac5119be91b51f4839d94599872c9a93c3e2691294914034001d3a278cb4a84d4ae048c0201a97e4cf1341ee663a162f5b586355018b9e5e30624ccdbeacf7d0382afacaf45f08e84d30c50bcd4e55c3138377261deb4e8c2931cd3c51cee94a048ae4839517b6e6537a5c0148d3830a33fea719ef9b4fa437e4d5fecdb646397c19ee56a0973c362a81803895cdc67246352dc566689cb203f9ebda900a5537bbb75aa25ddf3d4ab87b88737a58d760e1d271f08265daae1fe056e71971a8b826e5b215a05b71f99315b167dd2ec78874189657acafac2b5eeb9a901913f55f7ab69e1f9b203504448d414e71098b932a2309db57257eb3fef9de2f2a5a69aa46747d7b827df838345d38b95772bdab8c178c45777b92e8773864964b8e12ae29dbc1b21bf6527589f6bec71ff1cbb9928477409811c2e8150c79c3f21027ee954863b716875d3e9adfc6fdb18cd57a49bb395ca5c42da56f3beb78aad3a7a487de34a870bca61f3cdec422061328c83c910ab32ea7403c354915b7ebee29e1fea5a75158197e4a68e103f017fd7de5a70148ee7ce59356b1a74f83492e14faaa6cd4870bcc004e6eb0114d3429b74ea98fe2851b4553467a7660074e69b040aa31220d0e405d9166dbaf15e3ae2d8ec3b049ed99d17e0743bb6a1a7c3890bbdb7117f7374ad7a59aa1ab47d10445b28f4bc033794a71f88a8bf024189e9d27f9dc5859a4296437585b215656f807aca9dad35747494a43b8a1cf38be2b18a13de32a262ab29f9ba271c4fbce1a470a8243ebf9e7fd37b09262314afbb9a7e1802",
Blocktime: 1521645728,
@ -51,15 +38,14 @@ func init() {
},
Vout: []bchain.Vout{
{
Value: 181.88266638,
N: 0,
ValueSat: *big.NewInt(18188266638),
N: 0,
ScriptPubKey: bchain.ScriptPubKey{
Hex: "76a9149bb8229741305d8316ba3ca6a8d20740ce33c24188ac",
Addresses: []string{
"t1Y4yL14ACHaAbjemkdpW7nYNHWnv1yQbDA",
},
},
Address: addr1,
},
},
}
@ -82,31 +68,74 @@ func init() {
},
Vout: []bchain.Vout{
{
Value: 65.20547107,
N: 0,
ValueSat: *big.NewInt(6520547107),
N: 0,
ScriptPubKey: bchain.ScriptPubKey{
Hex: "76a914826f87806ddd4643730be99b41c98acc379e83db88ac",
Addresses: []string{
"t1VmHTTwpEtwvojxodN2CSQqLYi1hzY3cAq",
},
},
Address: addr2,
},
{
Value: .1,
N: 1,
ValueSat: *big.NewInt(10000000),
N: 1,
ScriptPubKey: bchain.ScriptPubKey{
Hex: "76a914e395634b7684289285926d4c64db395b783720ec88ac",
Addresses: []string{
"t1ecxMXpphUTRQXGLXnVhJ6ucqD3DZipddg",
},
},
Address: addr3,
},
},
}
}
func TestGetAddrDesc(t *testing.T) {
type args struct {
tx bchain.Tx
parser *ZCashParser
}
tests := []struct {
name string
args args
}{
{
name: "zec-1",
args: args{
tx: testTx1,
parser: NewZCashParser(GetChainParams("main"), &btc.Configuration{}),
},
},
{
name: "zec-2",
args: args{
tx: testTx2,
parser: NewZCashParser(GetChainParams("main"), &btc.Configuration{}),
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
for n, vout := range tt.args.tx.Vout {
got1, err := tt.args.parser.GetAddrDescFromVout(&vout)
if err != nil {
t.Errorf("getAddrDescFromVout() error = %v, vout = %d", err, n)
return
}
got2, err := tt.args.parser.GetAddrDescFromAddress(vout.ScriptPubKey.Addresses[0])
if err != nil {
t.Errorf("getAddrDescFromAddress() error = %v, vout = %d", err, n)
return
}
if !bytes.Equal(got1, got2) {
t.Errorf("Address descriptors mismatch: got1 = %v, got2 = %v", got1, got2)
}
}
})
}
}
func TestPackTx(t *testing.T) {
type args struct {
tx bchain.Tx
@ -126,7 +155,7 @@ func TestPackTx(t *testing.T) {
tx: testTx1,
height: 292272,
blockTime: 1521645728,
parser: NewZCashParser(&btc.Configuration{}),
parser: NewZCashParser(GetChainParams("main"), &btc.Configuration{}),
},
want: testTxPacked1,
wantErr: false,
@ -137,7 +166,7 @@ func TestPackTx(t *testing.T) {
tx: testTx2,
height: 292217,
blockTime: 1521637604,
parser: NewZCashParser(&btc.Configuration{}),
parser: NewZCashParser(GetChainParams("main"), &btc.Configuration{}),
},
want: testTxPacked2,
wantErr: false,
@ -174,7 +203,7 @@ func TestUnpackTx(t *testing.T) {
name: "zec-1",
args: args{
packedTx: testTxPacked1,
parser: NewZCashParser(&btc.Configuration{}),
parser: NewZCashParser(GetChainParams("main"), &btc.Configuration{}),
},
want: &testTx1,
want1: 292272,
@ -184,7 +213,7 @@ func TestUnpackTx(t *testing.T) {
name: "zec-2",
args: args{
packedTx: testTxPacked2,
parser: NewZCashParser(&btc.Configuration{}),
parser: NewZCashParser(GetChainParams("main"), &btc.Configuration{}),
},
want: &testTx2,
want1: 292217,

View File

@ -22,6 +22,7 @@ func NewZCashRPC(config json.RawMessage, pushHandler func(bchain.NotificationTyp
BitcoinRPC: b.(*btc.BitcoinRPC),
}
z.RPCMarshaler = btc.JSONMarshalerV1{}
z.ChainConfig.SupportsEstimateSmartFee = false
return z, nil
}
@ -34,7 +35,7 @@ func (z *ZCashRPC) Initialize() error {
params := GetChainParams(chainName)
z.Parser = NewZCashParser(z.ChainConfig)
z.Parser = NewZCashParser(params, z.ChainConfig)
// parameters for getInfo request
if params.Net == MainnetMagic {
@ -114,14 +115,6 @@ func (z *ZCashRPC) GetTransactionForMempool(txid string) (*bchain.Tx, error) {
return z.GetTransaction(txid)
}
// EstimateSmartFee returns fee estimation.
func (z *ZCashRPC) EstimateSmartFee(blocks int, conservative bool) (float64, error) {
glog.V(1).Info("rpc: estimatesmartfee")
// return z.estimateFee(blocks)
return z.EstimateFee(blocks)
}
// GetMempoolEntry returns mempool data for given transaction
func (z *ZCashRPC) GetMempoolEntry(txid string) (*bchain.MempoolEntry, error) {
return nil, errors.New("GetMempoolEntry: not implemented")

View File

@ -1,128 +0,0 @@
// +build integration
package zec
import (
"blockbook/bchain"
"blockbook/bchain/tests/rpc"
"encoding/json"
"flag"
"os"
"testing"
)
func getRPCClient(cfg json.RawMessage) (bchain.BlockChain, error) {
c, err := NewZCashRPC(cfg, nil)
if err != nil {
return nil, err
}
cli := c.(*ZCashRPC)
cli.Parser = NewZCashParser(cli.ChainConfig)
cli.Mempool = bchain.NewUTXOMempool(cli, cli.ChainConfig.MempoolWorkers, cli.ChainConfig.MempoolSubWorkers)
return cli, nil
}
var tests struct {
mainnet *rpc.Test
testnet *rpc.Test
}
func TestMain(m *testing.M) {
flag.Parse()
t, err := rpc.NewTest("Zcash", getRPCClient)
if err != nil {
panic(err)
}
tests.mainnet = t
t, err = rpc.NewTest("Zcash Testnet", getRPCClient)
if err != nil {
panic(err)
}
tests.testnet = t
os.Exit(m.Run())
}
func TestZCashRPC_GetBlockHash(t *testing.T) {
tests.mainnet.TestGetBlockHash(t)
}
func TestZCashRPC_GetBlock(t *testing.T) {
tests.mainnet.TestGetBlock(t)
}
func TestZCashRPC_GetTransaction(t *testing.T) {
tests.mainnet.TestGetTransaction(t)
}
func TestZCashRPC_GetTransactionForMempool(t *testing.T) {
tests.mainnet.TestGetTransactionForMempool(t)
}
func TestZCashRPC_MempoolSync(t *testing.T) {
tests.mainnet.TestMempoolSync(t)
}
func TestZCashRPC_EstimateSmartFee(t *testing.T) {
tests.mainnet.TestEstimateSmartFee(t)
}
func TestZCashRPC_EstimateFee(t *testing.T) {
tests.mainnet.TestEstimateFee(t)
}
func TestZCashRPC_GetBestBlockHash(t *testing.T) {
tests.mainnet.TestGetBestBlockHash(t)
}
func TestZCashRPC_GetBestBlockHeight(t *testing.T) {
tests.mainnet.TestGetBestBlockHeight(t)
}
func TestZCashRPC_GetBlockHeader(t *testing.T) {
tests.mainnet.TestGetBlockHeader(t)
}
func TestZCashTestnetRPC_GetBlockHash(t *testing.T) {
tests.testnet.TestGetBlockHash(t)
}
func TestZCashTestnetRPC_GetBlock(t *testing.T) {
tests.testnet.TestGetBlock(t)
}
func TestZCashTestnetRPC_GetTransaction(t *testing.T) {
tests.testnet.TestGetTransaction(t)
}
func TestZCashTestnetRPC_GetTransactionForMempool(t *testing.T) {
tests.testnet.TestGetTransactionForMempool(t)
}
func TestZCashTestnetRPC_MempoolSync(t *testing.T) {
tests.testnet.TestMempoolSync(t)
}
func TestZCashTestnetRPC_EstimateSmartFee(t *testing.T) {
tests.testnet.TestEstimateSmartFee(t)
}
func TestZCashTestnetRPC_EstimateFee(t *testing.T) {
tests.testnet.TestEstimateFee(t)
}
func TestZCashTestnetRPC_GetBestBlockHash(t *testing.T) {
tests.mainnet.TestGetBestBlockHash(t)
}
func TestZCashTestnetRPC_GetBestBlockHeight(t *testing.T) {
tests.mainnet.TestGetBestBlockHeight(t)
}
func TestZCashTestnetRPC_GetBlockHeader(t *testing.T) {
tests.mainnet.TestGetBlockHeader(t)
}

View File

@ -12,7 +12,7 @@ type NonUTXOMempool struct {
chain BlockChain
mux sync.Mutex
txToInputOutput map[string][]addrIndex
addrIDToTx map[string][]outpoint
addrDescToTx map[string][]outpoint
}
// NewNonUTXOMempool creates new mempool handler.
@ -23,13 +23,18 @@ func NewNonUTXOMempool(chain BlockChain) *NonUTXOMempool {
// GetTransactions returns slice of mempool transactions for given address
func (m *NonUTXOMempool) GetTransactions(address string) ([]string, error) {
parser := m.chain.GetChainParser()
addrID, err := parser.GetAddrIDFromAddress(address)
addrDesc, err := parser.GetAddrDescFromAddress(address)
if err != nil {
return nil, err
}
return m.GetAddrDescTransactions(addrDesc)
}
// GetAddrDescTransactions returns slice of mempool transactions for given address descriptor
func (m *NonUTXOMempool) GetAddrDescTransactions(addrDesc AddressDescriptor) ([]string, error) {
m.mux.Lock()
defer m.mux.Unlock()
outpoints := m.addrIDToTx[string(addrID)]
outpoints := m.addrDescToTx[string(addrDesc)]
txs := make([]string, 0, len(outpoints))
for _, o := range outpoints {
txs = append(txs, o.txid)
@ -37,11 +42,11 @@ func (m *NonUTXOMempool) GetTransactions(address string) ([]string, error) {
return txs, nil
}
func (m *NonUTXOMempool) updateMappings(newTxToInputOutput map[string][]addrIndex, newAddrIDToTx map[string][]outpoint) {
func (m *NonUTXOMempool) updateMappings(newTxToInputOutput map[string][]addrIndex, newAddrDescToTx map[string][]outpoint) {
m.mux.Lock()
defer m.mux.Unlock()
m.txToInputOutput = newTxToInputOutput
m.addrIDToTx = newAddrIDToTx
m.addrDescToTx = newAddrDescToTx
}
// Resync gets mempool transactions and maps outputs to transactions.
@ -57,7 +62,7 @@ func (m *NonUTXOMempool) Resync(onNewTxAddr OnNewTxAddrFunc) (int, error) {
parser := m.chain.GetChainParser()
// allocate slightly larger capacity of the maps
newTxToInputOutput := make(map[string][]addrIndex, len(m.txToInputOutput)+5)
newAddrIDToTx := make(map[string][]outpoint, len(m.addrIDToTx)+5)
newAddrDescToTx := make(map[string][]outpoint, len(m.addrDescToTx)+5)
for _, txid := range txs {
io, exists := m.txToInputOutput[txid]
if !exists {
@ -68,15 +73,15 @@ func (m *NonUTXOMempool) Resync(onNewTxAddr OnNewTxAddrFunc) (int, error) {
}
io = make([]addrIndex, 0, len(tx.Vout)+len(tx.Vin))
for _, output := range tx.Vout {
addrID, err := parser.GetAddrIDFromVout(&output)
addrDesc, err := parser.GetAddrDescFromVout(&output)
if err != nil {
if err != ErrAddressMissing {
glog.Error("error in output addrID in ", txid, " ", output.N, ": ", err)
glog.Error("error in output addrDesc in ", txid, " ", output.N, ": ", err)
}
continue
}
if len(addrID) > 0 {
io = append(io, addrIndex{string(addrID), int32(output.N)})
if len(addrDesc) > 0 {
io = append(io, addrIndex{string(addrDesc), int32(output.N)})
}
if onNewTxAddr != nil && len(output.ScriptPubKey.Addresses) == 1 {
onNewTxAddr(tx.Txid, output.ScriptPubKey.Addresses[0], true)
@ -85,12 +90,12 @@ func (m *NonUTXOMempool) Resync(onNewTxAddr OnNewTxAddrFunc) (int, error) {
for _, input := range tx.Vin {
for i, a := range input.Addresses {
if len(a) > 0 {
addrID, err := parser.GetAddrIDFromAddress(a)
addrDesc, err := parser.GetAddrDescFromAddress(a)
if err != nil {
glog.Error("error in input addrID in ", txid, " ", a, ": ", err)
glog.Error("error in input addrDesc in ", txid, " ", a, ": ", err)
continue
}
io = append(io, addrIndex{string(addrID), int32(^i)})
io = append(io, addrIndex{string(addrDesc), int32(^i)})
if onNewTxAddr != nil {
onNewTxAddr(tx.Txid, a, false)
}
@ -100,10 +105,10 @@ func (m *NonUTXOMempool) Resync(onNewTxAddr OnNewTxAddrFunc) (int, error) {
}
newTxToInputOutput[txid] = io
for _, si := range io {
newAddrIDToTx[si.addrID] = append(newAddrIDToTx[si.addrID], outpoint{txid, si.n})
newAddrDescToTx[si.addrDesc] = append(newAddrDescToTx[si.addrDesc], outpoint{txid, si.n})
}
}
m.updateMappings(newTxToInputOutput, newAddrIDToTx)
m.updateMappings(newTxToInputOutput, newAddrDescToTx)
glog.Info("Mempool: resync finished in ", time.Since(start), ", ", len(m.txToInputOutput), " transactions in mempool")
return len(m.txToInputOutput), nil
}

View File

@ -9,8 +9,8 @@ import (
// addrIndex and outpoint are used also in non utxo mempool
type addrIndex struct {
addrID string
n int32
addrDesc string
n int32
}
type outpoint struct {
@ -28,7 +28,7 @@ type UTXOMempool struct {
chain BlockChain
mux sync.Mutex
txToInputOutput map[string][]addrIndex
addrIDToTx map[string][]outpoint
addrDescToTx map[string][]outpoint
chanTxid chan string
chanAddrIndex chan txidio
onNewTxAddr OnNewTxAddrFunc
@ -70,13 +70,18 @@ func NewUTXOMempool(chain BlockChain, workers int, subworkers int) *UTXOMempool
// GetTransactions returns slice of mempool transactions for given address
func (m *UTXOMempool) GetTransactions(address string) ([]string, error) {
parser := m.chain.GetChainParser()
addrID, err := parser.GetAddrIDFromAddress(address)
addrDesc, err := parser.GetAddrDescFromAddress(address)
if err != nil {
return nil, err
}
return m.GetAddrDescTransactions(addrDesc)
}
// GetAddrDescTransactions returns slice of mempool transactions for given address descriptor
func (m *UTXOMempool) GetAddrDescTransactions(addrDesc AddressDescriptor) ([]string, error) {
m.mux.Lock()
defer m.mux.Unlock()
outpoints := m.addrIDToTx[string(addrID)]
outpoints := m.addrDescToTx[string(addrDesc)]
txs := make([]string, 0, len(outpoints))
for _, o := range outpoints {
txs = append(txs, o.txid)
@ -84,15 +89,14 @@ func (m *UTXOMempool) GetTransactions(address string) ([]string, error) {
return txs, nil
}
func (m *UTXOMempool) updateMappings(newTxToInputOutput map[string][]addrIndex, newAddrIDToTx map[string][]outpoint) {
func (m *UTXOMempool) updateMappings(newTxToInputOutput map[string][]addrIndex, newAddrDescToTx map[string][]outpoint) {
m.mux.Lock()
defer m.mux.Unlock()
m.txToInputOutput = newTxToInputOutput
m.addrIDToTx = newAddrIDToTx
m.addrDescToTx = newAddrDescToTx
}
func (m *UTXOMempool) getInputAddress(input outpoint) *addrIndex {
// TODO - possibly get from DB unspenttxs - however some output txs can be also in mempool
itx, err := m.chain.GetTransactionForMempool(input.txid)
if err != nil {
glog.Error("cannot get transaction ", input.txid, ": ", err)
@ -102,12 +106,12 @@ func (m *UTXOMempool) getInputAddress(input outpoint) *addrIndex {
glog.Error("Vout len in transaction ", input.txid, " ", len(itx.Vout), " input.Vout=", input.vout)
return nil
}
addrID, err := m.chain.GetChainParser().GetAddrIDFromVout(&itx.Vout[input.vout])
addrDesc, err := m.chain.GetChainParser().GetAddrDescFromVout(&itx.Vout[input.vout])
if err != nil {
glog.Error("error in addrID in ", input.txid, " ", input.vout, ": ", err)
glog.Error("error in addrDesc in ", input.txid, " ", input.vout, ": ", err)
return nil
}
return &addrIndex{string(addrID), ^input.vout}
return &addrIndex{string(addrDesc), ^input.vout}
}
@ -120,13 +124,13 @@ func (m *UTXOMempool) getTxAddrs(txid string, chanInput chan outpoint, chanResul
glog.V(2).Info("mempool: gettxaddrs ", txid, ", ", len(tx.Vin), " inputs")
io := make([]addrIndex, 0, len(tx.Vout)+len(tx.Vin))
for _, output := range tx.Vout {
addrID, err := m.chain.GetChainParser().GetAddrIDFromVout(&output)
addrDesc, err := m.chain.GetChainParser().GetAddrDescFromVout(&output)
if err != nil {
glog.Error("error in addrID in ", txid, " ", output.N, ": ", err)
glog.Error("error in addrDesc in ", txid, " ", output.N, ": ", err)
continue
}
if len(addrID) > 0 {
io = append(io, addrIndex{string(addrID), int32(output.N)})
if len(addrDesc) > 0 {
io = append(io, addrIndex{string(addrDesc), int32(output.N)})
}
if m.onNewTxAddr != nil && len(output.ScriptPubKey.Addresses) == 1 {
m.onNewTxAddr(tx.Txid, output.ScriptPubKey.Addresses[0], true)
@ -177,13 +181,13 @@ func (m *UTXOMempool) Resync(onNewTxAddr OnNewTxAddrFunc) (int, error) {
glog.V(2).Info("mempool: resync ", len(txs), " txs")
// allocate slightly larger capacity of the maps
newTxToInputOutput := make(map[string][]addrIndex, len(m.txToInputOutput)+5)
newAddrIDToTx := make(map[string][]outpoint, len(m.addrIDToTx)+5)
newAddrDescToTx := make(map[string][]outpoint, len(m.addrDescToTx)+5)
dispatched := 0
onNewData := func(txid string, io []addrIndex) {
if len(io) > 0 {
newTxToInputOutput[txid] = io
for _, si := range io {
newAddrIDToTx[si.addrID] = append(newAddrIDToTx[si.addrID], outpoint{txid, si.n})
newAddrDescToTx[si.addrDesc] = append(newAddrDescToTx[si.addrDesc], outpoint{txid, si.n})
}
}
}
@ -212,7 +216,7 @@ func (m *UTXOMempool) Resync(onNewTxAddr OnNewTxAddrFunc) (int, error) {
tio := <-m.chanAddrIndex
onNewData(tio.txid, tio.io)
}
m.updateMappings(newTxToInputOutput, newAddrIDToTx)
m.updateMappings(newTxToInputOutput, newAddrDescToTx)
m.onNewTxAddr = nil
glog.Info("mempool: resync finished in ", time.Since(start), ", ", len(m.txToInputOutput), " transactions in mempool")
return len(m.txToInputOutput), nil

View File

@ -1,62 +0,0 @@
{
"Bcash": {
"url": "http://localhost:8031",
"user": "rpc",
"pass": "rpc"
},
"Bitcoin": {
"url": "http://localhost:8030",
"user": "rpc",
"pass": "rpc"
},
"Dash": {
"url": "http://localhost:8033",
"user": "rpc",
"pass": "rpc"
},
"Ethereum": {
"url": "ws://localhost:8036",
"user": null,
"pass": null
},
"Zcash": {
"url": "http://localhost:8032",
"user": "rpc",
"pass": "rpc"
},
"Vertcoin": {
"url": "http://localhost:8040",
"user": "rpc",
"pass": "rpc"
},
"Namecoin": {
"url": "http://localhost:8039",
"user": "rpc",
"pass": "rpc"
},
"Bcash Testnet": {
"url": "http://localhost:18031",
"user": "rpc",
"pass": "rpc"
},
"Bitcoin Testnet": {
"url": "http://localhost:18030",
"user": "rpc",
"pass": "rpc"
},
"Dash Testnet": {
"url": "http://localhost:18033",
"user": "rpc",
"pass": "rpc"
},
"Ethereum Testnet": {
"url": "ws://localhost:18036",
"user": null,
"pass": null
},
"Zcash Testnet": {
"url": "http://localhost:18032",
"user": "rpc",
"pass": "rpc"
}
}

View File

@ -1,16 +1,22 @@
// +build integration
package rpc
import (
"blockbook/bchain"
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"path/filepath"
"strings"
)
type TestData struct {
BlockHeight uint32 `json:"blockHeight"`
BlockHash string `json:"blockHash"`
BlockTime int64 `json:"blockTime"`
BlockTxs []string `json:"blockTxs"`
TxDetails map[string]*bchain.Tx `json:"txDetails"`
}
func joinPathsWithCommonElement(p1, p2 string) (string, bool) {
idx := strings.IndexRune(p2, filepath.Separator)
if idx <= 0 {
@ -43,47 +49,7 @@ func readDataFile(dir, relDir, filename string) ([]byte, error) {
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) {
func LoadTestData(coin string, parser bchain.BlockChainParser) (*TestData, error) {
b, err := readDataFile(".", "bchain/tests/rpc/testdata", coin+".json")
if err != nil {
return nil, err
@ -93,5 +59,16 @@ func LoadTestData(coin string) (*TestData, error) {
if err != nil {
return nil, err
}
// convert amounts in test json to bit.Int and clear the temporary JsonValue
for _, tx := range v.TxDetails {
for i := range tx.Vout {
vout := &tx.Vout[i]
vout.ValueSat, err = parser.AmountToBigInt(vout.JsonValue)
if err != nil {
return nil, err
}
vout.JsonValue = ""
}
}
return &v, nil
}

View File

@ -1,448 +0,0 @@
// +build integration
package rpc
import (
"blockbook/bchain"
"encoding/json"
"math/rand"
"net"
"reflect"
"testing"
"github.com/deckarep/golang-set"
)
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"`
BlockTxs []string `json:"blockTxs"`
TxDetails map[string]*bchain.Tx `json:"txDetails"`
}
type Test struct {
Client bchain.BlockChain
TestData *TestData
connected bool
}
type TestChainFactoryFunc func(json.RawMessage) (bchain.BlockChain, error)
func NewTest(coin string, factory TestChainFactoryFunc) (*Test, error) {
var (
connected = true
cli bchain.BlockChain
cfg json.RawMessage
td *TestData
err error
)
cfg, err = LoadRPCConfig(coin)
if err != nil {
return nil, err
}
cli, err = factory(cfg)
if err != nil {
if isNetError(err) {
connected = false
} else {
return nil, err
}
} else {
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
}
}
}
_, err = cli.GetBlockChainInfo()
if err != nil && isNetError(err) {
connected = false
}
}
return &Test{Client: cli, TestData: td, connected: connected}, nil
}
func isNetError(err error) bool {
if _, ok := err.(net.Error); ok {
return true
}
return false
}
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) skipUnconnected(t *testing.T) {
if !rt.connected {
t.Skip("Skipping test, not connected to backend service")
}
}
func (rt *Test) TestGetBlockHash(t *testing.T) {
rt.skipUnconnected(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) {
rt.skipUnconnected(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) {
rt.skipUnconnected(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 {
t.Errorf("GetTransaction() got struct with invalid Confirmations field")
continue
}
got.Confirmations = 0
if !reflect.DeepEqual(got, want) {
t.Errorf("GetTransaction() got %v, want %v", got, want)
}
}
}
func (rt *Test) TestGetTransactionForMempool(t *testing.T) {
rt.skipUnconnected(t)
for txid, want := range rt.TestData.TxDetails {
// reset fields that are not parsed by BlockChainParser
want.Confirmations, want.Blocktime, want.Time = 0, 0, 0
got, err := rt.Client.GetTransactionForMempool(txid)
if err != nil {
t.Fatal(err)
}
// transactions parsed from JSON may contain additional data
got.Confirmations, got.Blocktime, got.Time = 0, 0, 0
if !reflect.DeepEqual(got, want) {
t.Errorf("GetTransactionForMempool() got %v, want %v", got, want)
}
}
}
func (rt *Test) getMempool(t *testing.T) []string {
txs, err := rt.Client.GetMempool()
if err != nil {
t.Fatal(err)
}
if len(txs) == 0 {
t.Skip("Skipping test, mempool is empty")
}
return txs
}
func (rt *Test) getMempoolAddresses(t *testing.T, txs []string) map[string][]string {
txid2addrs := map[string][]string{}
for i := 0; i < len(txs); i++ {
tx, err := rt.Client.GetTransactionForMempool(txs[i])
if err != nil {
t.Fatal(err)
}
addrs := []string{}
for _, vin := range tx.Vin {
for _, a := range vin.Addresses {
addrs = append(addrs, a)
}
}
for _, vout := range tx.Vout {
for _, a := range vout.ScriptPubKey.Addresses {
addrs = append(addrs, a)
}
}
if len(addrs) > 0 {
txid2addrs[tx.Txid] = addrs
}
}
return txid2addrs
}
func (rt *Test) TestMempoolSync(t *testing.T) {
rt.skipUnconnected(t)
for i := 0; i < 3; i++ {
txs := rt.getMempool(t)
n, err := rt.Client.ResyncMempool(nil)
if err != nil {
t.Fatal(err)
}
if n == 0 {
// no transactions to test
continue
}
txs = intersect(txs, rt.getMempool(t))
if len(txs) == 0 {
// no transactions to test
continue
}
txid2addrs := rt.getMempoolAddresses(t, txs)
if len(txid2addrs) == 0 {
t.Skip("Skipping test, no addresses in mempool")
}
for txid, addrs := range txid2addrs {
for _, a := range addrs {
got, err := rt.Client.GetMempoolTransactions(a)
if err != nil {
t.Fatal(err)
}
if !containsString(got, txid) {
t.Errorf("ResyncMempool() - for address %s, transaction %s wasn't found in mempool", a, txid)
return
}
}
}
// done
return
}
t.Skip("Skipping test, all attempts to sync mempool failed due to network state changes")
}
func intersect(a, b []string) []string {
setA := mapset.NewSet()
for _, v := range a {
setA.Add(v)
}
setB := mapset.NewSet()
for _, v := range b {
setB.Add(v)
}
inter := setA.Intersect(setB)
res := make([]string, 0, inter.Cardinality())
for v := range inter.Iter() {
res = append(res, v.(string))
}
return res
}
func containsString(slice []string, s string) bool {
for i := 0; i < len(slice); i++ {
if slice[i] == s {
return true
}
}
return false
}
func (rt *Test) TestGetMempoolEntry(t *testing.T) {
rt.skipUnconnected(t)
for i := 0; i < 3; i++ {
txs := rt.getMempool(t)
h, err := rt.Client.GetBestBlockHeight()
if err != nil {
t.Fatal(err)
}
txid := txs[rand.Intn(len(txs))]
tx, err := rt.Client.GetTransactionForMempool(txid)
if err != nil {
t.Fatal(err)
}
if tx.Confirmations > 0 {
// tx confirmed
continue
}
e, err := rt.Client.GetMempoolEntry(txid)
if err != nil {
if err, ok := err.(*bchain.RPCError); ok && err.Code == -5 {
// tx confirmed
continue
}
t.Fatal(err)
}
if d := int(e.Height) - int(h); d < -1 || d > 1 {
t.Errorf("GetMempoolEntry() got height %d, want %d", e.Height, h)
}
if e.Size <= 0 {
t.Errorf("GetMempoolEntry() got zero or negative size %d", e.Size)
}
if e.Fee <= 0 {
t.Errorf("GetMempoolEntry() got zero or negative fee %f", e.Fee)
}
// done
return
}
t.Skip("Skipping test, all attempts to get mempool entry failed due to network state changes")
}
func (rt *Test) TestEstimateSmartFee(t *testing.T) {
rt.skipUnconnected(t)
for _, blocks := range []int{1, 2, 3, 5, 10} {
fee, err := rt.Client.EstimateSmartFee(blocks, true)
if err != nil {
t.Error(err)
}
if fee != -1 && fee < 0 {
t.Errorf("EstimateSmartFee() returned unexpected fee rate: %f", fee)
}
}
}
func (rt *Test) TestEstimateFee(t *testing.T) {
rt.skipUnconnected(t)
for _, blocks := range []int{1, 2, 3, 5, 10} {
fee, err := rt.Client.EstimateFee(blocks)
if err != nil {
t.Error(err)
}
if fee != -1 && fee < 0 {
t.Errorf("EstimateFee() returned unexpected fee rate: %f", fee)
}
}
}
func (rt *Test) TestGetBestBlockHash(t *testing.T) {
rt.skipUnconnected(t)
for i := 0; i < 3; i++ {
hash, err := rt.Client.GetBestBlockHash()
if err != nil {
t.Fatal(err)
}
height, err := rt.Client.GetBestBlockHeight()
if err != nil {
t.Fatal(err)
}
hh, err := rt.Client.GetBlockHash(height)
if err != nil {
t.Fatal(err)
}
if hash != hh {
continue
}
// we expect no next block
_, err = rt.Client.GetBlock("", height+1)
if err != nil {
if err != bchain.ErrBlockNotFound {
t.Error(err)
}
return
}
}
t.Error("GetBestBlockHash() didn't get the best hash")
}
func (rt *Test) TestGetBestBlockHeight(t *testing.T) {
rt.skipUnconnected(t)
for i := 0; i < 3; i++ {
height, err := rt.Client.GetBestBlockHeight()
if err != nil {
t.Fatal(err)
}
// we expect no next block
_, err = rt.Client.GetBlock("", height+1)
if err != nil {
if err != bchain.ErrBlockNotFound {
t.Error(err)
}
return
}
}
t.Error("GetBestBlockHeigh() didn't get the the best heigh")
}
func (rt *Test) TestGetBlockHeader(t *testing.T) {
rt.skipUnconnected(t)
want := &bchain.BlockHeader{
Hash: rt.TestData.BlockHash,
Height: rt.TestData.BlockHeight,
}
got, err := rt.Client.GetBlockHeader(rt.TestData.BlockHash)
if err != nil {
t.Fatal(err)
}
// Confirmations is variable field, we just check if is set and reset it
if got.Confirmations <= 0 {
t.Fatalf("GetBlockHeader() got struct with invalid Confirmations field")
}
got.Confirmations = 0
got.Prev, got.Next = "", ""
if !reflect.DeepEqual(got, want) {
t.Errorf("GetBlockHeader() got=%v, want=%v", got, want)
}
}

View File

@ -0,0 +1,492 @@
// +build integration
package rpc
import (
"blockbook/bchain"
"blockbook/bchain/coins"
"blockbook/build/tools"
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"net"
"os"
"path/filepath"
"reflect"
"strings"
"testing"
"time"
"github.com/deckarep/golang-set"
)
var testMap = map[string]func(t *testing.T, th *TestHandler){
"GetBlockHash": testGetBlockHash,
"GetBlock": testGetBlock,
"GetTransaction": testGetTransaction,
"GetTransactionForMempool": testGetTransactionForMempool,
"MempoolSync": testMempoolSync,
"EstimateSmartFee": testEstimateSmartFee,
"EstimateFee": testEstimateFee,
"GetBestBlockHash": testGetBestBlockHash,
"GetBestBlockHeight": testGetBestBlockHeight,
"GetBlockHeader": testGetBlockHeader,
}
type TestHandler struct {
Client bchain.BlockChain
TestData *TestData
connected bool
}
var notConnectedError = errors.New("Not connected to backend server")
func TestRPCIntegration(t *testing.T) {
src := os.Getenv("BLOCKBOOK_SRC")
if src == "" {
t.Fatalf("Missing environment variable BLOCKBOOK_SRC")
}
configsDir := filepath.Join(src, "configs")
templateDir := filepath.Join(src, "build/templates")
noTests := 0
skippedTests := make([]string, 0, 10)
err := filepath.Walk(filepath.Join(configsDir, "coins"), func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if info.IsDir() || info.Name()[0] == '.' {
return nil
}
n := strings.TrimSuffix(info.Name(), ".json")
c, err := build.LoadConfig(configsDir, n)
if err != nil {
t.Errorf("%s: cannot load configuration: %s", n, err)
return nil
}
if len(c.IntegrationTests["rpc"]) == 0 {
return nil
}
cfg, err := makeBlockChainConfig(c, templateDir)
if err != nil {
t.Errorf("%s: cannot make blockchain config: %s", n, err)
return nil
}
t.Run(c.Coin.Alias, func(t *testing.T) {
noTests += 1
err := runTests(t, c.Coin.Name, c.Coin.Alias, cfg, c.IntegrationTests["rpc"])
if err != nil {
if err == notConnectedError {
skippedTests = append(skippedTests, c.Coin.Alias)
t.Skip(err)
}
t.Fatal(err)
}
})
return nil
})
if err != nil {
t.Fatal(err)
}
if len(skippedTests) > 0 {
t.Errorf("Too many skipped tests due to connection issues: %q", skippedTests)
}
}
func makeBlockChainConfig(c *build.Config, templateDir string) (json.RawMessage, error) {
outputDir, err := ioutil.TempDir("", "rpc_test")
if err != nil {
return nil, err
}
defer os.RemoveAll(outputDir)
err = build.GeneratePackageDefinitions(c, templateDir, outputDir)
if err != nil {
return nil, err
}
b, err := ioutil.ReadFile(filepath.Join(outputDir, "blockbook", "blockchaincfg.json"))
if err != nil {
return nil, err
}
var v json.RawMessage
err = json.Unmarshal(b, &v)
if err != nil {
return nil, err
}
return v, nil
}
func runTests(t *testing.T, coinName, coinAlias string, cfg json.RawMessage, tests []string) error {
cli, err := initBlockChain(coinName, cfg)
if err != nil {
if err == notConnectedError {
return err
}
t.Fatal(err)
}
td, err := LoadTestData(coinAlias, cli.GetChainParser())
if err != nil {
t.Fatalf("Test data loading failed: %s", err)
}
if td.TxDetails != nil {
parser := cli.GetChainParser()
for _, tx := range td.TxDetails {
err := setTxAddresses(parser, tx)
if err != nil {
t.Fatalf("Test data loading failed: %s", err)
}
}
}
h := TestHandler{Client: cli, TestData: td}
for _, test := range tests {
if f, found := testMap[test]; found {
t.Run(test, func(t *testing.T) { f(t, &h) })
} else {
t.Errorf("%s: test not found", test)
continue
}
}
return nil
}
func initBlockChain(coinName string, cfg json.RawMessage) (bchain.BlockChain, error) {
factory, found := coins.BlockChainFactories[coinName]
if !found {
return nil, fmt.Errorf("Factory function not found")
}
cli, err := factory(cfg, func(_ bchain.NotificationType) {})
if err != nil {
if isNetError(err) {
return nil, notConnectedError
}
return nil, fmt.Errorf("Factory function failed: %s", err)
}
err = cli.Initialize()
if err != nil {
if isNetError(err) {
return nil, notConnectedError
}
return nil, fmt.Errorf("BlockChain initialization failed: %s", err)
}
return cli, nil
}
func isNetError(err error) bool {
if _, ok := err.(net.Error); ok {
return true
}
return false
}
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
}
}
}
return err
}
func testGetBlockHash(t *testing.T, h *TestHandler) {
hash, err := h.Client.GetBlockHash(h.TestData.BlockHeight)
if err != nil {
t.Error(err)
return
}
if hash != h.TestData.BlockHash {
t.Errorf("GetBlockHash() got %q, want %q", hash, h.TestData.BlockHash)
}
}
func testGetBlock(t *testing.T, h *TestHandler) {
blk, err := h.Client.GetBlock(h.TestData.BlockHash, 0)
if err != nil {
t.Error(err)
return
}
if len(blk.Txs) != len(h.TestData.BlockTxs) {
t.Errorf("GetBlock() number of transactions: got %d, want %d", len(blk.Txs), len(h.TestData.BlockTxs))
}
for ti, tx := range blk.Txs {
if tx.Txid != h.TestData.BlockTxs[ti] {
t.Errorf("GetBlock() transaction %d: got %s, want %s", ti, tx.Txid, h.TestData.BlockTxs[ti])
}
}
}
func testGetTransaction(t *testing.T, h *TestHandler) {
for txid, want := range h.TestData.TxDetails {
got, err := h.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 {
t.Errorf("GetTransaction() got struct with invalid Confirmations field")
continue
}
got.Confirmations = 0
if !reflect.DeepEqual(got, want) {
t.Errorf("GetTransaction() got %+v, want %+v", got, want)
}
}
}
func testGetTransactionForMempool(t *testing.T, h *TestHandler) {
for txid, want := range h.TestData.TxDetails {
// reset fields that are not parsed by BlockChainParser
want.Confirmations, want.Blocktime, want.Time = 0, 0, 0
got, err := h.Client.GetTransactionForMempool(txid)
if err != nil {
t.Fatal(err)
}
// transactions parsed from JSON may contain additional data
got.Confirmations, got.Blocktime, got.Time = 0, 0, 0
if !reflect.DeepEqual(got, want) {
t.Errorf("GetTransactionForMempool() got %+v, want %+v", got, want)
}
}
}
func testMempoolSync(t *testing.T, h *TestHandler) {
for i := 0; i < 3; i++ {
txs := getMempool(t, h)
n, err := h.Client.ResyncMempool(nil)
if err != nil {
t.Fatal(err)
}
if n == 0 {
// no transactions to test
continue
}
txs = intersect(txs, getMempool(t, h))
if len(txs) == 0 {
// no transactions to test
continue
}
txid2addrs := getMempoolAddresses(t, h, txs)
if len(txid2addrs) == 0 {
t.Skip("Skipping test, no addresses in mempool")
}
for txid, addrs := range txid2addrs {
for _, a := range addrs {
got, err := h.Client.GetMempoolTransactions(a)
if err != nil {
t.Fatal(err)
}
if !containsString(got, txid) {
t.Errorf("ResyncMempool() - for address %s, transaction %s wasn't found in mempool", a, txid)
return
}
}
}
// done
return
}
t.Skip("Skipping test, all attempts to sync mempool failed due to network state changes")
}
func testEstimateSmartFee(t *testing.T, h *TestHandler) {
for _, blocks := range []int{1, 2, 3, 5, 10} {
fee, err := h.Client.EstimateSmartFee(blocks, true)
if err != nil {
t.Error(err)
}
if fee.Sign() == -1 {
sf := h.Client.GetChainParser().AmountToDecimalString(&fee)
if sf != "-1" {
t.Errorf("EstimateSmartFee() returned unexpected fee rate: %v", sf)
}
}
}
}
func testEstimateFee(t *testing.T, h *TestHandler) {
for _, blocks := range []int{1, 2, 3, 5, 10} {
fee, err := h.Client.EstimateFee(blocks)
if err != nil {
t.Error(err)
}
if fee.Sign() == -1 {
sf := h.Client.GetChainParser().AmountToDecimalString(&fee)
if sf != "-1" {
t.Errorf("EstimateFee() returned unexpected fee rate: %v", sf)
}
}
}
}
func testGetBestBlockHash(t *testing.T, h *TestHandler) {
for i := 0; i < 3; i++ {
hash, err := h.Client.GetBestBlockHash()
if err != nil {
t.Fatal(err)
}
height, err := h.Client.GetBestBlockHeight()
if err != nil {
t.Fatal(err)
}
hh, err := h.Client.GetBlockHash(height)
if err != nil {
t.Fatal(err)
}
if hash != hh {
time.Sleep(time.Millisecond * 100)
continue
}
// we expect no next block
_, err = h.Client.GetBlock("", height+1)
if err != nil {
if err != bchain.ErrBlockNotFound {
t.Error(err)
}
return
}
}
t.Error("GetBestBlockHash() didn't get the best hash")
}
func testGetBestBlockHeight(t *testing.T, h *TestHandler) {
for i := 0; i < 3; i++ {
height, err := h.Client.GetBestBlockHeight()
if err != nil {
t.Fatal(err)
}
// we expect no next block
_, err = h.Client.GetBlock("", height+1)
if err != nil {
if err != bchain.ErrBlockNotFound {
t.Error(err)
}
return
}
}
t.Error("GetBestBlockHeigh() didn't get the the best heigh")
}
func testGetBlockHeader(t *testing.T, h *TestHandler) {
want := &bchain.BlockHeader{
Hash: h.TestData.BlockHash,
Height: h.TestData.BlockHeight,
Time: h.TestData.BlockTime,
}
got, err := h.Client.GetBlockHeader(h.TestData.BlockHash)
if err != nil {
t.Fatal(err)
}
// Confirmations is variable field, we just check if is set and reset it
if got.Confirmations <= 0 {
t.Fatalf("GetBlockHeader() got struct with invalid Confirmations field")
}
got.Confirmations = 0
got.Prev, got.Next = "", ""
if !reflect.DeepEqual(got, want) {
t.Errorf("GetBlockHeader() got=%+v, want=%+v", got, want)
}
}
func getMempool(t *testing.T, h *TestHandler) []string {
txs, err := h.Client.GetMempool()
if err != nil {
t.Fatal(err)
}
if len(txs) == 0 {
t.Skip("Skipping test, mempool is empty")
}
return txs
}
func getMempoolAddresses(t *testing.T, h *TestHandler, txs []string) map[string][]string {
txid2addrs := map[string][]string{}
for i := 0; i < len(txs); i++ {
tx, err := h.Client.GetTransactionForMempool(txs[i])
if err != nil {
t.Fatal(err)
}
addrs := []string{}
for _, vin := range tx.Vin {
for _, a := range vin.Addresses {
if isSearchableAddr(a) {
addrs = append(addrs, a)
}
}
}
for _, vout := range tx.Vout {
for _, a := range vout.ScriptPubKey.Addresses {
if isSearchableAddr(a) {
addrs = append(addrs, a)
}
}
}
if len(addrs) > 0 {
txid2addrs[tx.Txid] = addrs
}
}
return txid2addrs
}
func isSearchableAddr(addr string) bool {
return len(addr) > 3 && addr[:3] != "OP_"
}
func intersect(a, b []string) []string {
setA := mapset.NewSet()
for _, v := range a {
setA.Add(v)
}
setB := mapset.NewSet()
for _, v := range b {
setB.Add(v)
}
inter := setA.Intersect(setB)
res := make([]string, 0, inter.Cardinality())
for v := range inter.Iter() {
res = append(res, v.(string))
}
return res
}
func containsString(slice []string, s string) bool {
for i := 0; i < len(slice); i++ {
if slice[i] == s {
return true
}
}
return false
}

View File

@ -1,56 +0,0 @@
{
"blockHeight": 2870000,
"blockHash": "0xeccd6b0031015a19cb7d4e10f28590ba65a6a54ad1baa322b50fe5ad16903895",
"blockTxs": [
"0x17ee235fc0359155b25419e0e4c65d9c500df6e71e8288d6ef020d04cc2f2cb3",
"0xe6b168d6bb3d8ed78e03dbf828b6bfd1fb613f6e129cba624964984553724c5d",
"0xe14e6628e866555091fda44f934dfc6bfbbcd8a81e1281bb682eaf293d248be8",
"0xcffe6d5f4022822ba5f1e64f5b413c7f91c3a48c3739429d72065ec1a8b82e91",
"0x1d261bf23077b7e68ad9300ce9d88e7f4f95075fe98ea76b70534acaf5cf891b",
"0x92f73c26c99ea3b15535829cf686541a1318623baf8c49fe2bf0168bf3c3e272",
"0x392394bb4e4463c9ed59182797b5dbf23aa41c6f6edd7f4b5025d82acf43c357",
"0x3dd838b8d5d9b7155c960f8a138a9c499b87d84b7c9d9a513d8022b1991f959c",
"0x4889b4d1ad3652f6c410a6b6c05a459b86e68363d795f38ec13d5dc6d595d977",
"0xd1e1ff22a80135d904d119252c5d56a8c5b07af9d26de79fd3dda8aeffccf9ae",
"0xd1c3b7835c6032eb9eb07fdce90944f26d470b1c027adf2951b329a783e8e628",
"0x7f0d140329941f120b5b3fc751e30adeb87b2aebbfce5adcd0216604a34b6cc0"
],
"txDetails": {
"0xe6b168d6bb3d8ed78e03dbf828b6bfd1fb613f6e129cba624964984553724c5d": {
"hex": "7b226e6f6e6365223a2230783239666165222c226761735072696365223a223078313261303566323030222c22676173223a2230786462626130222c22746f223a22307836383262373930336131313039386366373730633761656634616130326138356233663336303161222c2276616c7565223a22307830222c22696e707574223a223078663032356361616630303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030323235222c2268617368223a22307865366231363864366262336438656437386530336462663832386236626664316662363133663665313239636261363234393634393834353533373234633564222c22626c6f636b4e756d626572223a223078326263616630222c2266726f6d223a22307864616363396336313735346130633436313666633533323364633934366538396562323732333032222c227472616e73616374696f6e496e646578223a22307831222c2276223a2230783162222c2272223a22307831626434306133313132326330333931386466366431363664373430613661336132326630386132353933346365623136383863363239373736363163383063222c2273223a22307836303766626331356331663739393561343235386635613962636363363362303430333632643139393164356566653133363163353632323265346361383966227d",
"txid": "0xe6b168d6bb3d8ed78e03dbf828b6bfd1fb613f6e129cba624964984553724c5d",
"blocktime": 1521515026,
"time": 1521515026,
"vin": [
{
"addresses": ["0xdacc9c61754a0c4616fc5323dc946e89eb272302"]
}
],
"vout": [
{
"scriptPubKey": {
"addresses": ["0x682b7903a11098cf770c7aef4aa02a85b3f3601a"]
}
}
]
},
"0x17ee235fc0359155b25419e0e4c65d9c500df6e71e8288d6ef020d04cc2f2cb3": {
"hex": "7b226e6f6e6365223a223078346566222c226761735072696365223a223078626134336237343030222c22676173223a2230783936653838222c22746f223a22307864303534323939633438326164356362333961336661383734373235663965326132306433343963222c2276616c7565223a22307830222c22696e707574223a2230783165626366613630303030303030303030303030303030303030303030303030633034383732383566313736663532306635636434363732306236383662366534366165636430373030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303031303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030313430303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030633461303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303530303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030353831303030303030303030303030303030303030303030303030633365313631343261306432366336616530623163656238643961373236653734653164613664663666656562653137653437306464626434656261663364623938333136386235316337623136363732613131623966306662383864623438323962653237346230303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303034363336663665363230303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303437323666373336313030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030222c2268617368223a22307831376565323335666330333539313535623235343139653065346336356439633530306466366537316538323838643665663032306430346363326632636233222c22626c6f636b4e756d626572223a223078326263616630222c2266726f6d223a22307838623461353365353739303739343466633261653539623837373066393331623639326232373062222c227472616e73616374696f6e496e646578223a22307830222c2276223a2230783163222c2272223a22307866633739633836353638323039313030323134616339353662373930653066383935656130343135313438643233613239353632356564393761633936333534222c2273223a223078373232303833616331643764663662626162393939383537616163323561336665646265386130633561326266376364653835393738363266373862313937227d",
"txid": "0x17ee235fc0359155b25419e0e4c65d9c500df6e71e8288d6ef020d04cc2f2cb3",
"blocktime": 1521515026,
"time": 1521515026,
"vin": [
{
"addresses": ["0x8b4a53e57907944fc2ae59b8770f931b692b270b"]
}
],
"vout": [
{
"scriptPubKey": {
"addresses": ["0xd054299c482ad5cb39a3fa874725f9e2a20d349c"]
}
}
]
}
}
}

View File

@ -1,6 +1,7 @@
{
"blockHeight": 538226,
"blockHash": "0000000000000000008f48dad3a9efb25275fe36bb31b460e775af57783cc9af",
"blockTime": 1531135480,
"blockTxs": [
"77360353ff628d53534201712db7a7689a3f9318cad240fbc093dfb4778fdf84",
"82d95972a276b4197806ec0d9e3e56b66523849b2a7597ac509f4dfc8f7db4fd",
@ -70,6 +71,7 @@
"blocktime":1531135480,
"time":1531135480,
"locktime": 0,
"version": 1,
"vin": [
{
"txid": "fe496933e0a3582ef020bd35c38fe8244a80fa7c63e7607f6f9ccb0806f419e4",
@ -175,6 +177,7 @@
"blocktime":1531135480,
"time":1531135480,
"locktime": 0,
"version": 1,
"vin": [
{
"txid": "52f37874fd5a2497c5d84619ab415ca17ebb1d49d559f0d468869f70537354b9",

View File

@ -1,6 +1,7 @@
{
"blockHeight": 1241851,
"blockHash": "00000000ff54973b45ac277fc9e92408479703d785eae0e96508bc5aa792d621",
"blockTime": 1529571678,
"blockTxs": [
"0cbcd28da300a850fd922caf633f2aaca0fd0a2d4ce46c6f07314c644287527a",
"32fe2a3a60eb952d6ade53b7030ddb6ad8af5797391857baaad37c5b20f3b5ca"
@ -12,6 +13,7 @@
"blocktime":1529571678,
"time":1529571678,
"locktime": 0,
"version": 1,
"vin": [
{
"coinbase": "03fbf212055374617368",
@ -34,6 +36,7 @@
"blocktime":1529571678,
"time":1529571678,
"locktime": 0,
"version": 1,
"vin": [
{
"txid": "58ff2450a71b3c228d17d04ec8edcaae452bc97d5ccc4591e2741fd8b031d221",

View File

@ -1,6 +1,7 @@
{
"blockHeight": 529150,
"blockHash": "00000000000000000035835503f43c878ebb643f3b40bdfd0dfda760da74e73c",
"blockTime": 1529915213,
"blockTxs": [
"8dd1379174e262d12a32d217e87a7caf09fa1b9e48a6fe010cac219f18c6de58",
"5fce44793b328ca5f142caadbf29efc78a0059d7a6379dff81fc6447b519a7c3",
@ -120,6 +121,7 @@
"blocktime": 1529915213,
"time": 1529915213,
"locktime": 0,
"version": 1,
"vin": [
{
"txid": "83eff5bcc738d52e04528984cd5cf601b69e7df65b2b10ed2475c0072cdde14c",
@ -153,6 +155,7 @@
"blocktime": 1529915213,
"time": 1529915213,
"locktime": 529149,
"version": 2,
"vin": [
{
"txid": "413988d546516707f3b20ca9876e026a9472bd814662e42f37f4e57a15713da7",

View File

@ -1,6 +1,7 @@
{
"blockHeight": 1325168,
"blockHash": "000000000000004ed0834f3de922e66d024ec4da9fcc2da17be61369cb6dc041",
"blockTime": 1528788394,
"blockTxs": [
"e1179f205aabbf48dc2ce4ebd9ed255571b0578e4de551f6574a50cb81120007",
"00a5aa2891d41af9eb1dc30c940f142a609ecab8f370eb0874ba7d32252d1b1b",
@ -43,6 +44,7 @@
"blocktime": 1528788394,
"time": 1528788394,
"locktime": 0,
"version": 1,
"vin": [
{
"txid": "4097f5265397047bffe219a1bca65ea5726402c3d9aeecd577fa1c274fc1b8a4",
@ -84,6 +86,7 @@
"blocktime": 1528788394,
"time": 1528788394,
"locktime": 0,
"version": 1,
"vin": [
{
"txid": "80437ba4e81bde8b584258f5adad0d08dea60f1e38026344442ad59a4ef797c9",

View File

@ -1,6 +1,7 @@
{
"blockHeight": 894503,
"blockHash": "0000000000000026e2b7c7bb478f0ee846dd976ac6b97c3f3f7c8c65beab88a4",
"blockTime": 1530189699,
"blockTxs": [
"6ea3a7fee4c40170d8782719d6fbeafd4cabb830f880d91bcf34811ad79e57bb",
"c269940d8e46d94ff61a4adec8aa6dfb13803723a95a5b61c7831d3aea308cbc",
@ -27,6 +28,7 @@
"blocktime": 1530189699,
"time": 1530189699,
"locktime": 0,
"version": 1,
"vin": [
{
"txid": "a78824a88c089ea1344fe2a33c454024e6501e76cca2590515b390d85082bd23",

View File

@ -1,7 +1,8 @@
{
"blockHeight": 139521,
"blockHash": "000000000296ea05e13b5479f6c041de575eec90759f58f57d311a8918b0af17",
"blockTxs": [
"blockTime": 1528713762,
"blockTxs": [
"cfe2d2c5bd9929349a9d8f3d8f2423f4f0c9b408ed41b3002be5e7437a20aa7d",
"3f7ac6c55c0b383feb17715aef3cebf29182039fc42577d7c517b2fc04096942",
"3f296b929f6ee4f0ad3d6fce35873d6aeacbdf06aaf2abe384beb6e3f2a4dd03",
@ -31,6 +32,7 @@
"blocktime": 1528713762,
"time": 1528713762,
"locktime": 139520,
"version": 2,
"vin": [
{
"txid": "a41616c7585c98aeda98d6ff6766b15455e327c9472582b80289dab7597ad309",
@ -64,6 +66,7 @@
"blocktime": 1528713762,
"time": 1528713762,
"locktime": 139520,
"version": 2,
"vin": [
{
"txid": "187ae015e41dff766f5a18ae705f59db950a6729a06fa5fd04630c362a9aee27",

View File

@ -0,0 +1,53 @@
{
"blockHeight": 2000002,
"blockHash": "6db0b6fc543cacbc4244f5fab23af56792a87eacc3149957a1922fd59d4d03e0",
"blockTime": 1512601001,
"blockTxs": [
"cc3c92a9da8e28f18faf17efe74e96f541f03e3a300c55fef0c4dbc9e5b14c01",
"cb3cb43e34385556c9617699687b15f3dbad57cb0e8548114b66c55512c51a52",
"f6be02faa646a1ad764e4eb49fb6f02cbae69e5bc396a8977e59c7d887aa38b2",
"79883da5f57c942feb922e4bb635af631219aae2e70037459b07840945d6466c",
"23b5caddcb3c1222a0b97ff6f67185266b982465ef1a3546c7fc6ad8414ba7d0",
"82cf9f6822362ecfc0d47cc6b7372862ef0fdd14b259c42c8f2def8589f266f0",
"12a4e4daa6e3b842a2395c315f296f284ac2085e2f2fb4f356f94e08117963ba",
"cc136353be3db35e051e5dd34ff29f9cfe47a7b3f6ab7383fd644bc00ab1e348",
"f2db6749bc5f7e727f9e6c9a5553ad44dc59d9994dcadc24a57cf6042d1d07e7"
],
"txDetails": {
"f6be02faa646a1ad764e4eb49fb6f02cbae69e5bc396a8977e59c7d887aa38b2": {
"hex": "0100000002361742ebea8bdce921359dbe528b23cc8050dc86950fd07011a74258cd0215dde5030000db00483045022100885238254408979e8f44c087c06333376980a73594c4d51c8cdf457934514e34022026287952d554f9f9f24c0a47096cce0db53547099aa700a91fa9046eb5c4960801483045022100a4ce7700bdf654dfe2a4fc5796bd3466be6af3691dc263821d4c71f91f9d9d1e02200b988bea6bf9e7d44e9a4b8cee9e390c72d284e3df1aacf6ab23368560b3e53e0147522102a39a18546545ee2944481953c25b3a5f4942cd8ed533ae2daabb4339c68c599b210349ed7f7284feff2b9d0b3493fde313afcc63b41347592c435f5408852c9ea05d52aeffffffff85f2900e642720043fae056075b48ea83cbd3653797c40fb4e7e512938dcda7573020000da004730440220138a8dd272f3081e54b172fb3e6cf43fe5375369d7e93c310ceba852c22ba16f022050de068f932f6d6db9889a8d4e489ffeda2ed1848cd2a71a06d635e6d1799b8e01483045022100af99d412c97062219ef56c3c8ad99023feab94042d310f792392940c61df9bdd02200b4542395624412260c96c46427509c2b801df33e15da08c6c505594c7c142010147522102a39a18546545ee2944481953c25b3a5f4942cd8ed533ae2daabb4339c68c599b210349ed7f7284feff2b9d0b3493fde313afcc63b41347592c435f5408852c9ea05d52aeffffffff01c0ebf7b90100000017a9147541523df4d0d0875c024e1906b0d195abaf20958700000000",
"txid": "f6be02faa646a1ad764e4eb49fb6f02cbae69e5bc396a8977e59c7d887aa38b2",
"blocktime": 1512601001,
"time": 1512601001,
"locktime": 0,
"version": 1,
"vin": [
{
"txid": "dd1502cd5842a71170d00f9586dc5080cc238b52be9d3521e9dc8beaeb421736",
"vout": 997,
"scriptSig": {
"hex": "00483045022100885238254408979e8f44c087c06333376980a73594c4d51c8cdf457934514e34022026287952d554f9f9f24c0a47096cce0db53547099aa700a91fa9046eb5c4960801483045022100a4ce7700bdf654dfe2a4fc5796bd3466be6af3691dc263821d4c71f91f9d9d1e02200b988bea6bf9e7d44e9a4b8cee9e390c72d284e3df1aacf6ab23368560b3e53e0147522102a39a18546545ee2944481953c25b3a5f4942cd8ed533ae2daabb4339c68c599b210349ed7f7284feff2b9d0b3493fde313afcc63b41347592c435f5408852c9ea05d52ae"
},
"sequence": 4294967295
},
{
"txid": "75dadc3829517e4efb407c795336bd3ca88eb4756005ae3f042027640e90f285",
"vout": 627,
"scriptSig": {
"hex": "004730440220138a8dd272f3081e54b172fb3e6cf43fe5375369d7e93c310ceba852c22ba16f022050de068f932f6d6db9889a8d4e489ffeda2ed1848cd2a71a06d635e6d1799b8e01483045022100af99d412c97062219ef56c3c8ad99023feab94042d310f792392940c61df9bdd02200b4542395624412260c96c46427509c2b801df33e15da08c6c505594c7c142010147522102a39a18546545ee2944481953c25b3a5f4942cd8ed533ae2daabb4339c68c599b210349ed7f7284feff2b9d0b3493fde313afcc63b41347592c435f5408852c9ea05d52ae"
},
"sequence": 4294967295
}
],
"vout": [
{
"value": 74.15000000,
"n": 0,
"scriptPubKey": {
"hex": "a9147541523df4d0d0875c024e1906b0d195abaf209587"
}
}
]
}
}
}

View File

@ -0,0 +1,40 @@
{
"blockHeight": 2870000,
"blockHash": "0xeccd6b0031015a19cb7d4e10f28590ba65a6a54ad1baa322b50fe5ad16903895",
"blockTime": 1521515026,
"blockTxs": [
"0x17ee235fc0359155b25419e0e4c65d9c500df6e71e8288d6ef020d04cc2f2cb3",
"0xe6b168d6bb3d8ed78e03dbf828b6bfd1fb613f6e129cba624964984553724c5d",
"0xe14e6628e866555091fda44f934dfc6bfbbcd8a81e1281bb682eaf293d248be8",
"0xcffe6d5f4022822ba5f1e64f5b413c7f91c3a48c3739429d72065ec1a8b82e91",
"0x1d261bf23077b7e68ad9300ce9d88e7f4f95075fe98ea76b70534acaf5cf891b",
"0x92f73c26c99ea3b15535829cf686541a1318623baf8c49fe2bf0168bf3c3e272",
"0x392394bb4e4463c9ed59182797b5dbf23aa41c6f6edd7f4b5025d82acf43c357",
"0x3dd838b8d5d9b7155c960f8a138a9c499b87d84b7c9d9a513d8022b1991f959c",
"0x4889b4d1ad3652f6c410a6b6c05a459b86e68363d795f38ec13d5dc6d595d977",
"0xd1e1ff22a80135d904d119252c5d56a8c5b07af9d26de79fd3dda8aeffccf9ae",
"0xd1c3b7835c6032eb9eb07fdce90944f26d470b1c027adf2951b329a783e8e628",
"0x7f0d140329941f120b5b3fc751e30adeb87b2aebbfce5adcd0216604a34b6cc0"
],
"txDetails": {
"0x7f0d140329941f120b5b3fc751e30adeb87b2aebbfce5adcd0216604a34b6cc0": {
"hex": "7b226e6f6e6365223a223078333632306136222c226761735072696365223a223078313261303566323030222c22676173223a22307835323038222c22746f223a22307831623137626331326166623635643563346238316139666632613037366234326131396661616136222c2276616c7565223a223078646530623662336137363430303030222c22696e707574223a223078222c2268617368223a22307837663064313430333239393431663132306235623366633735316533306164656238376232616562626663653561646364303231363630346133346236636330222c22626c6f636b4e756d626572223a223078326263616630222c2266726f6d223a22307838316237653038663635626466353634383630366338393939386139636338313634333937363437222c227472616e73616374696f6e496e646578223a22307862222c2276223a2230783162222c2272223a22307862666662323864633865373939383833366639356664616139396433626435666635346365663562313839636462383537333537666161326431616231393136222c2273223a22307833616462316365313264396664306538616662373064386639346534636538356137666639346465333966623636333139363638363435663464643138646432227d",
"txid": "0x7f0d140329941f120b5b3fc751e30adeb87b2aebbfce5adcd0216604a34b6cc0",
"blocktime": 1521515026,
"time": 1521515026,
"vin": [
{
"addresses": ["0x81b7e08f65bdf5648606c89998a9cc8164397647"]
}
],
"vout": [
{
"value": 1,
"scriptPubKey": {
"addresses": ["0x1b17bc12afb65d5c4b81a9ff2a076b42a19faaa6"]
}
}
]
}
}
}

View File

@ -0,0 +1,49 @@
{
"blockHeight": 1377592,
"blockHash": "bddb1cfbd474e9516399b373e411bd33c1a71cb01aa8469a27d397ef0a891c7d",
"blockTime": 1519947864,
"blockTxs": [
"84e9147bf6e171adbda3b3961e467652286d9d9c2933d19326bf84766d047922",
"8d6e628b891dd17bfe3bb5a24a6c7f02ebc2cf499a85515d0325033aa74ab53a",
"5b77ca9735f65d110b086be410658d0239e1fcee13231942a262b35a5b8d6a91",
"19ad3daa2447be4e000d822f79ce252f274b016dacf1418b433d36c0aaf24f18",
"5bffbf0c8ff66d298d94dc323c3644e21932dfc733603d6637ff46cb8d34466c",
"90d587e35b23905f0125111f41d69bb6c7eed44f0944caad2903aae1f174ac49"
],
"txDetails": {
"19ad3daa2447be4e000d822f79ce252f274b016dacf1418b433d36c0aaf24f18": {
"hex": "010000000001011f9216c16c78386540d7ae7d32657c388ef5f204596f84ea0851dcb78c479a87010000001716001432d094c8e2efd308d2c69affd6712ebbf7a5a286ffffffff0289544a110000000017a91457ca840d6c811dd6808722babe3f88d2fdb2ca14875bb1c9180000000017a91482696a9b4188eda8f93b120315cad4260cbb90db8702483045022100aa256153317133fa719180935017671e33ca77df6f5426554f5b0855f07a392b02202684e8c30623e1c4e753ea23a95bd571e1fda7d15dc0b1e2d54ff5bc50329256012103399a1d98f0733ef400ff8d4f43fe4543065f7f387c863361c77a8826321ca6fb00000000",
"txid": "19ad3daa2447be4e000d822f79ce252f274b016dacf1418b433d36c0aaf24f18",
"blocktime": 1519947864,
"time": 1519947864,
"locktime": 0,
"version": 1,
"vin": [
{
"txid": "879a478cb7dc5108ea846f5904f2f58e387c65327daed7406538786cc116921f",
"vout": 1,
"scriptSig": {
"hex": "16001432d094c8e2efd308d2c69affd6712ebbf7a5a286"
},
"sequence": 4294967295
}
],
"vout": [
{
"value": 2.90083977,
"n": 0,
"scriptPubKey": {
"hex": "a91457ca840d6c811dd6808722babe3f88d2fdb2ca1487"
}
},
{
"value": 4.15871323,
"n": 1,
"scriptPubKey": {
"hex": "a91482696a9b4188eda8f93b120315cad4260cbb90db87"
}
}
]
}
}
}

View File

@ -0,0 +1,55 @@
{
"blockHeight": 1205643,
"blockHash": "60c80ce4f0a90f7b217bef8fab32ce2e499a4dc2067f4a6062048e3b940d76c6",
"blockTime": 1514835214,
"blockTxs": [
"38b4a919ab0f8b4570e1f26717740846b7994ba22f94c51d96eb32bc45c5886e",
"7f793aff2f74ca9cec6df08a7c0fcaa1ea301468459b46edbed693a871832298",
"c2bdcc759c1d4e6bb3eee859cc7400bc376479e30bc8328f31864aad92d52ade",
"8a26deeda15f1ddc3234acafb0da9829bf334ea108e359f11d8f281ae42512f8"
],
"txDetails": {
"7f793aff2f74ca9cec6df08a7c0fcaa1ea301468459b46edbed693a871832298": {
"hex": "0200000002d9d3592e550618b41347bd992d33b9eaebe7e26eb21df49bda5372ce1711f8e5010000006b483045022100b4fb223759753d82fd5ce833e84f62904c086b51fd2be11d7e3e9f183e2442850220079f8b3d6555610ff98da3216fa117bcb49f98e610a38471d69566ce7bc5770801210362c5e60c40f0b588216310dbb2f4a46e86038ba597f33d023ad64e66ff2e972bfeffffff4f4e9b373e6cd134d2f5f45bf3645a00c2689472ad66aa37ae022eef3dd6b16a010000006a47304402203dd8510301d68bc21169f953d2996d602f8411a309c494392b6f2bf85e5d58f102206fa2db245d15021f3b556d10747cfd8eba100fd19dfa1a0a9c5cebc58d7c22f6012103c65394a4c6705e3a13f6305e7997fb68feb192acce312a8ba144994b9a33507bfeffffff02bbe52c00000000001976a914eb0f5829a547f30b3d488ab38b425c1d93afb4b888ac00f90295000000001976a914883f0723e1c93db538d02c7a8d318322e655b59888ac89651200",
"txid": "7f793aff2f74ca9cec6df08a7c0fcaa1ea301468459b46edbed693a871832298",
"blocktime": 1514835214,
"time": 1514835214,
"locktime": 1205641,
"version": 2,
"vin": [
{
"txid": "e5f81117ce7253da9bf41db26ee2e7ebeab9332d99bd4713b41806552e59d3d9",
"vout": 1,
"scriptSig": {
"hex": "483045022100b4fb223759753d82fd5ce833e84f62904c086b51fd2be11d7e3e9f183e2442850220079f8b3d6555610ff98da3216fa117bcb49f98e610a38471d69566ce7bc5770801210362c5e60c40f0b588216310dbb2f4a46e86038ba597f33d023ad64e66ff2e972b"
},
"sequence": 4294967294
},
{
"txid": "6ab1d63def2e02ae37aa66ad729468c2005a64f35bf4f5d234d16c3e379b4e4f",
"vout": 1,
"scriptSig": {
"hex": "47304402203dd8510301d68bc21169f953d2996d602f8411a309c494392b6f2bf85e5d58f102206fa2db245d15021f3b556d10747cfd8eba100fd19dfa1a0a9c5cebc58d7c22f6012103c65394a4c6705e3a13f6305e7997fb68feb192acce312a8ba144994b9a33507b"
},
"sequence": 4294967294
}
],
"vout": [
{
"value": 0.02942395,
"n": 0,
"scriptPubKey": {
"hex": "76a914eb0f5829a547f30b3d488ab38b425c1d93afb4b888ac"
}
},
{
"value": 25.00000000,
"n": 1,
"scriptPubKey": {
"hex": "76a914883f0723e1c93db538d02c7a8d318322e655b59888ac"
}
}
]
}
}
}

View File

@ -1,6 +1,7 @@
{
"blockHeight": 404680,
"blockHash": "920fe53b840111f7e593d93ba58dc54e043e10f8fa4a678e86a98f5cb5b29614",
"blockTime": 1530003649,
"blockTxs": [
"80b8477d10df9ece7d8dde2d30817e2855af1fb66b7a9ac860e592118ae33f5f",
"afcd8e3638b11b1ce52055474dcce78c4129e95ebee3305574acda48deea8a65",
@ -39,6 +40,7 @@
"blocktime": 1530003649,
"time": 1530003649,
"locktime": 404678,
"version": 1,
"vin": [
{
"txid": "223a4ff7a613173ffbc67f9efffe1b0c3fd5ba2471935a44cc81814038272901",

View File

@ -1,6 +1,7 @@
{
"blockHeight": 952235,
"blockHash": "b2787dd022e3aa65b63dbf08af2c9bb4d4a362d95e3328c02743a5c8d75acb36",
"blockTime": 1529932850,
"blockTxs": [
"366eca05fa8579465d8822ad6462762120b26239201a34981e5f9d9efac3cc31",
"e74c247a5a77d4edd96a5dbb2930c74d9ab550affde991731f78f3e3a2f4b559",
@ -14,6 +15,7 @@
"blocktime": 1529932850,
"time": 1529932850,
"locktime": 952232,
"version": 2,
"vin": [
{
"txid": "8ec70404bdd16559c589736863ee9d9f69431ba4e6b2ab3b1641b3f9f3821624",
@ -47,6 +49,7 @@
"blocktime": 1529932850,
"time": 1529932850,
"locktime": 952233,
"version": 1,
"vin": [
{
"txid": "5765db7dd62cddada154b2c95505e298818a308cc1ce1e28d6e8565301cb8d74",

View File

@ -1,6 +1,7 @@
{
"blockHeight": 349410,
"blockHash": "000000000101b45343aeda139d06d7f188393a5cf4adabfa191cb1828d67f6bd",
"blockTime": 1530264033,
"blockTxs": [
"6ebf0114454ba776f878cf726be3cb65964b21fad8dfca5d2ee69bd4a32cfa97",
"3de894528d5f8dbe14f726244be70a88f9b738020215c91b2893fa5a46f48c14",
@ -22,6 +23,7 @@
"blocktime": 1530264033,
"time": 1530264033,
"locktime": 349399,
"version": 3,
"vin": [
{
"txid": "9c9faac29b0fa1b0e683727f2973bfcb87e4baadd07e9c997b431a31713cb30c",
@ -57,6 +59,7 @@
"blocktime": 1530264033,
"time": 1530264033,
"locktime": 0,
"version": 3,
"vin": [
{
"txid": "4440c8e9d5b57da7ca0fb3f62ec13f392267aeb797c123bb01e850adf8573dd0",

View File

@ -1,6 +1,7 @@
{
"blockHeight": 251102,
"blockHash": "001335906f981bbf0633e124e2fa8afef3d882e34a0306a4e0c55162e57e673d",
"blockTime": 1528781777,
"blockTxs": [
"f02aa1c4c86e1d0cef6ccbbc48b2b7b38355bc3612d8f77dd58d04be1ec6ba19",
"a9f7cc34d7e272d2d9fb68cfa1c1941e338f377e6e426ae2fea1c12616d89c63",
@ -13,6 +14,7 @@
"blocktime": 1528781777,
"time": 1528781777,
"locktime": 251028,
"version": 3,
"vin": [
{
"txid": "19a1d013b898239e9a2943faa07f8716b9be168bc8e001daf3625f535fde1a60",
@ -48,6 +50,7 @@
"blocktime": 1528781777,
"time": 1528781777,
"locktime": 251090,
"version": 3,
"vin": [
{
"txid": "9acab5f13cf94074e75f5686b59fccd938f54b5f20ddddfcb6077c679a13c0ea",

View File

@ -148,7 +148,7 @@ func (m *ProtoTransaction_VinType) GetAddresses() []string {
}
type ProtoTransaction_VoutType struct {
Value float64 `protobuf:"fixed64,1,opt,name=Value" json:"Value,omitempty"`
ValueSat []byte `protobuf:"bytes,1,opt,name=ValueSat,proto3" json:"ValueSat,omitempty"`
N uint32 `protobuf:"varint,2,opt,name=N" json:"N,omitempty"`
ScriptPubKeyHex []byte `protobuf:"bytes,3,opt,name=ScriptPubKeyHex,proto3" json:"ScriptPubKeyHex,omitempty"`
Addresses []string `protobuf:"bytes,4,rep,name=Addresses" json:"Addresses,omitempty"`
@ -159,11 +159,11 @@ func (m *ProtoTransaction_VoutType) String() string { return proto.Co
func (*ProtoTransaction_VoutType) ProtoMessage() {}
func (*ProtoTransaction_VoutType) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0, 1} }
func (m *ProtoTransaction_VoutType) GetValue() float64 {
func (m *ProtoTransaction_VoutType) GetValueSat() []byte {
if m != nil {
return m.Value
return m.ValueSat
}
return 0
return nil
}
func (m *ProtoTransaction_VoutType) GetN() uint32 {
@ -196,26 +196,26 @@ func init() {
func init() { proto.RegisterFile("tx.proto", fileDescriptor0) }
var fileDescriptor0 = []byte{
// 330 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x74, 0x92, 0xc1, 0x4e, 0xc2, 0x40,
0x10, 0x86, 0xb3, 0xb4, 0x14, 0x18, 0x21, 0x92, 0x89, 0x31, 0x0d, 0xf1, 0x50, 0x39, 0xf5, 0xd4,
0x03, 0xc6, 0x07, 0x50, 0x2f, 0x24, 0x1a, 0x42, 0x16, 0xd2, 0x7b, 0x5b, 0x36, 0xb0, 0x11, 0x77,
0xb1, 0xdd, 0x1a, 0x78, 0x16, 0x1f, 0xc1, 0x97, 0x34, 0x3b, 0x2d, 0x45, 0x48, 0xbc, 0xed, 0xff,
0xef, 0x4c, 0xff, 0x6f, 0xff, 0x14, 0xba, 0x66, 0x1f, 0xed, 0x72, 0x6d, 0x34, 0x7a, 0x69, 0xb6,
0x49, 0xa4, 0x1a, 0x7f, 0xbb, 0x30, 0x9c, 0x5b, 0x67, 0x99, 0x27, 0xaa, 0x48, 0x32, 0x23, 0xb5,
0x42, 0x04, 0x77, 0xb9, 0x97, 0x2b, 0x9f, 0x05, 0x2c, 0xec, 0x73, 0x3a, 0xe3, 0x10, 0x9c, 0xa9,
0xd8, 0xfb, 0x2d, 0xb2, 0xec, 0x11, 0xef, 0xa0, 0xf7, 0xbc, 0xd5, 0xd9, 0xbb, 0x91, 0x1f, 0xc2,
0x77, 0x02, 0x16, 0xba, 0xfc, 0x64, 0xe0, 0x08, 0xba, 0x6f, 0xc7, 0x4b, 0x37, 0x60, 0xe1, 0x80,
0x37, 0x1a, 0x6f, 0xc1, 0x9b, 0x0a, 0xb9, 0xde, 0x18, 0xbf, 0x4d, 0x37, 0xb5, 0xc2, 0x09, 0x38,
0xb1, 0x54, 0xbe, 0x17, 0x38, 0xe1, 0xd5, 0x24, 0x88, 0x2a, 0xc4, 0xe8, 0x12, 0x2f, 0x8a, 0xa5,
0x5a, 0x1e, 0x76, 0x82, 0xdb, 0x61, 0x7c, 0x04, 0x37, 0xd6, 0xa5, 0xf1, 0x3b, 0xb4, 0x74, 0xff,
0xff, 0x92, 0x2e, 0x0d, 0x6d, 0xd1, 0xf8, 0xe8, 0x87, 0x41, 0xa7, 0xfe, 0x8e, 0x45, 0x7d, 0xd1,
0x52, 0xa5, 0x49, 0x21, 0xe8, 0xc9, 0x3d, 0xde, 0xe8, 0xa6, 0x8a, 0xd6, 0x9f, 0x2a, 0xb0, 0x8e,
0x74, 0x08, 0x9e, 0xce, 0x38, 0x86, 0xfe, 0x22, 0xcb, 0xe5, 0xce, 0x2c, 0xe4, 0xda, 0xf6, 0xe4,
0xd2, 0xfc, 0x99, 0x67, 0x73, 0x16, 0xe2, 0xb3, 0x14, 0x2a, 0x13, 0xf5, 0xc3, 0x1b, 0x6d, 0xcb,
0x7c, 0x5a, 0xad, 0x72, 0x51, 0x14, 0xa2, 0xa0, 0x02, 0x7a, 0xfc, 0x64, 0x8c, 0xbe, 0xa0, 0x7b,
0xe4, 0xc7, 0x1b, 0x68, 0xc7, 0xc9, 0xb6, 0xac, 0x50, 0x19, 0xaf, 0x04, 0xf6, 0x81, 0xcd, 0x08,
0x72, 0xc0, 0xd9, 0x0c, 0x43, 0xb8, 0xae, 0x92, 0xe7, 0x65, 0xfa, 0x2a, 0x0e, 0x16, 0xc8, 0x21,
0xa0, 0x4b, 0xfb, 0x3c, 0xd7, 0xbd, 0xc8, 0x4d, 0x3d, 0xfa, 0x59, 0x1e, 0x7e, 0x03, 0x00, 0x00,
0xff, 0xff, 0x64, 0x1d, 0x38, 0xac, 0x38, 0x02, 0x00, 0x00,
// 331 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x74, 0x52, 0xc1, 0x6e, 0xea, 0x30,
0x10, 0x94, 0x89, 0x5f, 0x80, 0x7d, 0xa0, 0x87, 0xf6, 0xf0, 0x14, 0xa1, 0x1e, 0x52, 0x4e, 0x39,
0xe5, 0x40, 0xd5, 0x0f, 0x68, 0x7b, 0x41, 0x6a, 0x85, 0x90, 0x83, 0x72, 0x4f, 0x82, 0x05, 0x56,
0xa9, 0x4d, 0x13, 0x47, 0x02, 0xa9, 0x3f, 0xd3, 0x73, 0x7f, 0xb2, 0xf2, 0x12, 0x42, 0x41, 0xea,
0x6d, 0x67, 0xbc, 0xe3, 0x19, 0x4f, 0x02, 0x3d, 0xbb, 0x8f, 0x77, 0xa5, 0xb1, 0x06, 0xfd, 0xbc,
0xd8, 0x64, 0x4a, 0x4f, 0x3e, 0x39, 0x8c, 0x16, 0x8e, 0x59, 0x96, 0x99, 0xae, 0xb2, 0xc2, 0x2a,
0xa3, 0x11, 0x81, 0x2f, 0xf7, 0x6a, 0x15, 0xb0, 0x90, 0x45, 0x03, 0x41, 0x33, 0x8e, 0xc0, 0x9b,
0xc9, 0x7d, 0xd0, 0x21, 0xca, 0x8d, 0x78, 0x03, 0xfd, 0xc7, 0xad, 0x29, 0x5e, 0xad, 0x7a, 0x93,
0x81, 0x17, 0xb2, 0x88, 0x8b, 0x33, 0x81, 0x63, 0xe8, 0xbd, 0x9c, 0x0e, 0x79, 0xc8, 0xa2, 0xa1,
0x68, 0x31, 0xfe, 0x07, 0x7f, 0x26, 0xd5, 0x7a, 0x63, 0x83, 0x3f, 0x74, 0xd2, 0x20, 0x9c, 0x82,
0x97, 0x2a, 0x1d, 0xf8, 0xa1, 0x17, 0xfd, 0x9d, 0x86, 0xf1, 0x31, 0x62, 0x7c, 0x1d, 0x2f, 0x4e,
0x95, 0x5e, 0x1e, 0x76, 0x52, 0xb8, 0x65, 0xbc, 0x07, 0x9e, 0x9a, 0xda, 0x06, 0x5d, 0x12, 0xdd,
0xfe, 0x2e, 0x32, 0xb5, 0x25, 0x15, 0xad, 0x8f, 0xbf, 0x18, 0x74, 0x9b, 0x7b, 0x5c, 0xd4, 0x27,
0xa3, 0x74, 0x9e, 0x55, 0x92, 0x9e, 0xdc, 0x17, 0x2d, 0x6e, 0xab, 0xe8, 0xfc, 0xa8, 0x02, 0x1b,
0x4b, 0x8f, 0xc2, 0xd3, 0x8c, 0x13, 0x18, 0x24, 0x45, 0xa9, 0x76, 0x36, 0x51, 0x6b, 0xd7, 0x13,
0xa7, 0xfd, 0x0b, 0xce, 0xf9, 0x24, 0xf2, 0xbd, 0x96, 0xba, 0x90, 0xcd, 0xc3, 0x5b, 0xec, 0xca,
0x7c, 0x58, 0xad, 0x4a, 0x59, 0x55, 0xb2, 0xa2, 0x02, 0xfa, 0xe2, 0x4c, 0x8c, 0x3f, 0xa0, 0x77,
0xca, 0xef, 0x6e, 0x49, 0xb3, 0x6d, 0x2d, 0x93, 0xcc, 0x36, 0x1f, 0xa8, 0xc5, 0x38, 0x00, 0x36,
0xa7, 0xa8, 0x43, 0xc1, 0xe6, 0x18, 0xc1, 0xbf, 0xa3, 0xff, 0xa2, 0xce, 0x9f, 0xe5, 0xc1, 0xc5,
0xf2, 0x48, 0x70, 0x4d, 0x5f, 0xba, 0xf3, 0x2b, 0xf7, 0xdc, 0xa7, 0x5f, 0xe6, 0xee, 0x3b, 0x00,
0x00, 0xff, 0xff, 0x95, 0x9e, 0x00, 0x7a, 0x3e, 0x02, 0x00, 0x00,
}

View File

@ -11,7 +11,7 @@ syntax = "proto3";
repeated string Addresses = 6;
}
message VoutType {
double Value = 1;
bytes ValueSat = 1;
uint32 N = 2;
bytes ScriptPubKeyHex = 3;
repeated string Addresses = 4;

View File

@ -2,9 +2,11 @@ package bchain
import (
"context"
"encoding/hex"
"encoding/json"
"errors"
"fmt"
"math/big"
)
// errors with specific meaning returned by blockchain rpc
@ -42,17 +44,11 @@ type ScriptPubKey struct {
Addresses []string `json:"addresses"`
}
type Address interface {
String() string
AreEqual(addr string) bool
InSlice(addrs []string) bool
}
type Vout struct {
Value float64 `json:"value"`
ValueSat big.Int
JsonValue json.Number `json:"value"`
N uint32 `json:"n"`
ScriptPubKey ScriptPubKey `json:"scriptPubKey"`
Address Address
}
// Tx is blockchain transaction
@ -75,32 +71,57 @@ type Block struct {
Txs []Tx `json:"tx"`
}
type ThinBlock struct {
BlockHeader
Txids []string `json:"tx"`
}
// BlockHeader contains limited data (as needed for indexing) from backend block header
type BlockHeader struct {
Hash string `json:"hash"`
Prev string `json:"previousblockhash"`
Next string `json:"nextblockhash"`
Height uint32 `json:"height"`
Confirmations int `json:"confirmations"`
Size int `json:"size"`
Time int64 `json:"time,omitempty"`
}
// BlockInfo contains extended block header data and a list of block txids
type BlockInfo struct {
BlockHeader
Version json.Number `json:"version"`
MerkleRoot string `json:"merkleroot"`
Nonce json.Number `json:"nonce"`
Bits string `json:"bits"`
Difficulty json.Number `json:"difficulty"`
Txids []string `json:"tx,omitempty"`
}
type MempoolEntry struct {
Size uint32 `json:"size"`
Fee float64 `json:"fee"`
ModifiedFee float64 `json:"modifiedfee"`
Time float64 `json:"time"`
Height uint32 `json:"height"`
DescendantCount uint32 `json:"descendantcount"`
DescendantSize uint32 `json:"descendantsize"`
DescendantFees uint32 `json:"descendantfees"`
AncestorCount uint32 `json:"ancestorcount"`
AncestorSize uint32 `json:"ancestorsize"`
AncestorFees uint32 `json:"ancestorfees"`
Depends []string `json:"depends"`
Size uint32 `json:"size"`
FeeSat big.Int
Fee json.Number `json:"fee"`
ModifiedFeeSat big.Int
ModifiedFee json.Number `json:"modifiedfee"`
Time uint64 `json:"time"`
Height uint32 `json:"height"`
DescendantCount uint32 `json:"descendantcount"`
DescendantSize uint32 `json:"descendantsize"`
DescendantFees uint32 `json:"descendantfees"`
AncestorCount uint32 `json:"ancestorcount"`
AncestorSize uint32 `json:"ancestorsize"`
AncestorFees uint32 `json:"ancestorfees"`
Depends []string `json:"depends"`
}
type ChainInfo struct {
Chain string `json:"chain"`
Blocks int `json:"blocks"`
Headers int `json:"headers"`
Bestblockhash string `json:"bestblockhash"`
Difficulty string `json:"difficulty"`
SizeOnDisk int64 `json:"size_on_disk"`
Version string `json:"version"`
Subversion string `json:"subversion"`
ProtocolVersion string `json:"protocolversion"`
Timeoffset float64 `json:"timeoffset"`
Warnings string `json:"warnings"`
}
type RPCError struct {
@ -112,6 +133,13 @@ func (e *RPCError) Error() string {
return fmt.Sprintf("%d: %s", e.Code, e.Message)
}
// AddressDescriptor is an opaque type obtained by parser.GetAddrDesc* methods
type AddressDescriptor []byte
func (ad AddressDescriptor) String() string {
return "ad:" + hex.EncodeToString(ad)
}
// OnNewBlockFunc is used to send notification about a new block
type OnNewBlockFunc func(hash string, height uint32)
@ -128,22 +156,24 @@ type BlockChain interface {
GetNetworkName() string
GetSubversion() string
GetCoinName() string
GetChainInfo() (*ChainInfo, error)
// requests
GetBlockChainInfo() (string, error)
GetBestBlockHash() (string, error)
GetBestBlockHeight() (uint32, error)
GetBlockHash(height uint32) (string, error)
GetBlockHeader(hash string) (*BlockHeader, error)
GetBlock(hash string, height uint32) (*Block, error)
GetBlockInfo(hash string) (*BlockInfo, error)
GetMempool() ([]string, error)
GetTransaction(txid string) (*Tx, error)
GetTransactionForMempool(txid string) (*Tx, error)
EstimateSmartFee(blocks int, conservative bool) (float64, error)
EstimateFee(blocks int) (float64, error)
EstimateSmartFee(blocks int, conservative bool) (big.Int, error)
EstimateFee(blocks int) (big.Int, error)
SendRawTransaction(tx string) (string, error)
// mempool
ResyncMempool(onNewTxAddr OnNewTxAddrFunc) (int, error)
GetMempoolTransactions(address string) ([]string, error)
GetMempoolTransactionsForAddrDesc(addrDesc AddressDescriptor) ([]string, error)
GetMempoolEntry(txid string) (*MempoolEntry, error)
// parser
GetChainParser() BlockChainParser
@ -151,7 +181,7 @@ type BlockChain interface {
// BlockChainParser defines common interface to parsing and conversions of block chain data
type BlockChainParser interface {
// self description
// chain configuration description
// UTXO chains need "inputs" column in db, that map transactions to transactions that spend them
// non UTXO chains have mapping of address to input and output transactions directly in "outputs" column in db
IsUTXOChain() bool
@ -159,12 +189,16 @@ type BlockChainParser interface {
// and used in case of fork
// if 0 the blockaddresses column is not used at all (usually non UTXO chains)
KeepBlockAddresses() int
// address id conversions
GetAddrIDFromVout(output *Vout) ([]byte, error)
GetAddrIDFromAddress(address string) ([]byte, error)
// address to output script conversions
AddressToOutputScript(address string) ([]byte, error)
OutputScriptToAddresses(script []byte) ([]string, error)
// AmountToDecimalString converts amount in big.Int to string with decimal point in the correct place
AmountToDecimalString(a *big.Int) string
// AmountToBigInt converts amount in json.Number (string) to big.Int
// it uses string operations to avoid problems with rounding
AmountToBigInt(n json.Number) (big.Int, error)
// address descriptor conversions
GetAddrDescFromVout(output *Vout) (AddressDescriptor, error)
GetAddrDescFromAddress(address string) (AddressDescriptor, error)
GetAddressesFromAddrDesc(addrDesc AddressDescriptor) ([]string, bool, error)
GetScriptFromAddrDesc(addrDesc AddressDescriptor) ([]byte, error)
// transactions
PackedTxidLen() int
PackTxid(txid string) ([]byte, error)

View File

@ -1,10 +1,18 @@
package main
import (
"blockbook/api"
"blockbook/bchain"
"blockbook/bchain/coins"
"blockbook/common"
"blockbook/db"
"blockbook/server"
"context"
"flag"
"log"
"math/rand"
"net/http"
_ "net/http/pprof"
"os"
"os/signal"
"strings"
@ -12,18 +20,9 @@ import (
"syscall"
"time"
"github.com/juju/errors"
"blockbook/bchain"
"blockbook/bchain/coins"
"blockbook/common"
"blockbook/db"
"blockbook/server"
// "github.com/erikdubbelboer/gspt"
"github.com/golang/glog"
"net/http"
_ "net/http/pprof"
"github.com/juju/errors"
)
// debounce too close requests for resync
@ -38,7 +37,9 @@ const storeInternalStatePeriodMs = 59699
var (
blockchain = flag.String("blockchaincfg", "", "path to blockchain RPC service configuration json file")
dbPath = flag.String("datadir", "./data", "path to database directory")
dbPath = flag.String("datadir", "./data", "path to database directory")
dbCache = flag.Int("dbcache", 1<<29, "size of the rocksdb cache")
dbMaxOpenFiles = flag.Int("dbmaxopenfiles", 1<<14, "max open files by rocksdb")
blockFrom = flag.Int("blockheight", -1, "height of the starting block")
blockUntil = flag.Int("blockuntil", -1, "height of the final block")
@ -50,15 +51,17 @@ var (
repair = flag.Bool("repair", false, "repair the database")
prof = flag.String("prof", "", "http server binding [address]:port of the interface to profiling data /debug/pprof/ (default no profiling)")
syncChunk = flag.Int("chunk", 100, "block chunk size for processing")
syncWorkers = flag.Int("workers", 8, "number of workers to process blocks")
syncChunk = flag.Int("chunk", 100, "block chunk size for processing in bulk mode")
syncWorkers = flag.Int("workers", 8, "number of workers to process blocks in bulk mode")
dryRun = flag.Bool("dryrun", false, "do not index blocks, only download")
debugMode = flag.Bool("debug", false, "debug mode, return more verbose errors, reload templates on each request")
internalBinding = flag.String("internal", "", "internal http server binding [address]:port, (default no internal server)")
publicBinding = flag.String("public", "", "public http server binding [address]:port[/path], (default no public server)")
publicBinding = flag.String("public", "", "public http server binding [address]:port[/path] (default no public server)")
certFiles = flag.String("certfile", "", "to enable SSL specify path to certificate files without extension, expecting <certfile>.crt and <certfile>.key, (default no SSL)")
certFiles = flag.String("certfile", "", "to enable SSL specify path to certificate files without extension, expecting <certfile>.crt and <certfile>.key (default no SSL)")
explorerURL = flag.String("explorer", "", "address of blockchain explorer")
@ -83,6 +86,7 @@ var (
chain bchain.BlockChain
index *db.RocksDB
txCache *db.TxCache
metrics *common.Metrics
syncWorker *db.SyncWorker
internalState *common.InternalState
callbacksOnNewBlock []bchain.OnNewBlockFunc
@ -129,7 +133,7 @@ func main() {
chanOsSignal = make(chan os.Signal, 1)
signal.Notify(chanOsSignal, syscall.SIGHUP, syscall.SIGINT, syscall.SIGQUIT, syscall.SIGTERM)
glog.Infof("Blockbook: %+v", common.GetVersionInfo())
glog.Infof("Blockbook: %+v, debug mode %v", common.GetVersionInfo(), *debugMode)
if *prof != "" {
go func() {
@ -153,7 +157,9 @@ func main() {
glog.Fatal("config: ", err)
}
metrics, err := common.GetMetrics(coin)
// gspt.SetProcTitle("blockbook-" + normalizeName(coin))
metrics, err = common.GetMetrics(coin)
if err != nil {
glog.Fatal("metrics: ", err)
}
@ -162,7 +168,7 @@ func main() {
glog.Fatal("rpc: ", err)
}
index, err = db.NewRocksDB(*dbPath, chain.GetChainParser(), metrics)
index, err = db.NewRocksDB(*dbPath, *dbCache, *dbMaxOpenFiles, chain.GetChainParser(), metrics)
if err != nil {
glog.Fatal("rocksDB: ", err)
}
@ -175,7 +181,11 @@ func main() {
}
index.SetInternalState(internalState)
if internalState.DbState != common.DbStateClosed {
glog.Warning("internalState: database in not closed state ", internalState.DbState, ", possibly previous ungraceful shutdown")
if internalState.DbState == common.DbStateInconsistent {
glog.Error("internalState: database is in inconsistent state and cannot be used")
return
}
glog.Warning("internalState: database was left in open state, possibly previous ungraceful shutdown")
}
if *computeColumnStats {
@ -232,6 +242,11 @@ func main() {
return
}
// report BlockbookAppInfo metric, only log possible error
if err = blockbookAppInfoMetric(index, chain, txCache, internalState, metrics); err != nil {
glog.Error("blockbookAppInfoMetric ", err)
}
var internalServer *server.InternalServer
if *internalBinding != "" {
internalServer, err = server.NewInternalServer(*internalBinding, *certFiles, index, chain, txCache, internalState)
@ -252,20 +267,10 @@ func main() {
}()
}
if *synchronize {
if err := syncWorker.ResyncIndex(nil); err != nil {
glog.Error("resyncIndex ", err)
return
}
if _, err = chain.ResyncMempool(nil); err != nil {
glog.Error("resyncMempool ", err)
return
}
}
var publicServer *server.PublicServer
if *publicBinding != "" {
publicServer, err = server.NewPublicServer(*publicBinding, *certFiles, index, chain, txCache, *explorerURL, metrics, internalState)
// start public server in limited functionality, extend it after sync is finished by calling ConnectFullPublicInterface
publicServer, err = server.NewPublicServer(*publicBinding, *certFiles, index, chain, txCache, *explorerURL, metrics, internalState, *debugMode)
if err != nil {
glog.Error("socketio: ", err)
return
@ -286,10 +291,27 @@ func main() {
}
if *synchronize {
// start the synchronization loops after the server interfaces are started
internalState.SyncMode = true
internalState.InitialSync = true
if err := syncWorker.ResyncIndex(nil); err != nil {
glog.Error("resyncIndex ", err)
return
}
var mempoolCount int
if mempoolCount, err = chain.ResyncMempool(nil); err != nil {
glog.Error("resyncMempool ", err)
return
}
internalState.FinishedMempoolSync(mempoolCount)
go syncIndexLoop()
go syncMempoolLoop()
go storeInternalStateLoop()
internalState.InitialSync = false
}
go storeInternalStateLoop()
if *publicBinding != "" {
// start full public interface
publicServer.ConnectFullPublicInterface()
}
if *blockFrom >= 0 {
@ -327,6 +349,25 @@ func main() {
}
}
func blockbookAppInfoMetric(db *db.RocksDB, chain bchain.BlockChain, txCache *db.TxCache, is *common.InternalState, metrics *common.Metrics) error {
api, err := api.NewWorker(db, chain, txCache, is)
if err != nil {
return err
}
si, err := api.GetSystemInfo(false)
if err != nil {
return err
}
metrics.BlockbookAppInfo.Reset()
metrics.BlockbookAppInfo.With(common.Labels{
"blockbook_version": si.Blockbook.Version,
"blockbook_commit": si.Blockbook.GitCommit,
"backend_version": si.Backend.Version,
"backend_subversion": si.Backend.Subversion,
"backend_protocol_version": si.Backend.ProtocolVersion}).Set(float64(0))
return nil
}
func newInternalState(coin string, coinShortcut string, d *db.RocksDB) (*common.InternalState, error) {
is, err := d.LoadInternalState(coin)
if err != nil {
@ -424,6 +465,8 @@ func storeInternalStateLoop() {
lastCompute := time.Now()
// randomize the duration between ComputeInternalStateColumnStats to avoid peaks after reboot of machine with multiple blockbooks
computePeriod := 9*time.Hour + time.Duration(rand.Float64()*float64((2*time.Hour).Nanoseconds()))
lastAppInfo := time.Now()
logAppInfoPeriod := 15 * time.Minute
glog.Info("storeInternalStateLoop starting with db stats recompute period ", computePeriod)
tickAndDebounce(storeInternalStatePeriodMs*time.Millisecond, (storeInternalStatePeriodMs-1)*time.Millisecond, chanStoreInternalState, func() {
if !computeRunning && lastCompute.Add(computePeriod).Before(time.Now()) {
@ -440,6 +483,13 @@ func storeInternalStateLoop() {
if err := index.StoreInternalState(internalState); err != nil {
glog.Error("storeInternalStateLoop ", errors.ErrorStack(err))
}
if lastAppInfo.Add(logAppInfoPeriod).Before(time.Now()) {
glog.Info(index.GetMemoryStats())
if err := blockbookAppInfoMetric(index, chain, txCache, internalState, metrics); err != nil {
glog.Error("blockbookAppInfoMetric ", err)
}
lastAppInfo = time.Now()
}
})
glog.Info("storeInternalStateLoop stopped")
}
@ -495,3 +545,9 @@ func printResult(txid string, vout uint32, isOutput bool) error {
glog.Info(txid, vout, isOutput)
return nil
}
func normalizeName(s string) string {
s = strings.ToLower(s)
s = strings.Replace(s, " ", "-", -1)
return s
}

View File

@ -10,6 +10,7 @@ RUN apt-get update && \
apt-get clean
ENV GOLANG_VERSION=go1.10.linux-amd64
ENV ROCKSDB_VERSION=rocksdb-5.14.3
ENV GOPATH=/go
ENV PATH=$PATH:$GOPATH/bin
ENV CGO_CFLAGS="-I/opt/rocksdb/include"
@ -26,7 +27,7 @@ RUN echo -n "GO version: " && go version
RUN echo -n "GOPATH: " && echo $GOPATH
# install rocksdb
RUN cd /opt && git clone https://github.com/facebook/rocksdb.git
RUN cd /opt && git clone -b $ROCKSDB_VERSION --depth 1 https://github.com/facebook/rocksdb.git
RUN cd /opt/rocksdb && CFLAGS=-fPIC CXXFLAGS=-fPIC make -j 4 release
RUN strip /opt/rocksdb/ldb /opt/rocksdb/sst_dump && \
cp /opt/rocksdb/ldb /opt/rocksdb/sst_dump /build

View File

@ -4,17 +4,20 @@ VERSION ?= devel
GITCOMMIT = $(shell cd /src && git describe --tags --always --dirty)
BUILDTIME = $(shell date --iso-8601=seconds)
LDFLAGS := -X blockbook/common.version=$(VERSION) -X blockbook/common.gitcommit=$(GITCOMMIT) -X blockbook/common.buildtime=$(BUILDTIME)
BLOCKBOOK_SRC := $(GOPATH)/src/blockbook
ARGS ?=
export BLOCKBOOK_SRC
all: build tools
build: prepare-sources
cd $(GOPATH)/src/blockbook && go build -o $(CURDIR)/blockbook -ldflags="-s -w $(LDFLAGS)" $(ARGS)
cd $(BLOCKBOOK_SRC) && go build -o $(CURDIR)/blockbook -ldflags="-s -w $(LDFLAGS)" $(ARGS)
cp $(CURDIR)/blockbook /out/blockbook
chown $(PACKAGER) /out/blockbook
build-debug: prepare-sources
cd $(GOPATH)/src/blockbook && go build -o $(CURDIR)/blockbook -ldflags="$(LDFLAGS)" $(ARGS)
cd $(BLOCKBOOK_SRC) && go build -o $(CURDIR)/blockbook -ldflags="$(LDFLAGS)" $(ARGS)
cp $(CURDIR)/blockbook /out/blockbook
chown $(PACKAGER) /out/blockbook
@ -23,20 +26,20 @@ tools:
chown $(PACKAGER) /out/{ldb,sst_dump}
test: prepare-sources
cd $(GOPATH)/src/blockbook && go test -tags unittest `go list ./... | grep -v '^blockbook/contrib'` $(ARGS)
cd $(BLOCKBOOK_SRC) && go test -tags unittest `go list ./... | grep -v '^blockbook/contrib'` $(ARGS)
test-all: prepare-sources
cd $(GOPATH)/src/blockbook && go test -tags 'unittest integration' `go list ./... | grep -v '^blockbook/contrib'` $(ARGS)
cd $(BLOCKBOOK_SRC) && go test -tags 'unittest integration' `go list ./... | grep -v '^blockbook/contrib'` $(ARGS)
prepare-sources:
@ [ -n "`ls /src 2> /dev/null`" ] || (echo "/src doesn't exist or is empty" 1>&2 && exit 1)
[ -d $(GOPATH)/src/blockbook ] || cp -r /src $(GOPATH)/src/blockbook
[ -d $(BLOCKBOOK_SRC) ] || cp -r /src $(BLOCKBOOK_SRC)
$(MAKE) prepare-vendor
prepare-vendor:
@ if [ "$(UPDATE_VENDOR)" -eq 1 ]; then \
echo "Updating vendor"; \
rm -rf $(GOPATH)/src/blockbook/vendor && cd $(GOPATH)/src/blockbook && dep ensure -vendor-only ; \
rm -rf $(BLOCKBOOK_SRC)/vendor && cd $(BLOCKBOOK_SRC) && dep ensure -vendor-only ; \
else \
echo "Update of vendor not demanded, keeping version from src" ; \
fi

View File

@ -0,0 +1,98 @@
-----BEGIN PGP PUBLIC KEY BLOCK-----
mQINBFpZENYBEACgAfjxYOe3IG0/IUmrpTExiWFvBsVAI2qDn5F11vuimbr1YMGX
PqcmFGWrYZM/zEQCO6xNZEIQ9rh79N15hXfLc9dlLx8PjuxPHNKnPDprpdsYpa37
Dw0ufHpv+av2qqwG7o9zUan2a+QqsWXjCWhdJVNwYOFY9uRBKqkYMf+r0VOFGPIX
Pjb0n5TXdpI0FFvydeFb+HABnYqbrPcEAQQETpVukQZQtmE8UBDueO+Hz0RvJAw+
xz79ks4muyPf4MGoKOu6xWM2mViXeP1AN+Mi3nV3EUVPKLxS2EyByrgWEh8iIdgj
vu2fRSaHSuhJWbVSkMnAPJsC3bQE/aZZ8PtaLvgVUH/0euVoKHmxIg8bj1RzNvYb
VZ2VL8Sp8xHIptkTwhgj+spi88BDI1xkBXaHt0ZlddyhgMtK7dl75mn6d+UsGQsT
4dovbtRWRFU1hww7utgtgJniwOYMzhLdY6+OZ8CSA/uB1bTGRigbSd3UtTEwHnQi
xEkHzuF4IoIbaIRLluCASs331vwk+cCZoXD8hdNQdVOwI/4Ss16RAdMFa+DGbBKb
OUpnHBx9wmGH/nq1rfBZD1r/sE7X3LhDeQfqHrRGZOFTAF3CBsiZr7SDAJMCPys6
7y4KQtC+Kfyku+iDNsnObbCXc86qIrYEOspwMqrZgT3FmA0H+cX9EtoE8QARAQAB
tEhTaGFtbWFoIENoYW5jZWxsb3IgKEJpdGNvaW4gQUJDIGtleSkgPHNoYW1tYWgu
Y2hhbmNlbGxvckBiaXRjb2luYWJjLm9yZz6JAhwEEAEIAAYFAlsWzFAACgkQjVUt
ITLQxfZMCBAAmWotQ7ow3WIEdnBYUxZMMdGxU0WRz13lN+4YnwxAeKoBcpNwFhSz
0wGZAAVzm/hH2aLNMY/Xqt4vq2vuHC1ovOrRIL7Wk0msT/enlyPmK1j+r2zNz1Qk
92ybAfuZ2r/8zO6M1NXOrhFEOmBn6lPLQu79P6T0wGfQue3O0XPx53+WYKmNoyq7
DQRphmBbRJikdz5TP1ffEUZ+AL901Q/b3gJ4fLpowaJ44C4J+cH++OoLY2lQFPVR
v2JyVa8SeUFfB19ki0m0r9gYEm7RDHlwcLPKwuM45armV6JTE+PW31XZqsE3Xw5h
vGGOT9bY+KoMfgplYJYr9XH1qNgUBYJZ91BAs0vuqieIoMsMHl5h+ii/MDz6eJ9y
5mwR1BKuhS0oIgvoviVU/GhEpo0GQqhsaOejnNl1/kYFpHzosFSf+9EmLWHgicKO
zwumIwsi+AjSI97pg8K8YaxkaA/uagHkutQ1oFgt/tAd6eOGnyNDCBBRSbYVuFJV
YoiRG8IaIHZkUGmrHqDeZd2Qk5YwIG7tLAX9v0cgMlDLFLx1B8LQSY9wH06zjV1a
mY69q3RzQ3mvKCf2QidB5o1FA/CV9Sn0nTvN0UKyYIXNr/KeoyCZkBuge+hD5au/
OBrtCjGTzq6496hJbwQR83O6S3r4t4KBz2isQuoksrdxwR0cHg65hBqJAhwEEAEI
AAYFAls6O2kACgkQ5EhjVueoHSwGlA/6ApGOSYwQlppEgmwrN2BZLEmfN8pfn4ES
ymYoFKn5/4R2A11Xp5w2z9EE9JW7/yLnoar0jR/Hw16TPceiVoyRD1r5MCEeMDmj
TymDJF4p5ZeiWqkFq1CAvEdCsNB952O2F5PmqGnIm0SW7H8h49Hy1c1Cpa+d0xgg
eQkoEqtihxWabMJ7UW1r6T77dEDoPyAcP2Yi8Yv/e0Eaayv58gxB7ieGi+NsAXLL
LiGkRXhP+FHpBS9yLnxqLEPcDfivMaL3CGocJziA9GlZgSQGUjBkaVoI+Ynd9JTw
C30XususiGthXrqF5e56PIVubOhrxqAlKoxahx/kedRfWPlD/MApNxaokgmDqBpi
E2YLPzaY/RKek/AC5UAgw97yiDactt+UICcHbtG/tznQ6ECO8llI7+DSAWAKCNi1
iCPZmdjGqv+j/S5xR3X3EdB0LoXpqE6HJE2E1+mUN/QCsz/6u/zmw1Zj5x9niPOp
g5PiIPDTLrhropMKMmvxdfKfKWOXD+I9b/tKldWi7Dz8MPlvXpkEqeA8HKRK8Ek7
bq9iLdqGFTP/0T+nU77baOmMMSHsoimIIZRysg04n8WR6A4AAOACX/BoB6agxLn8
1N//xMTqUA0Aror9dLNXl0iCbUshnFkx1wiq93rdZAn6nCI0wlFtg2yhh/dWo1sZ
caGNIgGdxn2JAhwEEAEIAAYFAls7ja0ACgkQXXkiu9ZJxKdf1RAArXapCE/zxWpd
f9cNOClRBLXZlj7FiW7Mf63DgdO0ae4gK4sL812bSwEH4KeuQxkugYsOcH6oezWh
mXxJ8lHOeCFbW+a6VBx7kclUweEOpFtcfrJU1bgLywGM6FtWNn1m1mAMKCrqDgPQ
U2L6UnhY+H3QgRqqYo1nzRsfpqnAxw+7nsquD4ziQS/Mumbj2C0Gjg5ScjkfAidk
okp5jdoIEbanjKQ8zuSRWvomGeT6UF1AmPA3bNtN3ROD9co4H8fKeFKamrCzVZul
+eu5/Y+cCvQ5PEHZoflZYwa3qdTgQJhIlzBmGIipYstHVPfyeFKN9JYXv01FNvDT
h1ebvDzgvaBB/lln3CxWHSEkv7jTu902ljUO9iuPdd0tX89FN1FlLCqeXXoT9Qfi
NOaRJzNDkqdHl53VrhuNH5mZWXXAuD5hkZo1I5A90YuOLOQ5wnl/8MbDS4VW8tpK
HWqn5b0GFacB2xsSphYpletkQnwQWFy352NBveDf68E9rGA9WS5B7cVvnSiNZWNT
5hzE5krS6VZjSIo062PVySTWAqgCy1OWdEPZXo71BpKHV3AgBTAaoPj6EWDFaqS3
4/5s4odJBUgtceEg4naXFp5GdrrixzInLwjNu1FdpU3NM1thelWzErYi8k8OMVKy
QroQJ+ObZ3PIKBaLgAyOTJq6TZqxa/2JAjcEEwEIACsFAlpZENYJEH05WMREJ2dK
AhsDBQkHhM4ABQsJCAcCBhUKCQgLAgQWAgMBAABVdw/+Jc4K7/8rcWawTG7u2jQG
C3un44dHrVwFxft1IQWbc1ZYx7xvTFtAyOwatwL+PnHjJlMK6ODztJnNHS95CqPS
9dkUztrlQF+j4gmc6/7h0Ew3M56N9tVOXP/3Un+kPgGLdWvVYf0YZsy0r8BO7vQb
PXk2coKiWwfony55DDt9RaxLNlWukKq3dg+vejZzzucfKuZIlUcQDRJO4rCXxMBv
Oo/BtgfOXACjzi4O8aF2TNzEKyXLp8KqcB8j2knu2PIJidr8fwzyBwe3yF8tox3x
4nvSn5IYvvvP4EG8fhsGVBEc+EwZJnbrk5afRBnM/uYW+e+/7vBQwlZyvFOr/zMJ
rhbtcWwW8VkFhvbwc40K7qsiJOxfzXMfGVPtutbzhz8sRivija03LrE0ysgesoM4
LpxsHDhwScwTTupZ8qsvoNDqgyuJvEUeNXKv1fdY9IVtKgJuU5JHcLOyhUB8/xkd
2qle6fKQK1k776HhdmseU6Ba3cbe3zZYJfYGnoHXttfOZ5QhKNlFhyCdmv7CImP3
KFYKfYcbx+CVhGJpm5YEYk3WW2FyuBx80WrEbztGcvqZzPQmNPAhvRkVErNoxA/A
KCuLTNrz0jivgBny6JKGdY8drXRafovBroJcLGGFfTVjgZouUis1VlZX4Bo4Twcq
Pc5AK4OXCa/WK1IjXTSblDWJAlQEEwEIAD4WIQR6VaRPOjI5gnyKWU59OVjERCdn
SgUCWlkQ1gIbAwUJB4TOAAULCQgHAgYVCgkICwIEFgIDAQIeAQIXgAAKCRB9OVjE
RCdnSmNWD/47fhdIy2gdHKl+BqjUezcmBs5eR5V8/pz6kbT1+cDFHZ+ImcK+jf8I
8oQPeFWecBF1ZERoLaIRD6UwIVs6z7yAGYlCeweitWf7cfgvIvzP9b/sLweAfwD8
fNfKTQuw3wx6Yr66+y42lM5dasu+WyzBYLQhQI6q+WgsRL5j7IuNFTdtAc/gNY5c
GlEPXMl9RCjJlCeyo9UNvMeL61UrkPUMVOOqH4Bl8yatPilu0vyGtYogp+XTbmEk
ZJ1BWhL4Y8VkVdBhX9LhYZ+klI6UjNxnZEnVwTwVhZCMXBgi6t4X8EJqeVa5/gb5
LQ7L261z3y3wzyKZ9+wTjjihXaHX+5n7b1oTZF08+1cY9qe1tDIWfsDY5HjoJb15
MSqy5EdTJsH+h2JlzMD3t57pTCmGXPmA8mdec78q5DA2PlCdc0qewOTDJ8d56J/x
4PrSZ8TZDRIKA3fflRfWwKrAmB+xwR4cvAXbNC1PxC4BvlVOFKBseDHv7VwWJvnH
0Gnc6hdHdzJJYtTQBglOH6CifHBnksc6/pnFedQ8yWRKXFY3rsaqpzkiiqCPl9jG
QCPWAEvehT6v4iFFiQkZ9OTjBVmT9Ea6BvhpCplpgPo+AFKS3L1mMZYf8RIHazdo
fQof6w/oMxmwQWT/k3O0Z4JRNW0p2eyB8RS3tK3YT2zY3y4TSyYqY7kCDQRaWRDW
ARAA0zg0VamqvOmzSg+viNRFwPmetdPNmiKq7feDail0r8vtfAIKuXcjR6EF0FEB
w9eWlzbX0Eu6c/MVPRycj9Ka+7KqZbEAlOOC97gzA2tcRar8aEM1ZXDBzyr3ZbbK
aH6/XPYm+oxnYxwlMY99/LpiTcOkQyc4yJCXSC8GV9frQhVOGPI7gPca7DjroGdb
as/uTiyPNp4xq0JTI/xWYLRtH+dTEfH2/vTpoXku0itdfR4uoCPFzOYCegfObIOX
3UsUvkoNKWBoQGc14YarTMAjagDDUBhnhJ84b/ftOWTvyYyq/QR4oJdGUkTp854Z
4r2J8cWzW2tpuALxUrHs4AcTmW3cD37B7j3lhzpfA3NGLYwDBQfQkFMwtRIxskf3
EG7Ednq5fXJWCekIYSji8bDaGu/A5exuj5H0Mg3DKUcVQ+p2bRh6fjKZwG6gEQWU
rxu6RsjFYg6KuLUb0q0Y0Vt/vvLASkY4FoIxjj0zGR+KBlg8yJsU44HdiI40LRPy
2juYgGAhL2j53Yrr+ID0ZFbe9TpQePcBfD5lEWxVH9aBjjfEz1TUYguNQXo3mVrO
OWZek6f9Uh5Yt+WSPSrZfzbRsROcv3OOC83lSYcL2ugiFjt9oGXZBYhfYNLCKDpW
10nGVe0oVSjIA53QIdhQ233WPvwi6ur5Fa8j5OvaIWnwpecAEQEAAYkCPAQYAQgA
JhYhBHpVpE86MjmCfIpZTn05WMREJ2dKBQJaWRDWAhsMBQkHhM4AAAoJEH05WMRE
J2dK5VMP/2IIPi707oF3/iflM3JvQukjmGNLpm3sVrx0baSODW2IfSNQeMWYXvoa
xgaKrMG+WGzr1P/D1jv+HD8gxjakTvZYooJLHF/9nmZE1bD/p0BGFpoRuvX9L/J9
Yqu32S89lYqnvyXY3c6Aqt/sJj1SHCPdFWMT/kcYjGHeBaX2Ub5+zVm4THESwssv
GsT37N6Wzz0P2wI1rSRu29evKbRTebRIZXaSWiVlx4P7Y6CNFNptzkH7oqf6JmkB
qaQZuOZ3dhe6Z9HetOFN9D5X0ju3DgUITEmF6BYekGBEv/HVJs6rbTb9us8sV4Vu
WvmTYCCIn/fOnmFD6WomjVeX1E6DJ9YvRtCPtau0JcxxBy1L3/3IS2fEf3zpTK6O
aX2GnpijmLFweDGD7CocaEmfw3xUvwgDEpIIvaMNnlciujZFd+8MMJ/JeQ+l5xuw
yWcUciwnWIoCQBOTl3u0u/mWeQsHQr81pirknugjJ8OCPyMUbtsNm9aAySFj6lra
O18EF0uVo+oYDulM+2InSjEJQLYj392XOG4sWkO0bPWJ6FG8VFC2MH09bnsU7dyH
mN6ZWn6JZI3uPY3UbRsXTnVws4OG4bBCpuCe0mJ35gWGAqj0ZDm8SA5Utx4j2oEl
h0HChP/lLDxJ5t5PfBVyudK+xewFFx/cX+4f/n7GZp2+wvduWrs7
=Grfn
-----END PGP PUBLIC KEY BLOCK-----

View File

@ -7,6 +7,7 @@ After=network.target
ExecStart={{template "Backend.ExecCommandTemplate" .}}
User={{.Backend.SystemUser}}
Restart=on-failure
TimeoutStopSec=300
WorkingDirectory={{.Env.BackendInstallPath}}/{{.Coin.Alias}}
{{if eq .Backend.ServiceType "forking" -}}
Type=forking

View File

@ -9,6 +9,7 @@ ExecStart={{.Env.BlockbookInstallPath}}/{{.Coin.Alias}}/bin/blockbook -blockchai
User={{.Blockbook.SystemUser}}
Type=simple
Restart=on-failure
TimeoutStopSec=300
WorkingDirectory={{.Env.BlockbookInstallPath}}/{{.Coin.Alias}}
# Resource limits

View File

@ -1,126 +1,19 @@
package main
import (
"encoding/json"
"blockbook/build/tools"
"fmt"
"io"
"os"
"path/filepath"
"strings"
"text/template"
"time"
)
const (
configsDir = "configs"
inputDir = "build/templates"
outputDir = "build/pkg-defs"
configsDir = "configs"
templateDir = "build/templates"
outputDir = "build/pkg-defs"
)
type Config struct {
Meta struct {
BuildDatetime string // generated field
PackageMaintainer string `json:"package_maintainer"`
PackageMaintainerEmail string `json:"package_maintainer_email"`
}
Env struct {
Version string `json:"version"`
BackendInstallPath string `json:"backend_install_path"`
BackendDataPath string `json:"backend_data_path"`
BlockbookInstallPath string `json:"blockbook_install_path"`
BlockbookDataPath string `json:"blockbook_data_path"`
} `json:"env"`
Coin struct {
Name string `json:"name"`
Shortcut string `json:"shortcut"`
Label string `json:"label"`
Alias string `json:"alias"`
} `json:"coin"`
Ports struct {
BackendRPC int `json:"backend_rpc"`
BackendMessageQueue int `json:"backend_message_queue"`
BlockbookInternal int `json:"blockbook_internal"`
BlockbookPublic int `json:"blockbook_public"`
} `json:"ports"`
IPC struct {
RPCURLTemplate string `json:"rpc_url_template"`
RPCUser string `json:"rpc_user"`
RPCPass string `json:"rpc_pass"`
RPCTimeout int `json:"rpc_timeout"`
MessageQueueBindingTemplate string `json:"message_queue_binding_template"`
} `json:"ipc"`
Backend struct {
PackageName string `json:"package_name"`
PackageRevision string `json:"package_revision"`
SystemUser string `json:"system_user"`
Version string `json:"version"`
BinaryURL string `json:"binary_url"`
VerificationType string `json:"verification_type"`
VerificationSource string `json:"verification_source"`
ExtractCommand string `json:"extract_command"`
ExcludeFiles []string `json:"exclude_files"`
ExecCommandTemplate string `json:"exec_command_template"`
LogrotateFilesTemplate string `json:"logrotate_files_template"`
PostinstScriptTemplate string `json:"postinst_script_template"`
ServiceType string `json:"service_type"`
ServiceAdditionalParamsTemplate string `json:"service_additional_params_template"`
ProtectMemory bool `json:"protect_memory"`
Mainnet bool `json:"mainnet"`
ConfigFile string `json:"config_file"`
AdditionalParams interface{} `json:"additional_params"`
} `json:"backend"`
Blockbook struct {
PackageName string `json:"package_name"`
SystemUser string `json:"system_user"`
InternalBindingTemplate string `json:"internal_binding_template"`
PublicBindingTemplate string `json:"public_binding_template"`
ExplorerURL string `json:"explorer_url"`
AdditionalParams string `json:"additional_params"`
BlockChain struct {
Parse bool `json:"parse"`
Subversion string `json:"subversion"`
AddressFormat string `json:"address_format"`
MempoolWorkers int `json:"mempool_workers"`
MempoolSubWorkers int `json:"mempool_sub_workers"`
BlockAddressesToKeep int `json:"block_addresses_to_keep"`
AdditionalParams map[string]json.RawMessage `json:"additional_params"`
} `json:"block_chain"`
} `json:"blockbook"`
}
func jsonToString(msg json.RawMessage) (string, error) {
d, err := msg.MarshalJSON()
if err != nil {
return "", err
}
return string(d), nil
}
func (c *Config) ParseTemplate() *template.Template {
templates := map[string]string{
"IPC.RPCURLTemplate": c.IPC.RPCURLTemplate,
"IPC.MessageQueueBindingTemplate": c.IPC.MessageQueueBindingTemplate,
"Backend.ExecCommandTemplate": c.Backend.ExecCommandTemplate,
"Backend.LogrotateFilesTemplate": c.Backend.LogrotateFilesTemplate,
"Backend.PostinstScriptTemplate": c.Backend.PostinstScriptTemplate,
"Backend.ServiceAdditionalParamsTemplate": c.Backend.ServiceAdditionalParamsTemplate,
"Blockbook.InternalBindingTemplate": c.Blockbook.InternalBindingTemplate,
"Blockbook.PublicBindingTemplate": c.Blockbook.PublicBindingTemplate,
}
funcMap := template.FuncMap{
"jsonToString": jsonToString,
}
t := template.New("").Funcs(funcMap)
for name, def := range templates {
t = template.Must(t.Parse(fmt.Sprintf(`{{define "%s"}}%s{{end}}`, name, def)))
}
return t
}
func main() {
if len(os.Args) < 2 {
var coins []string
@ -136,171 +29,12 @@ func main() {
}
coin := os.Args[1]
config := loadConfig(coin)
generatePackageDefinitions(config)
config, err := build.LoadConfig(configsDir, coin)
if err == nil {
err = build.GeneratePackageDefinitions(config, templateDir, outputDir)
}
if err != nil {
panic(err)
}
fmt.Printf("Package files for %v generated to %v\n", coin, outputDir)
}
func loadConfig(coin string) *Config {
config := new(Config)
f, err := os.Open(filepath.Join(configsDir, "coins", coin+".json"))
if err != nil {
panic(err)
}
d := json.NewDecoder(f)
err = d.Decode(config)
if err != nil {
panic(err)
}
f, err = os.Open(filepath.Join(configsDir, "environ.json"))
if err != nil {
panic(err)
}
d = json.NewDecoder(f)
err = d.Decode(&config.Env)
if err != nil {
panic(err)
}
config.Meta.BuildDatetime = time.Now().Format("Mon, 02 Jan 2006 15:04:05 -0700")
if !isEmpty(config, "backend") {
switch config.Backend.ServiceType {
case "forking":
case "simple":
default:
panic("Invalid service type: " + config.Backend.ServiceType)
}
switch config.Backend.VerificationType {
case "":
case "gpg":
case "sha256":
case "gpg-sha256":
default:
panic("Invalid verification type: " + config.Backend.VerificationType)
}
}
return config
}
func isEmpty(config *Config, target string) bool {
switch target {
case "backend":
return config.Backend.PackageName == ""
case "blockbook":
return config.Blockbook.PackageName == ""
default:
panic("Invalid target name: " + target)
}
}
func generatePackageDefinitions(config *Config) {
templ := config.ParseTemplate()
makeOutputDir(outputDir)
for _, subdir := range []string{"backend", "blockbook"} {
if isEmpty(config, subdir) {
continue
}
root := filepath.Join(inputDir, subdir)
err := os.Mkdir(filepath.Join(outputDir, subdir), 0755)
if err != nil {
panic(err)
}
err = filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
if err != nil {
return fmt.Errorf("%s: %s", path, err)
}
if path == root {
return nil
}
if filepath.Base(path)[0] == '.' {
return nil
}
subpath := path[len(root)-len(subdir):]
if info.IsDir() {
err = os.Mkdir(filepath.Join(outputDir, subpath), info.Mode())
if err != nil {
return fmt.Errorf("%s: %s", path, err)
}
return nil
}
t := template.Must(templ.Clone())
t = template.Must(t.ParseFiles(path))
err = writeTemplate(filepath.Join(outputDir, subpath), info, t, config)
if err != nil {
return fmt.Errorf("%s: %s", path, err)
}
return nil
})
if err != nil {
panic(err)
}
}
if !isEmpty(config, "backend") {
writeBackendConfigFile(config)
}
}
func makeOutputDir(path string) {
err := os.RemoveAll(path)
if err == nil {
err = os.Mkdir(path, 0755)
}
if err != nil {
panic(err)
}
}
func writeTemplate(path string, info os.FileInfo, templ *template.Template, config *Config) error {
f, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY, info.Mode())
if err != nil {
return err
}
defer f.Close()
err = templ.ExecuteTemplate(f, "main", config)
if err != nil {
return err
}
return nil
}
func writeBackendConfigFile(config *Config) {
out, err := os.OpenFile(filepath.Join(outputDir, "backend/backend.conf"), os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
panic(err)
}
defer out.Close()
if config.Backend.ConfigFile == "" {
return
} else {
in, err := os.Open(filepath.Join(outputDir, "backend/config", config.Backend.ConfigFile))
if err != nil {
panic(err)
}
defer in.Close()
_, err = io.Copy(out, in)
if err != nil {
panic(err)
}
}
}

View File

@ -0,0 +1,288 @@
package build
import (
"encoding/json"
"fmt"
"io"
"os"
"path/filepath"
"text/template"
"time"
)
type Config struct {
Meta struct {
BuildDatetime string // generated field
PackageMaintainer string `json:"package_maintainer"`
PackageMaintainerEmail string `json:"package_maintainer_email"`
}
Env struct {
Version string `json:"version"`
BackendInstallPath string `json:"backend_install_path"`
BackendDataPath string `json:"backend_data_path"`
BlockbookInstallPath string `json:"blockbook_install_path"`
BlockbookDataPath string `json:"blockbook_data_path"`
} `json:"env"`
Coin struct {
Name string `json:"name"`
Shortcut string `json:"shortcut"`
Label string `json:"label"`
Alias string `json:"alias"`
} `json:"coin"`
Ports struct {
BackendRPC int `json:"backend_rpc"`
BackendMessageQueue int `json:"backend_message_queue"`
BlockbookInternal int `json:"blockbook_internal"`
BlockbookPublic int `json:"blockbook_public"`
} `json:"ports"`
IPC struct {
RPCURLTemplate string `json:"rpc_url_template"`
RPCUser string `json:"rpc_user"`
RPCPass string `json:"rpc_pass"`
RPCTimeout int `json:"rpc_timeout"`
MessageQueueBindingTemplate string `json:"message_queue_binding_template"`
} `json:"ipc"`
Backend struct {
PackageName string `json:"package_name"`
PackageRevision string `json:"package_revision"`
SystemUser string `json:"system_user"`
Version string `json:"version"`
BinaryURL string `json:"binary_url"`
VerificationType string `json:"verification_type"`
VerificationSource string `json:"verification_source"`
ExtractCommand string `json:"extract_command"`
ExcludeFiles []string `json:"exclude_files"`
ExecCommandTemplate string `json:"exec_command_template"`
LogrotateFilesTemplate string `json:"logrotate_files_template"`
PostinstScriptTemplate string `json:"postinst_script_template"`
ServiceType string `json:"service_type"`
ServiceAdditionalParamsTemplate string `json:"service_additional_params_template"`
ProtectMemory bool `json:"protect_memory"`
Mainnet bool `json:"mainnet"`
ConfigFile string `json:"config_file"`
AdditionalParams interface{} `json:"additional_params"`
} `json:"backend"`
Blockbook struct {
PackageName string `json:"package_name"`
SystemUser string `json:"system_user"`
InternalBindingTemplate string `json:"internal_binding_template"`
PublicBindingTemplate string `json:"public_binding_template"`
ExplorerURL string `json:"explorer_url"`
AdditionalParams string `json:"additional_params"`
BlockChain struct {
Parse bool `json:"parse"`
Subversion string `json:"subversion"`
AddressFormat string `json:"address_format"`
MempoolWorkers int `json:"mempool_workers"`
MempoolSubWorkers int `json:"mempool_sub_workers"`
BlockAddressesToKeep int `json:"block_addresses_to_keep"`
AdditionalParams map[string]json.RawMessage `json:"additional_params"`
} `json:"block_chain"`
} `json:"blockbook"`
IntegrationTests map[string][]string `json:"integration_tests"`
}
func jsonToString(msg json.RawMessage) (string, error) {
d, err := msg.MarshalJSON()
if err != nil {
return "", err
}
return string(d), nil
}
func (c *Config) ParseTemplate() *template.Template {
templates := map[string]string{
"IPC.RPCURLTemplate": c.IPC.RPCURLTemplate,
"IPC.MessageQueueBindingTemplate": c.IPC.MessageQueueBindingTemplate,
"Backend.ExecCommandTemplate": c.Backend.ExecCommandTemplate,
"Backend.LogrotateFilesTemplate": c.Backend.LogrotateFilesTemplate,
"Backend.PostinstScriptTemplate": c.Backend.PostinstScriptTemplate,
"Backend.ServiceAdditionalParamsTemplate": c.Backend.ServiceAdditionalParamsTemplate,
"Blockbook.InternalBindingTemplate": c.Blockbook.InternalBindingTemplate,
"Blockbook.PublicBindingTemplate": c.Blockbook.PublicBindingTemplate,
}
funcMap := template.FuncMap{
"jsonToString": jsonToString,
}
t := template.New("").Funcs(funcMap)
for name, def := range templates {
t = template.Must(t.Parse(fmt.Sprintf(`{{define "%s"}}%s{{end}}`, name, def)))
}
return t
}
func LoadConfig(configsDir, coin string) (*Config, error) {
config := new(Config)
f, err := os.Open(filepath.Join(configsDir, "coins", coin+".json"))
if err != nil {
return nil, err
}
d := json.NewDecoder(f)
err = d.Decode(config)
if err != nil {
return nil, err
}
f, err = os.Open(filepath.Join(configsDir, "environ.json"))
if err != nil {
return nil, err
}
d = json.NewDecoder(f)
err = d.Decode(&config.Env)
if err != nil {
return nil, err
}
config.Meta.BuildDatetime = time.Now().Format("Mon, 02 Jan 2006 15:04:05 -0700")
if !isEmpty(config, "backend") {
switch config.Backend.ServiceType {
case "forking":
case "simple":
default:
return nil, fmt.Errorf("Invalid service type: %s", config.Backend.ServiceType)
}
switch config.Backend.VerificationType {
case "":
case "gpg":
case "sha256":
case "gpg-sha256":
default:
return nil, fmt.Errorf("Invalid verification type: %s", config.Backend.VerificationType)
}
}
return config, nil
}
func isEmpty(config *Config, target string) bool {
switch target {
case "backend":
return config.Backend.PackageName == ""
case "blockbook":
return config.Blockbook.PackageName == ""
default:
panic("Invalid target name: " + target)
}
}
func GeneratePackageDefinitions(config *Config, templateDir, outputDir string) error {
templ := config.ParseTemplate()
err := makeOutputDir(outputDir)
if err != nil {
return err
}
for _, subdir := range []string{"backend", "blockbook"} {
if isEmpty(config, subdir) {
continue
}
root := filepath.Join(templateDir, subdir)
err = os.Mkdir(filepath.Join(outputDir, subdir), 0755)
if err != nil {
return err
}
err = filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
if err != nil {
return fmt.Errorf("%s: %s", path, err)
}
if path == root {
return nil
}
if filepath.Base(path)[0] == '.' {
return nil
}
subpath := path[len(root)-len(subdir):]
if info.IsDir() {
err = os.Mkdir(filepath.Join(outputDir, subpath), info.Mode())
if err != nil {
return fmt.Errorf("%s: %s", path, err)
}
return nil
}
t := template.Must(templ.Clone())
t = template.Must(t.ParseFiles(path))
err = writeTemplate(filepath.Join(outputDir, subpath), info, t, config)
if err != nil {
return fmt.Errorf("%s: %s", path, err)
}
return nil
})
if err != nil {
return err
}
}
if !isEmpty(config, "backend") {
err = writeBackendConfigFile(config, outputDir)
if err != nil {
return err
}
}
return nil
}
func makeOutputDir(path string) error {
err := os.RemoveAll(path)
if err == nil {
err = os.Mkdir(path, 0755)
}
return err
}
func writeTemplate(path string, info os.FileInfo, templ *template.Template, config *Config) error {
f, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY, info.Mode())
if err != nil {
return err
}
defer f.Close()
err = templ.ExecuteTemplate(f, "main", config)
if err != nil {
return err
}
return nil
}
func writeBackendConfigFile(config *Config, outputDir string) error {
out, err := os.OpenFile(filepath.Join(outputDir, "backend/backend.conf"), os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
return err
}
defer out.Close()
if config.Backend.ConfigFile == "" {
return nil
} else {
in, err := os.Open(filepath.Join(outputDir, "backend/config", config.Backend.ConfigFile))
if err != nil {
return err
}
defer in.Close()
_, err = io.Copy(out, in)
if err != nil {
return err
}
}
return nil
}

View File

@ -11,6 +11,8 @@ const (
DbStateClosed = uint32(iota)
// DbStateOpen means db is open or application died without closing the db
DbStateOpen
// DbStateInconsistent means db is in inconsistent state and cannot be used
DbStateInconsistent
)
// InternalStateColumn contains the data of a db column
@ -35,6 +37,10 @@ type InternalState struct {
LastStore time.Time `json:"lastStore"`
// true if application is with flag --sync
SyncMode bool `json:"syncMode"`
InitialSync bool `json:"initialSync"`
IsSynchronized bool `json:"isSynchronized"`
BestHeight uint32 `json:"bestHeight"`
LastSync time.Time `json:"lastSync"`
@ -62,6 +68,14 @@ func (is *InternalState) FinishedSync(bestHeight uint32) {
is.LastSync = time.Now()
}
// UpdateBestHeight sets new best height, without changing IsSynchronized flag
func (is *InternalState) UpdateBestHeight(bestHeight uint32) {
is.mux.Lock()
defer is.mux.Unlock()
is.BestHeight = bestHeight
is.LastSync = time.Now()
}
// FinishedSyncNoChange marks end of synchronization in case no index update was necessary, it does not update lastSync time
func (is *InternalState) FinishedSyncNoChange() {
is.mux.Lock()

View File

@ -21,6 +21,7 @@ type Metrics struct {
MempoolSize prometheus.Gauge
DbColumnRows *prometheus.GaugeVec
DbColumnSize *prometheus.GaugeVec
BlockbookAppInfo *prometheus.GaugeVec
}
type Labels = prometheus.Labels
@ -139,6 +140,14 @@ func GetMetrics(coin string) (*Metrics, error) {
},
[]string{"column"},
)
metrics.BlockbookAppInfo = prometheus.NewGaugeVec(
prometheus.GaugeOpts{
Name: "blockbook_app_info",
Help: "Information about blockbook and backend application versions",
ConstLabels: Labels{"coin": coin},
},
[]string{"blockbook_version", "blockbook_commit", "backend_version", "backend_subversion", "backend_protocol_version"},
)
v := reflect.ValueOf(metrics)
for i := 0; i < v.NumField(); i++ {

View File

@ -22,10 +22,10 @@
"package_name": "backend-bcash",
"package_revision": "satoshilabs-1",
"system_user": "bcash",
"version": "0.17.1",
"binary_url": "https://download.bitcoinabc.org/0.17.1/linux/bitcoin-abc-0.17.1-x86_64-linux-gnu.tar.gz",
"version": "0.18.2",
"binary_url": "https://download.bitcoinabc.org/0.18.2/linux/bitcoin-abc-0.18.2-x86_64-linux-gnu.tar.gz",
"verification_type": "sha256",
"verification_source": "eccf8b61ba0549f6839e586c7dc6fc4bf6d7591ac432aaea8a7df0266b113d27",
"verification_source": "28d8511789a126aff16e256a03288948f2660c3c8cb0a4c809c5a8618a519a16",
"extract_command": "tar -C backend --strip 1 -xf",
"exclude_files": [
"bin/bitcoin-qt"
@ -44,12 +44,12 @@
"system_user": "blockbook-bcash",
"internal_binding_template": ":{{.Ports.BlockbookInternal}}",
"public_binding_template": ":{{.Ports.BlockbookPublic}}",
"explorer_url": "https://bitcoincash.blockexplorer.com",
"explorer_url": "",
"additional_params": "",
"block_chain": {
"parse": true,
"subversion": "/Bitcoin ABC:0.17.1/",
"address_format": "legacy",
"address_format": "cashaddr",
"mempool_workers": 8,
"mempool_sub_workers": 2,
"block_addresses_to_keep": 300,
@ -59,5 +59,9 @@
"meta": {
"package_maintainer": "Jakub Matys",
"package_maintainer_email": "jakub.matys@satoshilabs.com"
},
"integration_tests": {
"rpc": ["GetBlock", "GetBlockHash", "GetTransaction", "GetTransactionForMempool", "MempoolSync", "EstimateSmartFee",
"EstimateFee", "GetBestBlockHash", "GetBestBlockHeight", "GetBlockHeader"]
}
}

View File

@ -22,10 +22,10 @@
"package_name": "backend-bcash-testnet",
"package_revision": "satoshilabs-1",
"system_user": "bcash",
"version": "0.17.1",
"binary_url": "https://download.bitcoinabc.org/0.17.1/linux/bitcoin-abc-0.17.1-x86_64-linux-gnu.tar.gz",
"version": "0.18.2",
"binary_url": "https://download.bitcoinabc.org/0.18.2/linux/bitcoin-abc-0.18.2-x86_64-linux-gnu.tar.gz",
"verification_type": "sha256",
"verification_source": "eccf8b61ba0549f6839e586c7dc6fc4bf6d7591ac432aaea8a7df0266b113d27",
"verification_source": "28d8511789a126aff16e256a03288948f2660c3c8cb0a4c809c5a8618a519a16",
"extract_command": "tar -C backend --strip 1 -xf",
"exclude_files": [
"bin/bitcoin-qt"
@ -44,12 +44,12 @@
"system_user": "blockbook-bcash",
"internal_binding_template": ":{{.Ports.BlockbookInternal}}",
"public_binding_template": ":{{.Ports.BlockbookPublic}}",
"explorer_url": "https://bitcoincash.blockexplorer.com",
"explorer_url": "",
"additional_params": "",
"block_chain": {
"parse": true,
"subversion": "/Bitcoin ABC:0.17.1/",
"address_format": "legacy",
"address_format": "cashaddr",
"mempool_workers": 8,
"mempool_sub_workers": 2,
"block_addresses_to_keep": 300,
@ -59,5 +59,9 @@
"meta": {
"package_maintainer": "Jakub Matys",
"package_maintainer_email": "jakub.matys@satoshilabs.com"
},
"integration_tests": {
"rpc": ["GetBlock", "GetBlockHash", "GetTransaction", "GetTransactionForMempool", "MempoolSync", "EstimateSmartFee",
"EstimateFee", "GetBestBlockHash", "GetBestBlockHeight", "GetBlockHeader"]
}
}

View File

@ -22,10 +22,10 @@
"package_name": "backend-bgold",
"package_revision": "satoshilabs-1",
"system_user": "bgold",
"version": "0.15.1",
"binary_url": "https://github.com/BTCGPU/BTCGPU/releases/download/v0.15.1/bitcoin-gold-0.15.1-x86_64-linux-gnu.tar.gz",
"version": "0.15.2",
"binary_url": "https://github.com/BTCGPU/BTCGPU/releases/download/v0.15.2/bitcoin-gold-0.15.2-x86_64-linux-gnu.tar.gz",
"verification_type": "gpg-sha256",
"verification_source": "https://github.com/BTCGPU/BTCGPU/releases/download/v0.15.1/SHA256SUMS.asc",
"verification_source": "https://github.com/BTCGPU/BTCGPU/releases/download/v0.15.2/SHA256SUMS.asc",
"extract_command": "tar -C backend --strip 1 -xf",
"exclude_files": [
"bin/bitcoin-qt"
@ -239,11 +239,11 @@
"system_user": "blockbook-bgold",
"internal_binding_template": ":{{.Ports.BlockbookInternal}}",
"public_binding_template": ":{{.Ports.BlockbookPublic}}",
"explorer_url": "https://btg-explorer.trezor.io/",
"explorer_url": "",
"additional_params": "",
"block_chain": {
"parse": true,
"subversion": "/Bitcoin Gold:0.15.1/",
"subversion": "/Bitcoin Gold:0.15.2/",
"mempool_workers": 8,
"mempool_sub_workers": 2,
"block_addresses_to_keep": 300,

View File

@ -22,10 +22,10 @@
"package_name": "backend-bitcoin",
"package_revision": "satoshilabs-1",
"system_user": "bitcoin",
"version": "0.16.1",
"binary_url": "https://bitcoin.org/bin/bitcoin-core-0.16.1/bitcoin-0.16.1-x86_64-linux-gnu.tar.gz",
"version": "0.16.3",
"binary_url": "https://bitcoin.org/bin/bitcoin-core-0.16.3/bitcoin-0.16.3-x86_64-linux-gnu.tar.gz",
"verification_type": "gpg-sha256",
"verification_source": "https://bitcoin.org/bin/bitcoin-core-0.16.1/SHA256SUMS.asc",
"verification_source": "https://bitcoin.org/bin/bitcoin-core-0.16.3/SHA256SUMS.asc",
"extract_command": "tar -C backend --strip 1 -xf",
"exclude_files": [
"bin/bitcoin-qt"
@ -47,8 +47,8 @@
"system_user": "blockbook-bitcoin",
"internal_binding_template": ":{{.Ports.BlockbookInternal}}",
"public_binding_template": ":{{.Ports.BlockbookPublic}}",
"explorer_url": "https://btc-explorer.trezor.io/",
"additional_params": "",
"explorer_url": "",
"additional_params": "-dbcache=1073741824",
"block_chain": {
"parse": true,
"mempool_workers": 8,
@ -60,5 +60,9 @@
"meta": {
"package_maintainer": "Jakub Matys",
"package_maintainer_email": "jakub.matys@satoshilabs.com"
},
"integration_tests": {
"rpc": ["GetBlock", "GetBlockHash", "GetTransaction", "GetTransactionForMempool", "MempoolSync", "EstimateSmartFee",
"EstimateFee", "GetBestBlockHash", "GetBestBlockHeight", "GetBlockHeader"]
}
}

View File

@ -22,10 +22,10 @@
"package_name": "backend-bitcoin-testnet",
"package_revision": "satoshilabs-1",
"system_user": "bitcoin",
"version": "0.16.1",
"binary_url": "https://bitcoin.org/bin/bitcoin-core-0.16.1/bitcoin-0.16.1-x86_64-linux-gnu.tar.gz",
"version": "0.16.3",
"binary_url": "https://bitcoin.org/bin/bitcoin-core-0.16.3/bitcoin-0.16.3-x86_64-linux-gnu.tar.gz",
"verification_type": "gpg-sha256",
"verification_source": "https://bitcoin.org/bin/bitcoin-core-0.16.1/SHA256SUMS.asc",
"verification_source": "https://bitcoin.org/bin/bitcoin-core-0.16.3/SHA256SUMS.asc",
"extract_command": "tar -C backend --strip 1 -xf",
"exclude_files": [
"bin/bitcoin-qt"
@ -47,7 +47,7 @@
"system_user": "blockbook-bitcoin",
"internal_binding_template": ":{{.Ports.BlockbookInternal}}",
"public_binding_template": ":{{.Ports.BlockbookPublic}}",
"explorer_url": "https://btc-testnet-explorer.trezor.io/",
"explorer_url": "",
"additional_params": "",
"block_chain": {
"parse": true,
@ -60,5 +60,9 @@
"meta": {
"package_maintainer": "Jakub Matys",
"package_maintainer_email": "jakub.matys@satoshilabs.com"
},
"integration_tests": {
"rpc": ["GetBlock", "GetBlockHash", "GetTransaction", "GetTransactionForMempool", "MempoolSync", "EstimateSmartFee",
"EstimateFee", "GetBestBlockHash", "GetBestBlockHeight", "GetBlockHeader"]
}
}

View File

@ -22,10 +22,10 @@
"package_name": "backend-dash",
"package_revision": "satoshilabs-1",
"system_user": "dash",
"version": "0.12.3",
"binary_url": "https://github.com/dashpay/dash/releases/download/v0.12.3.2/dashcore-0.12.3.2-x86_64-linux-gnu.tar.gz",
"version": "0.12.3.3",
"binary_url": "https://github.com/dashpay/dash/releases/download/v0.12.3.3/dashcore-0.12.3.3-x86_64-linux-gnu.tar.gz",
"verification_type": "gpg-sha256",
"verification_source": "https://github.com/dashpay/dash/releases/download/v0.12.3.2/SHA256SUMS.asc",
"verification_source": "https://github.com/dashpay/dash/releases/download/v0.12.3.3/SHA256SUMS.asc",
"extract_command": "tar -C backend --strip 1 -xf",
"exclude_files": [
"bin/dash-qt"
@ -47,11 +47,11 @@
"system_user": "blockbook-dash",
"internal_binding_template": ":{{.Ports.BlockbookInternal}}",
"public_binding_template": ":{{.Ports.BlockbookPublic}}",
"explorer_url": "https://dash-explorer.trezor.io",
"explorer_url": "",
"additional_params": "",
"block_chain": {
"parse": true,
"subversion": "/Dash Core:0.12.3.2/",
"subversion": "/Dash Core:0.12.3.3/",
"mempool_workers": 8,
"mempool_sub_workers": 2,
"block_addresses_to_keep": 300,
@ -61,5 +61,9 @@
"meta": {
"package_maintainer": "Jakub Matys",
"package_maintainer_email": "jakub.matys@satoshilabs.com"
},
"integration_tests": {
"rpc": ["GetBlock", "GetBlockHash", "GetTransaction", "GetTransactionForMempool", "MempoolSync", "EstimateSmartFee",
"EstimateFee", "GetBestBlockHash", "GetBestBlockHeight", "GetBlockHeader"]
}
}

View File

@ -22,10 +22,10 @@
"package_name": "backend-dash-testnet",
"package_revision": "satoshilabs-1",
"system_user": "dash",
"version": "0.12.3",
"binary_url": "https://github.com/dashpay/dash/releases/download/v0.12.3.2/dashcore-0.12.3.2-x86_64-linux-gnu.tar.gz",
"version": "0.12.3.3",
"binary_url": "https://github.com/dashpay/dash/releases/download/v0.12.3.3/dashcore-0.12.3.3-x86_64-linux-gnu.tar.gz",
"verification_type": "gpg-sha256",
"verification_source": "https://github.com/dashpay/dash/releases/download/v0.12.3.2/SHA256SUMS.asc",
"verification_source": "https://github.com/dashpay/dash/releases/download/v0.12.3.3/SHA256SUMS.asc",
"extract_command": "tar -C backend --strip 1 -xf",
"exclude_files": [
"bin/dash-qt"
@ -47,11 +47,11 @@
"system_user": "blockbook-dash",
"internal_binding_template": ":{{.Ports.BlockbookInternal}}",
"public_binding_template": ":{{.Ports.BlockbookPublic}}",
"explorer_url": "https://dash-explorer.trezor.io",
"explorer_url": "",
"additional_params": "",
"block_chain": {
"parse": true,
"subversion": "/Dash Core:0.12.3.2/",
"subversion": "/Dash Core:0.12.3.3/",
"mempool_workers": 8,
"mempool_sub_workers": 2,
"block_addresses_to_keep": 300,
@ -61,5 +61,9 @@
"meta": {
"package_maintainer": "Jakub Matys",
"package_maintainer_email": "jakub.matys@satoshilabs.com"
},
"integration_tests": {
"rpc": ["GetBlock", "GetBlockHash", "GetTransaction", "GetTransactionForMempool", "MempoolSync", "EstimateSmartFee",
"EstimateFee", "GetBestBlockHash", "GetBestBlockHeight", "GetBlockHeader"]
}
}

View File

@ -63,5 +63,8 @@
"meta": {
"package_maintainer": "Jakub Matys",
"package_maintainer_email": "jakub.matys@satoshilabs.com"
},
"integration_tests": {
"rpc": ["GetBlock", "GetBlockHash", "GetTransaction", "GetTransactionForMempool", "MempoolSync"]
}
}

View File

@ -53,5 +53,8 @@
"meta": {
"package_maintainer": "Jakub Matys",
"package_maintainer_email": "jakub.matys@satoshilabs.com"
},
"integration_tests": {
"rpc": ["GetBlock", "GetBlockHash", "GetTransaction", "EstimateFee", "GetBestBlockHash", "GetBestBlockHeight", "GetBlockHeader"]
}
}

View File

@ -22,10 +22,10 @@
"package_name": "backend-litecoin",
"package_revision": "satoshilabs-1",
"system_user": "litecoin",
"version": "0.16.0",
"binary_url": "https://download.litecoin.org/litecoin-0.16.0/linux/litecoin-0.16.0-x86_64-linux-gnu.tar.gz",
"version": "0.16.3",
"binary_url": "https://download.litecoin.org/litecoin-0.16.3/linux/litecoin-0.16.3-x86_64-linux-gnu.tar.gz",
"verification_type": "gpg-sha256",
"verification_source": "https://download.litecoin.org/litecoin-0.16.0/linux/litecoin-0.16.0-linux-signatures.asc",
"verification_source": "https://download.litecoin.org/litecoin-0.16.3/linux/litecoin-0.16.3-linux-signatures.asc",
"extract_command": "tar -C backend --strip 1 -xf",
"exclude_files": [
"bin/litecoin-qt"
@ -47,7 +47,7 @@
"system_user": "blockbook-litecoin",
"internal_binding_template": ":{{.Ports.BlockbookInternal}}",
"public_binding_template": ":{{.Ports.BlockbookPublic}}",
"explorer_url": "https://ltc-explorer.trezor.io/",
"explorer_url": "",
"additional_params": "",
"block_chain": {
"parse": true,
@ -60,5 +60,9 @@
"meta": {
"package_maintainer": "Jakub Matys",
"package_maintainer_email": "jakub.matys@satoshilabs.com"
},
"integration_tests": {
"rpc": ["GetBlock", "GetBlockHash", "GetTransaction", "GetTransactionForMempool", "MempoolSync", "EstimateSmartFee",
"EstimateFee"]
}
}

View File

@ -22,10 +22,10 @@
"package_name": "backend-litecoin-testnet",
"package_revision": "satoshilabs-1",
"system_user": "litecoin",
"version": "0.16.0",
"binary_url": "https://download.litecoin.org/litecoin-0.16.0/linux/litecoin-0.16.0-x86_64-linux-gnu.tar.gz",
"version": "0.16.3",
"binary_url": "https://download.litecoin.org/litecoin-0.16.3/linux/litecoin-0.16.3-x86_64-linux-gnu.tar.gz",
"verification_type": "gpg-sha256",
"verification_source": "https://download.litecoin.org/litecoin-0.16.0/linux/litecoin-0.16.0-linux-signatures.asc",
"verification_source": "https://download.litecoin.org/litecoin-0.16.3/linux/litecoin-0.16.3-linux-signatures.asc",
"extract_command": "tar -C backend --strip 1 -xf",
"exclude_files": [
"bin/litecoin-qt"
@ -47,7 +47,7 @@
"system_user": "blockbook-litecoin",
"internal_binding_template": ":{{.Ports.BlockbookInternal}}",
"public_binding_template": ":{{.Ports.BlockbookPublic}}",
"explorer_url": "https://ltc-explorer.trezor.io",
"explorer_url": "",
"additional_params": "",
"block_chain": {
"parse": true,

View File

@ -22,10 +22,10 @@
"package_name": "backend-monacoin",
"package_revision": "satoshilabs-1",
"system_user": "monacoin",
"version": "0.15.1",
"binary_url": "https://github.com/monacoinproject/monacoin/releases/download/monacoin-0.15.1/monacoin-0.15.1-x86_64-linux-gnu.tar.gz",
"version": "0.16.3",
"binary_url": "https://github.com/monacoinproject/monacoin/releases/download/monacoin-0.16.3/monacoin-0.16.3-x86_64-linux-gnu.tar.gz",
"verification_type": "gpg-sha256",
"verification_source": "https://github.com/monacoinproject/monacoin/releases/download/monacoin-0.15.1/monacoin-0.15.1-signatures.asc",
"verification_source": "https://github.com/monacoinproject/monacoin/releases/download/monacoin-0.16.3/monacoin-0.16.3-signatures.asc",
"extract_command": "tar -C backend --strip 1 -xf",
"exclude_files": [
"bin/monacoin-qt"
@ -60,5 +60,9 @@
"meta": {
"package_maintainer": "wakiyamap",
"package_maintainer_email": "wakiyamap@gmail.com"
},
"integration_tests": {
"rpc": ["GetBlock", "GetBlockHash", "GetTransaction", "GetTransactionForMempool", "MempoolSync", "EstimateSmartFee",
"EstimateFee"]
}
}

View File

@ -22,10 +22,10 @@
"package_name": "backend-monacoin-testnet",
"package_revision": "satoshilabs-1",
"system_user": "monacoin",
"version": "0.15.1",
"binary_url": "https://github.com/monacoinproject/monacoin/releases/download/monacoin-0.15.1/monacoin-0.15.1-x86_64-linux-gnu.tar.gz",
"version": "0.16.3",
"binary_url": "https://github.com/monacoinproject/monacoin/releases/download/monacoin-0.16.3/monacoin-0.16.3-x86_64-linux-gnu.tar.gz",
"verification_type": "gpg-sha256",
"verification_source": "https://github.com/monacoinproject/monacoin/releases/download/monacoin-0.15.1/monacoin-0.15.1-signatures.asc",
"verification_source": "https://github.com/monacoinproject/monacoin/releases/download/monacoin-0.16.3/monacoin-0.16.3-signatures.asc",
"extract_command": "tar -C backend --strip 1 -xf",
"exclude_files": [
"bin/monacoin-qt"

View File

@ -22,10 +22,10 @@
"package_name": "backend-namecoin",
"package_revision": "satoshilabs-1",
"system_user": "namecoin",
"version": "0.13.99",
"binary_url": "https://namecoin.org/files/namecoin-core-0.13.99-name-tab-beta1-notreproduced/namecoin-0.13.99-x86_64-linux-gnu.tar.gz",
"version": "0.16.3",
"binary_url": "https://www.namecoin.org/files/namecoin-core-0.16.3/namecoin-0.16.3-x86_64-linux-gnu.tar.gz",
"verification_type": "sha256",
"verification_source": "294b1106001d6ea2b9d9ee6a655021ef207a24e8f1dec8efd5899728b3849129",
"verification_source": "14ebaaf6f22f69b057a5bcb9b6959548f0a3f1b62cc113f19581d2297044827e",
"extract_command": "tar -C backend --strip 1 -xf",
"exclude_files": [
"bin/namecoin-qt"
@ -54,7 +54,7 @@
"system_user": "blockbook-namecoin",
"internal_binding_template": ":{{.Ports.BlockbookInternal}}",
"public_binding_template": ":{{.Ports.BlockbookPublic}}",
"explorer_url": "https://namecha.in/",
"explorer_url": "",
"additional_params": "",
"block_chain": {
"parse": true,
@ -67,5 +67,9 @@
"meta": {
"package_maintainer": "Jakub Matys",
"package_maintainer_email": "jakub.matys@satoshilabs.com"
},
"integration_tests": {
"rpc": ["GetBlock", "GetBlockHash", "GetTransaction", "MempoolSync", "EstimateSmartFee",
"EstimateFee", "GetBestBlockHash", "GetBestBlockHeight", "GetBlockHeader"]
}
}

View File

@ -22,10 +22,10 @@
"package_name": "backend-vertcoin",
"package_revision": "satoshilabs-1",
"system_user": "vertcoin",
"version": "0.13.2",
"binary_url": "https://github.com/vertcoin-project/vertcoin-core/releases/download/0.13.2/vertcoind-v0.13.2-linux-amd64.zip",
"version": "0.13.3",
"binary_url": "https://github.com/vertcoin-project/vertcoin-core/releases/download/0.13.3/vertcoind-v0.13.3-linux-amd64.zip",
"verification_type": "gpg",
"verification_source": "https://github.com/vertcoin-project/vertcoin-core/releases/download/0.13.2/vertcoind-v0.13.2-linux-amd64.zip.sig",
"verification_source": "https://github.com/vertcoin-project/vertcoin-core/releases/download/0.13.3/vertcoind-v0.13.3-linux-amd64.zip.sig",
"extract_command": "unzip -d backend",
"exclude_files": [],
"exec_command_template": "{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/vertcoind -datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend -conf={{.Env.BackendInstallPath}}/{{.Coin.Alias}}/{{.Coin.Alias}}.conf -pid=/run/{{.Coin.Alias}}/{{.Coin.Alias}}.pid",
@ -45,7 +45,7 @@
"system_user": "blockbook-vertcoin",
"internal_binding_template": ":{{.Ports.BlockbookInternal}}",
"public_binding_template": ":{{.Ports.BlockbookPublic}}",
"explorer_url": "https://insight.vertcoin.org",
"explorer_url": "",
"additional_params": "",
"block_chain": {
"parse": true,
@ -58,5 +58,9 @@
"meta": {
"package_maintainer": "Jakub Matys",
"package_maintainer_email": "jakub.matys@satoshilabs.com"
},
"integration_tests": {
"rpc": ["GetBlock", "GetBlockHash", "GetTransaction", "GetTransactionForMempool", "MempoolSync", "EstimateSmartFee",
"EstimateFee", "GetBestBlockHash", "GetBestBlockHeight", "GetBlockHeader"]
}
}

View File

@ -22,10 +22,10 @@
"package_name": "backend-vertcoin-testnet",
"package_revision": "satoshilabs-1",
"system_user": "vertcoin",
"version": "0.13.2",
"binary_url": "https://github.com/vertcoin-project/vertcoin-core/releases/download/0.13.2/vertcoind-v0.13.2-linux-amd64.zip",
"version": "0.13.3",
"binary_url": "https://github.com/vertcoin-project/vertcoin-core/releases/download/0.13.3/vertcoind-v0.13.3-linux-amd64.zip",
"verification_type": "gpg",
"verification_source": "https://github.com/vertcoin-project/vertcoin-core/releases/download/0.13.2/vertcoind-v0.13.2-linux-amd64.zip.sig",
"verification_source": "https://github.com/vertcoin-project/vertcoin-core/releases/download/0.13.3/vertcoind-v0.13.3-linux-amd64.zip.sig",
"extract_command": "unzip -d backend",
"exclude_files": [],
"exec_command_template": "{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/vertcoind -datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend -conf={{.Env.BackendInstallPath}}/{{.Coin.Alias}}/{{.Coin.Alias}}.conf -pid=/run/{{.Coin.Alias}}/{{.Coin.Alias}}.pid",
@ -45,7 +45,7 @@
"system_user": "blockbook-vertcoin",
"internal_binding_template": ":{{.Ports.BlockbookInternal}}",
"public_binding_template": ":{{.Ports.BlockbookPublic}}",
"explorer_url": "https://insight.vertcoin.org/",
"explorer_url": "",
"additional_params": "",
"block_chain": {
"parse": true,

View File

@ -22,10 +22,10 @@
"package_name": "backend-zcash",
"package_revision": "satoshilabs-1",
"system_user": "zcash",
"version": "1.1.1",
"binary_url": "https://z.cash/downloads/zcash-1.1.1-linux64.tar.gz",
"version": "2.0.0",
"binary_url": "https://z.cash/downloads/zcash-2.0.0-linux64.tar.gz",
"verification_type": "gpg",
"verification_source": "https://z.cash/downloads/zcash-1.1.1-linux64.tar.gz.asc",
"verification_source": "https://z.cash/downloads/zcash-2.0.0-linux64.tar.gz.asc",
"extract_command": "tar -C backend --strip 1 -xf",
"exclude_files": [],
"exec_command_template": "{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/bin/zcashd -datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend -conf={{.Env.BackendInstallPath}}/{{.Coin.Alias}}/{{.Coin.Alias}}.conf -pid=/run/{{.Coin.Alias}}/{{.Coin.Alias}}.pid",
@ -47,7 +47,7 @@
"system_user": "blockbook-zcash",
"internal_binding_template": ":{{.Ports.BlockbookInternal}}",
"public_binding_template": ":{{.Ports.BlockbookPublic}}",
"explorer_url": "https://zcash.blockexplorer.com/",
"explorer_url": "",
"additional_params": "",
"block_chain": {
"parse": true,
@ -60,5 +60,9 @@
"meta": {
"package_maintainer": "Jakub Matys",
"package_maintainer_email": "jakub.matys@satoshilabs.com"
},
"integration_tests": {
"rpc": ["GetBlock", "GetBlockHash", "GetTransaction", "GetTransactionForMempool", "MempoolSync", "EstimateSmartFee",
"EstimateFee", "GetBestBlockHash", "GetBestBlockHeight", "GetBlockHeader"]
}
}

View File

@ -22,10 +22,10 @@
"package_name": "backend-zcash-testnet",
"package_revision": "satoshilabs-1",
"system_user": "zcash",
"version": "1.1.1",
"binary_url": "https://z.cash/downloads/zcash-1.1.1-linux64.tar.gz",
"version": "2.0.0",
"binary_url": "https://z.cash/downloads/zcash-2.0.0-linux64.tar.gz",
"verification_type": "gpg",
"verification_source": "https://z.cash/downloads/zcash-1.1.1-linux64.tar.gz.asc",
"verification_source": "https://z.cash/downloads/zcash-2.0.0-linux64.tar.gz.asc",
"extract_command": "tar -C backend --strip 1 -xf",
"exclude_files": [],
"exec_command_template": "{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/bin/zcashd -datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend -conf={{.Env.BackendInstallPath}}/{{.Coin.Alias}}/{{.Coin.Alias}}.conf -pid=/run/{{.Coin.Alias}}/{{.Coin.Alias}}.pid",
@ -47,7 +47,7 @@
"system_user": "blockbook-zcash",
"internal_binding_template": ":{{.Ports.BlockbookInternal}}",
"public_binding_template": ":{{.Ports.BlockbookPublic}}",
"explorer_url": "https://explorer.testnet.z.cash/",
"explorer_url": "",
"additional_params": "",
"block_chain": {
"parse": true,
@ -60,5 +60,9 @@
"meta": {
"package_maintainer": "Jakub Matys",
"package_maintainer_email": "jakub.matys@satoshilabs.com"
},
"integration_tests": {
"rpc": ["GetBlock", "GetBlockHash", "GetTransaction", "GetTransactionForMempool", "MempoolSync", "EstimateSmartFee",
"EstimateFee", "GetBestBlockHash", "GetBestBlockHeight", "GetBlockHeader"]
}
}

Some files were not shown because too many files have changed in this diff Show More