Add get raw block API request #736
This commit is contained in:
parent
ad18eda804
commit
93ea126123
@ -399,6 +399,11 @@ type Block struct {
|
||||
Transactions []*Tx `json:"txs,omitempty"`
|
||||
}
|
||||
|
||||
// BlockRaw contains raw block in hex
|
||||
type BlockRaw struct {
|
||||
Hex string `json:"hex"`
|
||||
}
|
||||
|
||||
// BlockbookInfo contains information about the running blockbook instance
|
||||
type BlockbookInfo struct {
|
||||
Coin string `json:"coin"`
|
||||
|
||||
@ -1486,8 +1486,8 @@ func (w *Worker) GetFiatRatesTickersList(timestamp int64) (*db.ResultTickerListA
|
||||
}, nil
|
||||
}
|
||||
|
||||
// getBlockInfoFromBlockID returns block info from block height or block hash
|
||||
func (w *Worker) getBlockInfoFromBlockID(bid string) (*bchain.BlockInfo, error) {
|
||||
// getBlockHashBlockID returns block hash from block height or block hash
|
||||
func (w *Worker) getBlockHashBlockID(bid string) string {
|
||||
// try to decide if passed string (bid) is block height or block hash
|
||||
// if it's a number, must be less than int32
|
||||
var hash string
|
||||
@ -1500,6 +1500,12 @@ func (w *Worker) getBlockInfoFromBlockID(bid string) (*bchain.BlockInfo, error)
|
||||
} else {
|
||||
hash = bid
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
||||
// getBlockInfoFromBlockID returns block info from block height or block hash
|
||||
func (w *Worker) getBlockInfoFromBlockID(bid string) (*bchain.BlockInfo, error) {
|
||||
hash:=w.getBlockHashBlockID(bid)
|
||||
if hash == "" {
|
||||
return nil, NewAPIError("Block not found", true)
|
||||
}
|
||||
@ -1678,6 +1684,16 @@ func (w *Worker) GetBlock(bid string, page int, txsOnPage int) (*Block, error) {
|
||||
}, nil
|
||||
}
|
||||
|
||||
// GetBlock returns paged data about block
|
||||
func (w *Worker) GetBlockRaw(bid string) (*BlockRaw, error) {
|
||||
hash:=w.getBlockHashBlockID(bid)
|
||||
if hash == "" {
|
||||
return nil, NewAPIError("Block not found", true)
|
||||
}
|
||||
hex, err := w.chain.GetBlockRaw(hash)
|
||||
return &BlockRaw{Hex: hex}, err
|
||||
}
|
||||
|
||||
// ComputeFeeStats computes fee distribution in defined blocks and logs them to log
|
||||
func (w *Worker) ComputeFeeStats(blockFrom, blockTo int, stopCompute chan os.Signal) error {
|
||||
bestheight, _, err := w.db.GetBestBlock()
|
||||
|
||||
@ -29,6 +29,11 @@ func (b *BaseChain) GetNetworkName() string {
|
||||
return b.Network
|
||||
}
|
||||
|
||||
// GetBlockRaw is not supported by default
|
||||
func (b *BaseChain) GetBlockRaw(hash string) (string, error) {
|
||||
return "", errors.New("GetBlockRaw: not supported")
|
||||
}
|
||||
|
||||
// GetMempoolEntry is not supported by default
|
||||
func (b *BaseChain) GetMempoolEntry(txid string) (*MempoolEntry, error) {
|
||||
return nil, errors.New("GetMempoolEntry: not supported")
|
||||
|
||||
@ -95,7 +95,7 @@ func (b *BCashRPC) GetBlock(hash string, height uint32) (*bchain.Block, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
data, err := b.GetBlockRaw(hash)
|
||||
data, err := b.GetBlockBytes(hash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -111,7 +111,7 @@ func (b *BCashRPC) GetBlock(hash string, height uint32) (*bchain.Block, error) {
|
||||
}
|
||||
|
||||
// GetBlockRaw returns block with given hash as bytes.
|
||||
func (b *BCashRPC) GetBlockRaw(hash string) ([]byte, error) {
|
||||
func (b *BCashRPC) GetBlockRaw(hash string) (string, error) {
|
||||
glog.V(1).Info("rpc: getblock (verbose=0) ", hash)
|
||||
|
||||
res := btc.ResGetBlockRaw{}
|
||||
@ -121,15 +121,24 @@ func (b *BCashRPC) GetBlockRaw(hash string) ([]byte, error) {
|
||||
err := b.Call(&req, &res)
|
||||
|
||||
if err != nil {
|
||||
return nil, errors.Annotatef(err, "hash %v", hash)
|
||||
return "", errors.Annotatef(err, "hash %v", hash)
|
||||
}
|
||||
if res.Error != nil {
|
||||
if isErrBlockNotFound(res.Error) {
|
||||
return nil, bchain.ErrBlockNotFound
|
||||
return "", bchain.ErrBlockNotFound
|
||||
}
|
||||
return nil, errors.Annotatef(res.Error, "hash %v", hash)
|
||||
return "", errors.Annotatef(res.Error, "hash %v", hash)
|
||||
}
|
||||
return hex.DecodeString(res.Result)
|
||||
return res.Result,nil
|
||||
}
|
||||
|
||||
// GetBlockBytes returns block with given hash as bytes
|
||||
func (b *BCashRPC) GetBlockBytes(hash string) ([]byte, error) {
|
||||
block,err:=b.GetBlockRaw(hash)
|
||||
if err != nil {
|
||||
return nil,err
|
||||
}
|
||||
return hex.DecodeString(block)
|
||||
}
|
||||
|
||||
// GetBlockInfo returns extended header (more info than in bchain.BlockHeader) with a list of txids
|
||||
|
||||
@ -78,7 +78,7 @@ func (f *BitcoreRPC) GetBlock(hash string, height uint32) (*bchain.Block, error)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
data, err := f.GetBlockRaw(hash)
|
||||
data, err := f.GetBlockBytes(hash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@ -254,6 +254,11 @@ func (c *blockChainWithMetrics) GetBlockInfo(hash string) (v *bchain.BlockInfo,
|
||||
return c.b.GetBlockInfo(hash)
|
||||
}
|
||||
|
||||
func (c *blockChainWithMetrics) GetBlockRaw(hash string) (v string, err error) {
|
||||
defer func(s time.Time) { c.observeRPCLatency("GetBlockRaw", s, err) }(time.Now())
|
||||
return c.b.GetBlockRaw(hash)
|
||||
}
|
||||
|
||||
func (c *blockChainWithMetrics) GetMempoolTransactions() (v []string, err error) {
|
||||
defer func(s time.Time) { c.observeRPCLatency("GetMempoolTransactions", s, err) }(time.Now())
|
||||
return c.b.GetMempoolTransactions()
|
||||
|
||||
@ -551,7 +551,7 @@ func (b *BitcoinRPC) GetBlock(hash string, height uint32) (*bchain.Block, error)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
data, err := b.GetBlockRaw(hash)
|
||||
data, err := b.GetBlockBytes(hash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -588,7 +588,7 @@ func (b *BitcoinRPC) GetBlockInfo(hash string) (*bchain.BlockInfo, 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) {
|
||||
data, err := b.GetBlockRaw(hash)
|
||||
data, err := b.GetBlockBytes(hash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -601,8 +601,8 @@ func (b *BitcoinRPC) GetBlockWithoutHeader(hash string, height uint32) (*bchain.
|
||||
return block, nil
|
||||
}
|
||||
|
||||
// GetBlockRaw returns block with given hash as bytes
|
||||
func (b *BitcoinRPC) GetBlockRaw(hash string) ([]byte, error) {
|
||||
// GetBlockRaw returns block with given hash as hex string
|
||||
func (b *BitcoinRPC) GetBlockRaw(hash string) (string, error) {
|
||||
glog.V(1).Info("rpc: getblock (verbosity=0) ", hash)
|
||||
|
||||
res := ResGetBlockRaw{}
|
||||
@ -612,15 +612,24 @@ func (b *BitcoinRPC) GetBlockRaw(hash string) ([]byte, error) {
|
||||
err := b.Call(&req, &res)
|
||||
|
||||
if err != nil {
|
||||
return nil, errors.Annotatef(err, "hash %v", hash)
|
||||
return "", errors.Annotatef(err, "hash %v", hash)
|
||||
}
|
||||
if res.Error != nil {
|
||||
if IsErrBlockNotFound(res.Error) {
|
||||
return nil, bchain.ErrBlockNotFound
|
||||
return "", bchain.ErrBlockNotFound
|
||||
}
|
||||
return nil, errors.Annotatef(res.Error, "hash %v", hash)
|
||||
return "", errors.Annotatef(res.Error, "hash %v", hash)
|
||||
}
|
||||
return hex.DecodeString(res.Result)
|
||||
return res.Result,nil
|
||||
}
|
||||
|
||||
// GetBlockBytes returns block with given hash as bytes
|
||||
func (b *BitcoinRPC) GetBlockBytes(hash string) ([]byte, error) {
|
||||
block,err:=b.GetBlockRaw(hash)
|
||||
if err != nil {
|
||||
return nil,err
|
||||
}
|
||||
return hex.DecodeString(block)
|
||||
}
|
||||
|
||||
// GetBlockFull returns block with given hash
|
||||
|
||||
@ -81,7 +81,7 @@ func (zc *FiroRPC) GetBlock(hash string, height uint32) (*bchain.Block, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
data, err := zc.GetBlockRaw(hash)
|
||||
data, err := zc.GetBlockBytes(hash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -118,7 +118,7 @@ func (zc *FiroRPC) GetBlockInfo(hash string) (*bchain.BlockInfo, error) {
|
||||
}
|
||||
|
||||
func (zc *FiroRPC) GetBlockWithoutHeader(hash string, height uint32) (*bchain.Block, error) {
|
||||
data, err := zc.GetBlockRaw(hash)
|
||||
data, err := zc.GetBlockBytes(hash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -134,7 +134,8 @@ func (zc *FiroRPC) GetBlockWithoutHeader(hash string, height uint32) (*bchain.Bl
|
||||
return block, nil
|
||||
}
|
||||
|
||||
func (zc *FiroRPC) GetBlockRaw(hash string) ([]byte, error) {
|
||||
// GetBlockRaw returns block with given hash as hex string
|
||||
func (zc *FiroRPC) GetBlockRaw(hash string) (string, error) {
|
||||
glog.V(1).Info("rpc: getblock (verbosity=false) ", hash)
|
||||
|
||||
res := btc.ResGetBlockRaw{}
|
||||
@ -144,15 +145,24 @@ func (zc *FiroRPC) GetBlockRaw(hash string) ([]byte, error) {
|
||||
err := zc.Call(&req, &res)
|
||||
|
||||
if err != nil {
|
||||
return nil, errors.Annotatef(err, "hash %v", hash)
|
||||
return "", errors.Annotatef(err, "hash %v", hash)
|
||||
}
|
||||
if res.Error != nil {
|
||||
if btc.IsErrBlockNotFound(res.Error) {
|
||||
return nil, bchain.ErrBlockNotFound
|
||||
return "", bchain.ErrBlockNotFound
|
||||
}
|
||||
return nil, errors.Annotatef(res.Error, "hash %v", hash)
|
||||
return "", errors.Annotatef(res.Error, "hash %v", hash)
|
||||
}
|
||||
return hex.DecodeString(res.Result)
|
||||
return res.Result,nil
|
||||
}
|
||||
|
||||
// GetBlockBytes returns block with given hash as bytes
|
||||
func (zc *FiroRPC) GetBlockBytes(hash string) ([]byte, error) {
|
||||
block,err:=zc.GetBlockRaw(hash)
|
||||
if err != nil {
|
||||
return nil,err
|
||||
}
|
||||
return hex.DecodeString(block)
|
||||
}
|
||||
|
||||
func (zc *FiroRPC) GetTransactionForMempool(txid string) (*bchain.Tx, error) {
|
||||
|
||||
@ -78,7 +78,7 @@ func (f *FloRPC) GetBlock(hash string, height uint32) (*bchain.Block, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
data, err := f.GetBlockRaw(hash)
|
||||
data, err := f.GetBlockBytes(hash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@ -164,7 +164,8 @@ func (z *ZCashRPC) GetMempoolEntry(txid string) (*bchain.MempoolEntry, error) {
|
||||
return nil, errors.New("GetMempoolEntry: not implemented")
|
||||
}
|
||||
|
||||
func isErrBlockNotFound(err *bchain.RPCError) bool {
|
||||
return err.Message == "Block not found" ||
|
||||
err.Message == "Block height out of range"
|
||||
// GetBlockRaw is not supported
|
||||
func (z *ZCashRPC) GetBlockRaw(hash string) (string, error) {
|
||||
return "", errors.New("GetBlockRaw: not supported")
|
||||
}
|
||||
|
||||
|
||||
@ -285,6 +285,7 @@ type BlockChain interface {
|
||||
GetBlockHeader(hash string) (*BlockHeader, error)
|
||||
GetBlock(hash string, height uint32) (*Block, error)
|
||||
GetBlockInfo(hash string) (*BlockInfo, error)
|
||||
GetBlockRaw(hash string) (string, error)
|
||||
GetMempoolTransactions() ([]string, error)
|
||||
GetTransaction(txid string) (*Tx, error)
|
||||
GetTransactionForMempool(txid string) (*Tx, error)
|
||||
|
||||
@ -174,6 +174,7 @@ func (s *PublicServer) ConnectFullPublicInterface() {
|
||||
serveMux.HandleFunc(path+"api/xpub/", s.jsonHandler(s.apiXpub, apiDefault))
|
||||
serveMux.HandleFunc(path+"api/utxo/", s.jsonHandler(s.apiUtxo, apiDefault))
|
||||
serveMux.HandleFunc(path+"api/block/", s.jsonHandler(s.apiBlock, apiDefault))
|
||||
serveMux.HandleFunc(path+"api/rawblock/", s.jsonHandler(s.apiBlockRaw, apiDefault))
|
||||
serveMux.HandleFunc(path+"api/sendtx/", s.jsonHandler(s.apiSendTx, apiDefault))
|
||||
serveMux.HandleFunc(path+"api/estimatefee/", s.jsonHandler(s.apiEstimateFee, apiDefault))
|
||||
serveMux.HandleFunc(path+"api/balancehistory/", s.jsonHandler(s.apiBalanceHistory, apiDefault))
|
||||
@ -185,6 +186,7 @@ func (s *PublicServer) ConnectFullPublicInterface() {
|
||||
serveMux.HandleFunc(path+"api/v2/xpub/", s.jsonHandler(s.apiXpub, apiV2))
|
||||
serveMux.HandleFunc(path+"api/v2/utxo/", s.jsonHandler(s.apiUtxo, apiV2))
|
||||
serveMux.HandleFunc(path+"api/v2/block/", s.jsonHandler(s.apiBlock, apiV2))
|
||||
serveMux.HandleFunc(path+"api/v2/rawblock/", s.jsonHandler(s.apiBlockRaw, apiDefault))
|
||||
serveMux.HandleFunc(path+"api/v2/sendtx/", s.jsonHandler(s.apiSendTx, apiV2))
|
||||
serveMux.HandleFunc(path+"api/v2/estimatefee/", s.jsonHandler(s.apiEstimateFee, apiV2))
|
||||
serveMux.HandleFunc(path+"api/v2/feestats/", s.jsonHandler(s.apiFeeStats, apiV2))
|
||||
@ -1139,6 +1141,16 @@ func (s *PublicServer) apiBlock(r *http.Request, apiVersion int) (interface{}, e
|
||||
return block, err
|
||||
}
|
||||
|
||||
func (s *PublicServer) apiBlockRaw(r *http.Request, apiVersion int) (interface{}, error) {
|
||||
var block *api.BlockRaw
|
||||
var err error
|
||||
s.metrics.ExplorerViews.With(common.Labels{"action": "api-block-raw"}).Inc()
|
||||
if i := strings.LastIndexByte(r.URL.Path, '/'); i > 0 {
|
||||
block, err = s.api.GetBlockRaw(r.URL.Path[i+1:])
|
||||
}
|
||||
return block, err
|
||||
}
|
||||
|
||||
func (s *PublicServer) apiFeeStats(r *http.Request, apiVersion int) (interface{}, error) {
|
||||
var feeStats *api.FeeStats
|
||||
var err error
|
||||
|
||||
@ -937,6 +937,15 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) {
|
||||
`{"page":1,"totalPages":1,"itemsOnPage":1000,"hash":"0000000076fbbed90fd75b0e18856aa35baa984e9c9d444cf746ad85e94e2997","nextBlockHash":"00000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b6","height":225493,"confirmations":2,"size":1234567,"time":1521515026,"version":0,"merkleRoot":"","nonce":"","bits":"","difficulty":"","txCount":2,"txs":[{"txid":"00b2c06055e5e90e9c82bd4181fde310104391a7fa4f289b1704e5d90caa3840","vin":[],"vout":[{"value":"100000000","n":0,"addresses":["mfcWp7DB6NuaZsExybTTXpVgWz559Np4Ti"],"isAddress":true},{"value":"12345","n":1,"spent":true,"addresses":["mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz"],"isAddress":true},{"value":"12345","n":2,"addresses":["mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz"],"isAddress":true}],"blockHash":"0000000076fbbed90fd75b0e18856aa35baa984e9c9d444cf746ad85e94e2997","blockHeight":225493,"confirmations":2,"blockTime":1521515026,"value":"100024690","valueIn":"0","fees":"0"},{"txid":"effd9ef509383d536b1c8af5bf434c8efbf521a4f2befd4022bbd68694b4ac75","vin":[],"vout":[{"value":"1234567890123","n":0,"spent":true,"addresses":["mv9uLThosiEnGRbVPS7Vhyw6VssbVRsiAw"],"isAddress":true},{"value":"1","n":1,"spent":true,"addresses":["2MzmAKayJmja784jyHvRUW1bXPget1csRRG"],"isAddress":true},{"value":"9876","n":2,"spent":true,"addresses":["2NEVv9LJmAnY99W1pFoc5UJjVdypBqdnvu1"],"isAddress":true}],"blockHash":"0000000076fbbed90fd75b0e18856aa35baa984e9c9d444cf746ad85e94e2997","blockHeight":225493,"confirmations":2,"blockTime":1521515026,"value":"1234567900000","valueIn":"0","fees":"0"}]}`,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "apiGetRawBlock",
|
||||
r: newGetRequest(ts.URL + "/api/v2/rawblock/225493"),
|
||||
status: http.StatusOK,
|
||||
contentType: "application/json; charset=utf-8",
|
||||
body: []string{
|
||||
`{"hex":"00e0ff3fd42677a86f1515bafcf9802c1765e02226655a9b97fd44132602000000000000"}`,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
|
||||
@ -136,6 +136,11 @@ func getTxInBlock(b *bchain.Block, txid string) *bchain.Tx {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *fakeBlockChain) GetBlockRaw(hash string) (string, error) {
|
||||
return "00e0ff3fd42677a86f1515bafcf9802c1765e02226655a9b97fd44132602000000000000", nil
|
||||
}
|
||||
|
||||
|
||||
func (c *fakeBlockChain) GetTransaction(txid string) (v *bchain.Tx, err error) {
|
||||
v = getTxInBlock(GetTestBitcoinTypeBlock1(c.Parser), txid)
|
||||
if v == nil {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user