Skip to content

Commit

Permalink
Merge pull request #342 from qianbin/expanded-block-api-doc
Browse files Browse the repository at this point in the history
api/doc: add query option 'expanded' for GET /blocks/{rev}
  • Loading branch information
libotony committed Mar 13, 2020
2 parents a0dd4ac + 95d78dd commit eb84b13
Show file tree
Hide file tree
Showing 6 changed files with 342 additions and 167 deletions.
30 changes: 26 additions & 4 deletions api/blocks/blocks.go
Expand Up @@ -32,6 +32,11 @@ 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"))
}

summary, err := b.getBlockSummary(revision)
if err != nil {
if b.repo.IsNotFound(err) {
Expand All @@ -43,11 +48,28 @@ func (b *Blocks) handleGetBlock(w http.ResponseWriter, req *http.Request) error
if err != nil {
return err
}
blk, err := convertBlock(summary, isTrunk)
if err != nil {
return err

jSummary := buildJSONBlockSummary(summary, isTrunk)
if expanded == "true" {
txs, err := b.repo.GetBlockTransactions(summary.Header.ID())
if err != nil {
return err
}
receipts, err := b.repo.GetBlockReceipts(summary.Header.ID())
if err != nil {
return err
}

return utils.WriteJSON(w, &JSONExpandedBlock{
jSummary,
buildJSONEmbeddedTxs(txs, receipts),
})
}
return utils.WriteJSON(w, blk)

return utils.WriteJSON(w, &JSONCollapsedBlock{
jSummary,
summary.Txs,
})
}

func (b *Blocks) parseRevision(revision string) (interface{}, error) {
Expand Down
11 changes: 5 additions & 6 deletions api/blocks/blocks_test.go
Expand Up @@ -3,7 +3,7 @@
// Distributed under the GNU Lesser General Public License v3.0 software license, see the accompanying
// file LICENSE or <https://www.gnu.org/licenses/lgpl-3.0.html>

package blocks_test
package blocks

import (
"encoding/json"
Expand All @@ -17,7 +17,6 @@ import (
"github.com/ethereum/go-ethereum/crypto"
"github.com/gorilla/mux"
"github.com/stretchr/testify/assert"
"github.com/vechain/thor/api/blocks"
"github.com/vechain/thor/block"
"github.com/vechain/thor/chain"
"github.com/vechain/thor/genesis"
Expand Down Expand Up @@ -50,8 +49,8 @@ func TestBlock(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, statusCode)

res, statusCode = httpGet(t, ts.URL+"/blocks/"+blk.Header().ID().String())
rb := new(blocks.Block)
if err := json.Unmarshal(res, &rb); err != nil {
rb := new(JSONCollapsedBlock)
if err := json.Unmarshal(res, rb); err != nil {
t.Fatal(err)
}
checkBlock(t, blk, rb)
Expand Down Expand Up @@ -123,12 +122,12 @@ func initBlockServer(t *testing.T) {
t.Fatal(err)
}
router := mux.NewRouter()
blocks.New(repo).Mount(router, "/blocks")
New(repo).Mount(router, "/blocks")
ts = httptest.NewServer(router)
blk = block
}

func checkBlock(t *testing.T, expBl *block.Block, actBl *blocks.Block) {
func checkBlock(t *testing.T, expBl *block.Block, actBl *JSONCollapsedBlock) {
header := expBl.Header()
assert.Equal(t, header.Number(), actBl.Number, "Number should be equal")
assert.Equal(t, header.ID(), actBl.ID, "Hash should be equal")
Expand Down
181 changes: 155 additions & 26 deletions api/blocks/types.go
Expand Up @@ -6,39 +6,93 @@
package blocks

import (
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/common/math"
"github.com/vechain/thor/chain"
"github.com/vechain/thor/thor"
"github.com/vechain/thor/tx"
)

//Block block
type Block struct {
Number uint32 `json:"number"`
ID thor.Bytes32 `json:"id"`
Size uint32 `json:"size"`
ParentID thor.Bytes32 `json:"parentID"`
Timestamp uint64 `json:"timestamp"`
GasLimit uint64 `json:"gasLimit"`
Beneficiary thor.Address `json:"beneficiary"`
GasUsed uint64 `json:"gasUsed"`
TotalScore uint64 `json:"totalScore"`
TxsRoot thor.Bytes32 `json:"txsRoot"`
TxsFeatures uint32 `json:"txsFeatures"`
StateRoot thor.Bytes32 `json:"stateRoot"`
ReceiptsRoot thor.Bytes32 `json:"receiptsRoot"`
Signer thor.Address `json:"signer"`
IsTrunk bool `json:"isTrunk"`
type JSONBlockSummary struct {
Number uint32 `json:"number"`
ID thor.Bytes32 `json:"id"`
Size uint32 `json:"size"`
ParentID thor.Bytes32 `json:"parentID"`
Timestamp uint64 `json:"timestamp"`
GasLimit uint64 `json:"gasLimit"`
Beneficiary thor.Address `json:"beneficiary"`
GasUsed uint64 `json:"gasUsed"`
TotalScore uint64 `json:"totalScore"`
TxsRoot thor.Bytes32 `json:"txsRoot"`
TxsFeatures uint32 `json:"txsFeatures"`
StateRoot thor.Bytes32 `json:"stateRoot"`
ReceiptsRoot thor.Bytes32 `json:"receiptsRoot"`
Signer thor.Address `json:"signer"`
IsTrunk bool `json:"isTrunk"`
}

type JSONCollapsedBlock struct {
*JSONBlockSummary
Transactions []thor.Bytes32 `json:"transactions"`
}

func convertBlock(summary *chain.BlockSummary, isTrunk bool) (*Block, error) {
header := summary.Header
type JSONClause struct {
To *thor.Address `json:"to"`
Value math.HexOrDecimal256 `json:"value"`
Data string `json:"data"`
}

signer, err := header.Signer()
if err != nil {
return nil, err
}
type JSONTransfer struct {
Sender thor.Address `json:"sender"`
Recipient thor.Address `json:"recipient"`
Amount *math.HexOrDecimal256 `json:"amount"`
}

type JSONEvent struct {
Address thor.Address `json:"address"`
Topics []thor.Bytes32 `json:"topics"`
Data string `json:"data"`
}

type JSONOutput struct {
ContractAddress *thor.Address `json:"contractAddress"`
Events []*JSONEvent `json:"events"`
Transfers []*JSONTransfer `json:"transfers"`
}

type JSONEmbeddedTx struct {
ID thor.Bytes32 `json:"id"`
ChainTag byte `json:"chainTag"`
BlockRef string `json:"blockRef"`
Expiration uint32 `json:"expiration"`
Clauses []*JSONClause `json:"clauses"`
GasPriceCoef uint8 `json:"gasPriceCoef"`
Gas uint64 `json:"gas"`
Origin thor.Address `json:"origin"`
Delegator *thor.Address `json:"delegator"`
Nonce math.HexOrDecimal64 `json:"nonce"`
DependsOn *thor.Bytes32 `json:"dependsOn"`
Size uint32 `json:"size"`

// receipt part
GasUsed uint64 `json:"gasUsed"`
GasPayer thor.Address `json:"gasPayer"`
Paid *math.HexOrDecimal256 `json:"paid"`
Reward *math.HexOrDecimal256 `json:"reward"`
Reverted bool `json:"reverted"`
Outputs []*JSONOutput `json:"outputs"`
}

type JSONExpandedBlock struct {
*JSONBlockSummary
Transactions []*JSONEmbeddedTx `json:"transactions"`
}

func buildJSONBlockSummary(summary *chain.BlockSummary, isTrunk bool) *JSONBlockSummary {
header := summary.Header
signer, _ := header.Signer()

return &Block{
return &JSONBlockSummary{
Number: header.Number(),
ID: header.ID(),
ParentID: header.ParentID(),
Expand All @@ -54,6 +108,81 @@ func convertBlock(summary *chain.BlockSummary, isTrunk bool) (*Block, error) {
TxsRoot: header.TxsRoot(),
TxsFeatures: uint32(header.TxsFeatures()),
IsTrunk: isTrunk,
Transactions: summary.Txs,
}, nil
}
}

func buildJSONOutput(txID thor.Bytes32, index uint32, c *tx.Clause, o *tx.Output) *JSONOutput {
jo := &JSONOutput{
ContractAddress: nil,
Events: make([]*JSONEvent, 0, len(o.Events)),
Transfers: make([]*JSONTransfer, 0, len(o.Transfers)),
}
if c.To() == nil {
addr := thor.CreateContractAddress(txID, index, 0)
jo.ContractAddress = &addr
}
for _, e := range o.Events {
jo.Events = append(jo.Events, &JSONEvent{
Address: e.Address,
Data: hexutil.Encode(e.Data),
Topics: e.Topics,
})
}
for _, t := range o.Transfers {
jo.Transfers = append(jo.Transfers, &JSONTransfer{
Sender: t.Sender,
Recipient: t.Recipient,
Amount: (*math.HexOrDecimal256)(t.Amount),
})
}
return jo
}

func buildJSONEmbeddedTxs(txs tx.Transactions, receipts tx.Receipts) []*JSONEmbeddedTx {
jTxs := make([]*JSONEmbeddedTx, 0, len(txs))
for itx, tx := range txs {
receipt := receipts[itx]

clauses := tx.Clauses()
blockRef := tx.BlockRef()
origin, _ := tx.Origin()
delegator, _ := tx.Delegator()

jcs := make([]*JSONClause, 0, len(clauses))
jos := make([]*JSONOutput, 0, len(receipt.Outputs))

for i, c := range clauses {
jcs = append(jcs, &JSONClause{
c.To(),
math.HexOrDecimal256(*c.Value()),
hexutil.Encode(c.Data()),
})
if !receipt.Reverted {
jos = append(jos, buildJSONOutput(tx.ID(), uint32(i), c, receipt.Outputs[i]))
}
}

jTxs = append(jTxs, &JSONEmbeddedTx{
ID: tx.ID(),
ChainTag: tx.ChainTag(),
BlockRef: hexutil.Encode(blockRef[:]),
Expiration: tx.Expiration(),
Clauses: jcs,
GasPriceCoef: tx.GasPriceCoef(),
Gas: tx.Gas(),
Origin: origin,
Delegator: delegator,
Nonce: math.HexOrDecimal64(tx.Nonce()),
DependsOn: tx.DependsOn(),
Size: uint32(tx.Size()),

GasUsed: receipt.GasUsed,
GasPayer: receipt.GasPayer,
Paid: (*math.HexOrDecimal256)(receipt.Paid),
Reward: (*math.HexOrDecimal256)(receipt.Reward),
Reverted: receipt.Reverted,
Outputs: jos,
})
}
return jTxs
}
51 changes: 21 additions & 30 deletions api/doc/bindata.go

Large diffs are not rendered by default.

0 comments on commit eb84b13

Please sign in to comment.