Skip to content

Commit

Permalink
add 'raw' query parameter to the blocks (#899)
Browse files Browse the repository at this point in the history
* add 'raw' query parameter to the blocks

* summary -> summary.Header

Co-authored-by: libotony <[email protected]>

* change variable name

* make expanded and raw mutually exclusive

* add unit tests

* fix linting

---------

Co-authored-by: libotony <[email protected]>
  • Loading branch information
YeahNotSewerSide and libotony authored Dec 6, 2024
1 parent 1ea83b2 commit 3aa2935
Show file tree
Hide file tree
Showing 5 changed files with 110 additions and 4 deletions.
29 changes: 25 additions & 4 deletions api/blocks/blocks.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,11 @@
package blocks

import (
"encoding/hex"
"fmt"
"net/http"

"github.com/ethereum/go-ethereum/rlp"
"github.com/gorilla/mux"
"github.com/pkg/errors"
"github.com/vechain/thor/v2/api/utils"
Expand All @@ -34,9 +37,17 @@ func (b *Blocks) handleGetBlock(w http.ResponseWriter, req *http.Request) error
if err != nil {
return utils.BadRequest(errors.WithMessage(err, "revision"))
}
expanded := req.URL.Query().Get("expanded")
if expanded != "" && expanded != "false" && expanded != "true" {
return utils.BadRequest(errors.WithMessage(errors.New("should be boolean"), "expanded"))
raw, err := utils.StringToBoolean(req.URL.Query().Get("raw"), false)
if err != nil {
return utils.BadRequest(errors.WithMessage(err, "raw"))
}
expanded, err := utils.StringToBoolean(req.URL.Query().Get("expanded"), false)
if err != nil {
return utils.BadRequest(errors.WithMessage(err, "expanded"))
}

if raw && expanded {
return utils.BadRequest(errors.WithMessage(errors.New("Raw and Expanded are mutually exclusive"), "raw&expanded"))
}

summary, err := utils.GetSummary(revision, b.repo, b.bft)
Expand All @@ -47,6 +58,16 @@ func (b *Blocks) handleGetBlock(w http.ResponseWriter, req *http.Request) error
return err
}

if raw {
rlpEncoded, err := rlp.EncodeToBytes(summary.Header)
if err != nil {
return err
}
return utils.WriteJSON(w, &JSONRawBlockSummary{
fmt.Sprintf("0x%s", hex.EncodeToString(rlpEncoded)),
})
}

isTrunk, err := b.isTrunk(summary.Header.ID(), summary.Header.Number())
if err != nil {
return err
Expand All @@ -61,7 +82,7 @@ func (b *Blocks) handleGetBlock(w http.ResponseWriter, req *http.Request) error
}

jSummary := buildJSONBlockSummary(summary, isTrunk, isFinalized)
if expanded == "true" {
if expanded {
txs, err := b.repo.GetBlockTransactions(summary.Header.ID())
if err != nil {
return err
Expand Down
55 changes: 55 additions & 0 deletions api/blocks/blocks_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
package blocks_test

import (
"encoding/hex"
"encoding/json"
"math"
"math/big"
Expand All @@ -15,6 +16,7 @@ import (
"strings"
"testing"

"github.com/ethereum/go-ethereum/rlp"
"github.com/gorilla/mux"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
Expand Down Expand Up @@ -55,6 +57,8 @@ func TestBlock(t *testing.T) {
"testGetFinalizedBlock": testGetFinalizedBlock,
"testGetJustifiedBlock": testGetJustifiedBlock,
"testGetBlockWithRevisionNumberTooHigh": testGetBlockWithRevisionNumberTooHigh,
"testMutuallyExclusiveQueries": testMutuallyExclusiveQueries,
"testGetRawBlock": testGetRawBlock,
} {
t.Run(name, tt)
}
Expand All @@ -67,6 +71,22 @@ func testBadQueryParams(t *testing.T) {

assert.Equal(t, http.StatusBadRequest, statusCode)
assert.Equal(t, "expanded: should be boolean", strings.TrimSpace(string(res)))

badQueryParams = "?raw=1"
res, statusCode, err = tclient.RawHTTPClient().RawHTTPGet("/blocks/best" + badQueryParams)
require.NoError(t, err)

assert.Equal(t, http.StatusBadRequest, statusCode)
assert.Equal(t, "raw: should be boolean", strings.TrimSpace(string(res)))
}

func testMutuallyExclusiveQueries(t *testing.T) {
badQueryParams := "?expanded=true&raw=true"
res, statusCode, err := tclient.RawHTTPClient().RawHTTPGet("/blocks/best" + badQueryParams)
require.NoError(t, err)

assert.Equal(t, http.StatusBadRequest, statusCode)
assert.Equal(t, "raw&expanded: Raw and Expanded are mutually exclusive", strings.TrimSpace(string(res)))
}

func testGetBestBlock(t *testing.T) {
Expand All @@ -80,6 +100,41 @@ func testGetBestBlock(t *testing.T) {
assert.Equal(t, http.StatusOK, statusCode)
}

func testGetRawBlock(t *testing.T) {
res, statusCode, err := tclient.RawHTTPClient().RawHTTPGet("/blocks/best?raw=true")
require.NoError(t, err)
rawBlock := new(blocks.JSONRawBlockSummary)
if err := json.Unmarshal(res, &rawBlock); err != nil {
t.Fatal(err)
}

blockBytes, err := hex.DecodeString(rawBlock.Raw[2:len(rawBlock.Raw)])
if err != nil {
t.Fatal(err)
}

header := block.Header{}
err = rlp.DecodeBytes(blockBytes, &header)
if err != nil {
t.Fatal(err)
}

expHeader := blk.Header()
assert.Equal(t, expHeader.Number(), header.Number(), "Number should be equal")
assert.Equal(t, expHeader.ID(), header.ID(), "Hash should be equal")
assert.Equal(t, expHeader.ParentID(), header.ParentID(), "ParentID should be equal")
assert.Equal(t, expHeader.Timestamp(), header.Timestamp(), "Timestamp should be equal")
assert.Equal(t, expHeader.TotalScore(), header.TotalScore(), "TotalScore should be equal")
assert.Equal(t, expHeader.GasLimit(), header.GasLimit(), "GasLimit should be equal")
assert.Equal(t, expHeader.GasUsed(), header.GasUsed(), "GasUsed should be equal")
assert.Equal(t, expHeader.Beneficiary(), header.Beneficiary(), "Beneficiary should be equal")
assert.Equal(t, expHeader.TxsRoot(), header.TxsRoot(), "TxsRoot should be equal")
assert.Equal(t, expHeader.StateRoot(), header.StateRoot(), "StateRoot should be equal")
assert.Equal(t, expHeader.ReceiptsRoot(), header.ReceiptsRoot(), "ReceiptsRoot should be equal")

assert.Equal(t, http.StatusOK, statusCode)
}

func testGetBlockByHeight(t *testing.T) {
res, statusCode, err := tclient.RawHTTPClient().RawHTTPGet("/blocks/1")
require.NoError(t, err)
Expand Down
4 changes: 4 additions & 0 deletions api/blocks/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ type JSONBlockSummary struct {
IsFinalized bool `json:"isFinalized"`
}

type JSONRawBlockSummary struct {
Raw string `json:"raw"`
}

type JSONCollapsedBlock struct {
*JSONBlockSummary
Transactions []thor.Bytes32 `json:"transactions"`
Expand Down
13 changes: 13 additions & 0 deletions api/doc/thor.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,7 @@ paths:
parameters:
- $ref: '#/components/parameters/RevisionInPath'
- $ref: '#/components/parameters/ExpandedInQuery'
- $ref: '#/components/parameters/RawBlockInQuery'
tags:
- Blocks
summary: Retrieve a block
Expand Down Expand Up @@ -2353,6 +2354,18 @@ components:
type: boolean
example: false

RawBlockInQuery:
name: raw
in: query
required: false
description: |
Whether the block should be returned in RLP encoding or not.
- `true` returns `block` as an RLP encoded object
- `false` returns `block` as a structured JSON object
schema:
type: boolean
example: false

PendingInQuery:
name: pending
in: query
Expand Down
13 changes: 13 additions & 0 deletions api/utils/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import (
"encoding/json"
"io"
"net/http"

"github.com/pkg/errors"
)

type httpError struct {
Expand Down Expand Up @@ -36,6 +38,17 @@ func BadRequest(cause error) error {
}
}

func StringToBoolean(boolStr string, defaultVal bool) (bool, error) {
if boolStr == "" {
return defaultVal, nil
} else if boolStr == "false" {
return false, nil
} else if boolStr == "true" {
return true, nil
}
return false, errors.New("should be boolean")
}

// Forbidden convenience method to create http forbidden error.
func Forbidden(cause error) error {
return &httpError{
Expand Down

0 comments on commit 3aa2935

Please sign in to comment.