From 635733574c55ad7936095875bc7fd9ed744fab04 Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Fri, 14 Sep 2018 14:48:43 +0200 Subject: [PATCH] Add bchain method GetChainInfo with extended information about backend --- bchain/coins/blockchain.go | 6 +-- bchain/coins/btc/bitcoinrpc.go | 87 +++++++++++++++++++++++++++------- bchain/coins/eth/ethrpc.go | 18 +++++-- bchain/tests/rpc/rpc.go | 2 +- bchain/types.go | 16 ++++++- 5 files changed, 102 insertions(+), 27 deletions(-) diff --git a/bchain/coins/blockchain.go b/bchain/coins/blockchain.go index 8e4b7527..89e84de6 100644 --- a/bchain/coins/blockchain.go +++ b/bchain/coins/blockchain.go @@ -132,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) { diff --git a/bchain/coins/btc/bitcoinrpc.go b/bchain/coins/btc/bitcoinrpc.go index 9fbc62d5..de4c94d6 100644 --- a/bchain/coins/btc/bitcoinrpc.go +++ b/bchain/coins/btc/bitcoinrpc.go @@ -11,6 +11,7 @@ import ( "math/big" "net" "net/http" + "strconv" "time" "github.com/btcsuite/btcd/wire" @@ -102,10 +103,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 { @@ -217,10 +219,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 float64 `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 int `json:"version"` + Subversion string `json:"subversion"` + ProtocolVersion int `json:"protocolversion"` + Timeoffset float64 `json:"timeoffset"` + Warnings string `json:"warnings"` } `json:"result"` } @@ -386,21 +408,52 @@ 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: resCi.Result.Difficulty, + Headers: resCi.Result.Headers, + SizeOnDisk: resCi.Result.SizeOnDisk, + Subversion: resNi.Result.Subversion, + Timeoffset: resNi.Result.Timeoffset, + } + if resNi.Result.Version > 0 { + rv.Version = strconv.Itoa(resNi.Result.Version) + } + if resNi.Result.ProtocolVersion > 0 { + rv.ProtocolVersion = strconv.Itoa(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 { @@ -454,7 +507,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 @@ -483,7 +536,7 @@ 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 +// 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) diff --git a/bchain/coins/eth/ethrpc.go b/bchain/coins/eth/ethrpc.go index 5895827d..da97c3d3 100644 --- a/bchain/coins/eth/ethrpc.go +++ b/bchain/coins/eth/ethrpc.go @@ -6,6 +6,7 @@ import ( "encoding/json" "fmt" "math/big" + "strconv" "sync" "time" @@ -258,16 +259,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) { diff --git a/bchain/tests/rpc/rpc.go b/bchain/tests/rpc/rpc.go index 675e6015..5f972707 100644 --- a/bchain/tests/rpc/rpc.go +++ b/bchain/tests/rpc/rpc.go @@ -72,7 +72,7 @@ func NewTest(coin string, factory TestChainFactoryFunc) (*Test, error) { } } - _, err = cli.GetBlockChainInfo() + _, err = cli.GetChainInfo() if err != nil && isNetError(err) { connected = false } diff --git a/bchain/types.go b/bchain/types.go index 7a269aa3..bdb47a2a 100644 --- a/bchain/types.go +++ b/bchain/types.go @@ -103,6 +103,20 @@ type MempoolEntry struct { Depends []string `json:"depends"` } +type ChainInfo struct { + Chain string `json:"chain"` + Blocks int `json:"blocks"` + Headers int `json:"headers"` + Bestblockhash string `json:"bestblockhash"` + Difficulty float64 `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 { Code int `json:"code"` Message string `json:"message"` @@ -135,8 +149,8 @@ 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)