diff --git a/CHANGELOG.md b/CHANGELOG.md index 5205c2c..81fd7d7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,21 @@ All notable changes to this project will be documented in this file. +## [0.62.0] - 2023-07-20 + +### New + +- `trace` field in transaction is a list of VM step info objects + +### Fixed + +- implemented support for `archive` parameter in all blockchain queries. +- `archive` parameter was not propagated to the nested join queries. + +### Improved + +- All user parameters that are enums are now checked for typos on startup + ## [0.61.0] - 2023-07-04 ### New diff --git a/package.json b/package.json index 9de607d..fb5558a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ton-q-server", - "version": "0.61.0", + "version": "0.62.0", "description": "TON Q Server – realtime queries over TON blockchain.", "main": "index.js", "repository": "git@github.com:tonlabs/ton-q-server.git", diff --git a/res/type-defs-blockchain/codegen-stubs.graphql b/res/type-defs-blockchain/codegen-stubs.graphql index 1d819f3..7546b0c 100644 --- a/res/type-defs-blockchain/codegen-stubs.graphql +++ b/res/type-defs-blockchain/codegen-stubs.graphql @@ -188,6 +188,19 @@ enum BounceTypeEnum { Ok } +type TransactionTrace { + info_type: String, + step: Int, + cmd_str: String, + stack: [String], + gas_used: String, + gas_cmd: String, + cmd_code_rem_bits: Int, + cmd_code_hex: String, + cmd_code_cell_hash: String, + cmd_code_offset: Int, +} + """ Due to GraphQL limitations big numbers are returned as a string. You can specify format used to string representation for big integers. diff --git a/res/type-defs-blockchain/transaction.graphql b/res/type-defs-blockchain/transaction.graphql index 653d743..c8e6792 100644 --- a/res/type-defs-blockchain/transaction.graphql +++ b/res/type-defs-blockchain/transaction.graphql @@ -102,7 +102,7 @@ type BlockchainTransaction implements Node { tr_type: Int tr_type_name: TransactionTypeEnum "VM debug trace" - trace: String + trace: [TransactionTrace] tt: String "Workchain id of the account address (account_addr field)" workchain_id: Int diff --git a/res/type-defs-generated.graphql b/res/type-defs-generated.graphql index 884e4cc..376dbce 100644 --- a/res/type-defs-generated.graphql +++ b/res/type-defs-generated.graphql @@ -678,6 +678,19 @@ type TransactionSplitInfo { this_addr: String } +type TransactionTrace { + cmd_code_cell_hash: String + cmd_code_hex: String + cmd_code_offset: Float + cmd_code_rem_bits: Float + cmd_str: String + gas_cmd: String + gas_used: String + info_type: String + stack: [String] + step: Float +} + type BlockValueFlow { created(format: BigIntFormat): String created_other: [OtherCurrency] @@ -1233,8 +1246,7 @@ type Transaction { """ tr_type: Int tr_type_name: TransactionTypeEnum - "VM debug trace" - trace: String + trace: [TransactionTrace] tt: String "Workchain id of the account address (account_addr field)" workchain_id: Int @@ -2103,6 +2115,20 @@ input TransactionSplitInfoFilter { OR: TransactionSplitInfoFilter } +input TransactionTraceFilter { + cmd_code_cell_hash: StringFilter + cmd_code_hex: StringFilter + cmd_code_offset: FloatFilter + cmd_code_rem_bits: FloatFilter + cmd_str: StringFilter + gas_cmd: StringFilter + gas_used: StringFilter + info_type: StringFilter + stack: StringArrayFilter + step: FloatFilter + OR: TransactionTraceFilter +} + input BlockValueFlowFilter { created: StringFilter created_other: OtherCurrencyArrayFilter @@ -2605,6 +2631,11 @@ input MessageArrayFilter { all: MessageFilter } +input TransactionTraceArrayFilter { + any: TransactionTraceFilter + all: TransactionTraceFilter +} + input TransactionProcessingStatusEnumFilter { eq: TransactionProcessingStatusEnum ne: TransactionProcessingStatusEnum @@ -2737,8 +2768,7 @@ input TransactionFilter { """ tr_type: IntFilter tr_type_name: TransactionTypeEnumFilter - "VM debug trace" - trace: StringFilter + trace: TransactionTraceArrayFilter tt: StringFilter "Workchain id of the account address (account_addr field)" workchain_id: IntFilter diff --git a/src/__tests__/config-test.ts b/src/__tests__/config-test.ts index 6d04b44..1fa2207 100644 --- a/src/__tests__/config-test.ts +++ b/src/__tests__/config-test.ts @@ -73,6 +73,46 @@ test("Config Priority", () => { expect(only_env_options.server.port).toEqual(8081) }) +test("Test SubscriptionsMode enum", () => { + const config = resolveConfig({}, {}, { Q_SUBSCRIPTIONS_MODE: "disabled" }) + expect(config.subscriptionsMode).toEqual("disabled") + + expect(() => { + resolveConfig({}, {}, { Q_SUBSCRIPTIONS_MODE: "disable" }) + }).toThrowError(/Unknown subscriptions-mode/) +}) + +test("Test SlowQueriesMode enum", () => { + const config = resolveConfig({}, {}, { Q_SLOW_QUERIES: "redirect" }) + expect(config.queries.slowQueries).toEqual("redirect") + + expect(() => { + resolveConfig({}, {}, { Q_SLOW_QUERIES: "something" }) + }).toThrowError(/Unknown slow-queries/) +}) + +test("Test RequestsMode enum", () => { + const config = resolveConfig({}, {}, { Q_REQUESTS_MODE: "kafka" }) + expect(config.requests.mode).toEqual("kafka") + + expect(() => { + resolveConfig({}, {}, { Q_REQUESTS_MODE: "something" }) + }).toThrowError(/Unknown requests-mode/) +}) + +test("Test FilterOrConversion enum", () => { + const config = resolveConfig( + {}, + {}, + { Q_FILTER_OR_CONVERSION: "sub-queries" }, + ) + expect(config.queries.filter.orConversion).toEqual("sub-queries") + + expect(() => { + resolveConfig({}, {}, { Q_FILTER_OR_CONVERSION: "something" }) + }).toThrowError(/Unknown filter-or-conversion/) +}) + test("Arango Config", () => { expect(parseArangoConfig("arango")).toMatchObject({ server: "https://arango", diff --git a/src/server/config.ts b/src/server/config.ts index a3b107b..dd8acf0 100644 --- a/src/server/config.ts +++ b/src/server/config.ts @@ -507,15 +507,6 @@ function checkSubscriptionsConfig( ? SubscriptionsMode.Arango : SubscriptionsMode.Disabled } - if (!Object.values(SubscriptionsMode).includes(config.subscriptionsMode)) { - throw QError.invalidConfig( - `Unknown subscriptions-mode: got ${ - config.subscriptionsMode - }, but expected one of [${Object.values(SubscriptionsMode).join( - ", ", - )}]`, - ) - } } function applyDatabaseDefaults(config: string[], def: string[]) { @@ -548,6 +539,15 @@ export function resolveConfig( applyDataProviderDefaults(config.blockchain.blocks, config) applyDataProviderDefaults(config.blockchain.transactions, config) checkSubscriptionsConfig(config, specified) + // Check all parameters that are enums + checkEnum("subscriptions-mode", config.subscriptionsMode, SubscriptionsMode) + checkEnum("slow-queries", config.queries.slowQueries, SlowQueriesMode) + checkEnum("requests-mode", config.requests.mode, RequestsMode) + checkEnum( + "filter-or-conversion", + config.queries.filter.orConversion, + FilterOrConversion, + ) const slow = config.slowQueriesBlockchain if (slow !== undefined) { @@ -573,3 +573,17 @@ export function resolveConfig( } return config } + +function checkEnum( + paramName: string, + paramValue: string, + matchedEnum: Record, +) { + if (!Object.values(matchedEnum).includes(`${paramValue}`)) { + throw QError.invalidConfig( + `Unknown ${paramName}: got ${paramValue}, but expected one of [${Object.values( + matchedEnum, + ).join(", ")}]`, + ) + } +} diff --git a/src/server/data/data-provider.ts b/src/server/data/data-provider.ts index 6364590..27756d8 100644 --- a/src/server/data/data-provider.ts +++ b/src/server/data/data-provider.ts @@ -86,7 +86,7 @@ export type QDataProviderQueryParams = { request: QRequestContext traceSpan: QTraceSpan maxRuntimeInS?: number - archive?: boolean + archive?: boolean | null } export interface QDataProvider { diff --git a/src/server/graphql/blockchain/blockchain.ts b/src/server/graphql/blockchain/blockchain.ts index db1c274..cc9153a 100644 --- a/src/server/graphql/blockchain/blockchain.ts +++ b/src/server/graphql/blockchain/blockchain.ts @@ -186,6 +186,7 @@ export const resolvers: Resolvers = { context, info, traceSpan, + args.archive, ) }) }, @@ -235,6 +236,7 @@ export const resolvers: Resolvers = { context, info, traceSpan, + args.archive, ) }) }, @@ -258,6 +260,7 @@ export const resolvers: Resolvers = { context, info, traceSpan, + args.archive, ) }) }, diff --git a/src/server/graphql/blockchain/config.ts b/src/server/graphql/blockchain/config.ts index 3b6e395..c4ba3b5 100644 --- a/src/server/graphql/blockchain/config.ts +++ b/src/server/graphql/blockchain/config.ts @@ -225,6 +225,7 @@ export type CompiledCollectionConfig = { context: QRequestContext, traceSpan: QTraceSpan, maxJoinDepth: number, + archive: boolean | undefined | null, ) => Promise qDataCollectionSelector: (context: QRequestContext) => QDataCollection } @@ -357,6 +358,7 @@ export function compileCollectionConfig( context: QRequestContext, traceSpan: QTraceSpan, maxJoinDepth: number, + archive: boolean | undefined | null, ) => { for (const join of collection.joins || []) { const joinSelectionSet = getFieldSelectionSet( @@ -412,6 +414,7 @@ export function compileCollectionConfig( orderBy: [], request: context, traceSpan, + archive, }, )) as any[][] for (const joinItem of queryResult.flat()) { @@ -461,6 +464,7 @@ export function compileCollectionConfig( context, traceSpan, maxJoinDepth - 1, + archive, ) } } diff --git a/src/server/graphql/blockchain/fetchers/accounts.ts b/src/server/graphql/blockchain/fetchers/accounts.ts index b601aed..c71967b 100644 --- a/src/server/graphql/blockchain/fetchers/accounts.ts +++ b/src/server/graphql/blockchain/fetchers/accounts.ts @@ -58,6 +58,7 @@ export async function resolve_account( context, traceSpan, maxJoinDepth, + false, ) return queryResult[0] diff --git a/src/server/graphql/blockchain/fetchers/blocks.ts b/src/server/graphql/blockchain/fetchers/blocks.ts index d1906b9..8d342d8 100644 --- a/src/server/graphql/blockchain/fetchers/blocks.ts +++ b/src/server/graphql/blockchain/fetchers/blocks.ts @@ -30,6 +30,7 @@ async function fetch_blocks( context: QRequestContext, info: GraphQLResolveInfo, traceSpan: QTraceSpan, + archive: boolean | undefined | null, additionalFields: KeyOf[] = [], maxJoinDepth = 1, ) { @@ -59,6 +60,7 @@ async function fetch_blocks( orderBy: [], request: context, traceSpan, + archive, }, )) as BlockchainBlock[] @@ -68,6 +70,7 @@ async function fetch_blocks( context, traceSpan, maxJoinDepth, + archive, ) return queryResult @@ -78,6 +81,7 @@ export async function resolve_block( context: QRequestContext, info: GraphQLResolveInfo, traceSpan: QTraceSpan, + archive: boolean | undefined | null, ) { return ( await fetch_blocks( @@ -85,7 +89,7 @@ export async function resolve_block( context, info, traceSpan, - // TODO: shard + archive, ) )[0] } @@ -109,6 +113,7 @@ export async function resolve_block_by_seq_no( context, info, traceSpan, + args.archive, ) )[0] } @@ -146,6 +151,7 @@ export async function resolve_prev_shard_blocks( orderBy: [], request: context, traceSpan, + archive: args.archive, }, )) as BlockchainBlock[] @@ -155,6 +161,7 @@ export async function resolve_prev_shard_blocks( context, traceSpan, 0, + args.archive, ) return queryResult @@ -174,6 +181,7 @@ export async function resolve_next_shard_blocks( context, info, traceSpan, + args.archive, ["after_split"], 0, ) @@ -236,7 +244,7 @@ export async function resolve_key_blocks( ], request: context, traceSpan, - archive: args.archive ?? undefined, + archive: args.archive, }, )) as BlockchainBlock[] @@ -312,7 +320,7 @@ export async function resolve_blockchain_blocks( ], request: context, traceSpan, - archive: args.archive ?? undefined, + archive: args.archive, }, )) as BlockchainBlock[] diff --git a/src/server/graphql/blockchain/fetchers/messages.ts b/src/server/graphql/blockchain/fetchers/messages.ts index 88afc28..78e1b7d 100644 --- a/src/server/graphql/blockchain/fetchers/messages.ts +++ b/src/server/graphql/blockchain/fetchers/messages.ts @@ -28,6 +28,7 @@ export async function resolve_message( context: QRequestContext, info: GraphQLResolveInfo, traceSpan: QTraceSpan, + archive: boolean | undefined | null, ) { const maxJoinDepth = 1 @@ -56,6 +57,7 @@ export async function resolve_message( orderBy: [], request: context, traceSpan, + archive, }, )) as BlockchainMessage[] @@ -65,6 +67,7 @@ export async function resolve_message( context, traceSpan, maxJoinDepth, + archive, ) return queryResult[0] @@ -266,7 +269,7 @@ export async function resolve_account_messages( distinctBy: "account_chain_order", request: context, traceSpan, - archive: args.archive ?? undefined, + archive: args.archive, }, )) as (BlockchainMessage & { account_chain_order?: string })[] @@ -282,6 +285,7 @@ export async function resolve_account_messages( context, traceSpan, maxJoinDepth, + args.archive, ) }, )) as BlockchainMessagesConnection diff --git a/src/server/graphql/blockchain/fetchers/transactions.ts b/src/server/graphql/blockchain/fetchers/transactions.ts index 0ed6e2d..1ac0585 100644 --- a/src/server/graphql/blockchain/fetchers/transactions.ts +++ b/src/server/graphql/blockchain/fetchers/transactions.ts @@ -30,6 +30,7 @@ export async function resolve_transaction( context: QRequestContext, info: GraphQLResolveInfo, traceSpan: QTraceSpan, + archive: boolean | undefined | null, ) { const maxJoinDepth = 2 @@ -58,6 +59,7 @@ export async function resolve_transaction( orderBy: [], request: context, traceSpan, + archive, }, )) as BlockchainTransaction[] @@ -98,6 +100,7 @@ export async function resolve_transactions_by_in_msg( orderBy: [], request: context, traceSpan, + archive: args.archive, }, )) as BlockchainTransaction[] @@ -164,7 +167,7 @@ export async function resolve_blockchain_transactions( ], request: context, traceSpan, - archive: args.archive ?? undefined, + archive: args.archive, }, )) as BlockchainTransaction[] @@ -180,6 +183,7 @@ export async function resolve_blockchain_transactions( context, traceSpan, maxJoinDepth, + args.archive, ) }, )) as BlockchainTransactionsConnection @@ -245,7 +249,7 @@ export async function resolve_account_transactions( ], request: context, traceSpan, - archive: args.archive ?? undefined, + archive: args.archive, }, )) as BlockchainTransaction[] return (await processPaginatedQueryResult( @@ -260,6 +264,7 @@ export async function resolve_account_transactions( context, traceSpan, maxJoinDepth, + args.archive, ) }, )) as BlockchainTransactionsConnection @@ -321,7 +326,7 @@ export async function resolve_account_transactions_by_lt( ], request: context, traceSpan, - archive: args.archive ?? undefined, + archive: args.archive, }, )) as BlockchainTransaction[] @@ -337,6 +342,7 @@ export async function resolve_account_transactions_by_lt( context, traceSpan, maxJoinDepth, + args.archive, ) }, )) as BlockchainTransactionsConnection diff --git a/src/server/graphql/blockchain/resolvers-types-generated.ts b/src/server/graphql/blockchain/resolvers-types-generated.ts index a32a854..3c4c926 100644 --- a/src/server/graphql/blockchain/resolvers-types-generated.ts +++ b/src/server/graphql/blockchain/resolvers-types-generated.ts @@ -1145,7 +1145,7 @@ export type BlockchainTransaction = Node & { tr_type?: Maybe tr_type_name?: Maybe /** VM debug trace */ - trace?: Maybe + trace?: Maybe>> tt?: Maybe /** Workchain id of the account address (account_addr field) */ workchain_id?: Maybe @@ -1876,6 +1876,20 @@ export type TransactionStorageStorage_Fees_DueArgs = { format?: Maybe } +export type TransactionTrace = { + __typename?: "TransactionTrace" + info_type?: Maybe + step?: Maybe + cmd_str?: Maybe + stack?: Maybe>> + gas_used?: Maybe + gas_cmd?: Maybe + cmd_code_rem_bits?: Maybe + cmd_code_hex?: Maybe + cmd_code_cell_hash?: Maybe + cmd_code_offset?: Maybe +} + export enum TransactionTypeEnum { Ordinary = "Ordinary", Storage = "Storage", @@ -2103,6 +2117,7 @@ export type ResolversTypes = { TransactionProcessingStatusEnum: TransactionProcessingStatusEnum TransactionSplitInfo: ResolverTypeWrapper TransactionStorage: ResolverTypeWrapper + TransactionTrace: ResolverTypeWrapper TransactionTypeEnum: TransactionTypeEnum ValidatorSet: ResolverTypeWrapper ValidatorSetList: ResolverTypeWrapper @@ -2177,6 +2192,7 @@ export type ResolversParentTypes = { TransactionCredit: TransactionCredit TransactionSplitInfo: TransactionSplitInfo TransactionStorage: TransactionStorage + TransactionTrace: TransactionTrace ValidatorSet: ValidatorSet ValidatorSetList: ValidatorSetList } @@ -3527,7 +3543,11 @@ export type BlockchainTransactionResolvers< ParentType, ContextType > - trace?: Resolver, ParentType, ContextType> + trace?: Resolver< + Maybe>>, + ParentType, + ContextType + > tt?: Resolver, ParentType, ContextType> workchain_id?: Resolver< Maybe, @@ -4691,6 +4711,51 @@ export type TransactionStorageResolvers< __isTypeOf?: IsTypeOfResolverFn } +export type TransactionTraceResolvers< + ContextType = any, + ParentType extends ResolversParentTypes["TransactionTrace"] = ResolversParentTypes["TransactionTrace"], +> = { + info_type?: Resolver< + Maybe, + ParentType, + ContextType + > + step?: Resolver, ParentType, ContextType> + cmd_str?: Resolver, ParentType, ContextType> + stack?: Resolver< + Maybe>>, + ParentType, + ContextType + > + gas_used?: Resolver< + Maybe, + ParentType, + ContextType + > + gas_cmd?: Resolver, ParentType, ContextType> + cmd_code_rem_bits?: Resolver< + Maybe, + ParentType, + ContextType + > + cmd_code_hex?: Resolver< + Maybe, + ParentType, + ContextType + > + cmd_code_cell_hash?: Resolver< + Maybe, + ParentType, + ContextType + > + cmd_code_offset?: Resolver< + Maybe, + ParentType, + ContextType + > + __isTypeOf?: IsTypeOfResolverFn +} + export type ValidatorSetResolvers< ContextType = any, ParentType extends ResolversParentTypes["ValidatorSet"] = ResolversParentTypes["ValidatorSet"], @@ -4812,6 +4877,7 @@ export type Resolvers = { TransactionCredit?: TransactionCreditResolvers TransactionSplitInfo?: TransactionSplitInfoResolvers TransactionStorage?: TransactionStorageResolvers + TransactionTrace?: TransactionTraceResolvers ValidatorSet?: ValidatorSetResolvers ValidatorSetList?: ValidatorSetListResolvers } diff --git a/src/server/graphql/resolvers-generated.ts b/src/server/graphql/resolvers-generated.ts index 88a0199..2923008 100644 --- a/src/server/graphql/resolvers-generated.ts +++ b/src/server/graphql/resolvers-generated.ts @@ -426,6 +426,19 @@ const TransactionSplitInfo = struct({ this_addr: stringLowerFilter, }) +const TransactionTrace = struct({ + cmd_code_cell_hash: scalar, + cmd_code_hex: scalar, + cmd_code_offset: scalar, + cmd_code_rem_bits: scalar, + cmd_str: scalar, + gas_cmd: scalar, + gas_used: scalar, + info_type: scalar, + stack: StringArray, + step: scalar, +}) + const BlockValueFlow = struct({ created: bigUInt2, created_other: OtherCurrencyArray, @@ -629,6 +642,7 @@ const Account = struct( true, ) +const TransactionTraceArray = array(() => TransactionTrace) const Transaction = struct( { id: stringLowerFilter, @@ -707,7 +721,7 @@ const Transaction = struct( MergePrepare: 6, MergeInstall: 7, }), - trace: scalar, + trace: TransactionTraceArray, tt: scalar, workchain_id: scalar, }, @@ -1848,7 +1862,46 @@ scalarFields.set("transactions.total_fees_other.value", { type: "uint1024", path: "doc.total_fees_other[*].value", }) -scalarFields.set("transactions.trace", { type: "string", path: "doc.trace" }) +scalarFields.set("transactions.trace.cmd_code_cell_hash", { + type: "string", + path: "doc.trace[*].cmd_code_cell_hash", +}) +scalarFields.set("transactions.trace.cmd_code_hex", { + type: "string", + path: "doc.trace[*].cmd_code_hex", +}) +scalarFields.set("transactions.trace.cmd_code_offset", { + type: "number", + path: "doc.trace[*].cmd_code_offset", +}) +scalarFields.set("transactions.trace.cmd_code_rem_bits", { + type: "number", + path: "doc.trace[*].cmd_code_rem_bits", +}) +scalarFields.set("transactions.trace.cmd_str", { + type: "string", + path: "doc.trace[*].cmd_str", +}) +scalarFields.set("transactions.trace.gas_cmd", { + type: "string", + path: "doc.trace[*].gas_cmd", +}) +scalarFields.set("transactions.trace.gas_used", { + type: "string", + path: "doc.trace[*].gas_used", +}) +scalarFields.set("transactions.trace.info_type", { + type: "string", + path: "doc.trace[*].info_type", +}) +scalarFields.set("transactions.trace.stack", { + type: "string", + path: "doc.trace[*].stack[**]", +}) +scalarFields.set("transactions.trace.step", { + type: "number", + path: "doc.trace[*].step", +}) scalarFields.set("transactions.tt", { type: "string", path: "doc.tt" }) scalarFields.set("transactions.workchain_id", { type: "number", @@ -4650,6 +4703,7 @@ export { TransactionAction, TransactionBounce, TransactionSplitInfo, + TransactionTrace, BlockValueFlow, BlockAccountBlocksTransactions, BlockAccountBlocks, diff --git a/src/server/schema/db-schema.ts b/src/server/schema/db-schema.ts index 99ccc4b..7d6f891 100644 --- a/src/server/schema/db-schema.ts +++ b/src/server/schema/db-schema.ts @@ -388,7 +388,18 @@ const Transaction: TypeDef = { chain_order: stringWithLowerFilter(docs.transaction.chain_order), ext_in_msg_fee: grams(docs.transaction.ext_in_msg_fee), master_seq_no: u32(docs.transaction.master_seq_no), - trace: string(docs.transaction.trace), + trace: arrayOf({ + info_type: string(), + step: u32(), + cmd_str: string(), + stack: arrayOf(string()), + gas_used: string(), + gas_cmd: string(), + cmd_code_rem_bits: u32(), + cmd_code_hex: string(), + cmd_code_cell_hash: string(), + cmd_code_offset: u32(), + }), } // BLOCK SIGNATURES