Skip to content

Commit

Permalink
add /explorer/block/:id endpoint
Browse files Browse the repository at this point in the history
  • Loading branch information
chris124567 committed Jan 3, 2024
1 parent 60bc47d commit 3d189c7
Show file tree
Hide file tree
Showing 5 changed files with 91 additions and 4 deletions.
8 changes: 8 additions & 0 deletions api/client.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package api

import (
"fmt"

"go.sia.tech/core/consensus"
"go.sia.tech/core/types"
"go.sia.tech/jape"
Expand Down Expand Up @@ -64,3 +66,9 @@ func (c *Client) Tip() (resp types.ChainIndex, err error) {
err = c.c.GET("/explorer/tip", &resp)
return
}

// Block returns a block with the given block ID.
func (c *Client) Block(id types.BlockID) (resp types.Block, err error) {
err = c.c.GET(fmt.Sprintf("/explorer/block/%s", id), &resp)
return
}
20 changes: 18 additions & 2 deletions api/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ type (
// Explorer implements a Sia explorer.
Explorer interface {
Tip() (types.ChainIndex, error)
Block(id types.BlockID) (types.Block, error)
}
)

Expand Down Expand Up @@ -132,10 +133,24 @@ func (s *server) txpoolBroadcastHandler(jc jape.Context) {

func (s *server) explorerTipHandler(jc jape.Context) {
tip, err := s.e.Tip()
jc.Check("failed to get tip", err)
if jc.Check("failed to get tip", err) != nil {
return
}
jc.Encode(tip)
}

func (s *server) explorerBlockHandler(jc jape.Context) {
var id types.BlockID
if jc.DecodeParam("id", &id) != nil {
return
}
block, err := s.e.Block(id)
if jc.Check("failed to get block", err) != nil {
return
}
jc.Encode(block)
}

// NewServer returns an HTTP handler that serves the explored API.
func NewServer(e Explorer, cm ChainManager, s Syncer) http.Handler {
srv := server{
Expand All @@ -152,6 +167,7 @@ func NewServer(e Explorer, cm ChainManager, s Syncer) http.Handler {
"GET /txpool/fee": srv.txpoolFeeHandler,
"POST /txpool/broadcast": srv.txpoolBroadcastHandler,

"GET /explorer/tip": srv.explorerTipHandler,
"GET /explorer/tip": srv.explorerTipHandler,
"GET /explorer/block/:id": srv.explorerBlockHandler,
})
}
5 changes: 5 additions & 0 deletions explorer/explorer.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ type Store interface {
chain.Subscriber

Tip() (types.ChainIndex, error)
Block(id types.BlockID) (types.Block, error)
}

// Explorer implements a Sia explorer.
Expand All @@ -26,3 +27,7 @@ func NewExplorer(s Store) *Explorer {
func (e *Explorer) Tip() (types.ChainIndex, error) {

Check warning on line 27 in explorer/explorer.go

View workflow job for this annotation

GitHub Actions / test (ubuntu-latest, 1.19)

exported: exported method Explorer.Tip should have comment or be unexported (revive)
return e.s.Tip()
}

func (e *Explorer) Block(id types.BlockID) (types.Block, error) {

Check warning on line 31 in explorer/explorer.go

View workflow job for this annotation

GitHub Actions / test (ubuntu-latest, 1.19)

exported: exported method Explorer.Block should have comment or be unexported (revive)
return e.s.Block(id)
}
61 changes: 59 additions & 2 deletions persist/sqlite/query.go
Original file line number Diff line number Diff line change
@@ -1,18 +1,75 @@
package sqlite

import "go.sia.tech/core/types"
import (
"time"

"go.sia.tech/core/types"
)

func decode(obj types.DecoderFrom, data []byte) error {
d := types.NewBufDecoder(data)
obj.DecodeFrom(d)
return d.Err()
}

func decodeUint64(x *uint64, data []byte) error {
d := types.NewBufDecoder(data)
if x != nil {
*x = d.ReadUint64()
}
return d.Err()
}

// Tip implements explorer.Store.
func (s *Store) Tip() (result types.ChainIndex, err error) {
var data []byte
if err = s.queryRow("SELECT id, height FROM Blocks WHERE height = (SELECT MAX(height) from Blocks)").Scan(&data, &result.Height); err != nil {
return
}
decode(&result.ID, data)
if err = decode(&result.ID, data); err != nil {
return
}
return
}

// Block implements explorer.Store.
func (s *Store) Block(id types.BlockID) (result types.Block, err error) {
{
var timestamp int64
var parentID, nonce []byte
if err = s.queryRow("SELECT parent_id, nonce, timestamp FROM Blocks WHERE id = ?", encode(id)).Scan(&parentID, &nonce, &timestamp); err != nil {
return
}
result.Timestamp = time.Unix(timestamp, 0).UTC()
if err = decode(&result.ParentID, parentID); err != nil {
return
}
if err = decodeUint64(&result.Nonce, nonce); err != nil {
return
}
}

{
var rows *loggedRows
if rows, err = s.query("SELECT address, value FROM MinerPayouts WHERE block_id = ? ORDER BY block_order", encode(id)); err != nil {
return
}

var address, value []byte
for rows.Next() {
if err = rows.Scan(&address, &value); err != nil {
return
}
var minerPayout types.SiacoinOutput
if err = decode(&minerPayout.Address, address); err != nil {
return
}
if err = decode(&minerPayout.Value, value); err != nil {
return
}
result.MinerPayouts = append(result.MinerPayouts, minerPayout)
}
}

return
}
1 change: 1 addition & 0 deletions persist/sqlite/txn.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ func encodeUint64(x uint64) []byte {
}

func (s *Store) addBlock(b types.Block, height uint64) error {
// nonce is encoded because database/sql doesn't support uint64 with high bit set
_, err := s.exec("INSERT INTO Blocks(id, height, parent_id, nonce, timestamp) VALUES (?, ?, ?, ?, ?);", encode(b.ID()), height, encode(b.ParentID), encodeUint64(b.Nonce), b.Timestamp.Unix())
return err
}
Expand Down

0 comments on commit 3d189c7

Please sign in to comment.