WIP: zcash

This commit is contained in:
Jakub Matys 2018-03-19 15:46:29 +01:00
parent fbfb33cb5d
commit d09333fa2e
5 changed files with 207 additions and 37 deletions

View File

@ -9,6 +9,7 @@ import (
"fmt"
"io/ioutil"
"reflect"
"time"
"github.com/juju/errors"
)
@ -38,5 +39,10 @@ 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, metrics)
if err != nil {
return nil, err
}
bc.Initialize(bchain.NewMempool(bc, metrics))
return bc, nil
}

View File

@ -92,8 +92,6 @@ func NewBitcoinRPC(config json.RawMessage, pushHandler func(*bchain.MQMessage),
}
s.mq = mq
s.Mempool = bchain.NewMempool(s, metrics)
return s, nil
}
@ -107,6 +105,10 @@ func (b *BitcoinRPC) Shutdown() error {
return nil
}
func (b *BitcoinRPC) Initialize(mempool *bchain.Mempool) {
b.Mempool = mempool
}
func (b *BitcoinRPC) IsTestnet() bool {
return b.Testnet
}
@ -188,12 +190,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 +230,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 +284,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.Method, &req, &res)
if err != nil {
return "", err
@ -309,7 +301,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.Method, &req, &res)
if err != nil {
return 0, err
@ -326,7 +318,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.Method, &req, &res)
if err != nil {
return "", err
@ -344,7 +336,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.Method, &req, &res)
if err != nil {
return "", errors.Annotatef(err, "height %v", height)
@ -359,11 +351,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.Method, &req, &res)
if err != nil {
return nil, errors.Annotatef(err, "hash %v", hash)
@ -402,9 +394,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 +415,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.Method, &req, &res)
if err != nil {
return nil, errors.Annotatef(err, "hash %v", hash)
@ -446,7 +435,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.Method, &req, &res)
if err != nil {
return nil, errors.Annotatef(err, "hash %v", hash)
@ -478,7 +467,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.Method, &req, &res)
if err != nil {
return nil, errors.Annotatef(err, "hash %v", hash)
@ -495,7 +484,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.Method, &req, &res)
if err != nil {
return nil, err
@ -510,11 +499,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.Method, &req, &res)
if err != nil {
return nil, errors.Annotatef(err, "txid %v", txid)
@ -553,7 +542,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.Method, &req, &res)
if err != nil {
return 0, err
@ -571,7 +560,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.Method, &req, &res)
if err != nil {
return "", err
@ -590,7 +579,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.Method, &req, &res)
if err != nil {
return nil, err
@ -601,9 +590,9 @@ func (b *BitcoinRPC) GetMempoolEntry(txid string) (*bchain.MempoolEntry, error)
return res.Result, nil
}
func (b *BitcoinRPC) observeRPCLatency(method string, fn func() error) error {
func (b *BitcoinRPC) Call(method string, req interface{}, res interface{}) error {
start := time.Now()
err := fn()
err := b.call(req, res)
if err == nil {
b.metrics.RPCLatency.With(common.Labels{"method": method}).Observe(float64(time.Since(start)) / 1e6) // in milliseconds
}

View File

@ -4,7 +4,10 @@ import (
"blockbook/bchain"
"blockbook/bchain/coins/btc"
"blockbook/common"
"encoding/json"
"time"
"github.com/golang/glog"
"github.com/juju/errors"
)
type ZCashRPC struct {
@ -21,3 +24,129 @@ func NewZCashRPC(config json.RawMessage, pushHandler func(*bchain.MQMessage), me
}
return z, 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.Method, &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.Method, &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.Method, &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.Method, &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
}
// GetChainParser returns BlockChainParser
func (z *ZCashRPC) GetChainParser() bchain.BlockChainParser {
return z.Parser
}

45
bchain/coins/zec/zec.md Normal file
View 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
```

View File

@ -86,7 +86,8 @@ func (e *RPCError) Error() string {
}
type BlockChain interface {
// cleanup
// life-cycle methods
Initialize(mempool *Mempool)
Shutdown() error
// chain info
IsTestnet() bool