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 explorerpull/68/head
commit
6c4204e93f
|
@ -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.
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
153
api/types.go
153
api/types.go
|
@ -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"`
|
||||
}
|
||||
|
|
539
api/worker.go
539
api/worker.go
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
}
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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,
|
||||
},
|
||||
},
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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)
|
||||
}
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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"]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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",
|
|
@ -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",
|
|
@ -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",
|
|
@ -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",
|
|
@ -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",
|
|
@ -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",
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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"]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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",
|
|
@ -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",
|
|
@ -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",
|
|
@ -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",
|
|
@ -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,
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
104
bchain/types.go
104
bchain/types.go
|
@ -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)
|
||||
|
|
124
blockbook.go
124
blockbook.go
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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-----
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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()
|
||||
|
|
|
@ -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++ {
|
||||
|
|
|
@ -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"]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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"]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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"]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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"]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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"]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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"]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -63,5 +63,8 @@
|
|||
"meta": {
|
||||
"package_maintainer": "Jakub Matys",
|
||||
"package_maintainer_email": "jakub.matys@satoshilabs.com"
|
||||
},
|
||||
"integration_tests": {
|
||||
"rpc": ["GetBlock", "GetBlockHash", "GetTransaction", "GetTransactionForMempool", "MempoolSync"]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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"]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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"]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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"]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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"]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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"]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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"]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
Loading…
Reference in New Issue