From 7e19fd3f34c910168bff5003d879489c76417741 Mon Sep 17 00:00:00 2001 From: Maarten van den Hoven Date: Wed, 15 May 2024 11:33:16 +0200 Subject: [PATCH] Added neighbor info to blocks and phased out CHAIN_COUNT and NETWORK_ID env vars (#2086) * Added neighbor info to blocks and phased out CHAIN_COUNT and NETWORK_ID env vars * Fixed default values * fixed simulation * linting * fixed network config --- .changeset/polite-plums-decide.md | 5 +++ packages/apps/graph/.env.example | 3 -- packages/apps/graph/README.md | 29 ++++++++++---- packages/apps/graph/generated-schema.graphql | 9 ++++- .../graph/src/devnet/deployment/helper.ts | 3 +- .../deployment/marmalade/config/arguments.ts | 2 +- .../src/devnet/deployment/marmalade/deploy.ts | 5 +++ .../devnet/deployment/marmalade/namespace.ts | 4 +- .../simulation/coin/crosschain-transfer.ts | 3 +- .../devnet/simulation/coin/safe-transfer.ts | 5 ++- .../src/devnet/simulation/coin/simulate.ts | 10 ++++- .../src/devnet/simulation/coin/transfer.ts | 3 +- .../graph/src/devnet/simulation/helper.ts | 7 +++- .../simulation/marmalade/create-token-id.ts | 3 +- .../simulation/marmalade/create-token.ts | 3 +- .../simulation/marmalade/get-token-info.ts | 3 +- .../devnet/simulation/marmalade/mint-token.ts | 3 +- .../marmalade/transfer-create-token.ts | 3 +- packages/apps/graph/src/graph/builder.ts | 2 + .../apps/graph/src/graph/objects/block.ts | 40 +++++++++++++++++++ .../src/graph/objects/fungible-account.ts | 15 ++++--- .../src/graph/objects/non-fungible-account.ts | 10 ++--- .../src/graph/query/blocks-from-height.ts | 4 +- .../graph/query/completed-block-heights.ts | 26 +++++++----- .../graph/src/graph/query/fungible-account.ts | 4 +- .../query/fungible-accounts-by-public-key.ts | 4 +- .../src/graph/subscription/new-blocks.ts | 6 +-- .../graph/src/graph/types/graphql-types.ts | 5 +++ .../src/services/chainweb-node/mempool.ts | 3 +- packages/apps/graph/src/services/network.ts | 4 +- .../apps/graph/src/services/systems-check.ts | 5 ++- packages/apps/graph/src/utils/chains.ts | 5 --- packages/apps/graph/src/utils/dotenv.ts | 4 -- packages/apps/graph/src/utils/network.ts | 25 ++++++++++-- 34 files changed, 189 insertions(+), 76 deletions(-) create mode 100644 .changeset/polite-plums-decide.md delete mode 100644 packages/apps/graph/src/utils/chains.ts diff --git a/.changeset/polite-plums-decide.md b/.changeset/polite-plums-decide.md new file mode 100644 index 0000000000..f08a2a7a41 --- /dev/null +++ b/.changeset/polite-plums-decide.md @@ -0,0 +1,5 @@ +--- +"@kadena/graph": patch +--- + +Added neighbor info to blocks and phased out CHAIN_COUNT and NETWORK_ID env vars. diff --git a/packages/apps/graph/.env.example b/packages/apps/graph/.env.example index eed9289b8a..4e13c12cfe 100644 --- a/packages/apps/graph/.env.example +++ b/packages/apps/graph/.env.example @@ -1,5 +1,3 @@ -CHAIN_COUNT=20 - # Complexity COMPLEXITY_LIMIT=500 COMPLEXITY_ENABLED=false @@ -13,7 +11,6 @@ PRISMA_LOG_FILENAME="prisma.log" # The following variables are used to configure the network endpoints for the GraphQL API. NETWORK_HOST="http://localhost:8080" -NETWORK_ID="development" MEMPOOL_HOST="localhost:1789" NETWORK_STATISTICS_URL="http://localhost:8080/stats" diff --git a/packages/apps/graph/README.md b/packages/apps/graph/README.md index e36c73c3b5..530c4e1f3e 100644 --- a/packages/apps/graph/README.md +++ b/packages/apps/graph/README.md @@ -13,13 +13,21 @@ - [Method 1: Using the published npm package](#method-1-using-the-published-npm-package) - [Method 2: Building and running from source](#method-2-building-and-running-from-source) - [Running your first query](#running-your-first-query) + - [Example queries per use case](#example-queries-per-use-case) + - [Wallet related](#wallet-related) + - [Explorer related](#explorer-related) + - [Event related](#event-related) + - [Fungible related](#fungible-related) + - [Non-fungible related](#non-fungible-related) - [Features](#features) - [Tracing and trace analysis](#tracing-and-trace-analysis) - [Query Complexity](#query-complexity) - [Prisma JSON field queries](#prisma-json-field-queries) - [Paginated results](#paginated-results) - [Useful extra's](#useful-extras) + - [Network Information](#network-information) - [Running devnet](#running-devnet) + - [GraphQL](#graphql) - [Connecting to the database](#connecting-to-the-database) - [Simulating traffic on the devnet](#simulating-traffic-on-the-devnet) - [Coin simulation](#coin-simulation) @@ -99,25 +107,26 @@ We've curated a list of useful queries and subscriptions tailored for different To get started, ensure you have a local instance of the graph service running on `localhost:4000`. +#### Wallet related - -##### Wallet related - [Get account balance](http://localhost:4000/graphql?query=query+GetAccountBalance+%7B%0A++fungibleAccount%28accountName%3A+%22k%3A123456789...%22%29%7B%0A++++accountName%0A++++totalBalance%0A++++fungibleName%0A++++chainAccounts%7B%0A++++++chainId%0A++++++balance%0A++++%7D%0A++%7D%0A%7D) - [Getting account transactions](http://localhost:4000/graphql?query=query+GetAccountBalance%7B%0A++fungibleAccount%28accountName%3A+%22k%3A123456789...%22%29+%7B%0A++++transactions%28first%3A+10%29+%7B%0A++++++edges+%7B%0A++++++++node+%7B%0A++++++++++hash%0A++++++++++cmd+%7B%0A++++++++++++meta+%7B%0A++++++++++++++chainId%0A++++++++++++++creationTime%0A++++++++++++++gasLimit%0A++++++++++++++gasPrice%0A++++++++++++++sender%0A++++++++++++++ttl%0A++++++++++++%7D%0A++++++++++++payload+%7B%0A++++++++++++++...+on+ContinuationPayload+%7B%0A++++++++++++++++data%0A++++++++++++++++pactId%0A++++++++++++++++proof%0A++++++++++++++++rollback%0A++++++++++++++++step%0A++++++++++++++%7D%0A++++++++++++++...+on+ExecutionPayload+%7B%0A++++++++++++++++code%0A++++++++++++++++data%0A++++++++++++++%7D%0A++++++++++++%7D%0A++++++++++%7D%0A++++++++%7D%0A++++++%7D%0A++++%7D%0A++%7D%0A%7D) -##### Explorer related +#### Explorer related + - [Listen for a transaction](http://localhost:4000/graphql?query=subscription+ListenTransaction%7B%0A++transaction%28requestKey%3A+%22EBS5rExXr7ndvMp6nK_ie-372oIXWVX5JmmKMXkiD4Q%22%29%7B%0A++++sigs%7B%0A++++++sig%0A++++%7D%0A++++cmd%7B%0A++++++meta%7B%0A++++++++chainId%0A++++++++creationTime%0A++++++++gasLimit%0A++++++++gasPrice%0A++++++++sender%0A++++++++ttl%0A++++++%7D%0A++++++networkId%0A++++++nonce%0A++++++payload%7B%0A++++++++...on+ContinuationPayload%7B%0A++++++++++data%0A++++++++++pactId%0A++++++++++proof%0A++++++++++rollback%0A++++++++++step%0A++++++++%7D%0A++++++++...on+ExecutionPayload%7B%0A++++++++++code%0A++++++++++data%0A++++++++%7D%0A++++++%7D%0A++++++signers%7B%0A++++++++address%0A++++++++clist%7B%0A++++++++++args%0A++++++++++name%0A++++++++%7D%0A++++++++id%0A++++++++orderIndex%0A++++++++pubkey%0A++++++++scheme%0A++++++%7D%0A++++%7D%0A++++hash%0A++++id%0A++++result%7B%0A++++++...on+TransactionMempoolInfo%7B%0A++++++++status%0A++++++%7D%0A++++%7D%0A++%7D%0A%7D) - [Get the 5 latest confirmed blocks on chain 0 and 1](http://localhost:4000/graphql?query=query+GetLatestConfirmedBlocks+%7B%0A++blocksFromDepth%28first%3A+5%2C+minimumDepth%3A+6%2C+chainIds%3A+%5B%220%22%2C+%221%22%5D%29+%7B%0A++++edges+%7B%0A++++++node+%7B%0A++++++++height%0A++++++++hash%0A++++++%7D%0A++++%7D%0A++%7D%0A%7D) +#### Event related -##### Event related - [Listen to events](http://localhost:4000/graphql?query=subscription+GetLatestEvents%7B%0A++events%28qualifiedEventName%3A+%22coin.TRANSFER%22%29%7B%0A++++name%0A++++requestKey%0A++++parameters%0A++++orderIndex%0A++%7D%0A%7D) -##### Fungible related +#### Fungible related + - [Get data on a given account for a given fungible](http://localhost:4000/graphql?query=query+GetAccountInfoOnFungible%7B%0A++fungibleAccount%28accountName%3A%22test-coin-account%22%2C+fungibleName%3A+%22test-coin%22%29%7B%0A++++accountName%0A++++fungibleName%0A++++totalBalance%0A++++transactions%7B%0A++++++totalCount%0A++++%7D%0A++%7D%0A%7D) +#### Non-fungible related -##### Non-fungible related - [Get token balances for a given account](http://localhost:4000/graphql?query=query+GetNFTBalances+%7B%0A++nonFungibleAccount%28accountName%3A+%22k%3A123456789...%22%29%7B%0A++++accountName%0A++++nonFungibleTokenBalances%7B%0A++++++balance%0A++++++chainId%0A++++++id%0A++++++info%7B%0A++++++++precision%0A++++++++supply%0A++++++++uri%0A++++++%7D%0A++++++version%0A++++%7D%0A++++transactions%7B%0A++++++totalCount%0A++++%7D%0A++%7D%0A%7D) Remember, the GraphiQL Explorer is a powerful tool for understanding and interacting with our GraphQL API. Don't hesitate to experiment and learn! @@ -178,6 +187,12 @@ Note that `first` can only be used with `after`, and `last` can only be used wit ## Useful extra's +### Network Information + +On startup, the GraphQL server will fetch the network information from the Chainweb node. This will store it in memory and contains information about the chain ids, the network id, the network version and the neighbours of each chain. + +> ⚠️ **Note:** The network information is only fetched once on startup. If you want to update the network information, you will need to restart the server. + ### Running devnet Prerequisites: @@ -204,8 +219,8 @@ pnpm run fund -- -k -a An alternative is to run a full simulation of traffic on the devnet, see [Simulating traffic on the devnet](#simulating-traffic-on-the-devnet). +#### GraphQL -#### GraphQL The devnet includes a built-in GraphQL server operating on port `4000`. For convenience, the HTTP API proxies the GraphQL endpoint via the `/graphql` route. Additional information and resources can be accessed on the splash page at port `8080`. Please note: This built-in version is intended for consumption when not actively developing. If you're making changes to your project, be aware that the built-in GraphQL server will not reflect these updates. In a development scenario, you should set up your own instance of the GraphQL server to test your changes. diff --git a/packages/apps/graph/generated-schema.graphql b/packages/apps/graph/generated-schema.graphql index d2fb9c83e9..de6272ae5d 100644 --- a/packages/apps/graph/generated-schema.graphql +++ b/packages/apps/graph/generated-schema.graphql @@ -22,6 +22,7 @@ type Block implements Node { height: BigInt! id: ID! minerAccount: FungibleChainAccount! + neighbours: [BlockNeighbor!]! parent: Block payloadHash: String! @@ -43,6 +44,12 @@ type BlockEventsConnectionEdge { node: Event! } +"""The neighbor of a block.""" +type BlockNeighbor { + blockHash: String! + chainId: String! +} + type BlockTransactionsConnection { edges: [BlockTransactionsConnectionEdge!]! pageInfo: PageInfo! @@ -390,7 +397,7 @@ type Query { before: String """Default: all chains""" - chainIds: [String!] = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19"] + chainIds: [String!] completedHeights: Boolean = false first: Int heightCount: Int = 3 diff --git a/packages/apps/graph/src/devnet/deployment/helper.ts b/packages/apps/graph/src/devnet/deployment/helper.ts index 39dd6b017d..a307699ea8 100644 --- a/packages/apps/graph/src/devnet/deployment/helper.ts +++ b/packages/apps/graph/src/devnet/deployment/helper.ts @@ -14,6 +14,7 @@ import { } from '@kadena/client'; import { dotenv } from '@utils/dotenv'; import { logger } from '@utils/logger'; +import { networkData } from '@utils/network'; export interface IAccount { account: string; @@ -27,7 +28,7 @@ const getClient = (): IClient => { if (!client) { client = createClient( ({ chainId }) => - `${dotenv.NETWORK_HOST}/chainweb/0.0/${dotenv.NETWORK_ID}/chain/${chainId}/pact`, + `${dotenv.NETWORK_HOST}/chainweb/${networkData.apiVersion}/${networkData.networkId}/chain/${chainId}/pact`, ); } return client; diff --git a/packages/apps/graph/src/devnet/deployment/marmalade/config/arguments.ts b/packages/apps/graph/src/devnet/deployment/marmalade/config/arguments.ts index 2f61e96106..b9d618a640 100644 --- a/packages/apps/graph/src/devnet/deployment/marmalade/config/arguments.ts +++ b/packages/apps/graph/src/devnet/deployment/marmalade/config/arguments.ts @@ -11,7 +11,7 @@ export const argumentConfig = { is_upgrade: 'false', // eslint-disable-next-line @typescript-eslint/naming-convention upgrade_version_1: 'false', - network: dotenv.NETWORK_ID, + network: 'development', // Gets overwritten with the actual network id when called chain: dotenv.SIMULATE_DEFAULT_CHAIN_ID, sender: sender00.account, kip_namespace: 'kip', diff --git a/packages/apps/graph/src/devnet/deployment/marmalade/deploy.ts b/packages/apps/graph/src/devnet/deployment/marmalade/deploy.ts index 2b74c04640..de7ed8a1f5 100644 --- a/packages/apps/graph/src/devnet/deployment/marmalade/deploy.ts +++ b/packages/apps/graph/src/devnet/deployment/marmalade/deploy.ts @@ -8,6 +8,7 @@ import { join, relative } from 'path'; import { downloadGitFiles } from '@services/git/download-git-files'; import { flattenFolder } from '@services/git/path'; import { logger } from '@utils/logger'; +import { initializeNetworkConfig, networkData } from '@utils/network'; import { validateObjectProperties } from '@utils/validate-object'; import type { IAccount } from '../helper'; import { inspect, listen, signAndAssertTransaction, submit } from '../helper'; @@ -36,6 +37,10 @@ export async function deployMarmaladeContracts( codeFileDestinationPath: string = marmaladeLocalConfig.codeFilesPath, nsDestinationPath: string = marmaladeLocalConfig.namespacePath, ): Promise { + await initializeNetworkConfig(); + + argumentConfig.network = networkData.networkId; + logger.info('Validating repository data...'); validateConfig( marmaladeRepository, diff --git a/packages/apps/graph/src/devnet/deployment/marmalade/namespace.ts b/packages/apps/graph/src/devnet/deployment/marmalade/namespace.ts index acb843e9fb..989d3c6a66 100644 --- a/packages/apps/graph/src/devnet/deployment/marmalade/namespace.ts +++ b/packages/apps/graph/src/devnet/deployment/marmalade/namespace.ts @@ -2,8 +2,8 @@ import type { IAccount } from '@devnet/utils'; import { sender00 } from '@devnet/utils'; import type { ChainId, ICommand, IKeyPair } from '@kadena/client'; import { Pact } from '@kadena/client'; -import { dotenv } from '@utils/dotenv'; import { logger } from '@utils/logger'; +import { networkData } from '@utils/network'; import { readFileSync, readdirSync } from 'fs'; import { join } from 'path'; import { inspect, listen, signAndAssertTransaction, submit } from '../helper'; @@ -106,7 +106,7 @@ export async function createPactCommandFromFile( filepath: string, { chainId, - networkId = dotenv.NETWORK_ID, + networkId = networkData.networkId, signers = sender00.keys, meta = { gasLimit: 70000, diff --git a/packages/apps/graph/src/devnet/simulation/coin/crosschain-transfer.ts b/packages/apps/graph/src/devnet/simulation/coin/crosschain-transfer.ts index 4ee4a1a0de..351ee45c9b 100644 --- a/packages/apps/graph/src/devnet/simulation/coin/crosschain-transfer.ts +++ b/packages/apps/graph/src/devnet/simulation/coin/crosschain-transfer.ts @@ -5,6 +5,7 @@ import { createSignWithKeypair } from '@kadena/client'; import { transferCrossChain } from '@kadena/client-utils/coin'; import { dotenv } from '@utils/dotenv'; import { logger } from '@utils/logger'; +import { networkData } from '@utils/network'; export async function crossChainTransfer({ sender, @@ -60,7 +61,7 @@ export async function crossChainTransfer({ { host: dotenv.NETWORK_HOST, defaults: { - networkId: dotenv.NETWORK_ID, + networkId: networkData.networkId, }, sign: createSignWithKeypair([...sender.keys, ...gasPayer.keys]), }, diff --git a/packages/apps/graph/src/devnet/simulation/coin/safe-transfer.ts b/packages/apps/graph/src/devnet/simulation/coin/safe-transfer.ts index f609f6c105..594b772de6 100644 --- a/packages/apps/graph/src/devnet/simulation/coin/safe-transfer.ts +++ b/packages/apps/graph/src/devnet/simulation/coin/safe-transfer.ts @@ -7,6 +7,7 @@ import { PactNumber } from '@kadena/pactjs'; import type { ChainId } from '@kadena/types'; import { dotenv } from '@utils/dotenv'; import { logger } from '@utils/logger'; +import { networkData } from '@utils/network'; import { stringifyProperty } from '../helper'; export async function safeTransfer({ @@ -37,7 +38,7 @@ export async function safeTransfer({ host: dotenv.NETWORK_HOST, sign: createSignWithKeypair([...sender.keys, ...receiver.keys]), defaults: { - networkId: dotenv.NETWORK_ID, + networkId: networkData.networkId, }, })( Pact.builder @@ -87,7 +88,7 @@ export async function safeTransfer({ senderAccount: sender.account, ttl: 8 * 60 * 60, //8 hours }) - .setNetworkId(dotenv.NETWORK_ID) + .setNetworkId(networkData.networkId) .getCommand(), ).executeTo('listen'); } diff --git a/packages/apps/graph/src/devnet/simulation/coin/simulate.ts b/packages/apps/graph/src/devnet/simulation/coin/simulate.ts index d39b2216a9..00ef960c22 100644 --- a/packages/apps/graph/src/devnet/simulation/coin/simulate.ts +++ b/packages/apps/graph/src/devnet/simulation/coin/simulate.ts @@ -3,6 +3,7 @@ import { sender00 } from '@devnet/utils'; import type { ChainId } from '@kadena/client'; import { dotenv } from '@utils/dotenv'; import { logger } from '@utils/logger'; +import { initializeNetworkConfig, networkData } from '@utils/network'; import type { TransferType } from '../file'; import { appendToFile, createFile } from '../file'; import { @@ -41,6 +42,8 @@ export async function simulateCoin({ }: ISimulationOptions): Promise { const accounts: IAccount[] = []; + await initializeNetworkConfig(); + // Parameters validation if (tokenPool < maxAmount) { logger.error( @@ -172,13 +175,16 @@ export async function simulateCoin({ let result; // This is to simulate cross chain transfers - if (transferType === 'cross-chain-transfer' && dotenv.CHAIN_COUNT > 1) { + if ( + transferType === 'cross-chain-transfer' && + networkData.chainIds.length > 1 + ) { if (account.chainId === nextAccount.chainId) { nextAccount = { ...nextAccount, chainId: `${getRandomNumber( seededRandomNo, - dotenv.CHAIN_COUNT, + networkData.chainIds.length, )}` as ChainId, }; } diff --git a/packages/apps/graph/src/devnet/simulation/coin/transfer.ts b/packages/apps/graph/src/devnet/simulation/coin/transfer.ts index 4001b2a84c..3b807bf19a 100644 --- a/packages/apps/graph/src/devnet/simulation/coin/transfer.ts +++ b/packages/apps/graph/src/devnet/simulation/coin/transfer.ts @@ -6,6 +6,7 @@ import { transferCreate } from '@kadena/client-utils/coin'; import { PactNumber } from '@kadena/pactjs'; import { dotenv } from '@utils/dotenv'; import { logger } from '@utils/logger'; +import { networkData } from '@utils/network'; import { stringifyProperty } from '../helper'; export async function transfer({ @@ -48,7 +49,7 @@ export async function transfer({ { host: dotenv.NETWORK_HOST, defaults: { - networkId: dotenv.NETWORK_ID, + networkId: networkData.networkId, }, sign: createSignWithKeypair(sender.keys), }, diff --git a/packages/apps/graph/src/devnet/simulation/helper.ts b/packages/apps/graph/src/devnet/simulation/helper.ts index 89e7736802..661211a11f 100644 --- a/packages/apps/graph/src/devnet/simulation/helper.ts +++ b/packages/apps/graph/src/devnet/simulation/helper.ts @@ -4,12 +4,15 @@ import { getBalance } from '@kadena/client-utils/coin'; import { genKeyPair } from '@kadena/cryptography-utils'; import type { ChainId } from '@kadena/types'; import { dotenv } from '@utils/dotenv'; +import { initializeNetworkConfig, networkData } from '@utils/network'; import seedrandom from 'seedrandom'; export const generateAccount = async ( keys: number = 1, chainId: ChainId = dotenv.SIMULATE_DEFAULT_CHAIN_ID, ): Promise => { + await initializeNetworkConfig(); + const keyPairs = Array.from({ length: keys }, () => genKeyPair()); const account = await createPrincipal( { @@ -20,7 +23,7 @@ export const generateAccount = async ( { host: dotenv.NETWORK_HOST, defaults: { - networkId: dotenv.NETWORK_ID, + networkId: networkData.networkId, meta: { chainId }, }, }, @@ -101,7 +104,7 @@ export const getAccountBalance = async ({ }): Promise => { const result = await getBalance( account, - dotenv.NETWORK_ID, + networkData.networkId, chainId || dotenv.SIMULATE_DEFAULT_CHAIN_ID, dotenv.NETWORK_HOST, ); diff --git a/packages/apps/graph/src/devnet/simulation/marmalade/create-token-id.ts b/packages/apps/graph/src/devnet/simulation/marmalade/create-token-id.ts index 11833b92ec..3d5fd2b3cd 100644 --- a/packages/apps/graph/src/devnet/simulation/marmalade/create-token-id.ts +++ b/packages/apps/graph/src/devnet/simulation/marmalade/create-token-id.ts @@ -8,6 +8,7 @@ import { setMeta, } from '@kadena/client/fp'; import { dotenv } from '@utils/dotenv'; +import { networkData } from '@utils/network'; interface ICreateTokenIdInput { policies?: string[]; @@ -25,7 +26,7 @@ export async function createTokenId({ return (await dirtyReadClient({ host: dotenv.NETWORK_HOST, defaults: { - networkId: dotenv.NETWORK_ID, + networkId: networkData.networkId, }, })( composePactCommand( diff --git a/packages/apps/graph/src/devnet/simulation/marmalade/create-token.ts b/packages/apps/graph/src/devnet/simulation/marmalade/create-token.ts index 3e37fb94c8..98d5c07f34 100644 --- a/packages/apps/graph/src/devnet/simulation/marmalade/create-token.ts +++ b/packages/apps/graph/src/devnet/simulation/marmalade/create-token.ts @@ -11,6 +11,7 @@ import { } from '@kadena/client/fp'; import { PactNumber } from '@kadena/pactjs'; import { dotenv } from '@utils/dotenv'; +import { networkData } from '@utils/network'; interface ICreateTokenInput { policies?: string[]; @@ -60,7 +61,7 @@ export async function createToken({ const config = { host: dotenv.NETWORK_HOST, defaults: { - networkId: dotenv.NETWORK_ID, + networkId: networkData.networkId, }, sign: createSignWithKeypair(creator.keys), }; diff --git a/packages/apps/graph/src/devnet/simulation/marmalade/get-token-info.ts b/packages/apps/graph/src/devnet/simulation/marmalade/get-token-info.ts index 8986a91d54..745bf1467c 100644 --- a/packages/apps/graph/src/devnet/simulation/marmalade/get-token-info.ts +++ b/packages/apps/graph/src/devnet/simulation/marmalade/get-token-info.ts @@ -3,6 +3,7 @@ import { Pact } from '@kadena/client'; import { dirtyReadClient } from '@kadena/client-utils/core'; import { composePactCommand, execution, setMeta } from '@kadena/client/fp'; import { dotenv } from '@utils/dotenv'; +import { networkData } from '@utils/network'; interface ITokenInfo { supply: number; @@ -67,7 +68,7 @@ export const getTokenInfo = async ( const config = { host: dotenv.NETWORK_HOST, defaults: { - networkId: dotenv.NETWORK_ID, + networkId: networkData.networkId, }, }; diff --git a/packages/apps/graph/src/devnet/simulation/marmalade/mint-token.ts b/packages/apps/graph/src/devnet/simulation/marmalade/mint-token.ts index 2c2833c970..2806c0ab99 100644 --- a/packages/apps/graph/src/devnet/simulation/marmalade/mint-token.ts +++ b/packages/apps/graph/src/devnet/simulation/marmalade/mint-token.ts @@ -11,6 +11,7 @@ import { } from '@kadena/client/fp'; import type { IPactDecimal } from '@kadena/types'; import { dotenv } from '@utils/dotenv'; +import { networkData } from '@utils/network'; export interface ICreateTokenInput { tokenId: string; @@ -48,7 +49,7 @@ export async function mintToken({ const config = { host: dotenv.NETWORK_HOST, defaults: { - networkId: dotenv.NETWORK_ID, + networkId: networkData.networkId, }, sign: createSignWithKeypair(guard.keys), }; diff --git a/packages/apps/graph/src/devnet/simulation/marmalade/transfer-create-token.ts b/packages/apps/graph/src/devnet/simulation/marmalade/transfer-create-token.ts index 740606f1bf..d0cc3b5e9e 100644 --- a/packages/apps/graph/src/devnet/simulation/marmalade/transfer-create-token.ts +++ b/packages/apps/graph/src/devnet/simulation/marmalade/transfer-create-token.ts @@ -11,6 +11,7 @@ import { } from '@kadena/client/fp'; import type { IPactDecimal } from '@kadena/types'; import { dotenv } from '@utils/dotenv'; +import { networkData } from '@utils/network'; export interface ITransferCreateTokenInput { tokenId: string; @@ -59,7 +60,7 @@ export async function transferCreateToken({ const config = { host: dotenv.NETWORK_HOST, defaults: { - networkId: dotenv.NETWORK_ID, + networkId: networkData.networkId, }, sign: createSignWithKeypair(sender.keys), }; diff --git a/packages/apps/graph/src/graph/builder.ts b/packages/apps/graph/src/graph/builder.ts index 07c4482da9..a896882b5c 100644 --- a/packages/apps/graph/src/graph/builder.ts +++ b/packages/apps/graph/src/graph/builder.ts @@ -19,6 +19,7 @@ import { import type { IncomingMessage } from 'http'; import { prismaClient } from '../db/prisma-client'; import type { + IBlockNeighbor, IContinuationPayload, IExecutionPayload, IFungibleAccount, @@ -95,6 +96,7 @@ export const builder = new SchemaBuilder< TransactionSignature: ITransactionSignature; PactQueryResponse: IPactQueryResponse; NetworkInfo: INetworkInfo; + BlockNeighbor: IBlockNeighbor; }; } >({ diff --git a/packages/apps/graph/src/graph/objects/block.ts b/packages/apps/graph/src/graph/objects/block.ts index 5a2186d83f..8d8dca1b45 100644 --- a/packages/apps/graph/src/graph/objects/block.ts +++ b/packages/apps/graph/src/graph/objects/block.ts @@ -6,11 +6,20 @@ import { getDefaultConnectionComplexity, } from '@services/complexity'; import { normalizeError } from '@utils/errors'; +import { networkData } from '@utils/network'; import { builder } from '../builder'; import type { IGuard } from '../types/graphql-types'; import { FungibleChainAccountName } from '../types/graphql-types'; import FungibleChainAccount from './fungible-chain-account'; +const BlockNeighbor = builder.objectType('BlockNeighbor', { + description: 'The neighbor of a block.', + fields: (t) => ({ + chainId: t.exposeString('chainId'), + blockHash: t.exposeString('blockHash'), + }), +}); + export default builder.prismaNode(Prisma.ModelName.Block, { description: 'A unit of information that stores a set of verified transactions.', @@ -86,6 +95,37 @@ export default builder.prismaNode(Prisma.ModelName.Block, { }, }), + neighbours: t.field({ + type: [BlockNeighbor], + select: { + chainId: true, + height: true, + }, + async resolve(parent) { + try { + const neighbours = networkData.chainNeighbours.get( + parent.chainId.toString(), + ) as string[]; + + const blocks = await prismaClient.block.findMany({ + where: { + chainId: { + in: neighbours.map((x) => BigInt(x)), + }, + height: parent.height - 1n, + }, + }); + + return blocks.map((block) => ({ + chainId: block.chainId.toString(), + blockHash: block.hash, + })); + } catch (error) { + throw normalizeError(error); + } + }, + }), + difficulty: t.field({ type: 'BigInt', select: { diff --git a/packages/apps/graph/src/graph/objects/fungible-account.ts b/packages/apps/graph/src/graph/objects/fungible-account.ts index 1cfa08db46..aa382bc03b 100644 --- a/packages/apps/graph/src/graph/objects/fungible-account.ts +++ b/packages/apps/graph/src/graph/objects/fungible-account.ts @@ -5,9 +5,8 @@ import { COMPLEXITY, getDefaultConnectionComplexity, } from '@services/complexity'; -import { chainIds } from '@utils/chains'; -import { dotenv } from '@utils/dotenv'; import { normalizeError } from '@utils/errors'; +import { networkData } from '@utils/network'; import { builder } from '../builder'; import { fungibleAccountDetailsLoader } from '../data-loaders/fungible-account-details'; import type { @@ -55,12 +54,14 @@ export default builder.node( fungibleName: t.exposeString('fungibleName'), chainAccounts: t.field({ type: [FungibleChainAccountName], - complexity: COMPLEXITY.FIELD.CHAINWEB_NODE * dotenv.CHAIN_COUNT, + complexity: { + field: COMPLEXITY.FIELD.CHAINWEB_NODE, + }, async resolve(parent) { try { return ( await Promise.all( - chainIds.map((chainId) => { + networkData.chainIds.map((chainId) => { return getFungibleChainAccount({ chainId, fungibleName: parent.fungibleName, @@ -78,12 +79,14 @@ export default builder.node( }), totalBalance: t.field({ type: 'Decimal', - complexity: COMPLEXITY.FIELD.CHAINWEB_NODE * dotenv.CHAIN_COUNT, + complexity: { + field: COMPLEXITY.FIELD.CHAINWEB_NODE, + }, async resolve(parent) { try { return ( await Promise.all( - chainIds.map(async (chainId) => { + networkData.chainIds.map(async (chainId) => { return fungibleAccountDetailsLoader.load({ fungibleName: parent.fungibleName, accountName: parent.accountName, diff --git a/packages/apps/graph/src/graph/objects/non-fungible-account.ts b/packages/apps/graph/src/graph/objects/non-fungible-account.ts index 9bd3ca0706..73e22b898f 100644 --- a/packages/apps/graph/src/graph/objects/non-fungible-account.ts +++ b/packages/apps/graph/src/graph/objects/non-fungible-account.ts @@ -5,7 +5,6 @@ import { COMPLEXITY, getDefaultConnectionComplexity, } from '@services/complexity'; -import { dotenv } from '@utils/dotenv'; import { normalizeError } from '@utils/errors'; import { builder } from '../builder'; import { nonFungibleChainCheck } from '../data-loaders/non-fungible-chain-check'; @@ -49,10 +48,11 @@ export default builder.node( accountName: t.exposeString('accountName'), chainAccounts: t.field({ type: [NonFungibleChainAccountName], - complexity: - (COMPLEXITY.FIELD.CHAINWEB_NODE + - COMPLEXITY.FIELD.PRISMA_WITHOUT_RELATIONS) * - dotenv.CHAIN_COUNT, + complexity: { + field: + COMPLEXITY.FIELD.CHAINWEB_NODE + + COMPLEXITY.FIELD.PRISMA_WITHOUT_RELATIONS, + }, async resolve(parent) { try { const chainIds = await nonFungibleChainCheck.load({ diff --git a/packages/apps/graph/src/graph/query/blocks-from-height.ts b/packages/apps/graph/src/graph/query/blocks-from-height.ts index a4c0a1c0d9..c9d947650a 100644 --- a/packages/apps/graph/src/graph/query/blocks-from-height.ts +++ b/packages/apps/graph/src/graph/query/blocks-from-height.ts @@ -1,7 +1,7 @@ import { prismaClient } from '@db/prisma-client'; import { getDefaultConnectionComplexity } from '@services/complexity'; -import { chainIds as defaultChainIds } from '@utils/chains'; import { normalizeError } from '@utils/errors'; +import { networkData } from '@utils/network'; import { builder } from '../builder'; import Block from '../objects/block'; @@ -38,7 +38,7 @@ builder.queryField('blocksFromHeight', (t) => async resolve( query, __parent, - { startHeight, chainIds = defaultChainIds }, + { startHeight, chainIds = networkData.chainIds }, ) { try { return await prismaClient.block.findMany({ diff --git a/packages/apps/graph/src/graph/query/completed-block-heights.ts b/packages/apps/graph/src/graph/query/completed-block-heights.ts index 02c254494f..6471cf37df 100644 --- a/packages/apps/graph/src/graph/query/completed-block-heights.ts +++ b/packages/apps/graph/src/graph/query/completed-block-heights.ts @@ -1,8 +1,7 @@ import { prismaClient } from '@db/prisma-client'; import { COMPLEXITY } from '@services/complexity'; -import { chainIds as defaultChainIds } from '@utils/chains'; -import { dotenv } from '@utils/dotenv'; import { normalizeError } from '@utils/errors'; +import { networkData } from '@utils/network'; import { builder } from '../builder'; import Block from '../objects/block'; @@ -24,7 +23,6 @@ builder.queryField('completedBlockHeights', (t) => }), chainIds: t.arg.stringList({ required: false, - defaultValue: defaultChainIds, description: 'Default: all chains', validate: { minLength: 1, @@ -42,17 +40,25 @@ builder.queryField('completedBlockHeights', (t) => (args.heightCount as number) * // heightCount has a default value so cannot be null. Bug in pothos. 4, // In the worst case resolve scenario, it executes 4 queries. }), - async resolve(query, __parent, args) { + async resolve( + query, + __parent, + { + completedHeights: completedHeightsArg, + heightCount, + chainIds = networkData.chainIds, + }, + ) { try { - if (args.completedHeights) { + if (completedHeightsArg) { const completedHeights = (await prismaClient.$queryRaw` SELECT height FROM blocks b GROUP BY height - HAVING COUNT(*) >= ${dotenv.CHAIN_COUNT} AND + HAVING COUNT(*) >= ${networkData.chainIds} AND COUNT(CASE WHEN height = height THEN 1 ELSE NULL END) > 0 ORDER BY height DESC - LIMIT ${args.heightCount} + LIMIT ${heightCount} `) as { height: number }[]; if (completedHeights.length > 0) { @@ -62,7 +68,7 @@ builder.queryField('completedBlockHeights', (t) => AND: [ { chainId: { - in: args.chainIds?.map((x) => parseInt(x)), + in: chainIds?.map((x) => parseInt(x)), }, }, { @@ -92,7 +98,7 @@ builder.queryField('completedBlockHeights', (t) => HAVING COUNT(*) > 1 AND COUNT(CASE WHEN height = height THEN 1 ELSE NULL END) > 0 ORDER BY height DESC - LIMIT ${args.heightCount} + LIMIT ${heightCount} `) as { height: number }[]; return await prismaClient.block.findMany({ @@ -104,7 +110,7 @@ builder.queryField('completedBlockHeights', (t) => AND: [ { chainId: { - in: args.chainIds?.map((x) => parseInt(x)), + in: chainIds?.map((x) => parseInt(x)), }, }, { diff --git a/packages/apps/graph/src/graph/query/fungible-account.ts b/packages/apps/graph/src/graph/query/fungible-account.ts index c4382c22cb..17c5413c23 100644 --- a/packages/apps/graph/src/graph/query/fungible-account.ts +++ b/packages/apps/graph/src/graph/query/fungible-account.ts @@ -1,6 +1,6 @@ import { getFungibleChainAccount } from '@services/account-service'; -import { chainIds } from '@utils/chains'; import { dotenv } from '@utils/dotenv'; +import { networkData } from '@utils/network'; import { builder } from '../builder'; import FungibleAccount from '../objects/fungible-account'; import type { IFungibleChainAccount } from '../types/graphql-types'; @@ -29,7 +29,7 @@ builder.queryField('fungibleAccount', (t) => async resolve(__parent, args) { const chainAccounts = ( await Promise.all( - chainIds.map(async (chainId) => { + networkData.chainIds.map(async (chainId) => { return getFungibleChainAccount({ chainId: chainId, fungibleName: args.fungibleName || dotenv.DEFAULT_FUNGIBLE_NAME, diff --git a/packages/apps/graph/src/graph/query/fungible-accounts-by-public-key.ts b/packages/apps/graph/src/graph/query/fungible-accounts-by-public-key.ts index d1c1a07089..b97644a48a 100644 --- a/packages/apps/graph/src/graph/query/fungible-accounts-by-public-key.ts +++ b/packages/apps/graph/src/graph/query/fungible-accounts-by-public-key.ts @@ -1,7 +1,7 @@ import { prismaClient } from '@db/prisma-client'; import { getFungibleChainAccount } from '@services/account-service'; -import { chainIds } from '@utils/chains'; import { normalizeError } from '@utils/errors'; +import { networkData } from '@utils/network'; import { builder } from '../builder'; import FungibleAccount from '../objects/fungible-account'; import type { IFungibleChainAccount } from '../types/graphql-types'; @@ -41,7 +41,7 @@ builder.queryField('fungibleAccountsByPublicKey', (t) => accountAndFungibleNames.map(async (accountAndFungibleName) => { const chainAccounts = ( await Promise.all( - chainIds.map((chainId) => + networkData.chainIds.map((chainId) => getFungibleChainAccount({ chainId: chainId, fungibleName: accountAndFungibleName.fungiblename, diff --git a/packages/apps/graph/src/graph/subscription/new-blocks.ts b/packages/apps/graph/src/graph/subscription/new-blocks.ts index 177aaf1c3a..f0e45381ec 100644 --- a/packages/apps/graph/src/graph/subscription/new-blocks.ts +++ b/packages/apps/graph/src/graph/subscription/new-blocks.ts @@ -1,6 +1,6 @@ import { prismaClient } from '@db/prisma-client'; import type { Block } from '@prisma/client'; -import { chainIds as defaultChainIds } from '@utils/chains'; +import { networkData } from '@utils/network'; import { nullishOrEmpty } from '@utils/nullish-or-empty'; import type { IContext } from '../builder'; import { builder } from '../builder'; @@ -22,9 +22,9 @@ builder.subscriptionField('newBlocks', (t) => }, type: [GQLBlock], nullable: true, - subscribe: (__root, args, context) => + subscribe: async (__root, args, context) => iteratorFn( - args.chainIds?.length ? args.chainIds : defaultChainIds, + args.chainIds?.length ? args.chainIds : networkData.chainIds, context, ), resolve: (parent) => parent, diff --git a/packages/apps/graph/src/graph/types/graphql-types.ts b/packages/apps/graph/src/graph/types/graphql-types.ts index 7f42f095c5..f203f6f664 100644 --- a/packages/apps/graph/src/graph/types/graphql-types.ts +++ b/packages/apps/graph/src/graph/types/graphql-types.ts @@ -170,3 +170,8 @@ export interface INetworkInfo { networkHashRate: number; totalDifficulty: number; } + +export interface IBlockNeighbor { + chainId: string; + blockHash: string; +} diff --git a/packages/apps/graph/src/services/chainweb-node/mempool.ts b/packages/apps/graph/src/services/chainweb-node/mempool.ts index f5d71ddcbb..648d344f7d 100644 --- a/packages/apps/graph/src/services/chainweb-node/mempool.ts +++ b/packages/apps/graph/src/services/chainweb-node/mempool.ts @@ -1,5 +1,4 @@ import type { Signer } from '@prisma/client'; -import { chainIds } from '@utils/chains'; import { dotenv } from '@utils/dotenv'; import { networkData } from '@utils/network'; import { request } from 'https'; @@ -67,7 +66,7 @@ export async function mempoolLookup( chainId?: string, // eslint-disable-next-line @typescript-eslint/no-explicit-any ): Promise { - let chainsToCheck = chainIds; + let chainsToCheck = networkData.chainIds; if (chainId) { chainsToCheck = [chainId]; diff --git a/packages/apps/graph/src/services/network.ts b/packages/apps/graph/src/services/network.ts index 12a53c194e..e8c81e5f3b 100644 --- a/packages/apps/graph/src/services/network.ts +++ b/packages/apps/graph/src/services/network.ts @@ -1,6 +1,6 @@ import { prismaClient } from '@db/prisma-client'; -import { chainIds } from '@utils/chains'; import { dotenv } from '@utils/dotenv'; +import { networkData } from '@utils/network'; interface IBlockWithDifficulty { creationTime: Date; @@ -125,7 +125,7 @@ function calculateTotalDiffulty( for (let i = currentHeight; i > currentHeight - 3n; i--) { const blocksOfThisHeight = blocks.filter((block) => block.height === i); - if (blocksOfThisHeight.length === chainIds.length) { + if (blocksOfThisHeight.length === networkData.chainIds.length) { return blocksOfThisHeight.reduce( (acc, block) => acc + block.difficulty, 0n, diff --git a/packages/apps/graph/src/services/systems-check.ts b/packages/apps/graph/src/services/systems-check.ts index 32120d94f8..d9309c1e0f 100644 --- a/packages/apps/graph/src/services/systems-check.ts +++ b/packages/apps/graph/src/services/systems-check.ts @@ -3,7 +3,7 @@ import { createClient, createTransaction } from '@kadena/client'; import { composePactCommand } from '@kadena/client/fp'; import { Prisma } from '@prisma/client'; import { dotenv } from '@utils/dotenv'; -import { getNetworkConfig } from '@utils/network'; +import { initializeNetworkConfig } from '@utils/network'; import { readdir } from 'fs/promises'; import { Listr } from 'listr2'; import path from 'path'; @@ -64,7 +64,8 @@ export async function runSystemsCheck(): Promise { { title: 'Checking if chainweb node is running and reachable.', task: async () => { - const { apiVersion, networkId } = await getNetworkConfig(); + const { apiVersion, networkId } = + await initializeNetworkConfig(); await createClient( ({ chainId }) => diff --git a/packages/apps/graph/src/utils/chains.ts b/packages/apps/graph/src/utils/chains.ts deleted file mode 100644 index 78e3ea3bd7..0000000000 --- a/packages/apps/graph/src/utils/chains.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { dotenv } from './dotenv'; - -export const chainIds = [...Array(dotenv.CHAIN_COUNT).keys()].map((i) => - i.toString(), -); diff --git a/packages/apps/graph/src/utils/dotenv.ts b/packages/apps/graph/src/utils/dotenv.ts index b45fec6e1c..8557bbeea2 100644 --- a/packages/apps/graph/src/utils/dotenv.ts +++ b/packages/apps/graph/src/utils/dotenv.ts @@ -4,7 +4,6 @@ import * as _dotenv from 'dotenv'; _dotenv.config(); export const dotenv: { - CHAIN_COUNT: number; CHAINWEB_NODE_RETRY_ATTEMPTS: number; CHAINWEB_NODE_RETRY_DELAY: number; COMPLEXITY_ENABLED: boolean; @@ -24,7 +23,6 @@ export const dotenv: { MARMALADE_LOCAL_TEMPLATE_PATH: string; MEMPOOL_HOST: string; NETWORK_HOST: string; - NETWORK_ID: string; NETWORK_STATISTICS_URL: string; NODE_ENV: string; PORT: number; @@ -38,7 +36,6 @@ export const dotenv: { SIMULATE_LOG_FOLDER_NAME: string; TIMEOUT_PACT_QUERY: number; } = { - CHAIN_COUNT: parseInt(or(process.env.CHAIN_COUNT, '20'), 10), CHAINWEB_NODE_RETRY_ATTEMPTS: parseInt( or(process.env.CHAINWEB_NODE_RETRY_ATTEMPTS, '5'), 10, @@ -100,7 +97,6 @@ export const dotenv: { ), MEMPOOL_HOST: or(process.env.MEMPOOL_HOST, 'localhost:1789'), NETWORK_HOST: or(process.env.NETWORK_HOST, 'http://localhost:8080'), - NETWORK_ID: or(process.env.NETWORK_ID, 'development'), NETWORK_STATISTICS_URL: or( process.env.NETWORK_STATISTICS_URL, 'http://localhost:8080/stats', diff --git a/packages/apps/graph/src/utils/network.ts b/packages/apps/graph/src/utils/network.ts index b85b624ce0..444d581126 100644 --- a/packages/apps/graph/src/utils/network.ts +++ b/packages/apps/graph/src/utils/network.ts @@ -3,11 +3,13 @@ import { dotenv } from './dotenv'; interface INetworkData { networkId: string; apiVersion: string; + chainIds: string[]; + chainNeighbours: Map; } export let networkData: INetworkData; -export async function getNetworkConfig( +export async function initializeNetworkConfig( networkHost = dotenv.NETWORK_HOST, ): Promise { if (networkData) { @@ -15,16 +17,33 @@ export async function getNetworkConfig( } const res = await fetch(`${networkHost}/info`); - const data: { nodeVersion?: string; nodeApiVersion?: string } = - await res.json(); + const data: { + nodeVersion?: string; + nodeApiVersion?: string; + nodeChains?: string[]; + nodeGraphHistory?: [[number, [[number, [number]]]]]; + } = await res.json(); if (!data.nodeVersion) throw new Error('Network Id not found'); if (!data.nodeApiVersion) throw new Error('API Version not found'); + if (!data.nodeChains) throw new Error('ChainIDs not found'); + if (!data.nodeGraphHistory) throw new Error('Graph History not found'); + + const chainNeighbours = new Map(); + + for (const chain of data.nodeGraphHistory[0][1].sort((a, b) => a[0] - b[0])) { + chainNeighbours.set( + chain[0].toString(), + chain[1].map((n) => n.toString()), + ); + } // eslint-disable-next-line require-atomic-updates networkData = { networkId: data.nodeVersion, apiVersion: data.nodeApiVersion, + chainIds: data.nodeChains.sort(), + chainNeighbours, }; return networkData;