Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added /blocks and /block-height API method #1110

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions api/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -459,6 +459,10 @@ func (a BalanceHistories) SortAndAggregate(groupByTime uint32) BalanceHistories
return bhs
}

type BlockHeight struct {
Height uint32 `json:"height"`
}

// Blocks is list of blocks with paging information
type Blocks struct {
Paging
Expand Down
36 changes: 36 additions & 0 deletions api/worker.go
Original file line number Diff line number Diff line change
Expand Up @@ -911,6 +911,14 @@ func (w *Worker) txFromTxAddress(txid string, ta *db.TxAddresses, bi *db.BlockIn
}

func computePaging(count, page, itemsOnPage int) (Paging, int, int, int) {
if itemsOnPage == 0 {
totalPages := count - 1
return Paging{
ItemsOnPage: itemsOnPage,
Page: page + 1,
TotalPages: totalPages,
}, 0, 0, page
}
from := page * itemsOnPage
totalPages := (count - 1) / itemsOnPage
if totalPages < 0 {
Expand Down Expand Up @@ -2203,6 +2211,34 @@ func (w *Worker) GetBlock(bid string, page int, txsOnPage int) (*Block, error) {
if err != nil {
return nil, errors.Annotatef(err, "GetBestBlock")
}
if (txsOnPage == 0) {
pg, _, _, _ := computePaging(txCount, page, txsOnPage)
addresses := w.newAddressesMapForAliases()

txs := make([]*Tx, 0)

return &Block{
Paging: pg,
BlockInfo: BlockInfo{
Hash: bi.Hash,
Prev: bi.Prev,
Next: bi.Next,
Height: bi.Height,
Confirmations: bi.Confirmations,
Size: bi.Size,
Time: bi.Time,
Bits: bi.Bits,
Difficulty: string(bi.Difficulty),
MerkleRoot: bi.MerkleRoot,
Nonce: string(bi.Nonce),
Txids: bi.Txids,
Version: bi.Version,
},
TxCount: txCount,
Transactions: txs,
AddressAliases: w.getAddressAliases(addresses),
}, nil
}
pg, from, to, page := computePaging(txCount, page, txsOnPage)
txs := make([]*Tx, to-from)
txi := 0
Expand Down
7 changes: 6 additions & 1 deletion docs/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -603,9 +603,14 @@ Response:
Returns information about block with transactions, subject to paging.

```
GET /api/v2/block/<block height|block hash>
GET /api/v2/block/<block height|block hash>[?page=<page>&pageSize=<size>]
```

The optional query parameters:

- _page_: specifies page of returned transactions, starting from 1. If out of range, Blockbook returns the closest possible page.
- _pageSize_: number of transactions returned by call (default and maximum 1000)

Response:

```javascript
Expand Down
70 changes: 65 additions & 5 deletions server/public.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ const txsOnPage = 25
const blocksOnPage = 50
const mempoolTxsOnPage = 50
const txsInAPI = 1000
const blocksInAPI = 100

const secondaryCoinCookieName = "secondary_coin"

Expand Down Expand Up @@ -196,6 +197,7 @@ func (s *PublicServer) ConnectFullPublicInterface() {
serveMux.HandleFunc(path+"api/estimatefee/", s.jsonHandler(s.apiEstimateFee, apiDefault))
serveMux.HandleFunc(path+"api/balancehistory/", s.jsonHandler(s.apiBalanceHistory, apiDefault))
// v2 format
serveMux.HandleFunc(path+"api/v2/block-height/", s.jsonHandler(s.apiBlockHeight, apiV2))
serveMux.HandleFunc(path+"api/v2/block-index/", s.jsonHandler(s.apiBlockIndex, apiV2))
serveMux.HandleFunc(path+"api/v2/block-filters/", s.jsonHandler(s.apiBlockFilters, apiV2))
serveMux.HandleFunc(path+"api/v2/tx-specific/", s.jsonHandler(s.apiTxSpecific, apiV2))
Expand All @@ -204,6 +206,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/blocks/", s.jsonHandler(s.apiBlocks, 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))
Expand Down Expand Up @@ -928,6 +931,18 @@ func (s *PublicServer) getAddressQueryParams(r *http.Request, accountDetails api
}, filterParam, gap
}

func (s *PublicServer) getBlockQueryParams(r *http.Request, maxPageSize int) (int, int) {
page, ec := strconv.Atoi(r.URL.Query().Get("page"))
if ec != nil {
page = 0
}
pageSize, ec := strconv.Atoi(r.URL.Query().Get("pageSize"))
if ec != nil || pageSize > maxPageSize {
pageSize = maxPageSize
}
return page, pageSize
}

func (s *PublicServer) explorerAddress(w http.ResponseWriter, r *http.Request) (tpl, *TemplateData, error) {
var addressParam string
i := strings.LastIndexByte(r.URL.Path, '/')
Expand Down Expand Up @@ -1484,23 +1499,68 @@ func (s *PublicServer) apiBalanceHistory(r *http.Request, apiVersion int) (inter
return history, err
}

func (s *PublicServer) apiBlockHeight(r *http.Request, apiVersion int) (interface{}, error) {
s.metrics.ExplorerViews.With(common.Labels{"action": "api-block-height"}).Inc()
bestBlock, _, err := s.db.GetBestBlock()
if err == nil {
blockHeight := &api.BlockHeight{
Height: bestBlock,
}
return blockHeight, err
}
return nil, err
}

func (s *PublicServer) apiBlock(r *http.Request, apiVersion int) (interface{}, error) {
var block *api.Block
var err error
s.metrics.ExplorerViews.With(common.Labels{"action": "api-block"}).Inc()

if i := strings.LastIndexByte(r.URL.Path, '/'); i > 0 {
page, ec := strconv.Atoi(r.URL.Query().Get("page"))
if ec != nil {
page = 0
}
block, err = s.api.GetBlock(r.URL.Path[i+1:], page, txsInAPI)
page, pageSize := s.getBlockQueryParams(r, txsInAPI)
block, err = s.api.GetBlock(r.URL.Path[i+1:], page, pageSize)
if err == nil && apiVersion == apiV1 {
return s.api.BlockToV1(block), nil
}
}
return block, err
}

func (s *PublicServer) apiBlocks(r *http.Request, apiVersion int) (interface{}, error) {
var blocks []*api.Block

s.metrics.ExplorerViews.With(common.Labels{"action": "api-blocks"}).Inc()

page, pageSize := s.getBlockQueryParams(r, blocksInAPI)

blocks = make([]*api.Block, 0, pageSize)

bestBlock, _, err := s.db.GetBestBlock()

if err != nil {
return blocks, err
}

for i := 0; i < pageSize; i++ {
blockNumber := bestBlock - uint32(page * pageSize + i)

// We have no more blocks, return as is
if blockNumber < 0 {
return blocks, nil
}

block, err := s.api.GetBlock(strconv.FormatUint(uint64(blockNumber), 10), 1, 0)

if err != nil {
return blocks, err
}

blocks = append(blocks, block)
}

return blocks, err
}

func (s *PublicServer) apiBlockRaw(r *http.Request, apiVersion int) (interface{}, error) {
var block *api.BlockRaw
var err error
Expand Down