diff --git a/package-lock.json b/package-lock.json index f53b9faa..4eb880a6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -35,6 +35,19 @@ "typescript": "^4.9.4" } }, + "node_modules/@0no-co/graphql.web": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@0no-co/graphql.web/-/graphql.web-1.0.4.tgz", + "integrity": "sha512-W3ezhHGfO0MS1PtGloaTpg0PbaT8aZSmmaerL7idtU5F7oCI+uu25k+MsMS31BVFlp4aMkHSrNRxiD72IlK8TA==", + "peerDependencies": { + "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0" + }, + "peerDependenciesMeta": { + "graphql": { + "optional": true + } + } + }, "node_modules/@aashutoshrathi/word-wrap": { "version": "1.2.6", "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", @@ -6587,6 +6600,7 @@ "version": "1.3.6", "resolved": "https://registry.npmjs.org/@types/accepts/-/accepts-1.3.6.tgz", "integrity": "sha512-6+qlUg57yfE9OO63wnsJXLeq9cG3gSHBBIxNMOjNrbDRlDnm/NaR7RctfYcVCPq+j7d+MwOxqVEludH5+FKrlg==", + "dev": true, "dependencies": { "@types/node": "*" } @@ -6642,6 +6656,7 @@ "version": "1.19.3", "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.3.tgz", "integrity": "sha512-oyl4jvAfTGX9Bt6Or4H9ni1Z447/tQuxnZsytsCaExKlmJiU8sFgnIBRzJUpKwB5eWn9HuBYlUlVA74q/yN0eQ==", + "dev": true, "dependencies": { "@types/connect": "*", "@types/node": "*" @@ -6651,6 +6666,7 @@ "version": "3.4.36", "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.36.tgz", "integrity": "sha512-P63Zd/JUGq+PdrM1lv0Wv5SBYeA2+CORvbrXbngriYY0jzLUWfQMQQxOhjONEz/wlHOAxOdY7CY65rgQdTjq2w==", + "dev": true, "dependencies": { "@types/node": "*" } @@ -6658,12 +6674,14 @@ "node_modules/@types/content-disposition": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/@types/content-disposition/-/content-disposition-0.5.7.tgz", - "integrity": "sha512-V9/5u21RHFR1zfdm3rQ6pJUKV+zSSVQt+yq16i1YhdivVzWgPEoKedc3GdT8aFjsqQbakdxuy3FnEdePUQOamQ==" + "integrity": "sha512-V9/5u21RHFR1zfdm3rQ6pJUKV+zSSVQt+yq16i1YhdivVzWgPEoKedc3GdT8aFjsqQbakdxuy3FnEdePUQOamQ==", + "dev": true }, "node_modules/@types/cookies": { "version": "0.7.9", "resolved": "https://registry.npmjs.org/@types/cookies/-/cookies-0.7.9.tgz", "integrity": "sha512-SrGYvhKohd/WSOII0WpflC73RgdJhQoqpwq9q+n/qugNGiDSGYXfHy3QvB4+X+J/gYe27j2fSRnK4B+1A3nvsw==", + "dev": true, "dependencies": { "@types/connect": "*", "@types/express": "*", @@ -6699,6 +6717,7 @@ "version": "4.17.19", "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.19.tgz", "integrity": "sha512-UtOfBtzN9OvpZPPbnnYunfjM7XCI4jyk1NvnFhTVz5krYAnW4o5DCoIekvms+8ApqhB4+9wSge1kBijdfTSmfg==", + "dev": true, "dependencies": { "@types/body-parser": "*", "@types/express-serve-static-core": "^4.17.33", @@ -6710,6 +6729,7 @@ "version": "4.17.37", "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.37.tgz", "integrity": "sha512-ZohaCYTgGFcOP7u6aJOhY9uIZQgZ2vxC2yWoArY+FeDXlqeH66ZVBjgvg+RLVAS/DWNq4Ap9ZXu1+SUQiiWYMg==", + "dev": true, "dependencies": { "@types/node": "*", "@types/qs": "*", @@ -6752,12 +6772,14 @@ "node_modules/@types/http-assert": { "version": "1.5.4", "resolved": "https://registry.npmjs.org/@types/http-assert/-/http-assert-1.5.4.tgz", - "integrity": "sha512-/6M9aaVk+avzCsrv1lt39AlFw4faCNI6aGll91Rxj38ZE5JI8AxApyQIRy+i1McjiJiuQ0sfuoMLxqq374ZIbA==" + "integrity": "sha512-/6M9aaVk+avzCsrv1lt39AlFw4faCNI6aGll91Rxj38ZE5JI8AxApyQIRy+i1McjiJiuQ0sfuoMLxqq374ZIbA==", + "dev": true }, "node_modules/@types/http-errors": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.2.tgz", - "integrity": "sha512-lPG6KlZs88gef6aD85z3HNkztpj7w2R7HmR3gygjfXCQmsLloWNARFkMuzKiiY8FGdh1XDpgBdrSf4aKDiA7Kg==" + "integrity": "sha512-lPG6KlZs88gef6aD85z3HNkztpj7w2R7HmR3gygjfXCQmsLloWNARFkMuzKiiY8FGdh1XDpgBdrSf4aKDiA7Kg==", + "dev": true }, "node_modules/@types/humanize-duration": { "version": "3.27.2", @@ -6813,12 +6835,14 @@ "node_modules/@types/keygrip": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/@types/keygrip/-/keygrip-1.0.4.tgz", - "integrity": "sha512-/tjWYD8StMrINelsrHNmpXceo9s3/Y22AzePH1qCvXIgmz/aQp2YFFr6HqhNQVIOdcvaVyp5GS+yjHGuF7Rwsg==" + "integrity": "sha512-/tjWYD8StMrINelsrHNmpXceo9s3/Y22AzePH1qCvXIgmz/aQp2YFFr6HqhNQVIOdcvaVyp5GS+yjHGuF7Rwsg==", + "dev": true }, "node_modules/@types/koa": { "version": "2.13.10", "resolved": "https://registry.npmjs.org/@types/koa/-/koa-2.13.10.tgz", "integrity": "sha512-weKc5IBeORLDGwD1FMgPjaZIg0/mtP7KxXAXEzPRCN78k274D9U2acmccDNPL1MwyV40Jj+hQQ5N2eaV6O0z8g==", + "dev": true, "dependencies": { "@types/accepts": "*", "@types/content-disposition": "*", @@ -6834,6 +6858,7 @@ "version": "3.2.7", "resolved": "https://registry.npmjs.org/@types/koa-compose/-/koa-compose-3.2.7.tgz", "integrity": "sha512-smtvSL/oLICPuenxy73OmxKGh42VVfn2o2eutReH1yjij0LmxADBpGcAJbp4N+yJjPapPN7jAX9p7Ue0JMQ/Ag==", + "dev": true, "dependencies": { "@types/koa": "*" } @@ -6856,7 +6881,8 @@ "node_modules/@types/mime": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.3.tgz", - "integrity": "sha512-Ys+/St+2VF4+xuY6+kDIXGxbNRO0mesVg0bbxEfB97Od1Vjpjx9KD1qxs64Gcb3CWPirk9Xe+PT4YiiHQ9T+eg==" + "integrity": "sha512-Ys+/St+2VF4+xuY6+kDIXGxbNRO0mesVg0bbxEfB97Od1Vjpjx9KD1qxs64Gcb3CWPirk9Xe+PT4YiiHQ9T+eg==", + "dev": true }, "node_modules/@types/minimatch": { "version": "3.0.5", @@ -6899,12 +6925,14 @@ "node_modules/@types/qs": { "version": "6.9.8", "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.8.tgz", - "integrity": "sha512-u95svzDlTysU5xecFNTgfFG5RUWu1A9P0VzgpcIiGZA9iraHOdSzcxMxQ55DyeRaGCSxQi7LxXDI4rzq/MYfdg==" + "integrity": "sha512-u95svzDlTysU5xecFNTgfFG5RUWu1A9P0VzgpcIiGZA9iraHOdSzcxMxQ55DyeRaGCSxQi7LxXDI4rzq/MYfdg==", + "dev": true }, "node_modules/@types/range-parser": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.5.tgz", - "integrity": "sha512-xrO9OoVPqFuYyR/loIHjnbvvyRZREYKLjxV4+dY6v3FQR3stQ9ZxIGkaclF7YhI9hfjpuTbu14hZEy94qKLtOA==" + "integrity": "sha512-xrO9OoVPqFuYyR/loIHjnbvvyRZREYKLjxV4+dY6v3FQR3stQ9ZxIGkaclF7YhI9hfjpuTbu14hZEy94qKLtOA==", + "dev": true }, "node_modules/@types/semver": { "version": "7.5.3", @@ -6915,6 +6943,7 @@ "version": "0.17.2", "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.2.tgz", "integrity": "sha512-aAG6yRf6r0wQ29bkS+x97BIs64ZLxeE/ARwyS6wrldMm3C1MdKwCcnnEwMC1slI8wuxJOpiUH9MioC0A0i+GJw==", + "dev": true, "dependencies": { "@types/mime": "^1", "@types/node": "*" @@ -6924,6 +6953,7 @@ "version": "1.15.3", "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.3.tgz", "integrity": "sha512-yVRvFsEMrv7s0lGhzrggJjNOSmZCdgCjw9xWrPr/kNNLp6FaDfMC1KaYl3TSJ0c58bECwNBMoQrZJ8hA8E1eFg==", + "dev": true, "dependencies": { "@types/http-errors": "*", "@types/mime": "*", @@ -7178,6 +7208,15 @@ "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/@urql/core": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@urql/core/-/core-4.1.4.tgz", + "integrity": "sha512-wFm67yljv4uFAWNtPwcS1NMhF/n+p/68i+kZU6R1dPxhfq2nBW0142p4szeZsBDrtO7pBdOhp7YeSZROFFlXZg==", + "dependencies": { + "@0no-co/graphql.web": "^1.0.1", + "wonka": "^6.3.2" + } + }, "node_modules/@whatwg-node/events": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/@whatwg-node/events/-/events-0.1.1.tgz", @@ -27035,6 +27074,11 @@ "string-width": "^1.0.2 || 2 || 3 || 4" } }, + "node_modules/wonka": { + "version": "6.3.4", + "resolved": "https://registry.npmjs.org/wonka/-/wonka-6.3.4.tgz", + "integrity": "sha512-CjpbqNtBGNAeyNS/9W6q3kSkKE52+FjIj7AkFlLr11s/VWGUu6a2CdYSdGxocIhIVjaW/zchesBQUKPVU69Cqg==" + }, "node_modules/word-wrap": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", @@ -27435,12 +27479,14 @@ "graphql": "16.6.0", "graphql-scalars": "^1.22.4", "humanize-duration": "^3.30.0", + "koa": "^2.14.2", "lodash": "^4.17.21", "reflect-metadata": "^0.1.13", "type-graphql": "2.0.0-beta.1" }, "devDependencies": { "@jest/globals": "^29.5.0", + "@types/koa": "^2.13.10", "@types/lodash": "^4.14.194", "@types/ws": "^8.5.4" }, @@ -27451,6 +27497,27 @@ "tsyringe": "^4.7.0" } }, + "packages/client": { + "name": "@proto-kit/client", + "version": "0.1.1-develop.267+b252853", + "extraneous": true, + "license": "MIT", + "dependencies": { + "@urql/core": "^4.1.4", + "lodash": "^4.17.21", + "reflect-metadata": "^0.1.13" + }, + "devDependencies": { + "@jest/globals": "^29.5.0", + "@types/lodash": "^4.14.194" + }, + "peerDependencies": { + "@proto-kit/common": "*", + "@proto-kit/sequencer": "*", + "o1js": "0.13.1", + "tsyringe": "^4.7.0" + } + }, "packages/common": { "name": "@proto-kit/common", "version": "0.1.1-develop.267+b252853", @@ -27515,6 +27582,7 @@ "version": "0.1.1-develop.267+b252853", "license": "MIT", "dependencies": { + "@urql/core": "^4.1.4", "comlink": "^4.4.1", "lodash": "^4.17.21", "loglevel": "^1.8.1" @@ -27539,9 +27607,7 @@ "version": "0.1.1-develop.267+b252853", "license": "MIT", "dependencies": { - "@types/koa": "^2.13.10", "bullmq": "^3.15.0", - "koa": "^2.14.2", "lodash": "^4.17.21", "reflect-metadata": "^0.1.13" }, diff --git a/packages/api/package.json b/packages/api/package.json index e262be4d..bf39d391 100644 --- a/packages/api/package.json +++ b/packages/api/package.json @@ -24,6 +24,7 @@ "graphql-scalars": "^1.22.4", "humanize-duration": "^3.30.0", "lodash": "^4.17.21", + "koa": "^2.14.2", "reflect-metadata": "^0.1.13", "type-graphql": "2.0.0-beta.1" }, @@ -36,6 +37,7 @@ "devDependencies": { "@jest/globals": "^29.5.0", "@types/lodash": "^4.14.194", + "@types/koa": "^2.13.10", "@types/ws": "^8.5.4" }, "gitHead": "b2528538c73747d000cc3ea99ee26ee415d8248d" diff --git a/packages/api/src/graphql/GraphqlServer.ts b/packages/api/src/graphql/GraphqlServer.ts index 500b9093..28190e2a 100644 --- a/packages/api/src/graphql/GraphqlServer.ts +++ b/packages/api/src/graphql/GraphqlServer.ts @@ -79,6 +79,14 @@ export class GraphqlServer extends SequencerModule { }, }); + // eslint-disable-next-line no-warning-comments + // TODO Injection token of Graphql Container not respected atm, only class is used + + // Instantiate all modules at startup + modules.forEach((module) => { + dependencyContainer?.resolve(module); + }); + const schema = [resolverSchema, ...this.schemas].reduce( (schema1, schema2) => stitchSchemas({ diff --git a/packages/api/src/graphql/modules/QueryGraphqlModule.ts b/packages/api/src/graphql/modules/QueryGraphqlModule.ts index 4328ebce..0211ca36 100644 --- a/packages/api/src/graphql/modules/QueryGraphqlModule.ts +++ b/packages/api/src/graphql/modules/QueryGraphqlModule.ts @@ -1,7 +1,7 @@ // eslint-disable-next-line max-len /* eslint-disable @typescript-eslint/no-explicit-any,@typescript-eslint/no-unsafe-argument,@typescript-eslint/no-unsafe-assignment,putout/putout,max-lines,guard-for-in,@typescript-eslint/consistent-type-assertions */ -import { inject, injectable } from "tsyringe"; -import { Resolver } from "type-graphql"; +import { inject } from "tsyringe"; +import { Arg, Query as GraphqlQuery } from "type-graphql"; import { GraphQLBoolean, GraphQLFieldConfig, @@ -35,7 +35,7 @@ import { QueryGetterStateMap, QueryTransportModule, NetworkStateQuery, - BlockStorage + BlockStorage, } from "@proto-kit/sequencer"; import { graphqlModule, SchemaGeneratingGraphqlModule } from "../GraphqlModule"; import { @@ -78,6 +78,12 @@ export class QueryGraphqlModule< super(); } + @GraphqlQuery(() => [String], { nullable: true }) + public async state(@Arg("path") path: string): Promise { + const value = await this.queryTransportModule.get(Field(path)); + return value?.map((field) => field.toString()); + } + private jsonPrimitiveToGraphqlType(value: any): GraphQLScalarType { switch (typeof value) { case "symbol": diff --git a/packages/api/test/compile.ts b/packages/api/test/compile.ts deleted file mode 100644 index 8ff63328..00000000 --- a/packages/api/test/compile.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { - GraphqlSequencerModule, - GraphqlServer, - NodeStatusResolver, - QueryGraphqlModule -} from "../src"; - -const graphqlServer = GraphqlSequencerModule.from({ - modules: { - NodeStatusResolver, - QueryGraphqlModule - }, - config: { - NodeStatusResolver: {}, - QueryGraphqlModule: {} - } -}) \ No newline at end of file diff --git a/packages/common/src/dependencyFactory/DependencyFactory.ts b/packages/common/src/dependencyFactory/DependencyFactory.ts index 5d024b3b..47013ee1 100644 --- a/packages/common/src/dependencyFactory/DependencyFactory.ts +++ b/packages/common/src/dependencyFactory/DependencyFactory.ts @@ -41,7 +41,6 @@ export abstract class DependencyFactory { public initDependencies(container: DependencyContainer) { const dependencies = globalFactoryDependencies.get(this.constructor.name) ?? {}; - globalFactoryDependencies.delete(this.constructor.name); for (const [key, useFactory] of Object.entries(dependencies)) { container.register(`${key}_singleton-prototype`, { diff --git a/packages/common/src/utils.ts b/packages/common/src/utils.ts index f602f842..f3e2f18c 100644 --- a/packages/common/src/utils.ts +++ b/packages/common/src/utils.ts @@ -44,3 +44,8 @@ export function noop(): void {} export interface ToFieldable { toFields: () => Field[]; } + +export async function sleep(ms: number) { + // eslint-disable-next-line promise/avoid-new,no-promise-executor-return + await new Promise((resolve) => setTimeout(resolve, ms)); +} diff --git a/packages/module/src/runtime/MethodIdResolver.ts b/packages/module/src/runtime/MethodIdResolver.ts index f62a7cc6..e8f82e4c 100644 --- a/packages/module/src/runtime/MethodIdResolver.ts +++ b/packages/module/src/runtime/MethodIdResolver.ts @@ -33,14 +33,14 @@ export class MethodIdResolver { } public getMethodNameFromId(methodId: bigint): [string, string] | undefined { - const { moduleName, methodName } = this.dictionary[methodId.toString()]; + const methodPath = this.dictionary[methodId.toString()]; - // eslint-disable-next-line no-warning-comments - // TODO Replace by throwing exception? - if (moduleName === undefined || methodName === undefined) { + if (methodPath === undefined) { return undefined; } + const { moduleName, methodName } = methodPath; + this.runtime.assertIsValidModuleName(this.modules, moduleName); return [moduleName, methodName]; diff --git a/packages/sdk/package.json b/packages/sdk/package.json index 49709fea..dfa5855a 100644 --- a/packages/sdk/package.json +++ b/packages/sdk/package.json @@ -11,13 +11,14 @@ "test:file": "node --experimental-vm-modules --experimental-wasm-modules --experimental-wasm-threads ../../node_modules/jest/bin/jest.js", "test": "npm run test:file -- ./src/** ./test/**", "test:watch": "npm run test:file -- ./src/** ./test/** --watch", - "graphql": "cd ../api && npm run build && cd ../sdk && npm run build && node --experimental-vm-modules --experimental-wasm-modules --experimental-wasm-threads --es-module-specifier-resolution=node ./dist/graphql.js" + "graphql": "cd ../api && npm run build && cd ../sdk && npm run test:file -- test/graphql/run-graphql.test.ts" }, "main": "dist/index.js", "publishConfig": { "access": "public" }, "dependencies": { + "@urql/core": "^4.1.4", "comlink": "^4.4.1", "lodash": "^4.17.21", "loglevel": "^1.8.1" diff --git a/packages/sdk/src/appChain/AppChain.ts b/packages/sdk/src/appChain/AppChain.ts index d1592072..ecfb63fd 100644 --- a/packages/sdk/src/appChain/AppChain.ts +++ b/packages/sdk/src/appChain/AppChain.ts @@ -19,7 +19,8 @@ import { Sequencer, SequencerModulesRecord, UnsignedTransaction, - MockStorageDependencyFactory + MockStorageDependencyFactory, + QueryTransportModule, } from "@proto-kit/sequencer"; import { NetworkState, @@ -29,13 +30,12 @@ import { RuntimeMethodExecutionContext, ProtocolModule, } from "@proto-kit/protocol"; -import { container } from "tsyringe"; +import { container, DependencyContainer } from "tsyringe"; import { Field, FlexibleProvable, PublicKey, UInt64 } from "o1js"; import { AppChainTransaction } from "../transaction/AppChainTransaction"; import { Signer } from "../transaction/InMemorySigner"; import { TransactionSender } from "../transaction/InMemoryTransactionSender"; -import { StateServiceQueryModule } from "../query/StateServiceQueryModule"; import { AppChainModule } from "./AppChainModule"; import { AreProofsEnabledFactory } from "./AreProofsEnabledFactory"; @@ -190,9 +190,8 @@ export class AppChain< protocol: Query, ProtocolModules>; network: NetworkStateQuery; } { - const queryTransportModule = this.resolveOrFail( - "QueryTransportModule", - StateServiceQueryModule + const queryTransportModule = this.container.resolve( + "QueryTransportModule" ); const network = new NetworkStateQuery( @@ -283,7 +282,7 @@ export class AppChain< const runtimeModule = this.runtime.resolve(moduleName as any); // find types of args for the runtime method thats being called - const paramTypes: FlexibleProvable[] = Reflect.getMetadata( + const parameterTypes: FlexibleProvable[] = Reflect.getMetadata( "design:paramtypes", runtimeModule, methodName @@ -293,8 +292,8 @@ export class AppChain< * Use the type info obtained previously to convert * the args passed to fields */ - const argsFields = args.flatMap((arg, index) => - paramTypes[index].toFields(arg) + const argsFields = args.flatMap((argument, index) => + parameterTypes[index].toFields(argument) ); const unsignedTransaction = new UnsignedTransaction({ @@ -323,8 +322,8 @@ export class AppChain< /** * Starts the appchain and cross-registers runtime to sequencer */ - public async start() { - this.create(() => container); + public async start(dependencyContainer: DependencyContainer = container) { + this.create(() => dependencyContainer); this.registerDependencyFactories([ AreProofsEnabledFactory, diff --git a/packages/sdk/src/graphql.ts b/packages/sdk/src/graphql.ts deleted file mode 100644 index 8eab9968..00000000 --- a/packages/sdk/src/graphql.ts +++ /dev/null @@ -1,190 +0,0 @@ -import "reflect-metadata"; -import { Field, PrivateKey, PublicKey, UInt64 } from "o1js"; -import { - Runtime, - runtimeMethod, - RuntimeModule, - runtimeModule, - state, -} from "@proto-kit/module"; -import { - AccountStateModule, - Option, - State, - StateMap, - VanillaProtocol, -} from "@proto-kit/protocol"; -import { Presets, log } from "@proto-kit/common"; -import { - AsyncStateService, BlockProducerModule, LocalTaskQueue, LocalTaskWorkerModule, NoopBaseLayer, - PrivateMempool, - Sequencer, TimedBlockTrigger, - UnsignedTransaction -} from "@proto-kit/sequencer"; -import { - BlockStorageResolver, - GraphqlSequencerModule, - GraphqlServer, - MempoolResolver, - NodeStatusResolver, - QueryGraphqlModule -} from "@proto-kit/api"; - -import { AppChain } from "./appChain/AppChain"; -import { StateServiceQueryModule } from "./query/StateServiceQueryModule"; -import { InMemorySigner } from "./transaction/InMemorySigner"; -import { InMemoryTransactionSender } from "./transaction/InMemoryTransactionSender"; - -log.setLevel(log.levels.INFO); - -function createNewTx() { - const pk = PrivateKey.random(); - - const tx = new UnsignedTransaction({ - nonce: UInt64.zero, - args: [Field(1)], - methodId: Field(1), - sender: pk.toPublicKey(), - }).sign(pk); - - console.log(tx.toJSON()); -} -createNewTx(); - -@runtimeModule() -export class Balances extends RuntimeModule { - /** - * We use `satisfies` here in order to be able to access - * presets by key in a type safe way. - */ - public static presets = {} satisfies Presets; - - @state() public balances = StateMap.from( - PublicKey, - UInt64 - ); - - @state() public totalSupply = State.from(UInt64); - - @runtimeMethod() - public getBalance(address: PublicKey): Option { - return this.balances.get(address); - } - - @runtimeMethod() - public setBalance(address: PublicKey, balance: UInt64) { - this.balances.set(address, balance); - } -} - -const appChain = AppChain.from({ - runtime: Runtime.from({ - modules: { - Balances, - }, - - config: { - Balances: {}, - }, - }), - - protocol: VanillaProtocol.from( - { AccountStateModule }, - { AccountStateModule: {}, StateTransitionProver: {}, BlockProver: {} } - ), - - sequencer: Sequencer.from({ - modules: { - Mempool: PrivateMempool, - GraphqlServer, - LocalTaskWorkerModule, - BaseLayer: NoopBaseLayer, - BlockProducerModule, - BlockTrigger: TimedBlockTrigger, - TaskQueue: LocalTaskQueue, - - Graphql: GraphqlSequencerModule.from({ - modules: { - MempoolResolver, - QueryGraphqlModule, - BlockStorageResolver, - NodeStatusResolver, - }, - - config: { - MempoolResolver: {}, - QueryGraphqlModule: {}, - BlockStorageResolver: {}, - NodeStatusResolver: {}, - }, - }), - }, - }), - - modules: { - Signer: InMemorySigner, - TransactionSender: InMemoryTransactionSender, - QueryTransportModule: StateServiceQueryModule, - }, -}); - -appChain.configure({ - Runtime: { - Balances: {}, - }, - - Protocol: { - BlockProver: {}, - StateTransitionProver: {}, - AccountStateModule: {}, - }, - - Sequencer: { - GraphqlServer: { - port: 8080, - host: "0.0.0.0", - }, - - Graphql: { - QueryGraphqlModule: {}, - MempoolResolver: {}, - BlockStorageResolver: {}, - NodeStatusResolver: {} - }, - - Mempool: {}, - BlockProducerModule: {}, - LocalTaskWorkerModule: {}, - BaseLayer: {}, - TaskQueue: {}, - - BlockTrigger: { - blocktime: 5000 - }, - }, - - TransactionSender: {}, - QueryTransportModule: {}, - - Signer: { - signer: PrivateKey.random(), - }, -}); - -await appChain.start(); - -const pk = PublicKey.fromBase58( - "B62qmETai5Y8vvrmWSU8F4NX7pTyPqYLMhc1pgX3wD8dGc2wbCWUcqP" -); -console.log(pk.toJSON()); - -const balances = appChain.runtime.resolve("Balances"); - -console.log("Path:", balances.balances.getPath(pk).toString()); - -const asyncState = - appChain.sequencer.dependencyContainer.resolve( - "AsyncStateService" - ); -await asyncState.setAsync(balances.balances.getPath(pk), [Field(100)]); -await asyncState.setAsync(balances.totalSupply.path!, [Field(10_000)]); diff --git a/packages/sdk/src/graphql/GraphqlClient.ts b/packages/sdk/src/graphql/GraphqlClient.ts new file mode 100644 index 00000000..359ac582 --- /dev/null +++ b/packages/sdk/src/graphql/GraphqlClient.ts @@ -0,0 +1,25 @@ +import { AppChainModule } from "../appChain/AppChainModule"; +import { Client, fetchExchange } from "@urql/core"; + +export interface GraphqlClientConfig { + url: string; +} + +export class GraphqlClient extends AppChainModule{ + private initializedClient?: Client + + private createClient(): Client { + const { url } = this.config + return new Client({ + url, + exchanges: [fetchExchange], + }) + } + + public get client(): Client { + if(this.initializedClient === undefined){ + this.initializedClient = this.createClient(); + } + return this.initializedClient + } +} \ No newline at end of file diff --git a/packages/sdk/src/graphql/GraphqlQueryTransportModule.ts b/packages/sdk/src/graphql/GraphqlQueryTransportModule.ts new file mode 100644 index 00000000..d3243758 --- /dev/null +++ b/packages/sdk/src/graphql/GraphqlQueryTransportModule.ts @@ -0,0 +1,55 @@ +// eslint-disable-next-line max-len +/* eslint-disable @typescript-eslint/no-unsafe-member-access,@typescript-eslint/no-explicit-any */ +import { QueryTransportModule } from "@proto-kit/sequencer"; +import { Field } from "o1js"; +import { inject, injectable } from "tsyringe"; +import { gql } from "@urql/core"; + +import { AppChainModule } from "../appChain/AppChainModule"; + +import { GraphqlClient } from "./GraphqlClient"; + +function assertStringArray(array: any): asserts array is string[] { + if ( + array.length === undefined || + (array.length > 0 && typeof array[0] !== "string") + ) { + throw new Error("Array is not a string[]"); + } +} + +@injectable() +export class GraphqlQueryTransportModule + extends AppChainModule> + implements QueryTransportModule +{ + public constructor( + @inject("GraphqlClient") private readonly graphqlClient: GraphqlClient + ) { + super(); + } + + public async get(key: Field): Promise { + const query = gql` + query StateRaw($path: String!) { + state(path: $path) + } + `; + + const queryResult = await this.graphqlClient.client + .query(query, { path: key.toString() }) + .toPromise(); + + if (queryResult.error === undefined) { + const stringArray = queryResult.data?.state; + + if (stringArray === undefined || stringArray === null) { + return undefined; + } + + assertStringArray(stringArray); + return stringArray.map((string) => Field(string)); + } + throw new Error(queryResult.error.message); + } +} diff --git a/packages/sdk/src/graphql/GraphqlTransactionSender.ts b/packages/sdk/src/graphql/GraphqlTransactionSender.ts new file mode 100644 index 00000000..84c5a8ae --- /dev/null +++ b/packages/sdk/src/graphql/GraphqlTransactionSender.ts @@ -0,0 +1,41 @@ +import { AppChainModule } from "../appChain/AppChainModule"; +import { inject, injectable } from "tsyringe"; +import { GraphqlClient } from "./GraphqlClient"; +import { TransactionSender } from "../transaction/InMemoryTransactionSender"; +import { PendingTransaction } from "@proto-kit/sequencer"; +import { gql } from "@urql/core"; + +@injectable() +export class GraphqlTransactionSender + extends AppChainModule> + implements TransactionSender +{ + public constructor( + @inject("GraphqlClient") private readonly graphqlClient: GraphqlClient + ) { + super(); + } + + public async send(transaction: PendingTransaction): Promise { + const query = gql` + mutation SubmitTx($tx: TransactionObjectInput!) { + submitTx(tx: $tx) + } + `; + const txJson = transaction.toJSON(); + + const queryResult = await this.graphqlClient.client + .mutation(query, { tx: txJson }) + .toPromise(); + + if (queryResult.error === undefined) { + const hash = queryResult.data?.submitTx; + + if (hash === undefined) { + throw new Error("Mutation returned invalid result: submitTx"); + } + } else { + throw new Error(queryResult.error.message); + } + } +} diff --git a/packages/sdk/src/index.ts b/packages/sdk/src/index.ts index f2e4a885..f4b9b937 100644 --- a/packages/sdk/src/index.ts +++ b/packages/sdk/src/index.ts @@ -7,3 +7,6 @@ export * from "./transaction/AppChainTransaction"; export * from "./transaction/InMemorySigner"; export * from "./transaction/InMemoryTransactionSender"; export * from "./transaction/AuroSigner"; +export * from "./graphql/GraphqlClient"; +export * from "./graphql/GraphqlQueryTransportModule"; +export * from "./graphql/GraphqlTransactionSender"; diff --git a/packages/sdk/src/transaction/AppChainTransaction.ts b/packages/sdk/src/transaction/AppChainTransaction.ts index e4144729..496772e1 100644 --- a/packages/sdk/src/transaction/AppChainTransaction.ts +++ b/packages/sdk/src/transaction/AppChainTransaction.ts @@ -1,10 +1,10 @@ -/* eslint-disable import/no-unused-modules */ import { PendingTransaction, UnsignedTransaction } from "@proto-kit/sequencer"; + import { Signer } from "./InMemorySigner"; import { TransactionSender } from "./InMemoryTransactionSender"; export class AppChainTransaction { - public transaction?: UnsignedTransaction | PendingTransaction; + public transaction?: PendingTransaction | UnsignedTransaction; public constructor( public signer: Signer, @@ -16,7 +16,7 @@ export class AppChainTransaction { } public hasUnsignedTransaction( - transaction?: UnsignedTransaction | PendingTransaction + transaction?: PendingTransaction | UnsignedTransaction ): asserts transaction is UnsignedTransaction { const isUnsignedTransaction = transaction instanceof UnsignedTransaction; if (!isUnsignedTransaction) { @@ -25,7 +25,7 @@ export class AppChainTransaction { } public hasPendingTransaction( - transaction?: UnsignedTransaction | PendingTransaction + transaction?: PendingTransaction | UnsignedTransaction ): asserts transaction is PendingTransaction { const isUnsignedTransaction = transaction instanceof PendingTransaction; if (!isUnsignedTransaction) { @@ -37,6 +37,11 @@ export class AppChainTransaction { this.hasUnsignedTransaction(this.transaction); const signatureData = this.transaction.getSignatureData(); const signature = await this.signer.sign(signatureData); + + if (!signature.verify(this.transaction.sender, signatureData).toBoolean()) { + throw new Error("Signer didn't provide correct signature for tx"); + } + this.transaction = this.transaction.signed(signature); } diff --git a/packages/sdk/src/transaction/InMemoryTransactionSender.ts b/packages/sdk/src/transaction/InMemoryTransactionSender.ts index e4ccfff1..a5d5eeec 100644 --- a/packages/sdk/src/transaction/InMemoryTransactionSender.ts +++ b/packages/sdk/src/transaction/InMemoryTransactionSender.ts @@ -1,6 +1,3 @@ -/* eslint-disable import/no-unused-modules */ -import { RuntimeModulesRecord } from "@proto-kit/module"; -import { ProtocolModulesRecord } from "@proto-kit/protocol/src/protocol/Protocol"; import { PrivateMempool, Sequencer, @@ -8,7 +5,7 @@ import { PendingTransaction, } from "@proto-kit/sequencer"; import { inject, injectable } from "tsyringe"; -import { AppChain, AppChainModulesRecord } from "../appChain/AppChain"; + import { AppChainModule } from "../appChain/AppChainModule"; export interface TransactionSender extends AppChainModule { diff --git a/packages/sdk/test/graphql/graphql.test.ts b/packages/sdk/test/graphql/graphql.test.ts new file mode 100644 index 00000000..18261682 --- /dev/null +++ b/packages/sdk/test/graphql/graphql.test.ts @@ -0,0 +1,173 @@ +import "reflect-metadata"; +import { Field, PrivateKey, PublicKey, UInt64 } from "o1js"; +import { + Runtime, + runtimeMethod, + RuntimeModule, + runtimeModule, + state, +} from "@proto-kit/module"; +import { + AccountStateModule, + Option, + ReturnType, + State, + StateMap, + VanillaProtocol, +} from "@proto-kit/protocol"; +import { Presets, log, sleep } from "@proto-kit/common"; +import { + AsyncStateService, + BlockProducerModule, + LocalTaskQueue, + LocalTaskWorkerModule, + NoopBaseLayer, + PrivateMempool, + QueryTransportModule, + Sequencer, + TimedBlockTrigger, + UnsignedTransaction, +} from "@proto-kit/sequencer"; +import { + BlockStorageResolver, + GraphqlSequencerModule, + GraphqlServer, + MempoolResolver, + NodeStatusResolver, + QueryGraphqlModule, +} from "@proto-kit/api"; + +import { startServer, Balances } from "./graphql"; +import { beforeAll } from "@jest/globals"; +import { + AppChain, + InMemorySigner, + InMemoryTransactionSender, + StateServiceQueryModule, +} from "../../src"; +import { GraphqlTransactionSender } from "../../src/graphql/GraphqlTransactionSender"; +import { GraphqlQueryTransportModule } from "../../src/graphql/GraphqlQueryTransportModule"; +import { GraphqlClient } from "../../src/graphql/GraphqlClient"; +import { container } from "tsyringe"; + +log.setLevel(log.levels.INFO); + +const pk = PrivateKey.random(); + +function createNewTx(methodId: Field) { + const tx = new UnsignedTransaction({ + nonce: UInt64.zero, + args: pk.toPublicKey().toFields(), + methodId, + sender: pk.toPublicKey(), + }).sign(pk); + + console.log(tx.toJSON()); + + return tx; +} + +function prepare() { + const appChain = AppChain.from({ + runtime: Runtime.from({ + modules: { + Balances, + }, + + config: { + Balances: {}, + }, + }), + + protocol: VanillaProtocol.from( + { AccountStateModule }, + { AccountStateModule: {}, StateTransitionProver: {}, BlockProver: {} } + ), + + sequencer: Sequencer.from({ + modules: { + Mempool: PrivateMempool, + }, + }), + + modules: { + Signer: InMemorySigner, + TransactionSender: GraphqlTransactionSender, + QueryTransportModule: GraphqlQueryTransportModule, + GraphqlClient, + }, + }); + + appChain.configure({ + Runtime: { + Balances: {}, + }, + + Protocol: { + BlockProver: {}, + StateTransitionProver: {}, + AccountStateModule: {}, + }, + + Sequencer: { + Mempool: {}, + }, + + TransactionSender: {}, + QueryTransportModule: {}, + GraphqlClient: { + url: "http://127.0.0.1:8080/graphql", + }, + + Signer: { + signer: pk, + }, + }); + + return appChain; +} + +describe("graphql client test", function () { + let appChain: ReturnType | undefined = undefined; + + beforeAll(async () => { + const server = await startServer(); + + await sleep(2000); + + appChain = prepare(); + + await appChain.start(container.createChildContainer()); + }); + + it("should retrieve state", async () => { + const result = await appChain! + .resolve("QueryTransportModule") + .get(Field(1234)); + console.log(`Result: ${result?.toString()}`); + + const totalSupply = + await appChain!.query.runtime.Balances.totalSupply.get(); + + console.log(totalSupply.toString()); + }, 60000); + + it("should send transaction", async () => { + const tx = await appChain!.transaction(pk.toPublicKey(), () => { + appChain!.runtime + .resolve("Balances") + .setBalance(pk.toPublicKey(), UInt64.from(1000)); + }); + await tx.sign(); + await tx.send(); + + await sleep(10000); + + const balance = await appChain!.query.runtime.Balances.balances.get( + pk.toPublicKey() + ); + + expect(balance).toBeDefined(); + expect(balance!.toBigInt()).toBe(1000n); + }, 60000); +}); diff --git a/packages/sdk/test/graphql/graphql.ts b/packages/sdk/test/graphql/graphql.ts new file mode 100644 index 00000000..c1cae873 --- /dev/null +++ b/packages/sdk/test/graphql/graphql.ts @@ -0,0 +1,199 @@ +import "reflect-metadata"; +import { Field, PrivateKey, PublicKey, UInt64 } from "o1js"; +import { + Runtime, + runtimeMethod, + RuntimeModule, + runtimeModule, + state, +} from "@proto-kit/module"; +import { + AccountStateModule, + Option, + State, + StateMap, + VanillaProtocol, +} from "@proto-kit/protocol"; +import { Presets, log } from "@proto-kit/common"; +import { + AsyncStateService, BlockProducerModule, LocalTaskQueue, LocalTaskWorkerModule, NoopBaseLayer, + PrivateMempool, + Sequencer, TimedBlockTrigger, + UnsignedTransaction +} from "@proto-kit/sequencer"; +import { + BlockStorageResolver, + GraphqlSequencerModule, + GraphqlServer, + MempoolResolver, + NodeStatusResolver, + QueryGraphqlModule +} from "@proto-kit/api"; + +import { AppChain } from "../../src/appChain/AppChain"; +import { StateServiceQueryModule } from "../../src/query/StateServiceQueryModule"; +import { InMemorySigner } from "../../src/transaction/InMemorySigner"; +import { InMemoryTransactionSender } from "../../src/transaction/InMemoryTransactionSender"; +import { container } from "tsyringe"; + +log.setLevel(log.levels.INFO); + +function createNewTx() { + const pk = PrivateKey.random(); + + const tx = new UnsignedTransaction({ + nonce: UInt64.zero, + args: [Field(1)], + methodId: Field(1), + sender: pk.toPublicKey(), + }).sign(pk); + + console.log(tx.toJSON()); +} +createNewTx(); + +@runtimeModule() +export class Balances extends RuntimeModule { + /** + * We use `satisfies` here in order to be able to access + * presets by key in a type safe way. + */ + public static presets = {} satisfies Presets; + + @state() public balances = StateMap.from( + PublicKey, + UInt64 + ); + + @state() public totalSupply = State.from(UInt64); + + @runtimeMethod() + public getBalance(address: PublicKey): Option { + return this.balances.get(address); + } + + @runtimeMethod() + public setBalance(address: PublicKey, balance: UInt64) { + this.balances.set(address, balance); + } +} + +export async function startServer() { + + const appChain = AppChain.from({ + runtime: Runtime.from({ + modules: { + Balances, + }, + + config: { + Balances: {}, + }, + }), + + protocol: VanillaProtocol.from( + { AccountStateModule }, + { AccountStateModule: {}, StateTransitionProver: {}, BlockProver: {} } + ), + + sequencer: Sequencer.from({ + modules: { + Mempool: PrivateMempool, + GraphqlServer, + LocalTaskWorkerModule, + BaseLayer: NoopBaseLayer, + BlockProducerModule, + BlockTrigger: TimedBlockTrigger, + TaskQueue: LocalTaskQueue, + + Graphql: GraphqlSequencerModule.from({ + modules: { + MempoolResolver, + QueryGraphqlModule, + BlockStorageResolver, + NodeStatusResolver, + }, + + config: { + MempoolResolver: {}, + QueryGraphqlModule: {}, + BlockStorageResolver: {}, + NodeStatusResolver: {}, + }, + }), + }, + }), + + modules: { + Signer: InMemorySigner, + TransactionSender: InMemoryTransactionSender, + QueryTransportModule: StateServiceQueryModule, + }, + }); + + appChain.configure({ + Runtime: { + Balances: {}, + }, + + Protocol: { + BlockProver: {}, + StateTransitionProver: {}, + AccountStateModule: {}, + }, + + Sequencer: { + GraphqlServer: { + port: 8080, + host: "0.0.0.0", + graphiql: true + }, + + Graphql: { + QueryGraphqlModule: {}, + MempoolResolver: {}, + BlockStorageResolver: {}, + NodeStatusResolver: {} + }, + + Mempool: {}, + BlockProducerModule: {}, + LocalTaskWorkerModule: {}, + BaseLayer: {}, + TaskQueue: {}, + + BlockTrigger: { + blocktime: 5000 + }, + }, + + TransactionSender: {}, + QueryTransportModule: {}, + + Signer: { + signer: PrivateKey.random(), + }, + }); + + await appChain.start(container.createChildContainer()); + + const pk = PublicKey.fromBase58( + "B62qmETai5Y8vvrmWSU8F4NX7pTyPqYLMhc1pgX3wD8dGc2wbCWUcqP" + ); + console.log(pk.toJSON()); + + const balances = appChain.runtime.resolve("Balances"); + + console.log("Path:", balances.balances.getPath(pk).toString()); + + const asyncState = + appChain.sequencer.dependencyContainer.resolve( + "AsyncStateService" + ); + await asyncState.setAsync(balances.balances.getPath(pk), [Field(100)]); + await asyncState.setAsync(balances.totalSupply.path!, [Field(10_000)]); + + return appChain +} + +// await startServer(); \ No newline at end of file diff --git a/packages/sdk/test/graphql/run-graphql.test.ts b/packages/sdk/test/graphql/run-graphql.test.ts new file mode 100644 index 00000000..82262588 --- /dev/null +++ b/packages/sdk/test/graphql/run-graphql.test.ts @@ -0,0 +1,9 @@ +import { startServer } from "./graphql"; +import { sleep } from "@proto-kit/common"; + +describe("run graphql", () => { + it("run", async () => { + const server = await startServer(); + await sleep(1000000000); + }, 1000000000); +}); diff --git a/packages/sequencer/src/mempool/private/PrivateMempool.ts b/packages/sequencer/src/mempool/private/PrivateMempool.ts index f47d36e2..421e2ad1 100644 --- a/packages/sequencer/src/mempool/private/PrivateMempool.ts +++ b/packages/sequencer/src/mempool/private/PrivateMempool.ts @@ -28,7 +28,7 @@ export class PrivateMempool extends SequencerModule implements Mempool { return { transactionsHash: this.commitment }; } else { - throw new Error("Valdiation of tx failed"); + throw new Error("Validation of tx failed"); } } diff --git a/packages/sequencer/src/sequencer/executor/Sequencer.ts b/packages/sequencer/src/sequencer/executor/Sequencer.ts index 14d6a282..2b64cc7d 100644 --- a/packages/sequencer/src/sequencer/executor/Sequencer.ts +++ b/packages/sequencer/src/sequencer/executor/Sequencer.ts @@ -4,7 +4,7 @@ import { ModulesRecord, TypedClass, ModuleContainerDefinition, - log, + log, ChildContainerProvider } from "@proto-kit/common"; import { Runtime, RuntimeModulesRecord } from "@proto-kit/module"; import { Protocol, ProtocolModulesRecord } from "@proto-kit/protocol"; diff --git a/packages/sequencer/test/integration/BlockProduction.test.ts b/packages/sequencer/test/integration/BlockProduction.test.ts index 29bf8ad6..ef8e94b1 100644 --- a/packages/sequencer/test/integration/BlockProduction.test.ts +++ b/packages/sequencer/test/integration/BlockProduction.test.ts @@ -1,16 +1,15 @@ // eslint-disable-next-line max-len /* eslint-disable jest/no-restricted-matchers,@typescript-eslint/no-non-null-assertion,jest/max-expects,max-lines */ import "reflect-metadata"; +// eslint-disable-next-line no-warning-comments +// TODO this is actually a big issue +// eslint-disable-next-line import/no-extraneous-dependencies +import { AppChain } from "@proto-kit/sdk"; import { Fieldable, - InMemoryStateService, Runtime, MethodIdResolver, } from "@proto-kit/module"; -// eslint-disable-next-line no-warning-comments -// TODO this is actually a big issue -// eslint-disable-next-line import/no-extraneous-dependencies -import { AppChain } from "@proto-kit/sdk"; import { AccountState, AccountStateModule, @@ -92,17 +91,9 @@ describe("block production", () => { }, }); - console.log(new sequencerClass() instanceof Sequencer<{ - Mempool: typeof PrivateMempool; - LocalTaskWorkerModule: typeof LocalTaskWorkerModule; - BaseLayer: typeof NoopBaseLayer; - BlockProducerModule: typeof BlockProducerModule; - BlockTrigger: typeof ManualBlockTrigger; - TaskQueue: typeof LocalTaskQueue; - }>); - const protocolClass = VanillaProtocol.from( - { AccountStateModule } + { AccountStateModule }, + { AccountStateModule: {}, StateTransitionProver: {}, BlockProver: {} } ); const app = AppChain.from({