diff --git a/docs/docs.go b/docs/docs.go index 9c4699d..0eaf6c0 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -96,6 +96,32 @@ const docTemplate = `{ "summary": "Get Actions", "operationId": "api_v3_get_actions", "parameters": [ + { + "type": "string", + "description": "List of account addresses to get actions. Can be sent in hex, base64 or base64url form.", + "name": "account", + "in": "query" + }, + { + "type": "array", + "items": { + "type": "string" + }, + "collectionFormat": "multi", + "description": "Find actions by transaction hash.", + "name": "tx_hash", + "in": "query" + }, + { + "type": "array", + "items": { + "type": "string" + }, + "collectionFormat": "multi", + "description": "Find actions by message hash.", + "name": "msg_hash", + "in": "query" + }, { "type": "array", "items": { @@ -115,6 +141,68 @@ const docTemplate = `{ "description": "Find actions by the trace_id.", "name": "trace_id", "in": "query" + }, + { + "type": "integer", + "description": "Query actions of events which was completed in masterchain block with given seqno", + "name": "mc_seqno", + "in": "query" + }, + { + "minimum": 0, + "type": "integer", + "description": "Query actions for events, which was finished **after** given timestamp.", + "name": "start_utime", + "in": "query" + }, + { + "minimum": 0, + "type": "integer", + "description": "Query actions for events, which was finished **before** given timestamp.", + "name": "end_utime", + "in": "query" + }, + { + "minimum": 0, + "type": "integer", + "description": "Query actions for events with ` + "`" + `end_lt \u003e= start_lt` + "`" + `.", + "name": "start_lt", + "in": "query" + }, + { + "minimum": 0, + "type": "integer", + "description": "Query actions for events with ` + "`" + `end_lt \u003c= end_lt` + "`" + `.", + "name": "end_lt", + "in": "query" + }, + { + "maximum": 1000, + "minimum": 1, + "type": "integer", + "default": 10, + "description": "Limit number of queried rows. Use with *offset* to batch read.", + "name": "limit", + "in": "query" + }, + { + "minimum": 0, + "type": "integer", + "default": 0, + "description": "Skip first N rows. Use with *limit* to batch read.", + "name": "offset", + "in": "query" + }, + { + "enum": [ + "asc", + "desc" + ], + "type": "string", + "default": "desc", + "description": "Sort actions by lt.", + "name": "sort", + "in": "query" } ], "responses": { @@ -514,7 +602,7 @@ const docTemplate = `{ }, { "type": "integer", - "description": "Masterchain block seqno", + "description": "Query events that was completed in masterchain block with given seqno", "name": "mc_seqno", "in": "query" }, @@ -1097,6 +1185,23 @@ const docTemplate = `{ "name": "seqno", "in": "query", "required": true + }, + { + "maximum": 1000, + "minimum": 1, + "type": "integer", + "default": 10, + "description": "Limit number of queried rows. Use with *offset* to batch read.", + "name": "limit", + "in": "query" + }, + { + "minimum": 0, + "type": "integer", + "default": 0, + "description": "Skip first N rows. Use with *limit* to batch read.", + "name": "offset", + "in": "query" } ], "responses": { @@ -2279,9 +2384,6 @@ const docTemplate = `{ "end_utime": { "type": "integer" }, - "raw_action": { - "$ref": "#/definitions/RawAction" - }, "start_lt": { "type": "string", "example": "0" @@ -3180,188 +3282,6 @@ const docTemplate = `{ } } }, - "RawAction": { - "type": "object", - "properties": { - "actionId": { - "type": "string" - }, - "amount": { - "type": "string" - }, - "asset": { - "type": "string" - }, - "asset2": { - "type": "string" - }, - "asset2Secondary": { - "type": "string" - }, - "assetSecondary": { - "type": "string" - }, - "changeDNSRecordFlags": { - "type": "integer" - }, - "changeDNSRecordKey": { - "type": "string" - }, - "changeDNSRecordValue": { - "type": "string" - }, - "changeDNSRecordValueSchema": { - "type": "string" - }, - "destination": { - "type": "string" - }, - "destinationSecondary": { - "type": "string" - }, - "endLt": { - "type": "integer" - }, - "endUtime": { - "type": "integer" - }, - "jettonSwapDex": { - "type": "string" - }, - "jettonSwapDexIncomingTransferAmount": { - "type": "string" - }, - "jettonSwapDexIncomingTransferAsset": { - "type": "string" - }, - "jettonSwapDexIncomingTransferDestination": { - "type": "string" - }, - "jettonSwapDexIncomingTransferDestinationJettonWallet": { - "type": "string" - }, - "jettonSwapDexIncomingTransferSource": { - "type": "string" - }, - "jettonSwapDexIncomingTransferSourceJettonWallet": { - "type": "string" - }, - "jettonSwapDexOutgoingTransferAmount": { - "type": "string" - }, - "jettonSwapDexOutgoingTransferAsset": { - "type": "string" - }, - "jettonSwapDexOutgoingTransferDestination": { - "type": "string" - }, - "jettonSwapDexOutgoingTransferDestinationJettonWallet": { - "type": "string" - }, - "jettonSwapDexOutgoingTransferSource": { - "type": "string" - }, - "jettonSwapDexOutgoingTransferSourceJettonWallet": { - "type": "string" - }, - "jettonSwapPeerSwaps": { - "type": "array", - "items": { - "type": "string" - } - }, - "jettonSwapSender": { - "type": "string" - }, - "jettonTransferComment": { - "type": "string" - }, - "jettonTransferCustomPayload": { - "type": "string" - }, - "jettonTransferForwardAmount": { - "type": "string" - }, - "jettonTransferForwardPayload": { - "type": "string" - }, - "jettonTransferIsEncryptedComment": { - "type": "boolean" - }, - "jettonTransferQueryId": { - "type": "string" - }, - "jettonTransferResponseDestination": { - "type": "string" - }, - "nftmintNFTItemIndex": { - "type": "string" - }, - "nfttransferCustomPayload": { - "type": "string" - }, - "nfttransferForwardAmount": { - "type": "string" - }, - "nfttransferForwardPayload": { - "type": "string" - }, - "nfttransferIsPurchase": { - "type": "boolean" - }, - "nfttransferNFTItemIndex": { - "type": "string" - }, - "nfttransferPrice": { - "type": "string" - }, - "nfttransferQueryId": { - "type": "string" - }, - "nfttransferResponseDestination": { - "type": "string" - }, - "opcode": { - "type": "integer" - }, - "source": { - "type": "string" - }, - "sourceSecondary": { - "type": "string" - }, - "startLt": { - "type": "integer" - }, - "startUtime": { - "type": "integer" - }, - "success": { - "type": "boolean" - }, - "tonTransferContent": { - "type": "string" - }, - "tonTransferEncrypted": { - "type": "boolean" - }, - "traceId": { - "type": "string" - }, - "txHashes": { - "type": "array", - "items": { - "type": "string" - } - }, - "type": { - "type": "string" - }, - "value": { - "type": "string" - } - } - }, "RequestError": { "type": "object", "properties": { diff --git a/docs/swagger.json b/docs/swagger.json index 1452409..aa951b5 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -88,6 +88,32 @@ "summary": "Get Actions", "operationId": "api_v3_get_actions", "parameters": [ + { + "type": "string", + "description": "List of account addresses to get actions. Can be sent in hex, base64 or base64url form.", + "name": "account", + "in": "query" + }, + { + "type": "array", + "items": { + "type": "string" + }, + "collectionFormat": "multi", + "description": "Find actions by transaction hash.", + "name": "tx_hash", + "in": "query" + }, + { + "type": "array", + "items": { + "type": "string" + }, + "collectionFormat": "multi", + "description": "Find actions by message hash.", + "name": "msg_hash", + "in": "query" + }, { "type": "array", "items": { @@ -107,6 +133,68 @@ "description": "Find actions by the trace_id.", "name": "trace_id", "in": "query" + }, + { + "type": "integer", + "description": "Query actions of events which was completed in masterchain block with given seqno", + "name": "mc_seqno", + "in": "query" + }, + { + "minimum": 0, + "type": "integer", + "description": "Query actions for events, which was finished **after** given timestamp.", + "name": "start_utime", + "in": "query" + }, + { + "minimum": 0, + "type": "integer", + "description": "Query actions for events, which was finished **before** given timestamp.", + "name": "end_utime", + "in": "query" + }, + { + "minimum": 0, + "type": "integer", + "description": "Query actions for events with `end_lt \u003e= start_lt`.", + "name": "start_lt", + "in": "query" + }, + { + "minimum": 0, + "type": "integer", + "description": "Query actions for events with `end_lt \u003c= end_lt`.", + "name": "end_lt", + "in": "query" + }, + { + "maximum": 1000, + "minimum": 1, + "type": "integer", + "default": 10, + "description": "Limit number of queried rows. Use with *offset* to batch read.", + "name": "limit", + "in": "query" + }, + { + "minimum": 0, + "type": "integer", + "default": 0, + "description": "Skip first N rows. Use with *limit* to batch read.", + "name": "offset", + "in": "query" + }, + { + "enum": [ + "asc", + "desc" + ], + "type": "string", + "default": "desc", + "description": "Sort actions by lt.", + "name": "sort", + "in": "query" } ], "responses": { @@ -506,7 +594,7 @@ }, { "type": "integer", - "description": "Masterchain block seqno", + "description": "Query events that was completed in masterchain block with given seqno", "name": "mc_seqno", "in": "query" }, @@ -1089,6 +1177,23 @@ "name": "seqno", "in": "query", "required": true + }, + { + "maximum": 1000, + "minimum": 1, + "type": "integer", + "default": 10, + "description": "Limit number of queried rows. Use with *offset* to batch read.", + "name": "limit", + "in": "query" + }, + { + "minimum": 0, + "type": "integer", + "default": 0, + "description": "Skip first N rows. Use with *limit* to batch read.", + "name": "offset", + "in": "query" } ], "responses": { @@ -2271,9 +2376,6 @@ "end_utime": { "type": "integer" }, - "raw_action": { - "$ref": "#/definitions/RawAction" - }, "start_lt": { "type": "string", "example": "0" @@ -3172,188 +3274,6 @@ } } }, - "RawAction": { - "type": "object", - "properties": { - "actionId": { - "type": "string" - }, - "amount": { - "type": "string" - }, - "asset": { - "type": "string" - }, - "asset2": { - "type": "string" - }, - "asset2Secondary": { - "type": "string" - }, - "assetSecondary": { - "type": "string" - }, - "changeDNSRecordFlags": { - "type": "integer" - }, - "changeDNSRecordKey": { - "type": "string" - }, - "changeDNSRecordValue": { - "type": "string" - }, - "changeDNSRecordValueSchema": { - "type": "string" - }, - "destination": { - "type": "string" - }, - "destinationSecondary": { - "type": "string" - }, - "endLt": { - "type": "integer" - }, - "endUtime": { - "type": "integer" - }, - "jettonSwapDex": { - "type": "string" - }, - "jettonSwapDexIncomingTransferAmount": { - "type": "string" - }, - "jettonSwapDexIncomingTransferAsset": { - "type": "string" - }, - "jettonSwapDexIncomingTransferDestination": { - "type": "string" - }, - "jettonSwapDexIncomingTransferDestinationJettonWallet": { - "type": "string" - }, - "jettonSwapDexIncomingTransferSource": { - "type": "string" - }, - "jettonSwapDexIncomingTransferSourceJettonWallet": { - "type": "string" - }, - "jettonSwapDexOutgoingTransferAmount": { - "type": "string" - }, - "jettonSwapDexOutgoingTransferAsset": { - "type": "string" - }, - "jettonSwapDexOutgoingTransferDestination": { - "type": "string" - }, - "jettonSwapDexOutgoingTransferDestinationJettonWallet": { - "type": "string" - }, - "jettonSwapDexOutgoingTransferSource": { - "type": "string" - }, - "jettonSwapDexOutgoingTransferSourceJettonWallet": { - "type": "string" - }, - "jettonSwapPeerSwaps": { - "type": "array", - "items": { - "type": "string" - } - }, - "jettonSwapSender": { - "type": "string" - }, - "jettonTransferComment": { - "type": "string" - }, - "jettonTransferCustomPayload": { - "type": "string" - }, - "jettonTransferForwardAmount": { - "type": "string" - }, - "jettonTransferForwardPayload": { - "type": "string" - }, - "jettonTransferIsEncryptedComment": { - "type": "boolean" - }, - "jettonTransferQueryId": { - "type": "string" - }, - "jettonTransferResponseDestination": { - "type": "string" - }, - "nftmintNFTItemIndex": { - "type": "string" - }, - "nfttransferCustomPayload": { - "type": "string" - }, - "nfttransferForwardAmount": { - "type": "string" - }, - "nfttransferForwardPayload": { - "type": "string" - }, - "nfttransferIsPurchase": { - "type": "boolean" - }, - "nfttransferNFTItemIndex": { - "type": "string" - }, - "nfttransferPrice": { - "type": "string" - }, - "nfttransferQueryId": { - "type": "string" - }, - "nfttransferResponseDestination": { - "type": "string" - }, - "opcode": { - "type": "integer" - }, - "source": { - "type": "string" - }, - "sourceSecondary": { - "type": "string" - }, - "startLt": { - "type": "integer" - }, - "startUtime": { - "type": "integer" - }, - "success": { - "type": "boolean" - }, - "tonTransferContent": { - "type": "string" - }, - "tonTransferEncrypted": { - "type": "boolean" - }, - "traceId": { - "type": "string" - }, - "txHashes": { - "type": "array", - "items": { - "type": "string" - } - }, - "type": { - "type": "string" - }, - "value": { - "type": "string" - } - } - }, "RequestError": { "type": "object", "properties": { diff --git a/docs/swagger.yaml b/docs/swagger.yaml index b23ae24..664e2a7 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -70,8 +70,6 @@ definitions: type: string end_utime: type: integer - raw_action: - $ref: '#/definitions/RawAction' start_lt: example: "0" type: string @@ -672,127 +670,6 @@ definitions: $ref: '#/definitions/NFTTransfer' type: array type: object - RawAction: - properties: - actionId: - type: string - amount: - type: string - asset: - type: string - asset2: - type: string - asset2Secondary: - type: string - assetSecondary: - type: string - changeDNSRecordFlags: - type: integer - changeDNSRecordKey: - type: string - changeDNSRecordValue: - type: string - changeDNSRecordValueSchema: - type: string - destination: - type: string - destinationSecondary: - type: string - endLt: - type: integer - endUtime: - type: integer - jettonSwapDex: - type: string - jettonSwapDexIncomingTransferAmount: - type: string - jettonSwapDexIncomingTransferAsset: - type: string - jettonSwapDexIncomingTransferDestination: - type: string - jettonSwapDexIncomingTransferDestinationJettonWallet: - type: string - jettonSwapDexIncomingTransferSource: - type: string - jettonSwapDexIncomingTransferSourceJettonWallet: - type: string - jettonSwapDexOutgoingTransferAmount: - type: string - jettonSwapDexOutgoingTransferAsset: - type: string - jettonSwapDexOutgoingTransferDestination: - type: string - jettonSwapDexOutgoingTransferDestinationJettonWallet: - type: string - jettonSwapDexOutgoingTransferSource: - type: string - jettonSwapDexOutgoingTransferSourceJettonWallet: - type: string - jettonSwapPeerSwaps: - items: - type: string - type: array - jettonSwapSender: - type: string - jettonTransferComment: - type: string - jettonTransferCustomPayload: - type: string - jettonTransferForwardAmount: - type: string - jettonTransferForwardPayload: - type: string - jettonTransferIsEncryptedComment: - type: boolean - jettonTransferQueryId: - type: string - jettonTransferResponseDestination: - type: string - nftmintNFTItemIndex: - type: string - nfttransferCustomPayload: - type: string - nfttransferForwardAmount: - type: string - nfttransferForwardPayload: - type: string - nfttransferIsPurchase: - type: boolean - nfttransferNFTItemIndex: - type: string - nfttransferPrice: - type: string - nfttransferQueryId: - type: string - nfttransferResponseDestination: - type: string - opcode: - type: integer - source: - type: string - sourceSecondary: - type: string - startLt: - type: integer - startUtime: - type: integer - success: - type: boolean - tonTransferContent: - type: string - tonTransferEncrypted: - type: boolean - traceId: - type: string - txHashes: - items: - type: string - type: array - type: - type: string - value: - type: string - type: object RequestError: properties: code: @@ -1108,6 +985,25 @@ paths: description: Get actions by specified filter. operationId: api_v3_get_actions parameters: + - description: List of account addresses to get actions. Can be sent in hex, + base64 or base64url form. + in: query + name: account + type: string + - collectionFormat: multi + description: Find actions by transaction hash. + in: query + items: + type: string + name: tx_hash + type: array + - collectionFormat: multi + description: Find actions by message hash. + in: query + items: + type: string + name: msg_hash + type: array - collectionFormat: multi description: Find actions by the action_id. in: query @@ -1122,6 +1018,54 @@ paths: type: string name: trace_id type: array + - description: Query actions of events which was completed in masterchain block + with given seqno + in: query + name: mc_seqno + type: integer + - description: Query actions for events, which was finished **after** given + timestamp. + in: query + minimum: 0 + name: start_utime + type: integer + - description: Query actions for events, which was finished **before** given + timestamp. + in: query + minimum: 0 + name: end_utime + type: integer + - description: Query actions for events with `end_lt >= start_lt`. + in: query + minimum: 0 + name: start_lt + type: integer + - description: Query actions for events with `end_lt <= end_lt`. + in: query + minimum: 0 + name: end_lt + type: integer + - default: 10 + description: Limit number of queried rows. Use with *offset* to batch read. + in: query + maximum: 1000 + minimum: 1 + name: limit + type: integer + - default: 0 + description: Skip first N rows. Use with *limit* to batch read. + in: query + minimum: 0 + name: offset + type: integer + - default: desc + description: Sort actions by lt. + enum: + - asc + - desc + in: query + name: sort + type: string produces: - application/json responses: @@ -1378,7 +1322,8 @@ paths: type: string name: msg_hash type: array - - description: Masterchain block seqno + - description: Query events that was completed in masterchain block with given + seqno in: query name: mc_seqno type: integer @@ -1777,6 +1722,19 @@ paths: name: seqno required: true type: integer + - default: 10 + description: Limit number of queried rows. Use with *offset* to batch read. + in: query + maximum: 1000 + minimum: 1 + name: limit + type: integer + - default: 0 + description: Skip first N rows. Use with *limit* to batch read. + in: query + minimum: 0 + name: offset + type: integer produces: - application/json responses: diff --git a/index/crud.go b/index/crud.go index 850adf0..e06f6b5 100644 --- a/index/crud.go +++ b/index/crud.go @@ -748,8 +748,8 @@ func buildJettonTransfersQuery(transfer_req JettonTransferRequest, utime_req Uti return query, nil } -func buildActionsQuery(act_req ActionRequest, lim_req LimitRequest, settings RequestSettings) (string, error) { - clmn_query := `A.trace_id, A.action_id, A.start_lt, A.end_lt, A.start_utime, A.end_utime, A.source, A.source_secondary, +func buildActionsQuery(act_req ActionRequest, utime_req UtimeRequest, lt_req LtRequest, lim_req LimitRequest, settings RequestSettings) (string, error) { + clmn_query_default := `A.trace_id, A.action_id, A.start_lt, A.end_lt, A.start_utime, A.end_utime, A.source, A.source_secondary, A.destination, A.destination_secondary, A.asset, A.asset_secondary, A.asset2, A.asset2_secondary, A.opcode, A.tx_hashes, A.type, (A.ton_transfer_data).content, (A.ton_transfer_data).encrypted, A.value, A.amount, (A.jetton_transfer_data).response_destination, (A.jetton_transfer_data).forward_amount, (A.jetton_transfer_data).query_id, @@ -766,7 +766,8 @@ func buildActionsQuery(act_req ActionRequest, lim_req LimitRequest, settings Req ((A.jetton_swap_data).dex_outgoing_transfer).destination_jetton_wallet, (A.jetton_swap_data).peer_swaps, (A.change_dns_record_data).key, (A.change_dns_record_data).value_schema, (A.change_dns_record_data).value, (A.change_dns_record_data).flags, (A.nft_mint_data).nft_item_index, A.success` - from_query := `actions as A` + clmn_query := clmn_query_default + from_query := `actions as A join traces as E on A.trace_id = E.trace_id` filter_list := []string{} filter_query := `` orderby_query := `` @@ -775,19 +776,92 @@ func buildActionsQuery(act_req ActionRequest, lim_req LimitRequest, settings Req return "", err } + sort_order := "desc" + if v := lim_req.Sort; v != nil { + sort_order, err = getSortOrder(*v) + if err != nil { + return "", err + } + } + // time + order_by_now := false + if v := utime_req.StartUtime; v != nil { + filter_list = append(filter_list, fmt.Sprintf("E.end_utime >= %d", *v)) + order_by_now = true + } + if v := utime_req.EndUtime; v != nil { + filter_list = append(filter_list, fmt.Sprintf("E.end_utime <= %d", *v)) + order_by_now = true + } + if v := lt_req.StartLt; v != nil { + filter_list = append(filter_list, fmt.Sprintf("E.end_lt >= %d", *v)) + } + if v := lt_req.EndLt; v != nil { + filter_list = append(filter_list, fmt.Sprintf("E.end_lt <= %d", *v)) + } + if v := act_req.AccountAddress; v != nil && len(*v) > 0 { + filter_str := fmt.Sprintf("T.account = '%s'", *v) + filter_list = append(filter_list, filter_str) + + from_query = `actions as A join traces as E on E.trace_id = A.trace_id join transactions as T on E.trace_id = T.trace_id and A.tx_hashes @> array[T.hash::tonhash]` + if order_by_now { + clmn_query = `distinct on (E.end_utime, E.trace_id, A.end_utime, A.action_id) ` + clmn_query_default + } else { + clmn_query = `distinct on (E.end_lt, E.trace_id, A.end_lt, A.action_id) ` + clmn_query_default + } + } + if v := act_req.TransactionHash; v != nil { + filter_str := filterByArray("T.hash", v) + if len(filter_str) > 0 { + filter_list = append(filter_list, filter_str) + } + from_query = `actions as A join traces as E on E.trace_id = A.trace_id join transactions as T on E.trace_id = T.trace_id and A.tx_hashes @> array[T.hash::tonhash]` + if order_by_now { + clmn_query = `distinct on (E.end_utime, E.trace_id, A.end_utime, A.action_id) ` + clmn_query_default + } else { + clmn_query = `distinct on (E.end_lt, E.trace_id, A.end_lt, A.action_id) ` + clmn_query_default + } + } + if v := act_req.MessageHash; v != nil { + filter_str := filterByArray("M.msg_hash", v) + if len(filter_str) > 0 { + filter_list = append(filter_list, filter_str) + } + from_query = `actions as A join messages as M on A.trace_id = M.trace_id and array[M.tx_hash::tonhash] @> A.tx_hashes` + from_query = `actions as A join traces as E on E.trace_id = A.trace_id join messages as M on E.trace_id = T.trace_id and A.tx_hashes @> array[T.hash::tonhash]` + if order_by_now { + clmn_query = `distinct on (E.end_utime, E.trace_id, A.end_utime, A.action_id) ` + clmn_query_default + } else { + clmn_query = `distinct on (E.end_lt, E.trace_id, A.end_lt, A.action_id) ` + clmn_query_default + } + } + if v := act_req.McSeqno; v != nil { + filter_list = append(filter_list, `E.state = 'complete'`) + filter_list = append(filter_list, fmt.Sprintf("E.mc_seqno_end = %d", *v)) + from_query = `actions as A join traces as E on A.trace_id = E.trace_id` + clmn_query = clmn_query_default + } if v := act_req.ActionId; v != nil { filter_str := filterByArray("A.action_id", v) if len(filter_str) > 0 { - filter_list = append(filter_list, filter_str) + filter_list = []string{filter_str} } + from_query = `actions as A` + clmn_query = clmn_query_default } if v := act_req.TraceId; v != nil { filter_str := filterByArray("A.trace_id", v) if len(filter_str) > 0 { - filter_list = append(filter_list, filter_str) + filter_list = []string{filter_str} } } + if order_by_now { + orderby_query = fmt.Sprintf(" order by E.end_utime %s, E.trace_id %s, A.end_utime %s, A.action_id %s", sort_order, sort_order, sort_order, sort_order) + } else { + orderby_query = fmt.Sprintf(" order by E.end_lt %s, E.trace_id %s, A.end_lt %s, A.action_id %s", sort_order, sort_order, sort_order, sort_order) + } + // build query if len(filter_list) > 0 { filter_query = ` where ` + strings.Join(filter_list, " and ") @@ -823,12 +897,39 @@ func buildEventsQuery(event_req EventRequest, utime_req UtimeRequest, lt_req LtR } } + // time + order_by_now := false + if v := utime_req.StartUtime; v != nil { + filter_list = append(filter_list, fmt.Sprintf("E.end_utime >= %d", *v)) + order_by_now = true + } + if v := utime_req.EndUtime; v != nil { + filter_list = append(filter_list, fmt.Sprintf("E.end_utime <= %d", *v)) + order_by_now = true + } + if v := lt_req.StartLt; v != nil { + filter_list = append(filter_list, fmt.Sprintf("E.end_lt >= %d", *v)) + } + if v := lt_req.EndLt; v != nil { + filter_list = append(filter_list, fmt.Sprintf("E.end_lt <= %d", *v)) + } + + if order_by_now { + orderby_query = fmt.Sprintf(" order by E.end_utime %s, E.trace_id %s", sort_order, sort_order) + } else { + orderby_query = fmt.Sprintf(" order by E.end_lt %s, E.trace_id %s", sort_order, sort_order) + } + if v := event_req.AccountAddress; v != nil && len(*v) > 0 { filter_str := fmt.Sprintf("T.account = '%s'", *v) filter_list = append(filter_list, filter_str) from_query = `traces as E join transactions as T on E.trace_id = T.trace_id` - clmn_query = `distinct on (E.end_lt, E.trace_id) ` + clmn_query_default + if order_by_now { + clmn_query = `distinct on (E.end_utime, E.trace_id) ` + clmn_query_default + } else { + clmn_query = `distinct on (E.end_lt, E.trace_id) ` + clmn_query_default + } } if v := event_req.TransactionHash; v != nil { filter_str := filterByArray("T.hash", v) @@ -836,7 +937,12 @@ func buildEventsQuery(event_req EventRequest, utime_req UtimeRequest, lt_req LtR filter_list = append(filter_list, filter_str) } from_query = `traces as E join transactions as T on E.trace_id = T.trace_id` - clmn_query = `distinct on (E.end_lt, E.trace_id) ` + clmn_query_default + + if order_by_now { + clmn_query = `distinct on (E.end_utime, E.trace_id) ` + clmn_query_default + } else { + clmn_query = `distinct on (E.end_lt, E.trace_id) ` + clmn_query_default + } } if v := event_req.MessageHash; v != nil { filter_str := filterByArray("M.msg_hash", v) @@ -844,7 +950,12 @@ func buildEventsQuery(event_req EventRequest, utime_req UtimeRequest, lt_req LtR filter_list = append(filter_list, filter_str) } from_query = `traces as E join messages as M on E.trace_id = M.trace_id` - clmn_query = `distinct on (E.end_lt, E.trace_id) ` + clmn_query_default + + if order_by_now { + clmn_query = `distinct on (E.end_utime, E.trace_id) ` + clmn_query_default + } else { + clmn_query = `distinct on (E.end_lt, E.trace_id) ` + clmn_query_default + } } if v := event_req.TraceId; v != nil { @@ -858,29 +969,6 @@ func buildEventsQuery(event_req EventRequest, utime_req UtimeRequest, lt_req LtR filter_list = append(filter_list, fmt.Sprintf("E.mc_seqno_end = %d", *v)) } - // time - order_by_now := false - if v := utime_req.StartUtime; v != nil { - filter_list = append(filter_list, fmt.Sprintf("E.end_utime >= %d", *v)) - order_by_now = true - } - if v := utime_req.EndUtime; v != nil { - filter_list = append(filter_list, fmt.Sprintf("E.end_utime <= %d", *v)) - order_by_now = true - } - if v := lt_req.StartLt; v != nil { - filter_list = append(filter_list, fmt.Sprintf("E.end_lt >= %d", *v)) - } - if v := lt_req.EndLt; v != nil { - filter_list = append(filter_list, fmt.Sprintf("E.end_lt <= %d", *v)) - } - - if order_by_now { - orderby_query = fmt.Sprintf(" order by E.end_utime %s, E.trace_id asc", sort_order) - } else { - orderby_query = fmt.Sprintf(" order by E.end_lt %s, E.trace_id asc", sort_order) - } - // build query if len(filter_list) > 0 { filter_query = ` where ` + strings.Join(filter_list, " and ") @@ -1973,6 +2061,9 @@ func (db *DbClient) QueryTransactions( } book := AddressBook{} + if settings.NoAddressBook { + return txs, book, nil + } addr_list := []string{} for _, t := range txs { addr_list = append(addr_list, string(t.Account)) @@ -2026,6 +2117,9 @@ func (db *DbClient) QueryAdjacentTransactions( } book := AddressBook{} + if settings.NoAddressBook { + return txs, book, nil + } addr_list := []string{} for _, t := range txs { addr_list = append(addr_list, string(t.Account)) @@ -2077,6 +2171,9 @@ func (db *DbClient) QueryMessages( } book := AddressBook{} + if settings.NoAddressBook { + return msgs, book, nil + } addr_list := []string{} for _, m := range msgs { if m.Source != nil { @@ -2121,6 +2218,9 @@ func (db *DbClient) QueryNFTCollections( } book := AddressBook{} + if settings.NoAddressBook { + return res, book, nil + } addr_list := []string{} for _, t := range res { addr_list = append(addr_list, string(t.Address)) @@ -2163,6 +2263,9 @@ func (db *DbClient) QueryNFTItems( } book := AddressBook{} + if settings.NoAddressBook { + return res, book, nil + } addr_list := []string{} for _, t := range res { addr_list = append(addr_list, string(t.Address)) @@ -2207,6 +2310,9 @@ func (db *DbClient) QueryNFTTransfers( } book := AddressBook{} + if settings.NoAddressBook { + return res, book, nil + } addr_list := []string{} for _, t := range res { addr_list = append(addr_list, string(t.NftItemAddress)) @@ -2252,6 +2358,9 @@ func (db *DbClient) QueryJettonMasters( } book := AddressBook{} + if settings.NoAddressBook { + return res, book, nil + } addr_list := []string{} for _, t := range res { addr_list = append(addr_list, string(t.Address)) @@ -2295,6 +2404,9 @@ func (db *DbClient) QueryJettonWallets( } book := AddressBook{} + if settings.NoAddressBook { + return res, book, nil + } addr_list := []string{} for _, t := range res { addr_list = append(addr_list, string(t.Address)) @@ -2338,6 +2450,9 @@ func (db *DbClient) QueryJettonTransfers( } book := AddressBook{} + if settings.NoAddressBook { + return res, book, nil + } addr_list := []string{} for _, t := range res { addr_list = append(addr_list, string(t.Source)) @@ -2385,6 +2500,9 @@ func (db *DbClient) QueryJettonBurns( } book := AddressBook{} + if settings.NoAddressBook { + return res, book, nil + } addr_list := []string{} for _, t := range res { addr_list = append(addr_list, string(t.Owner)) @@ -2429,6 +2547,9 @@ func (db *DbClient) QueryAccountStates( } book := AddressBook{} + if settings.NoAddressBook { + return res, book, nil + } addr_list := []string{} for _, t := range res { if t.AccountAddress != nil { @@ -2488,10 +2609,12 @@ func (db *DbClient) QueryTopAccountBalances(lim_req LimitRequest, settings Reque // events func (db *DbClient) QueryActions( act_req ActionRequest, + utime_req UtimeRequest, + lt_req LtRequest, lim_req LimitRequest, settings RequestSettings, ) ([]Action, AddressBook, error) { - query, err := buildActionsQuery(act_req, lim_req, settings) + query, err := buildActionsQuery(act_req, utime_req, lt_req, lim_req, settings) if settings.DebugRequest { log.Println("Debug query:", query) } @@ -2506,6 +2629,17 @@ func (db *DbClient) QueryActions( } defer conn.Release() + // check block + if seqno := act_req.McSeqno; seqno != nil { + exists, err := queryBlockExists(*seqno, conn, settings) + if err != nil { + return nil, nil, err + } + if !exists { + return nil, nil, IndexError{Code: 404, Message: fmt.Sprintf("masterchain block %d not found", *seqno)} + } + } + raw_actions, err := queryRawActionsImpl(query, conn, settings) if err != nil { return nil, nil, IndexError{Code: 500, Message: err.Error()} @@ -2521,7 +2655,7 @@ func (db *DbClient) QueryActions( } actions = append(actions, *action) } - if len(addr_map) > 0 { + if len(addr_map) > 0 && !settings.NoAddressBook { addr_list := []string{} for k := range addr_map { addr_list = append(addr_list, string(k)) @@ -2557,6 +2691,17 @@ func (db *DbClient) QueryEvents( } defer conn.Release() + // check block + if seqno := event_req.McSeqno; seqno != nil { + exists, err := queryBlockExists(*seqno, conn, settings) + if err != nil { + return nil, nil, err + } + if !exists { + return nil, nil, IndexError{Code: 404, Message: fmt.Sprintf("masterchain block %d not found", *seqno)} + } + } + res, addr_list, err := queryEventsImpl(query, conn, settings) if err != nil { log.Println(query) @@ -2564,6 +2709,9 @@ func (db *DbClient) QueryEvents( } book := AddressBook{} + if settings.NoAddressBook { + return res, book, nil + } if len(addr_list) > 0 { book, err = queryAddressBookImpl(addr_list, conn, settings) if err != nil { diff --git a/index/models.go b/index/models.go index 9f8d3a0..1bcc511 100644 --- a/index/models.go +++ b/index/models.go @@ -586,7 +586,7 @@ type Action struct { Success *bool `json:"success"` Type string `json:"type"` Details interface{} `json:"details"` - RawAction *RawAction `json:"raw_action,omitempty"` + RawAction *RawAction `json:"raw_action,omitempty" swaggerignore:"true"` } // @name Action type EventMeta struct { diff --git a/index/request.go b/index/request.go index 942428f..5402aa3 100644 --- a/index/request.go +++ b/index/request.go @@ -16,6 +16,7 @@ type RequestSettings struct { MaxLimit int MaxEventTransactions int DebugRequest bool + NoAddressBook bool } // requests @@ -112,8 +113,12 @@ type AccountRequest struct { } type ActionRequest struct { - ActionId []HashType `query:"action_id"` - TraceId []HashType `query:"trace_id"` + AccountAddress *AccountAddress `query:"account"` + TransactionHash []HashType `query:"tx_hash"` + MessageHash []HashType `query:"msg_hash"` + TraceId []HashType `query:"trace_id"` + ActionId []HashType `query:"action_id"` + McSeqno *int32 `query:"mc_seqno"` } type EventRequest struct { diff --git a/main.go b/main.go index 39caeed..8cd40eb 100644 --- a/main.go +++ b/main.go @@ -169,6 +169,8 @@ func GetShards(c *fiber.Ctx) error { // @success 200 {object} index.TransactionsResponse // @failure 400 {object} index.RequestError // @param seqno query int32 true "Masterchain block seqno." +// @param limit query int32 false "Limit number of queried rows. Use with *offset* to batch read." minimum(1) maximum(1000) default(10) +// @param offset query int32 false "Skip first N rows. Use with *limit* to batch read." minimum(0) default(0) // @router /api/v3/masterchainBlockShards [get] // @security APIKeyHeader // @security APIKeyQuery @@ -176,9 +178,14 @@ func GetShardsDiff(c *fiber.Ctx) error { request_settings := GetRequestSettings(c, &settings) seqno := c.QueryInt("seqno") blk_req := index.BlockRequest{} + lim_req := index.LimitRequest{} blk_req.McSeqno = new(int32) *blk_req.McSeqno = int32(seqno) - blks, err := pool.QueryBlocks(blk_req, index.UtimeRequest{}, index.LtRequest{}, index.LimitRequest{}, request_settings) + if err := c.QueryParser(&lim_req); err != nil { + return index.IndexError{Code: 422, Message: err.Error()} + } + + blks, err := pool.QueryBlocks(blk_req, index.UtimeRequest{}, index.LtRequest{}, lim_req, request_settings) if err != nil { return err } @@ -960,7 +967,7 @@ func GetJettonBurns(c *fiber.Ctx) error { // @param account query string false "List of account addresses to get transactions. Can be sent in hex, base64 or base64url form." // @param tx_hash query []string false "Find event by transaction hash." // @param msg_hash query []string false "Find event by message hash." -// @param mc_seqno query int32 false "Masterchain block seqno" +// @param mc_seqno query int32 false "Query events that was completed in masterchain block with given seqno" // @param start_utime query int32 false "Query events, which was finished **after** given timestamp." minimum(0) // @param end_utime query int32 false "Query events, which was finished **before** given timestamp." minimum(0) // @param start_lt query int64 false "Query events with `end_lt >= start_lt`." minimum(0) @@ -1015,25 +1022,46 @@ func GetEvents(c *fiber.Ctx) error { // @Produce json // @success 200 {object} index.ActionsResponse // @failure 400 {object} index.RequestError +// @param account query string false "List of account addresses to get actions. Can be sent in hex, base64 or base64url form." +// @param tx_hash query []string false "Find actions by transaction hash." +// @param msg_hash query []string false "Find actions by message hash." // @param action_id query []string false "Find actions by the action_id." collectionFormat(multi) // @param trace_id query []string false "Find actions by the trace_id." collectionFormat(multi) +// @param mc_seqno query int32 false "Query actions of events which was completed in masterchain block with given seqno" +// @param start_utime query int32 false "Query actions for events, which was finished **after** given timestamp." minimum(0) +// @param end_utime query int32 false "Query actions for events, which was finished **before** given timestamp." minimum(0) +// @param start_lt query int64 false "Query actions for events with `end_lt >= start_lt`." minimum(0) +// @param end_lt query int64 false "Query actions for events with `end_lt <= end_lt`." minimum(0) +// @param limit query int32 false "Limit number of queried rows. Use with *offset* to batch read." minimum(1) maximum(1000) default(10) +// @param offset query int32 false "Skip first N rows. Use with *limit* to batch read." minimum(0) default(0) +// @param sort query string false "Sort actions by lt." Enums(asc, desc) default(desc) // @router /api/v3/actions [get] // @security APIKeyHeader // @security APIKeyQuery func GetActions(c *fiber.Ctx) error { request_settings := GetRequestSettings(c, &settings) - var act_req index.ActionRequest - var lim_req index.LimitRequest + act_req := index.ActionRequest{} + lim_req := index.LimitRequest{} + utime_req := index.UtimeRequest{} + lt_req := index.LtRequest{} if err := c.QueryParser(&act_req); err != nil { return index.IndexError{Code: 422, Message: err.Error()} } - + if err := c.QueryParser(&lim_req); err != nil { + return index.IndexError{Code: 422, Message: err.Error()} + } + if err := c.QueryParser(&utime_req); err != nil { + return index.IndexError{Code: 422, Message: err.Error()} + } + if err := c.QueryParser(<_req); err != nil { + return index.IndexError{Code: 422, Message: err.Error()} + } if err := c.QueryParser(&lim_req); err != nil { return index.IndexError{Code: 422, Message: err.Error()} } - res, book, err := pool.QueryActions(act_req, lim_req, request_settings) + res, book, err := pool.QueryActions(act_req, utime_req, lt_req, lim_req, request_settings) if err != nil { return err } @@ -1342,6 +1370,11 @@ func GetRequestSettings(c *fiber.Ctx, settings *Settings) index.RequestSettings request_settings.Timeout = time.Duration(value) * time.Millisecond } } + if value_str, ok := ExtractParam(c, "X-No-Address-Book", "x_no_address_book"); ok { + if value, err := strconv.ParseBool(value_str); err == nil { + request_settings.NoAddressBook = value + } + } return request_settings }