Skip to content

Commit

Permalink
method for getting transactions by masterchain seqno
Browse files Browse the repository at this point in the history
  • Loading branch information
mr-tron committed Dec 4, 2023
1 parent e7c14bf commit cc29b91
Show file tree
Hide file tree
Showing 16 changed files with 903 additions and 13 deletions.
8 changes: 5 additions & 3 deletions api/example.http
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
GET {{host}}/v2/blockchain/blocks/(-1,8000000000000000,27578880)

GET {{host}}/v2/blockchain/blocks/(-1,8000000000000000,120014)

###
GET {{host}}/v2/blockchain/transactions/2B7E6A0661AB41822E3F515F3A3D2E89643999CDC9CD7ADE9577957A71D73F04
Expand Down Expand Up @@ -79,4 +78,7 @@ GET {{host}}/v2/blockchain/messages/81C7CC0C66452E5F1BC26A5D89B2B30AAB49B3B88EC9
GET {{host}}/v2/staking/pools?available_for=0%3A366c84f5b37224e0220a1d05a620deca0197544e2c94a928181a38e5dbbd33f5

###
GET {{host}}/v2/blockchain/masterchain/27578880/shards
GET {{host}}/v2/blockchain/masterchain/27578880/shards

###
GET {{host}}/v2/blockchain/masterchain/120014/transactions
72 changes: 72 additions & 0 deletions api/openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -1569,6 +1569,20 @@
],
"type": "object"
},
"BlockchainBlocks": {
"properties": {
"blocks": {
"items": {
"$ref": "#/components/schemas/BlockchainBlock"
},
"type": "array"
}
},
"required": [
"blocks"
],
"type": "object"
},
"BlockchainConfig": {
"properties": {
"0": {
Expand Down Expand Up @@ -6385,6 +6399,35 @@
]
}
},
"/v2/blockchain/masterchain/{masterchain_seqno}/blocks": {
"get": {
"description": "Get all blocks in all shards and workchains between target and previous masterchain block according to shards last blocks snapshot in masterchain. We don't recommend to build your app around this method because it has problem with scalability and will work very slow in the future.",
"operationId": "getBlockchainMasterchainBlocks",
"parameters": [
{
"$ref": "#/components/parameters/masterchainSeqno"
}
],
"responses": {
"200": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/BlockchainBlocks"
}
}
},
"description": "blockchain blocks"
},
"default": {
"$ref": "#/components/responses/Error"
}
},
"tags": [
"Blockchain"
]
}
},
"/v2/blockchain/masterchain/{masterchain_seqno}/config": {
"get": {
"description": "Get blockchain config from a specific block, if present.",
Expand Down Expand Up @@ -6472,6 +6515,35 @@
]
}
},
"/v2/blockchain/masterchain/{masterchain_seqno}/transactions": {
"get": {
"description": "Get all transactions in all shards and workchains between target and previous masterchain block according to shards last blocks snapshot in masterchain. We don't recommend to build your app around this method because it has problem with scalability and will work very slow in the future.",
"operationId": "getBlockchainMasterchainTransactions",
"parameters": [
{
"$ref": "#/components/parameters/masterchainSeqno"
}
],
"responses": {
"200": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Transactions"
}
}
},
"description": "blockchain transactions"
},
"default": {
"$ref": "#/components/responses/Error"
}
},
"tags": [
"Blockchain"
]
}
},
"/v2/blockchain/message": {
"post": {
"description": "Send message to blockchain",
Expand Down
43 changes: 43 additions & 0 deletions api/openapi.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,40 @@ paths:
$ref: '#/components/schemas/BlockchainBlockShards'
'default':
$ref: '#/components/responses/Error'
/v2/blockchain/masterchain/{masterchain_seqno}/blocks:
get:
description: Get all blocks in all shards and workchains between target and previous masterchain block according to shards last blocks snapshot in masterchain. We don't recommend to build your app around this method because it has problem with scalability and will work very slow in the future.
operationId: getBlockchainMasterchainBlocks
tags:
- Blockchain
parameters:
- $ref: '#/components/parameters/masterchainSeqno'
responses:
'200':
description: blockchain blocks
content:
application/json:
schema:
$ref: '#/components/schemas/BlockchainBlocks'
'default':
$ref: '#/components/responses/Error'
/v2/blockchain/masterchain/{masterchain_seqno}/transactions:
get:
description: Get all transactions in all shards and workchains between target and previous masterchain block according to shards last blocks snapshot in masterchain. We don't recommend to build your app around this method because it has problem with scalability and will work very slow in the future.
operationId: getBlockchainMasterchainTransactions
tags:
- Blockchain
parameters:
- $ref: '#/components/parameters/masterchainSeqno'
responses:
'200':
description: blockchain transactions
content:
application/json:
schema:
$ref: '#/components/schemas/Transactions'
'default':
$ref: '#/components/responses/Error'
/v2/blockchain/masterchain/{masterchain_seqno}/config:
get:
description: Get blockchain config from a specific block, if present.
Expand Down Expand Up @@ -2889,6 +2923,15 @@ components:
created_by:
type: string
example: A6A0BD6608672B11B79538A50B2204E748305C12AA0DED9C16CF0006CE3AF8DB
BlockchainBlocks:
type: object
required:
- blocks
properties:
blocks:
type: array
items:
$ref: '#/components/schemas/BlockchainBlock'
BlockchainBlockShards:
type: object
required:
Expand Down
1 change: 0 additions & 1 deletion gen.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
package opentonapi

//go:generate go run github.com/ogen-go/ogen/cmd/ogen -clean -no-client -package oas -target pkg/oas api/openapi.yml
//go:generate go run github.com/ogen-go/ogen/cmd/ogen -convenient-errors -clean -no-server -package tonapi -target tonapi api/openapi.yml
//go:generate go run api/jsonify.go api/openapi.yml api/openapi.json
100 changes: 100 additions & 0 deletions pkg/api/blockchain_handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"encoding/json"
"errors"
"fmt"
"golang.org/x/exp/maps"
"net/http"

"github.com/tonkeeper/tongo/contract/elector"
Expand Down Expand Up @@ -37,6 +38,9 @@ func (h *Handler) GetBlockchainBlock(ctx context.Context, params oas.GetBlockcha

func (h *Handler) GetBlockchainMasterchainShards(ctx context.Context, params oas.GetBlockchainMasterchainShardsParams) (r *oas.BlockchainBlockShards, _ error) {
shards, err := h.storage.GetBlockShards(ctx, ton.BlockID{Shard: 0x8000000000000000, Seqno: uint32(params.MasterchainSeqno), Workchain: -1})
if errors.Is(err, core.ErrEntityNotFound) {
return nil, toError(http.StatusNotFound, err)
}
if err != nil {
return nil, toError(http.StatusInternalServerError, err)
}
Expand All @@ -51,6 +55,102 @@ func (h *Handler) GetBlockchainMasterchainShards(ctx context.Context, params oas
return &res, nil
}

func (h *Handler) blocksDiff(ctx context.Context, masterchainSeqno int32) ([]ton.BlockID, error) {
shards, err := h.storage.GetBlockShards(ctx, ton.BlockID{Shard: 0x8000000000000000, Seqno: uint32(masterchainSeqno), Workchain: -1})
if errors.Is(err, core.ErrEntityNotFound) {
return nil, toError(http.StatusNotFound, err)
}
if err != nil {
return nil, toError(http.StatusInternalServerError, err)
}
prevShards, err := h.storage.GetBlockShards(ctx, ton.BlockID{Shard: 0x8000000000000000, Seqno: uint32(masterchainSeqno) - 1, Workchain: -1})
if errors.Is(err, core.ErrEntityNotFound) {
return nil, toError(http.StatusNotFound, err)
}
if err != nil {
return nil, toError(http.StatusInternalServerError, err)
}
blocks := []ton.BlockID{{Shard: 0x8000000000000000, Seqno: uint32(masterchainSeqno), Workchain: -1}}

for _, s := range shards {
missedBlocks, err := findMissedBlocks(ctx, h.storage, s, prevShards)
if err != nil {
return nil, err
}
blocks = append(blocks, missedBlocks...)
}

return blocks, nil
}

func findMissedBlocks(ctx context.Context, s storage, id ton.BlockID, prev []ton.BlockID) ([]ton.BlockID, error) {
for _, p := range prev {
if id.Shard == p.Shard && id.Workchain == p.Workchain {
blocks := make([]ton.BlockID, 0, int(id.Seqno-p.Seqno))
for i := p.Seqno; i < id.Seqno; i++ {
blocks = append(blocks, ton.BlockID{Workchain: p.Workchain, Shard: p.Shard, Seqno: i})
}
return blocks, nil
}
}
blocks := []ton.BlockID{id}
header, err := s.GetBlockHeader(ctx, id)
if err != nil {
return nil, err
}
for _, p := range header.PrevBlocks {
missed, err := findMissedBlocks(ctx, s, p.BlockID, prev)
if err != nil {
return nil, err
}
blocks = append(blocks, missed...)
}
uniq := make(map[ton.BlockID]struct{}, len(blocks))
for i := range blocks {
uniq[blocks[i]] = struct{}{}
}
if len(blocks) == len(uniq) {
return blocks, nil
}
return maps.Keys(uniq), nil
}

func (h *Handler) GetBlockchainMasterchainBlocks(ctx context.Context, params oas.GetBlockchainMasterchainBlocksParams) (*oas.BlockchainBlocks, error) {
blockIDs, err := h.blocksDiff(ctx, params.MasterchainSeqno)
if err != nil {
return nil, err
}
result := oas.BlockchainBlocks{
Blocks: make([]oas.BlockchainBlock, len(blockIDs)),
}
for i, id := range blockIDs {
block, err := h.storage.GetBlockHeader(ctx, id)
if err != nil {
return nil, toError(http.StatusInternalServerError, err) //block should be in db so we shouldn't check notFound error
}
result.Blocks[i] = convertBlockHeader(*block)
}
return &result, nil
}

func (h *Handler) GetBlockchainMasterchainTransactions(ctx context.Context, params oas.GetBlockchainMasterchainTransactionsParams) (*oas.Transactions, error) {
blockIDs, err := h.blocksDiff(ctx, params.MasterchainSeqno)
if err != nil {
return nil, err
}
var result oas.Transactions
for _, id := range blockIDs {
txs, err := h.storage.GetBlockTransactions(ctx, id)
if err != nil {
return nil, toError(http.StatusInternalServerError, err)
}
for _, tx := range txs {
result.Transactions = append(result.Transactions, convertTransaction(*tx, h.addressBook))
}
}
return &result, nil
}

func (h *Handler) GetBlockchainBlockTransactions(ctx context.Context, params oas.GetBlockchainBlockTransactionsParams) (*oas.Transactions, error) {
blockID, err := ton.ParseBlockID(params.BlockID)
if err != nil {
Expand Down
4 changes: 4 additions & 0 deletions pkg/api/converters.go
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,10 @@ func stringToTVMStackRecord(s string) (tlb.VmStackValue, error) {
}
return tlb.VmStackValue{SumType: "VmStkTinyInt", VmStkTinyInt: i}, nil
}
cells, err := boc.DeserializeBocHex(s)
if err == nil && len(cells) == 1 {
return tlb.CellToVmCellSlice(cells[0])
}
c, err := boc.DeserializeSinglRootBase64(s)
if err != nil {
return tlb.VmStackValue{}, err
Expand Down
11 changes: 2 additions & 9 deletions pkg/api/middlewares.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,12 @@ import (
"context"
"encoding/json"
"errors"
"net/http"

"github.com/ogen-go/ogen/middleware"
"github.com/ogen-go/ogen/ogenerrors"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
"go.uber.org/zap"
"net/http"
)

func ogenLoggingMiddleware(logger *zap.Logger) middleware.Middleware {
Expand All @@ -24,13 +23,7 @@ func ogenLoggingMiddleware(logger *zap.Logger) middleware.Middleware {
if err != nil {
logger.Info("Fail", zap.Error(err))
} else {
var fields []zap.Field
if tresp, ok := resp.Type.(interface{ GetStatusCode() int }); ok {
fields = []zap.Field{
zap.Int("status_code", tresp.GetStatusCode()),
}
}
logger.Info("Success", fields...)
logger.Info("Success")
}
return resp, err
}
Expand Down
Loading

0 comments on commit cc29b91

Please sign in to comment.