Merge branch 'master' into ethereum
This commit is contained in:
commit
2567ae69b0
@ -10,11 +10,12 @@ import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"reflect"
|
||||
"time"
|
||||
|
||||
"github.com/juju/errors"
|
||||
)
|
||||
|
||||
type blockChainFactory func(config json.RawMessage, pushHandler func(*bchain.MQMessage), metrics *common.Metrics) (bchain.BlockChain, error)
|
||||
type blockChainFactory func(config json.RawMessage, pushHandler func(*bchain.MQMessage)) (bchain.BlockChain, error)
|
||||
|
||||
var blockChainFactories = make(map[string]blockChainFactory)
|
||||
|
||||
@ -41,5 +42,107 @@ func NewBlockChain(coin string, configfile string, pushHandler func(*bchain.MQMe
|
||||
if err != nil {
|
||||
return nil, errors.Annotatef(err, "Error parsing file %v", configfile)
|
||||
}
|
||||
return bcf(config, pushHandler, metrics)
|
||||
bc, err := bcf(config, pushHandler)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
bc.Initialize()
|
||||
return &blockChainWithMetrics{b: bc, m: metrics}, nil
|
||||
}
|
||||
|
||||
type blockChainWithMetrics struct {
|
||||
b bchain.BlockChain
|
||||
m *common.Metrics
|
||||
}
|
||||
|
||||
func (c *blockChainWithMetrics) observeRPCLatency(method string, start time.Time, err error) {
|
||||
var e string
|
||||
if err != nil {
|
||||
e = err.Error()
|
||||
}
|
||||
c.m.RPCLatency.With(common.Labels{"method": method, "error": e}).Observe(float64(time.Since(start)) / 1e6) // in milliseconds
|
||||
}
|
||||
|
||||
func (c *blockChainWithMetrics) Initialize() error {
|
||||
return c.b.Initialize()
|
||||
}
|
||||
|
||||
func (c *blockChainWithMetrics) Shutdown() error {
|
||||
return c.b.Shutdown()
|
||||
}
|
||||
|
||||
func (c *blockChainWithMetrics) IsTestnet() bool {
|
||||
return c.b.IsTestnet()
|
||||
}
|
||||
|
||||
func (c *blockChainWithMetrics) GetNetworkName() string {
|
||||
return c.b.GetNetworkName()
|
||||
}
|
||||
|
||||
func (c *blockChainWithMetrics) GetBestBlockHash() (v string, err error) {
|
||||
defer func(s time.Time) { c.observeRPCLatency("GetBestBlockHash", s, err) }(time.Now())
|
||||
return c.b.GetBestBlockHash()
|
||||
}
|
||||
|
||||
func (c *blockChainWithMetrics) GetBestBlockHeight() (v uint32, err error) {
|
||||
defer func(s time.Time) { c.observeRPCLatency("GetBestBlockHeight", s, err) }(time.Now())
|
||||
return c.b.GetBestBlockHeight()
|
||||
}
|
||||
|
||||
func (c *blockChainWithMetrics) GetBlockHash(height uint32) (v string, err error) {
|
||||
defer func(s time.Time) { c.observeRPCLatency("GetBlockHash", s, err) }(time.Now())
|
||||
return c.b.GetBlockHash(height)
|
||||
}
|
||||
|
||||
func (c *blockChainWithMetrics) GetBlockHeader(hash string) (v *bchain.BlockHeader, err error) {
|
||||
defer func(s time.Time) { c.observeRPCLatency("GetBlockHeader", s, err) }(time.Now())
|
||||
return c.b.GetBlockHeader(hash)
|
||||
}
|
||||
|
||||
func (c *blockChainWithMetrics) GetBlock(hash string, height uint32) (v *bchain.Block, err error) {
|
||||
defer func(s time.Time) { c.observeRPCLatency("GetBlock", s, err) }(time.Now())
|
||||
return c.b.GetBlock(hash, height)
|
||||
}
|
||||
|
||||
func (c *blockChainWithMetrics) GetMempool() (v []string, err error) {
|
||||
defer func(s time.Time) { c.observeRPCLatency("GetMempool", s, err) }(time.Now())
|
||||
return c.b.GetMempool()
|
||||
}
|
||||
|
||||
func (c *blockChainWithMetrics) GetTransaction(txid string) (v *bchain.Tx, err error) {
|
||||
defer func(s time.Time) { c.observeRPCLatency("GetTransaction", s, err) }(time.Now())
|
||||
return c.b.GetTransaction(txid)
|
||||
}
|
||||
|
||||
func (c *blockChainWithMetrics) EstimateSmartFee(blocks int, conservative bool) (v float64, err error) {
|
||||
defer func(s time.Time) { c.observeRPCLatency("EstimateSmartFee", s, err) }(time.Now())
|
||||
return c.b.EstimateSmartFee(blocks, conservative)
|
||||
}
|
||||
|
||||
func (c *blockChainWithMetrics) SendRawTransaction(tx string) (v string, err error) {
|
||||
defer func(s time.Time) { c.observeRPCLatency("SendRawTransaction", s, err) }(time.Now())
|
||||
return c.b.SendRawTransaction(tx)
|
||||
}
|
||||
|
||||
func (c *blockChainWithMetrics) ResyncMempool(onNewTxAddr func(txid string, addr string)) (err error) {
|
||||
defer func(s time.Time) { c.observeRPCLatency("ResyncMempool", s, err) }(time.Now())
|
||||
return c.b.ResyncMempool(onNewTxAddr)
|
||||
}
|
||||
|
||||
func (c *blockChainWithMetrics) GetMempoolTransactions(address string) (v []string, err error) {
|
||||
defer func(s time.Time) { c.observeRPCLatency("GetMempoolTransactions", s, err) }(time.Now())
|
||||
return c.b.GetMempoolTransactions(address)
|
||||
}
|
||||
|
||||
func (c *blockChainWithMetrics) GetMempoolSpentOutput(outputTxid string, vout uint32) (v string) {
|
||||
return c.b.GetMempoolSpentOutput(outputTxid, vout)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
func (c *blockChainWithMetrics) GetChainParser() bchain.BlockChainParser {
|
||||
return c.b.GetChainParser()
|
||||
}
|
||||
|
||||
@ -41,8 +41,8 @@ func (p *BitcoinBlockParser) GetUIDFromAddress(address string) ([]byte, error) {
|
||||
return p.AddressToOutputScript(address)
|
||||
}
|
||||
|
||||
func (p *BitcoinBlockParser) PackUID(script string) ([]byte, error) {
|
||||
return hex.DecodeString(script)
|
||||
func (p *BitcoinBlockParser) PackUID(str string) ([]byte, error) {
|
||||
return hex.DecodeString(str)
|
||||
}
|
||||
|
||||
func (p *BitcoinBlockParser) UnpackUID(buf []byte) string {
|
||||
|
||||
@ -2,7 +2,6 @@ package btc
|
||||
|
||||
import (
|
||||
"blockbook/bchain"
|
||||
"blockbook/common"
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
@ -24,12 +23,11 @@ type BitcoinRPC struct {
|
||||
rpcURL string
|
||||
user string
|
||||
password string
|
||||
Parser *BitcoinBlockParser
|
||||
Parser bchain.BlockChainParser
|
||||
Testnet bool
|
||||
Network string
|
||||
Mempool *bchain.Mempool
|
||||
ParseBlocks bool
|
||||
metrics *common.Metrics
|
||||
mq *bchain.MQ
|
||||
}
|
||||
|
||||
@ -43,7 +41,7 @@ type configuration struct {
|
||||
}
|
||||
|
||||
// NewBitcoinRPC returns new BitcoinRPC instance.
|
||||
func NewBitcoinRPC(config json.RawMessage, pushHandler func(*bchain.MQMessage), metrics *common.Metrics) (bchain.BlockChain, error) {
|
||||
func NewBitcoinRPC(config json.RawMessage, pushHandler func(*bchain.MQMessage)) (bchain.BlockChain, error) {
|
||||
var err error
|
||||
var c configuration
|
||||
err = json.Unmarshal(config, &c)
|
||||
@ -62,28 +60,7 @@ func NewBitcoinRPC(config json.RawMessage, pushHandler func(*bchain.MQMessage),
|
||||
user: c.RPCUser,
|
||||
password: c.RPCPass,
|
||||
ParseBlocks: c.Parse,
|
||||
metrics: metrics,
|
||||
}
|
||||
chainName, err := s.GetBlockChainInfo()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// always create parser
|
||||
s.Parser = &BitcoinBlockParser{
|
||||
Params: GetChainParams(chainName),
|
||||
}
|
||||
|
||||
// parameters for getInfo request
|
||||
if s.Parser.Params.Net == wire.MainNet {
|
||||
s.Testnet = false
|
||||
s.Network = "livenet"
|
||||
} else {
|
||||
s.Testnet = true
|
||||
s.Network = "testnet"
|
||||
}
|
||||
|
||||
glog.Info("rpc: block chain ", s.Parser.Params.Name)
|
||||
|
||||
mq, err := bchain.NewMQ(c.ZeroMQBinding, pushHandler)
|
||||
if err != nil {
|
||||
@ -92,11 +69,38 @@ func NewBitcoinRPC(config json.RawMessage, pushHandler func(*bchain.MQMessage),
|
||||
}
|
||||
s.mq = mq
|
||||
|
||||
s.Mempool = bchain.NewMempool(s, metrics)
|
||||
|
||||
return s, nil
|
||||
}
|
||||
|
||||
func (b *BitcoinRPC) Initialize() error {
|
||||
b.Mempool = bchain.NewMempool(b)
|
||||
|
||||
chainName, err := b.GetBlockChainInfo()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
params := GetChainParams(chainName)
|
||||
|
||||
// always create parser
|
||||
b.Parser = &BitcoinBlockParser{
|
||||
Params: params,
|
||||
}
|
||||
|
||||
// parameters for getInfo request
|
||||
if params.Net == wire.MainNet {
|
||||
b.Testnet = false
|
||||
b.Network = "livenet"
|
||||
} else {
|
||||
b.Testnet = true
|
||||
b.Network = "testnet"
|
||||
}
|
||||
|
||||
glog.Info("rpc: block chain ", params.Name)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *BitcoinRPC) Shutdown() error {
|
||||
if b.mq != nil {
|
||||
if err := b.mq.Shutdown(); err != nil {
|
||||
@ -188,12 +192,7 @@ type cmdGetBlockHeader struct {
|
||||
} `json:"params"`
|
||||
}
|
||||
|
||||
type resGetBlockHeaderRaw struct {
|
||||
Error *bchain.RPCError `json:"error"`
|
||||
Result string `json:"result"`
|
||||
}
|
||||
|
||||
type resGetBlockHeaderVerbose struct {
|
||||
type resGetBlockHeader struct {
|
||||
Error *bchain.RPCError `json:"error"`
|
||||
Result bchain.BlockHeader `json:"result"`
|
||||
}
|
||||
@ -233,12 +232,7 @@ type cmdGetRawTransaction struct {
|
||||
} `json:"params"`
|
||||
}
|
||||
|
||||
type resGetRawTransactionRaw struct {
|
||||
Error *bchain.RPCError `json:"error"`
|
||||
Result string `json:"result"`
|
||||
}
|
||||
|
||||
type resGetRawTransactionVerbose struct {
|
||||
type resGetRawTransaction struct {
|
||||
Error *bchain.RPCError `json:"error"`
|
||||
Result bchain.Tx `json:"result"`
|
||||
}
|
||||
@ -292,7 +286,7 @@ func (b *BitcoinRPC) GetBestBlockHash() (string, error) {
|
||||
|
||||
res := resGetBestBlockHash{}
|
||||
req := cmdGetBestBlockHash{Method: "getbestblockhash"}
|
||||
err := b.observeRPCLatency(req.Method, func() error { return b.call(&req, &res) })
|
||||
err := b.Call(&req, &res)
|
||||
|
||||
if err != nil {
|
||||
return "", err
|
||||
@ -309,7 +303,7 @@ func (b *BitcoinRPC) GetBestBlockHeight() (uint32, error) {
|
||||
|
||||
res := resGetBlockCount{}
|
||||
req := cmdGetBlockCount{Method: "getblockcount"}
|
||||
err := b.observeRPCLatency(req.Method, func() error { return b.call(&req, &res) })
|
||||
err := b.Call(&req, &res)
|
||||
|
||||
if err != nil {
|
||||
return 0, err
|
||||
@ -326,7 +320,7 @@ func (b *BitcoinRPC) GetBlockChainInfo() (string, error) {
|
||||
|
||||
res := resGetBlockChainInfo{}
|
||||
req := cmdGetBlockChainInfo{Method: "getblockchaininfo"}
|
||||
err := b.observeRPCLatency(req.Method, func() error { return b.call(&req, &res) })
|
||||
err := b.Call(&req, &res)
|
||||
|
||||
if err != nil {
|
||||
return "", err
|
||||
@ -344,7 +338,7 @@ func (b *BitcoinRPC) GetBlockHash(height uint32) (string, error) {
|
||||
res := resGetBlockHash{}
|
||||
req := cmdGetBlockHash{Method: "getblockhash"}
|
||||
req.Params.Height = height
|
||||
err := b.observeRPCLatency(req.Method, func() error { return b.call(&req, &res) })
|
||||
err := b.Call(&req, &res)
|
||||
|
||||
if err != nil {
|
||||
return "", errors.Annotatef(err, "height %v", height)
|
||||
@ -359,11 +353,11 @@ func (b *BitcoinRPC) GetBlockHash(height uint32) (string, error) {
|
||||
func (b *BitcoinRPC) GetBlockHeader(hash string) (*bchain.BlockHeader, error) {
|
||||
glog.V(1).Info("rpc: getblockheader")
|
||||
|
||||
res := resGetBlockHeaderVerbose{}
|
||||
res := resGetBlockHeader{}
|
||||
req := cmdGetBlockHeader{Method: "getblockheader"}
|
||||
req.Params.BlockHash = hash
|
||||
req.Params.Verbose = true
|
||||
err := b.observeRPCLatency(req.Method, func() error { return b.call(&req, &res) })
|
||||
err := b.Call(&req, &res)
|
||||
|
||||
if err != nil {
|
||||
return nil, errors.Annotatef(err, "hash %v", hash)
|
||||
@ -402,9 +396,6 @@ func (b *BitcoinRPC) GetBlock(hash string, height uint32) (*bchain.Block, error)
|
||||
// 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) {
|
||||
if !b.ParseBlocks {
|
||||
return b.GetBlockFull(hash)
|
||||
}
|
||||
data, err := b.GetBlockRaw(hash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -426,7 +417,7 @@ func (b *BitcoinRPC) GetBlockRaw(hash string) ([]byte, error) {
|
||||
req := cmdGetBlock{Method: "getblock"}
|
||||
req.Params.BlockHash = hash
|
||||
req.Params.Verbosity = 0
|
||||
err := b.observeRPCLatency(req.Method, func() error { return b.call(&req, &res) })
|
||||
err := b.Call(&req, &res)
|
||||
|
||||
if err != nil {
|
||||
return nil, errors.Annotatef(err, "hash %v", hash)
|
||||
@ -446,7 +437,7 @@ func (b *BitcoinRPC) GetBlockList(hash string) (*bchain.Block, error) {
|
||||
req := cmdGetBlock{Method: "getblock"}
|
||||
req.Params.BlockHash = hash
|
||||
req.Params.Verbosity = 1
|
||||
err := b.observeRPCLatency(req.Method, func() error { return b.call(&req, &res) })
|
||||
err := b.Call(&req, &res)
|
||||
|
||||
if err != nil {
|
||||
return nil, errors.Annotatef(err, "hash %v", hash)
|
||||
@ -478,7 +469,7 @@ func (b *BitcoinRPC) GetBlockFull(hash string) (*bchain.Block, error) {
|
||||
req := cmdGetBlock{Method: "getblock"}
|
||||
req.Params.BlockHash = hash
|
||||
req.Params.Verbosity = 2
|
||||
err := b.observeRPCLatency(req.Method, func() error { return b.call(&req, &res) })
|
||||
err := b.Call(&req, &res)
|
||||
|
||||
if err != nil {
|
||||
return nil, errors.Annotatef(err, "hash %v", hash)
|
||||
@ -495,7 +486,7 @@ func (b *BitcoinRPC) GetMempool() ([]string, error) {
|
||||
|
||||
res := resGetMempool{}
|
||||
req := cmdGetMempool{Method: "getrawmempool"}
|
||||
err := b.observeRPCLatency(req.Method, func() error { return b.call(&req, &res) })
|
||||
err := b.Call(&req, &res)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -510,11 +501,11 @@ func (b *BitcoinRPC) GetMempool() ([]string, error) {
|
||||
func (b *BitcoinRPC) GetTransaction(txid string) (*bchain.Tx, error) {
|
||||
glog.V(1).Info("rpc: getrawtransaction ", txid)
|
||||
|
||||
res := resGetRawTransactionVerbose{}
|
||||
res := resGetRawTransaction{}
|
||||
req := cmdGetRawTransaction{Method: "getrawtransaction"}
|
||||
req.Params.Txid = txid
|
||||
req.Params.Verbose = true
|
||||
err := b.observeRPCLatency(req.Method, func() error { return b.call(&req, &res) })
|
||||
err := b.Call(&req, &res)
|
||||
|
||||
if err != nil {
|
||||
return nil, errors.Annotatef(err, "txid %v", txid)
|
||||
@ -553,7 +544,7 @@ func (b *BitcoinRPC) EstimateSmartFee(blocks int, conservative bool) (float64, e
|
||||
} else {
|
||||
req.Params.EstimateMode = "ECONOMICAL"
|
||||
}
|
||||
err := b.observeRPCLatency(req.Method, func() error { return b.call(&req, &res) })
|
||||
err := b.Call(&req, &res)
|
||||
|
||||
if err != nil {
|
||||
return 0, err
|
||||
@ -571,7 +562,7 @@ func (b *BitcoinRPC) SendRawTransaction(tx string) (string, error) {
|
||||
res := resSendRawTransaction{}
|
||||
req := cmdSendRawTransaction{Method: "sendrawtransaction"}
|
||||
req.Params = []string{tx}
|
||||
err := b.observeRPCLatency(req.Method, func() error { return b.call(&req, &res) })
|
||||
err := b.Call(&req, &res)
|
||||
|
||||
if err != nil {
|
||||
return "", err
|
||||
@ -590,7 +581,7 @@ func (b *BitcoinRPC) GetMempoolEntry(txid string) (*bchain.MempoolEntry, error)
|
||||
Method: "getmempoolentry",
|
||||
Params: []string{txid},
|
||||
}
|
||||
err := b.observeRPCLatency(req.Method, func() error { return b.call(&req, &res) })
|
||||
err := b.Call(&req, &res)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -601,16 +592,7 @@ func (b *BitcoinRPC) GetMempoolEntry(txid string) (*bchain.MempoolEntry, error)
|
||||
return res.Result, nil
|
||||
}
|
||||
|
||||
func (b *BitcoinRPC) observeRPCLatency(method string, fn func() error) error {
|
||||
start := time.Now()
|
||||
err := fn()
|
||||
if err == nil {
|
||||
b.metrics.RPCLatency.With(common.Labels{"method": method}).Observe(float64(time.Since(start)) / 1e6) // in milliseconds
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (b *BitcoinRPC) call(req interface{}, res interface{}) error {
|
||||
func (b *BitcoinRPC) Call(req interface{}, res interface{}) error {
|
||||
httpData, err := json.Marshal(req)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
46
bchain/coins/zec/zcashparser.go
Normal file
46
bchain/coins/zec/zcashparser.go
Normal file
@ -0,0 +1,46 @@
|
||||
package zec
|
||||
|
||||
import (
|
||||
"blockbook/bchain"
|
||||
"blockbook/bchain/coins/btc"
|
||||
|
||||
"github.com/btcsuite/btcd/chaincfg"
|
||||
)
|
||||
|
||||
// bitcoinwire parsing
|
||||
|
||||
type ZCashBlockParser struct {
|
||||
btc.BitcoinBlockParser
|
||||
}
|
||||
|
||||
// getChainParams contains network parameters for the main Bitcoin network,
|
||||
// the regression test Bitcoin network, the test Bitcoin network and
|
||||
// the simulation test Bitcoin network, in this order
|
||||
func GetChainParams(chain string) *chaincfg.Params {
|
||||
switch chain {
|
||||
case "test":
|
||||
return &chaincfg.TestNet3Params
|
||||
case "regtest":
|
||||
return &chaincfg.RegressionNetParams
|
||||
}
|
||||
return &chaincfg.MainNetParams
|
||||
}
|
||||
|
||||
func (p *ZCashBlockParser) GetUIDFromVout(output *bchain.Vout) string {
|
||||
if len(output.ScriptPubKey.Addresses) != 1 {
|
||||
return ""
|
||||
}
|
||||
return output.ScriptPubKey.Addresses[0]
|
||||
}
|
||||
|
||||
func (p *ZCashBlockParser) GetUIDFromAddress(address string) ([]byte, error) {
|
||||
return p.PackUID(address)
|
||||
}
|
||||
|
||||
func (p *ZCashBlockParser) PackUID(str string) ([]byte, error) {
|
||||
return []byte(str), nil
|
||||
}
|
||||
|
||||
func (p *ZCashBlockParser) UnpackUID(buf []byte) string {
|
||||
return string(buf)
|
||||
}
|
||||
@ -3,16 +3,20 @@ package zec
|
||||
import (
|
||||
"blockbook/bchain"
|
||||
"blockbook/bchain/coins/btc"
|
||||
"blockbook/common"
|
||||
"encoding/json"
|
||||
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"github.com/juju/errors"
|
||||
)
|
||||
|
||||
type ZCashRPC struct {
|
||||
*btc.BitcoinRPC
|
||||
}
|
||||
|
||||
func NewZCashRPC(config json.RawMessage, pushHandler func(*bchain.MQMessage), metrics *common.Metrics) (bchain.BlockChain, error) {
|
||||
b, err := btc.NewBitcoinRPC(config, pushHandler, metrics)
|
||||
func NewZCashRPC(config json.RawMessage, pushHandler func(*bchain.MQMessage)) (bchain.BlockChain, error) {
|
||||
b, err := btc.NewBitcoinRPC(config, pushHandler)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -21,3 +25,155 @@ func NewZCashRPC(config json.RawMessage, pushHandler func(*bchain.MQMessage), me
|
||||
}
|
||||
return z, nil
|
||||
}
|
||||
|
||||
func (z *ZCashRPC) Initialize() error {
|
||||
z.Mempool = bchain.NewMempool(z)
|
||||
|
||||
chainName, err := z.GetBlockChainInfo()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
params := GetChainParams(chainName)
|
||||
|
||||
// always create parser
|
||||
z.Parser = &ZCashBlockParser{
|
||||
btc.BitcoinBlockParser{
|
||||
Params: params,
|
||||
},
|
||||
}
|
||||
|
||||
// parameters for getInfo request
|
||||
if params.Net == wire.MainNet {
|
||||
z.Testnet = false
|
||||
z.Network = "livenet"
|
||||
} else {
|
||||
z.Testnet = true
|
||||
z.Network = "testnet"
|
||||
}
|
||||
|
||||
glog.Info("rpc: block chain ", params.Name)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type untypedArrayParams struct {
|
||||
Method string `json:"method"`
|
||||
Params []interface{} `json:"params"`
|
||||
}
|
||||
|
||||
// getblockhash
|
||||
|
||||
type resGetBlockHash struct {
|
||||
Error *bchain.RPCError `json:"error"`
|
||||
Result string `json:"result"`
|
||||
}
|
||||
|
||||
// getblock
|
||||
|
||||
type resGetBlockThin struct {
|
||||
Error *bchain.RPCError `json:"error"`
|
||||
Result bchain.ThinBlock `json:"result"`
|
||||
}
|
||||
|
||||
// getrawtransaction
|
||||
|
||||
type resGetRawTransaction struct {
|
||||
Error *bchain.RPCError `json:"error"`
|
||||
Result bchain.Tx `json:"result"`
|
||||
}
|
||||
|
||||
// getblockheader
|
||||
|
||||
type resGetBlockHeader struct {
|
||||
Error *bchain.RPCError `json:"error"`
|
||||
Result bchain.BlockHeader `json:"result"`
|
||||
}
|
||||
|
||||
// GetBlock returns block with given hash.
|
||||
func (z *ZCashRPC) GetBlock(hash string, height uint32) (*bchain.Block, error) {
|
||||
glog.V(1).Info("rpc: getblock (verbosity=1) ", hash)
|
||||
|
||||
res := resGetBlockThin{}
|
||||
req := untypedArrayParams{Method: "getblock"}
|
||||
req.Params = append(req.Params, hash)
|
||||
req.Params = append(req.Params, true)
|
||||
err := z.Call(&req, &res)
|
||||
|
||||
if err != nil {
|
||||
return nil, errors.Annotatef(err, "hash %v", hash)
|
||||
}
|
||||
if res.Error != nil {
|
||||
return nil, errors.Annotatef(res.Error, "hash %v", hash)
|
||||
}
|
||||
|
||||
txs := make([]bchain.Tx, len(res.Result.Txids))
|
||||
for i, txid := range res.Result.Txids {
|
||||
tx, err := z.GetTransaction(txid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
txs[i] = *tx
|
||||
}
|
||||
block := &bchain.Block{
|
||||
BlockHeader: res.Result.BlockHeader,
|
||||
Txs: txs,
|
||||
}
|
||||
return block, nil
|
||||
}
|
||||
|
||||
// GetTransaction returns a transaction by the transaction ID.
|
||||
func (z *ZCashRPC) GetTransaction(txid string) (*bchain.Tx, error) {
|
||||
glog.V(1).Info("rpc: getrawtransaction ", txid)
|
||||
|
||||
res := resGetRawTransaction{}
|
||||
req := untypedArrayParams{Method: "getrawtransaction"}
|
||||
req.Params = append(req.Params, txid)
|
||||
req.Params = append(req.Params, 1)
|
||||
err := z.Call(&req, &res)
|
||||
|
||||
if err != nil {
|
||||
return nil, errors.Annotatef(err, "txid %v", txid)
|
||||
}
|
||||
if res.Error != nil {
|
||||
return nil, errors.Annotatef(res.Error, "txid %v", txid)
|
||||
}
|
||||
return &res.Result, nil
|
||||
}
|
||||
|
||||
// GetBlockHash returns hash of block in best-block-chain at given height.
|
||||
func (z *ZCashRPC) GetBlockHash(height uint32) (string, error) {
|
||||
glog.V(1).Info("rpc: getblockhash ", height)
|
||||
|
||||
res := resGetBlockHash{}
|
||||
req := untypedArrayParams{Method: "getblockhash"}
|
||||
req.Params = append(req.Params, height)
|
||||
err := z.Call(&req, &res)
|
||||
|
||||
if err != nil {
|
||||
return "", errors.Annotatef(err, "height %v", height)
|
||||
}
|
||||
if res.Error != nil {
|
||||
return "", errors.Annotatef(res.Error, "height %v", height)
|
||||
}
|
||||
return res.Result, nil
|
||||
}
|
||||
|
||||
// GetBlockHeader returns header of block with given hash.
|
||||
func (z *ZCashRPC) GetBlockHeader(hash string) (*bchain.BlockHeader, error) {
|
||||
glog.V(1).Info("rpc: getblockheader")
|
||||
|
||||
res := resGetBlockHeader{}
|
||||
req := untypedArrayParams{Method: "getblockheader"}
|
||||
req.Params = append(req.Params, hash)
|
||||
req.Params = append(req.Params, true)
|
||||
err := z.Call(&req, &res)
|
||||
|
||||
if err != nil {
|
||||
return nil, errors.Annotatef(err, "hash %v", hash)
|
||||
}
|
||||
if res.Error != nil {
|
||||
return nil, errors.Annotatef(res.Error, "hash %v", hash)
|
||||
}
|
||||
return &res.Result, nil
|
||||
}
|
||||
|
||||
45
bchain/coins/zec/zec.md
Normal file
45
bchain/coins/zec/zec.md
Normal file
@ -0,0 +1,45 @@
|
||||
## Zcash Setup
|
||||
Get Zcash client
|
||||
```
|
||||
wget https://z.cash/downloads/zcash-1.0.15-linux64.tar.gz
|
||||
tar xzf zcash-1.0.15-linux64.tar.gz
|
||||
```
|
||||
|
||||
Run command to download the parameters used to create and verify shielded transactions:
|
||||
```
|
||||
zcash-1.0.15/bin/zcash-fetch-params
|
||||
```
|
||||
|
||||
Data are stored in */data/zec* , in folders */data/zec/zcash* for Zcash client data, */data/zec/blockbook* for Blockbook data.
|
||||
|
||||
Create configuration file */data/zec/zcash/zcash.conf* with content
|
||||
```
|
||||
daemon=1
|
||||
server=1
|
||||
rpcuser=rpc
|
||||
rpcpassword=rpc
|
||||
rpcport=8232
|
||||
txindex=1
|
||||
mainnet=1
|
||||
addnode=mainnet.z.cash
|
||||
```
|
||||
|
||||
Create script *run-zec-zcashd.sh* that starts the zcashd daemon with increased rpcworkqueue and configured zeromq
|
||||
```
|
||||
#!/bin/bash
|
||||
|
||||
zcash-1.0.15/bin/zcashd -datadir=/data/zec/zcash -rpcworkqueue=32 -zmqpubhashblock=tcp://127.0.0.1:8234 -zmqpubrawblock=tcp://127.0.0.1:8234 -zmqpubhashtx=tcp://127.0.0.1:8234 -zmqpubrawtx=tcp://127.0.0.1:8234
|
||||
```
|
||||
|
||||
Run the *run-zec-zcashd.sh* to get initial import of data.
|
||||
|
||||
Create *run-zec-blockbook.sh* script that starts blockbook
|
||||
```
|
||||
#!/bin/bash
|
||||
./blockbook -path=/data/zec/blockbook/db -sync -parse -rpcurl=http://127.0.0.1:8232 -httpserver=:8235 -socketio=:8236 -certfile=server/testcert -zeromq=tcp://127.0.0.1:8234 -explorer=https://zec-bitcore1.trezor.io -coin=zec $1
|
||||
```
|
||||
|
||||
To run blockbook with logging to file (run with nohup or daemonize using screen)
|
||||
```
|
||||
./run-zec-blockbook.sh 2> /data/zec/blockbook/blockbook.log
|
||||
```
|
||||
@ -1,7 +1,6 @@
|
||||
package bchain
|
||||
|
||||
import (
|
||||
"blockbook/common"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
@ -27,28 +26,27 @@ type inputOutput struct {
|
||||
// Mempool is mempool handle.
|
||||
type Mempool struct {
|
||||
chain BlockChain
|
||||
chainParser BlockChainParser
|
||||
mux sync.Mutex
|
||||
txToInputOutput map[string]inputOutput
|
||||
scriptToTx map[string][]outpoint // TODO rename all occurences
|
||||
inputs map[outpoint]string
|
||||
metrics *common.Metrics
|
||||
}
|
||||
|
||||
// NewMempool creates new mempool handler.
|
||||
func NewMempool(chain BlockChain, metrics *common.Metrics) *Mempool {
|
||||
return &Mempool{chain: chain, chainParser: chain.GetChainParser(), metrics: metrics}
|
||||
func NewMempool(chain BlockChain) *Mempool {
|
||||
return &Mempool{chain: chain}
|
||||
}
|
||||
|
||||
// GetTransactions returns slice of mempool transactions for given output script.
|
||||
func (m *Mempool) GetTransactions(address string) ([]string, error) {
|
||||
m.mux.Lock()
|
||||
defer m.mux.Unlock()
|
||||
buf, err := m.chainParser.GetUIDFromAddress(address)
|
||||
parser := m.chain.GetChainParser()
|
||||
buf, err := parser.GetUIDFromAddress(address)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
outid := m.chainParser.UnpackUID(buf)
|
||||
outid := parser.UnpackUID(buf)
|
||||
outpoints := m.scriptToTx[outid]
|
||||
txs := make([]string, 0, len(outpoints)+len(outpoints)/2)
|
||||
for _, o := range outpoints {
|
||||
@ -83,9 +81,9 @@ func (m *Mempool) Resync(onNewTxAddr func(txid string, addr string)) error {
|
||||
glog.V(1).Info("Mempool: resync")
|
||||
txs, err := m.chain.GetMempool()
|
||||
if err != nil {
|
||||
m.metrics.MempoolResyncErrors.With(common.Labels{"error": err.Error()}).Inc()
|
||||
return err
|
||||
}
|
||||
parser := m.chain.GetChainParser()
|
||||
newTxToInputOutput := make(map[string]inputOutput, len(m.txToInputOutput)+1)
|
||||
newScriptToTx := make(map[string][]outpoint, len(m.scriptToTx)+1)
|
||||
newInputs := make(map[outpoint]string, len(m.inputs)+1)
|
||||
@ -94,13 +92,12 @@ func (m *Mempool) Resync(onNewTxAddr func(txid string, addr string)) error {
|
||||
if !exists {
|
||||
tx, err := m.chain.GetTransaction(txid)
|
||||
if err != nil {
|
||||
m.metrics.MempoolResyncErrors.With(common.Labels{"error": err.Error()}).Inc()
|
||||
glog.Error("cannot get transaction ", txid, ": ", err)
|
||||
continue
|
||||
}
|
||||
io.outputs = make([]scriptIndex, 0, len(tx.Vout))
|
||||
for _, output := range tx.Vout {
|
||||
outid := m.chainParser.GetUIDFromVout(&output)
|
||||
outid := parser.GetUIDFromVout(&output)
|
||||
if outid != "" {
|
||||
io.outputs = append(io.outputs, scriptIndex{outid, output.N})
|
||||
}
|
||||
@ -125,8 +122,6 @@ func (m *Mempool) Resync(onNewTxAddr func(txid string, addr string)) error {
|
||||
}
|
||||
}
|
||||
m.updateMappings(newTxToInputOutput, newScriptToTx, newInputs)
|
||||
d := time.Since(start)
|
||||
glog.Info("Mempool: resync finished in ", d, ", ", len(m.txToInputOutput), " transactions in mempool")
|
||||
m.metrics.MempoolResyncDuration.Observe(float64(d) / 1e6) // in milliseconds
|
||||
glog.Info("Mempool: resync finished in ", time.Since(start), ", ", len(m.txToInputOutput), " transactions in mempool")
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -86,7 +86,8 @@ func (e *RPCError) Error() string {
|
||||
}
|
||||
|
||||
type BlockChain interface {
|
||||
// cleanup
|
||||
// life-cycle methods
|
||||
Initialize() error
|
||||
Shutdown() error
|
||||
// chain info
|
||||
IsTestnet() bool
|
||||
|
||||
@ -16,7 +16,6 @@ type Metrics struct {
|
||||
TxCacheEfficiency *prometheus.CounterVec
|
||||
RPCLatency *prometheus.HistogramVec
|
||||
IndexResyncErrors *prometheus.CounterVec
|
||||
MempoolResyncErrors *prometheus.CounterVec
|
||||
IndexDBSize prometheus.Gauge
|
||||
}
|
||||
|
||||
@ -88,7 +87,7 @@ func GetMetrics(coin string) (*Metrics, error) {
|
||||
Buckets: []float64{1, 5, 10, 25, 50, 75, 100, 250},
|
||||
ConstLabels: Labels{"coin": coin},
|
||||
},
|
||||
[]string{"method"},
|
||||
[]string{"method", "error"},
|
||||
)
|
||||
metrics.IndexResyncErrors = prometheus.NewCounterVec(
|
||||
prometheus.CounterOpts{
|
||||
@ -98,14 +97,6 @@ func GetMetrics(coin string) (*Metrics, error) {
|
||||
},
|
||||
[]string{"error"},
|
||||
)
|
||||
metrics.MempoolResyncErrors = prometheus.NewCounterVec(
|
||||
prometheus.CounterOpts{
|
||||
Name: "blockbook_mempool_resync_errors",
|
||||
Help: "Number of errors of mempool resync operation",
|
||||
ConstLabels: Labels{"coin": coin},
|
||||
},
|
||||
[]string{"error"},
|
||||
)
|
||||
metrics.IndexDBSize = prometheus.NewGauge(
|
||||
prometheus.GaugeOpts{
|
||||
Name: "blockbook_index_db_size",
|
||||
|
||||
@ -266,7 +266,7 @@ func (d *RocksDB) writeOutputs(wb *gorocksdb.WriteBatch, block *bchain.Block, op
|
||||
for outid, outpoints := range records {
|
||||
bOutid, err := d.chainParser.PackUID(outid)
|
||||
if err != nil {
|
||||
glog.Warningf("rocksdb: packOutputID: %v - %d %s", err, block.Height, outid)
|
||||
glog.Warningf("rocksdb: packUID: %v - %d %s", err, block.Height, outid)
|
||||
continue
|
||||
}
|
||||
key, err := packOutputKey(bOutid, block.Height)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user