diff --git a/packages/api/src/graphql/modules/BlockResolver.ts b/packages/api/src/graphql/modules/BlockResolver.ts index 036184873..257b3a9d0 100644 --- a/packages/api/src/graphql/modules/BlockResolver.ts +++ b/packages/api/src/graphql/modules/BlockResolver.ts @@ -1,5 +1,5 @@ import { inject } from "tsyringe"; -import { Block, BlockStorage } from "@proto-kit/sequencer"; +import { Block, BlockStorage, PendingTransaction } from "@proto-kit/sequencer"; import { Arg, Field, ObjectType, Query } from "type-graphql"; import { GraphqlModule, graphqlModule } from "../GraphqlModule"; @@ -10,17 +10,17 @@ import { BatchTransactionModel } from "./model/BatchTransactionModel"; export class BlockModel { public static fromServiceLayerModel(block: Block) { return new BlockModel( - Number(block.networkState.during.block.height.toBigInt()), + Number(block.networkState.during.block.height), block.transactions.map((tx) => BatchTransactionModel.fromServiceLayerModel({ - tx: tx.tx, - status: tx.status.toBoolean(), + tx: PendingTransaction.fromJSON(tx.tx), + status: tx.status, statusMessage: tx.statusMessage, }) ), - block.transactionsHash.toString(), - block.hash.toString(), - block.previousBlockHash?.toString() + block.transactionsHash, + block.hash, + block.previousBlockHash ); } diff --git a/packages/api/src/graphql/modules/MempoolResolver.ts b/packages/api/src/graphql/modules/MempoolResolver.ts index 034eee651..17b871af0 100644 --- a/packages/api/src/graphql/modules/MempoolResolver.ts +++ b/packages/api/src/graphql/modules/MempoolResolver.ts @@ -125,10 +125,10 @@ export class MempoolResolver extends GraphqlModule { description: "Adds a transaction to the mempool and validates it", }) public async submitTx(@Arg("tx") tx: TransactionObject): Promise { - const decoded = PendingTransaction.fromJSON(tx); + const decoded = PendingTransaction.fromJSON(tx).toJSON(); await this.mempool.add(decoded); - return decoded.hash().toString(); + return decoded.hash; } // TODO Add retrieval of pending messages somewhere as well @@ -162,6 +162,6 @@ export class MempoolResolver extends GraphqlModule { }) public async transactions() { const txs = await this.transactionStorage.getPendingUserTransactions(); - return txs.map((x) => x.hash().toString()); + return txs.map((x) => x.hash); } } diff --git a/packages/api/src/metrics/SequencerInstrumentation.ts b/packages/api/src/metrics/SequencerInstrumentation.ts index 846daf704..956a2a69b 100644 --- a/packages/api/src/metrics/SequencerInstrumentation.ts +++ b/packages/api/src/metrics/SequencerInstrumentation.ts @@ -15,7 +15,7 @@ export class SequencerInstrumentation extends InstrumentationBase<{}> { super("protokit", "canary", {}); if (trigger !== undefined) { trigger.events.on("block-produced", (block) => { - this.blockProduced(parseInt(block.height.toString(), 10)); + this.blockProduced(parseInt(block.height, 10)); }); } } diff --git a/packages/indexer/src/IndexerNotifier.ts b/packages/indexer/src/IndexerNotifier.ts index 5f6816f8b..87d92f578 100644 --- a/packages/indexer/src/IndexerNotifier.ts +++ b/packages/indexer/src/IndexerNotifier.ts @@ -48,10 +48,7 @@ export class IndexerNotifier extends SequencerModule> { this.indexSettlementTask.inputSerializer(); this.sequencer.events.on("block-metadata-produced", async (block) => { - log.debug( - "Notifiying the indexer about block", - block.block.height.toBigInt() - ); + log.debug("Notifiying the indexer about block", block.block.height); const payload = await inputSerializer.toJSON(block); const sequencerId = this.sequencerIdProvider.getSequencerId(); @@ -69,7 +66,8 @@ export class IndexerNotifier extends SequencerModule> { const txQueue = await this.taskQueue.getQueue( this.indexPendingTxTask.name ); - const payload = await txInputSerializer.toJSON(tx); + // This part seems weird + const payload = await txInputSerializer.toJSON(tx.toJSON()); const sequencerId = this.sequencerIdProvider.getSequencerId(); const task: TaskPayload = { diff --git a/packages/indexer/src/tasks/IndexBlockTask.ts b/packages/indexer/src/tasks/IndexBlockTask.ts index 96d92677c..3bc46a2c4 100644 --- a/packages/indexer/src/tasks/IndexBlockTask.ts +++ b/packages/indexer/src/tasks/IndexBlockTask.ts @@ -37,12 +37,11 @@ export class IndexBlockTask await this.blockStorage.pushBlock(input.block); await this.blockStorage.pushResult(input.result); } catch (error) { - log.error("Failed to index block", input.block.height.toBigInt(), error); - return undefined; + log.error("Failed to index block", input.block.height, error); + return; } - log.info(`Block ${input.block.height.toBigInt()} indexed sucessfully`); - return ""; + log.info(`Block ${input.block.height} indexed sucessfully`); } public inputSerializer(): TaskSerializer { diff --git a/packages/indexer/src/tasks/IndexPendingTxTask.ts b/packages/indexer/src/tasks/IndexPendingTxTask.ts index db468660d..7583530ec 100644 --- a/packages/indexer/src/tasks/IndexPendingTxTask.ts +++ b/packages/indexer/src/tasks/IndexPendingTxTask.ts @@ -1,5 +1,5 @@ import { - PendingTransaction, + PendingTransactionJSONType, Task, TaskSerializer, TaskWorkerModule, @@ -13,7 +13,7 @@ import { IndexPendingTxTaskParametersSerializer } from "./IndexPendingTxTaskPara @injectable() export class IndexPendingTxTask extends TaskWorkerModule - implements Task + implements Task { public name = "index-pending-tx"; @@ -28,7 +28,9 @@ export class IndexPendingTxTask // eslint-disable-next-line @typescript-eslint/no-empty-function public async prepare(): Promise {} - public async compute(input: PendingTransaction): Promise { + public async compute( + input: PendingTransactionJSONType + ): Promise { try { await this.transactionStorage.pushUserTransaction(input); return ""; @@ -38,7 +40,7 @@ export class IndexPendingTxTask } } - public inputSerializer(): TaskSerializer { + public inputSerializer(): TaskSerializer { return this.taskSerializer; } diff --git a/packages/indexer/src/tasks/IndexPendingTxTaskParameters.ts b/packages/indexer/src/tasks/IndexPendingTxTaskParameters.ts index 5c60be517..f35643347 100644 --- a/packages/indexer/src/tasks/IndexPendingTxTaskParameters.ts +++ b/packages/indexer/src/tasks/IndexPendingTxTaskParameters.ts @@ -1,4 +1,4 @@ -import { PendingTransaction } from "@proto-kit/sequencer"; +import { PendingTransactionJSONType } from "@proto-kit/sequencer"; import { TransactionMapper } from "@proto-kit/persistance"; import { injectable } from "tsyringe"; @@ -6,13 +6,13 @@ import { injectable } from "tsyringe"; export class IndexPendingTxTaskParametersSerializer { public constructor(public transactionMapper: TransactionMapper) {} - public toJSON(parameters: PendingTransaction): string { + public toJSON(parameters: PendingTransactionJSONType): string { return JSON.stringify({ tx: this.transactionMapper.mapOut(parameters), }); } - public fromJSON(json: string): PendingTransaction { + public fromJSON(json: string): PendingTransactionJSONType { // eslint-disable-next-line @typescript-eslint/consistent-type-assertions const parsed = JSON.parse(json) as { tx: ReturnType; diff --git a/packages/indexer/test/GeneratedResolverFactoryGraphqlModule.test.ts b/packages/indexer/test/GeneratedResolverFactoryGraphqlModule.test.ts index 3ef0ffe06..9f24d049d 100644 --- a/packages/indexer/test/GeneratedResolverFactoryGraphqlModule.test.ts +++ b/packages/indexer/test/GeneratedResolverFactoryGraphqlModule.test.ts @@ -8,7 +8,7 @@ import { log } from "@proto-kit/common"; import { jest } from "@jest/globals"; import { Indexer } from "../src/Indexer"; -import { GeneratedResolverFactoryGraphqlModule } from "../src/api/GeneratedResolverFactoryGraphqlModule"; +import { GeneratedResolverFactoryGraphqlModule } from "../src"; log.setLevel("info"); diff --git a/packages/indexer/test/IndexBlockTask.test.ts b/packages/indexer/test/IndexBlockTask.test.ts index b5432e5a3..47213f251 100644 --- a/packages/indexer/test/IndexBlockTask.test.ts +++ b/packages/indexer/test/IndexBlockTask.test.ts @@ -54,8 +54,6 @@ describe("IndexBlockTask", () => { const storage = indexer.resolve("BlockStorage"); const latestBlock = await storage.getLatestBlock(); - expect(latestBlock?.block.hash.toBigInt()).toBe( - block.block.hash.toBigInt() - ); + expect(latestBlock?.block.hash).toBe(block.block.hash); }); }); diff --git a/packages/indexer/test/IndexerNotifier.test.ts b/packages/indexer/test/IndexerNotifier.test.ts index 31ab57210..073bc4959 100644 --- a/packages/indexer/test/IndexerNotifier.test.ts +++ b/packages/indexer/test/IndexerNotifier.test.ts @@ -141,7 +141,7 @@ async function sendTransactions( const txs = await mempool.getTxs(); console.log( "txs", - txs.map((tx) => tx.nonce.toBigInt()) + txs.map((tx) => tx.nonce) ); return await appChain.produceBlock(); @@ -183,7 +183,7 @@ describe.skip("IndexerNotifier", () => { .resolve(IndexBlockTaskParametersSerializer) .fromJSON(addTaskSpy.mock.lastCall?.[0].payload!); - expect(block.height.toBigInt()).toBe(0n); + expect(block.height).toBe("0"); expect(block.transactions.length).toBe(2); }); }); diff --git a/packages/library/src/hooks/RuntimeFeeAnalyzerService.ts b/packages/library/src/hooks/RuntimeFeeAnalyzerService.ts index 707d80a4c..22ec51bd1 100644 --- a/packages/library/src/hooks/RuntimeFeeAnalyzerService.ts +++ b/packages/library/src/hooks/RuntimeFeeAnalyzerService.ts @@ -110,8 +110,8 @@ export class RuntimeFeeAnalyzerService extends ConfigurableModule { - dict[this.getMethodId(moduleName, methodName).toString()] = { + dict[this.getMethodId(moduleName, methodName)] = { moduleName, methodName, }; @@ -83,8 +83,8 @@ export class MethodIdResolver { }, {}); } - public getMethodNameFromId(methodId: bigint): [string, string] | undefined { - const methodPath = this.dictionary[methodId.toString()]; + public getMethodNameFromId(methodId: string): [string, string] | undefined { + const methodPath = this.dictionary[methodId]; if (methodPath === undefined) { return undefined; @@ -97,12 +97,12 @@ export class MethodIdResolver { return [moduleName, methodName]; } - public getMethodId(moduleName: string, methodName: string): bigint { + public getMethodId(moduleName: string, methodName: string): string { this.runtime.assertIsValidModuleName(moduleName); return Poseidon.hash([ stringToField(moduleName), stringToField(methodName), - ]).toBigInt(); + ]).toString(); } } diff --git a/packages/module/src/runtime/Runtime.ts b/packages/module/src/runtime/Runtime.ts index 37bd65e7c..468940e8e 100644 --- a/packages/module/src/runtime/Runtime.ts +++ b/packages/module/src/runtime/Runtime.ts @@ -337,7 +337,7 @@ export class Runtime * Encoding: "stringToField(module.name) << 128 + stringToField(method-name)" */ public getMethodById( - methodId: bigint + methodId: string ): ((...args: unknown[]) => Promise) | undefined { const methodDescriptor = this.methodIdResolver.getMethodNameFromId(methodId); diff --git a/packages/persistance/src/services/prisma/PrismaBlockStorage.ts b/packages/persistance/src/services/prisma/PrismaBlockStorage.ts index f3146642e..be0f0ddd7 100644 --- a/packages/persistance/src/services/prisma/PrismaBlockStorage.ts +++ b/packages/persistance/src/services/prisma/PrismaBlockStorage.ts @@ -1,5 +1,5 @@ import { - TransactionExecutionResult, + TransactionExecutionResultJson, Block, BlockResult, BlockQueue, @@ -53,9 +53,10 @@ export class PrismaBlockStorage implements BlockQueue, BlockStorage { if (dbResult === null) { return undefined; } - const transactions = dbResult.transactions.map( - (txresult) => this.transactionResultMapper.mapIn([txresult, txresult.tx]) - ); + const transactions = + dbResult.transactions.map((txresult) => + this.transactionResultMapper.mapIn([txresult, txresult.tx]) + ); return { block: { @@ -80,7 +81,7 @@ export class PrismaBlockStorage implements BlockQueue, BlockStorage { public async pushBlock(block: Block): Promise { log.trace( "Pushing block to DB. Txs:", - block.transactions.map((x) => x.tx.hash().toString()) + block.transactions.map((x) => x.tx.hash) ); const transactions = block.transactions.map( @@ -88,7 +89,7 @@ export class PrismaBlockStorage implements BlockQueue, BlockStorage { const encoded = this.transactionResultMapper.mapOut(tx); return { ...encoded[0], - blockHash: block.hash.toString(), + blockHash: block.hash, }; } ); @@ -191,7 +192,7 @@ export class PrismaBlockStorage implements BlockQueue, BlockStorage { if (result !== undefined) { if (result.result === undefined) { throw new Error( - `Block result for block ${result.block.height.toString()} not found` + `Block result for block ${result.block.height} not found` ); } return { @@ -221,11 +222,10 @@ export class PrismaBlockStorage implements BlockQueue, BlockStorage { }); return blocks.map((block, index) => { - const transactions = block.transactions.map( - (txresult) => { + const transactions = + block.transactions.map((txresult) => { return this.transactionResultMapper.mapIn([txresult, txresult.tx]); - } - ); + }); const decodedBlock = this.blockMapper.mapIn(block); decodedBlock.transactions = transactions; diff --git a/packages/persistance/src/services/prisma/PrismaMessageStorage.ts b/packages/persistance/src/services/prisma/PrismaMessageStorage.ts index c1b113bb7..623c6c484 100644 --- a/packages/persistance/src/services/prisma/PrismaMessageStorage.ts +++ b/packages/persistance/src/services/prisma/PrismaMessageStorage.ts @@ -1,4 +1,7 @@ -import { MessageStorage, PendingTransaction } from "@proto-kit/sequencer"; +import { + MessageStorage, + PendingTransactionJSONType, +} from "@proto-kit/sequencer"; import { inject, injectable } from "tsyringe"; import type { PrismaConnection } from "../../PrismaDatabaseConnection"; @@ -21,7 +24,7 @@ export class PrismaMessageStorage implements MessageStorage { const batches: { fromMessagesHash: string; toMessagesHash: string; - messages: PendingTransaction[]; + messages: PendingTransactionJSONType[]; }[] = []; let currentHash = fromMessagesHash; @@ -43,7 +46,7 @@ export class PrismaMessageStorage implements MessageStorage { | { fromMessagesHash: string; toMessagesHash: string; - messages: PendingTransaction[]; + messages: PendingTransactionJSONType[]; } | undefined > { @@ -84,7 +87,7 @@ export class PrismaMessageStorage implements MessageStorage { public async pushMessages( fromMessageHash: string, toMessageHash: string, - messages: PendingTransaction[] + messages: PendingTransactionJSONType[] ): Promise { const transactions = messages.map((message) => this.transactionMapper.mapOut(message) diff --git a/packages/persistance/src/services/prisma/PrismaTransactionStorage.ts b/packages/persistance/src/services/prisma/PrismaTransactionStorage.ts index d4544a7c4..00411b2ac 100644 --- a/packages/persistance/src/services/prisma/PrismaTransactionStorage.ts +++ b/packages/persistance/src/services/prisma/PrismaTransactionStorage.ts @@ -1,6 +1,6 @@ import { inject, injectable } from "tsyringe"; import { - PendingTransaction, + PendingTransactionJSONType, trace, Tracer, TransactionStorage, @@ -19,7 +19,9 @@ export class PrismaTransactionStorage implements TransactionStorage { ) {} @trace("db.txs.get") - public async getPendingUserTransactions(): Promise { + public async getPendingUserTransactions(): Promise< + PendingTransactionJSONType[] + > { const { prismaClient } = this.connection; const txs = await prismaClient.transaction.findMany({ @@ -51,7 +53,9 @@ export class PrismaTransactionStorage implements TransactionStorage { } } - public async pushUserTransaction(tx: PendingTransaction): Promise { + public async pushUserTransaction( + tx: PendingTransactionJSONType + ): Promise { const { prismaClient } = this.connection; const result = await prismaClient.transaction.createMany({ @@ -64,7 +68,7 @@ export class PrismaTransactionStorage implements TransactionStorage { public async findTransaction(hash: string): Promise< | { - transaction: PendingTransaction; + transaction: PendingTransactionJSONType; block?: string; batch?: number; } diff --git a/packages/persistance/src/services/prisma/mappers/BlockMapper.ts b/packages/persistance/src/services/prisma/mappers/BlockMapper.ts index bd5ee9b06..d13fddbf4 100644 --- a/packages/persistance/src/services/prisma/mappers/BlockMapper.ts +++ b/packages/persistance/src/services/prisma/mappers/BlockMapper.ts @@ -1,8 +1,7 @@ import { singleton } from "tsyringe"; -import { Block } from "@proto-kit/sequencer"; -import { Block as PrismaBlock } from "@prisma/client"; -import { NetworkState } from "@proto-kit/protocol"; -import { Field } from "o1js"; +import { Block, UntypedStateTransitionJson } from "@proto-kit/sequencer"; +import { Prisma, Block as PrismaBlock } from "@prisma/client"; +import { NetworkStateJson } from "@proto-kit/protocol"; import { ObjectMapper } from "../../../ObjectMapper"; @@ -19,55 +18,48 @@ export class BlockMapper implements ObjectMapper { transactions: [], networkState: { - before: new NetworkState( - // eslint-disable-next-line @typescript-eslint/no-unsafe-argument - NetworkState.fromJSON(input.beforeNetworkState as any) - ), - during: new NetworkState( - // eslint-disable-next-line @typescript-eslint/no-unsafe-argument - NetworkState.fromJSON(input.duringNetworkState as any) - ), + before: input.beforeNetworkState as NetworkStateJson, + during: input.duringNetworkState as NetworkStateJson, }, - hash: Field(input.hash), - height: Field(input.height), - fromEternalTransactionsHash: Field(input.fromEternalTransactionsHash), - toEternalTransactionsHash: Field(input.toEternalTransactionsHash), - fromBlockHashRoot: Field(input.fromBlockHashRoot), - fromMessagesHash: Field(input.fromMessagesHash), - toMessagesHash: Field(input.toMessagesHash), - fromStateRoot: Field(input.fromStateRoot), + hash: input.hash, + height: input.height.toString(), + fromEternalTransactionsHash: input.fromEternalTransactionsHash, + toEternalTransactionsHash: input.toEternalTransactionsHash, + fromBlockHashRoot: input.fromBlockHashRoot, + fromMessagesHash: input.fromMessagesHash, + toMessagesHash: input.toMessagesHash, + fromStateRoot: input.fromStateRoot, - transactionsHash: Field(input.transactionsHash), + transactionsHash: input.transactionsHash, previousBlockHash: - input.parentHash !== null ? Field(input.parentHash) : undefined, + input.parentHash !== null ? input.parentHash : undefined, - beforeBlockStateTransitions: this.stArrayMapper.mapIn( - input.beforeBlockStateTransitions - ), + // This is cleaner to keep mapIn + beforeBlockStateTransitions: + input.beforeBlockStateTransitions as unknown as UntypedStateTransitionJson[], }; } public mapOut(input: Block): PrismaBlock { return { - height: Number(input.height.toBigInt()), - beforeNetworkState: NetworkState.toJSON(input.networkState.before), - duringNetworkState: NetworkState.toJSON(input.networkState.during), - fromEternalTransactionsHash: input.fromEternalTransactionsHash.toString(), - toEternalTransactionsHash: input.toEternalTransactionsHash.toString(), - fromBlockHashRoot: input.fromBlockHashRoot.toString(), - fromMessagesHash: input.fromMessagesHash.toString(), - toMessagesHash: input.toMessagesHash.toString(), - fromStateRoot: input.fromStateRoot.toString(), + height: Number(input.height), + beforeNetworkState: input.networkState.before, + duringNetworkState: input.networkState.during, + fromEternalTransactionsHash: input.fromEternalTransactionsHash, + toEternalTransactionsHash: input.toEternalTransactionsHash, + fromBlockHashRoot: input.fromBlockHashRoot, + fromMessagesHash: input.fromMessagesHash, + toMessagesHash: input.toMessagesHash, + fromStateRoot: input.fromStateRoot, - hash: input.hash.toString(), - transactionsHash: input.transactionsHash.toString(), - parentHash: input.previousBlockHash?.toString() ?? null, + hash: input.hash, + transactionsHash: input.transactionsHash, + parentHash: input.previousBlockHash ?? null, batchHeight: null, - beforeBlockStateTransitions: this.stArrayMapper.mapOut( - input.beforeBlockStateTransitions - ), + beforeBlockStateTransitions: + input.beforeBlockStateTransitions as unknown as Prisma.JsonArray, }; } } diff --git a/packages/persistance/src/services/prisma/mappers/BlockResultMapper.ts b/packages/persistance/src/services/prisma/mappers/BlockResultMapper.ts index fd65c6d02..d8afc7d6c 100644 --- a/packages/persistance/src/services/prisma/mappers/BlockResultMapper.ts +++ b/packages/persistance/src/services/prisma/mappers/BlockResultMapper.ts @@ -1,7 +1,10 @@ import { singleton } from "tsyringe"; import { BlockResult } from "@proto-kit/sequencer"; -import { BlockResult as DBBlockResult } from "@prisma/client"; -import { BlockHashMerkleTreeWitness, NetworkState } from "@proto-kit/protocol"; +import { BlockResult as DBBlockResult, Prisma } from "@prisma/client"; +import { + BlockHashMerkleTreeWitnessJson, + NetworkStateJson, +} from "@proto-kit/protocol"; import { ObjectMapper } from "../../../ObjectMapper"; @@ -17,23 +20,19 @@ export class BlockResultMapper public mapIn(input: DBBlockResult): BlockResult { return { - afterNetworkState: new NetworkState( - // eslint-disable-next-line @typescript-eslint/no-unsafe-argument - NetworkState.fromJSON(input.afterNetworkState as any) - ), - - stateRoot: BigInt(input.stateRoot), - blockHashRoot: BigInt(input.blockHashRoot), - blockHashWitness: new BlockHashMerkleTreeWitness( - // eslint-disable-next-line @typescript-eslint/no-unsafe-argument - BlockHashMerkleTreeWitness.fromJSON(input.blockHashWitness as any) - ), - afterBlockStateTransitions: this.stArrayMapper.mapIn( - input.afterBlockStateTransitions - ), - blockHash: BigInt(input.blockHash), - - witnessedRoots: [BigInt(input.witnessedRoots[0])], + afterNetworkState: input.afterNetworkState as NetworkStateJson, + + stateRoot: input.stateRoot, + blockHashRoot: input.blockHashRoot, + blockHashWitness: + input.blockHashWitness as BlockHashMerkleTreeWitnessJson, + + afterBlockStateTransitions: this.stArrayMapper + .mapIn(input.afterBlockStateTransitions) + .map((st) => st), + blockHash: input.blockHash, + + witnessedRoots: [input.witnessedRoots[0]], }; } @@ -43,13 +42,10 @@ export class BlockResultMapper blockHash: input.blockHash.toString(), blockHashRoot: input.blockHashRoot.toString(), - blockHashWitness: BlockHashMerkleTreeWitness.toJSON( - input.blockHashWitness - ), - afterBlockStateTransitions: this.stArrayMapper.mapOut( - input.afterBlockStateTransitions - ), - afterNetworkState: NetworkState.toJSON(input.afterNetworkState), + blockHashWitness: input.blockHashWitness, + afterBlockStateTransitions: + input.afterBlockStateTransitions as unknown as Prisma.JsonArray, + afterNetworkState: input.afterNetworkState, witnessedRoots: [input.witnessedRoots[0].toString()], }; diff --git a/packages/persistance/src/services/prisma/mappers/EventMapper.ts b/packages/persistance/src/services/prisma/mappers/EventMapper.ts index b56416a87..c80e7f6a6 100644 --- a/packages/persistance/src/services/prisma/mappers/EventMapper.ts +++ b/packages/persistance/src/services/prisma/mappers/EventMapper.ts @@ -1,31 +1,30 @@ import { singleton } from "tsyringe"; import { Prisma } from "@prisma/client"; -import { Field } from "o1js"; import { ObjectMapper } from "../../../ObjectMapper"; -type EventData = { +type EventDataJson = { eventName: string; - data: Field[]; + data: string[]; source: "afterTxHook" | "beforeTxHook" | "runtime"; }; @singleton() -export class EventMapper implements ObjectMapper { - public mapIn(input: Prisma.JsonObject): EventData { +export class EventMapper + implements ObjectMapper +{ + public mapIn(input: Prisma.JsonObject): EventDataJson { return { eventName: input.eventName as string, - data: (input.data as Prisma.JsonArray).map((field) => - Field.fromJSON(field as string) - ), + data: input.data as string[], source: this.sourceConvert(input.source as string), }; } - public mapOut(input: EventData): Prisma.JsonObject { + public mapOut(input: EventDataJson): Prisma.JsonObject { return { eventName: input.eventName, - data: input.data.map((field) => field.toString()), + data: input.data, source: input.source, } as Prisma.JsonObject; } @@ -46,11 +45,11 @@ export class EventMapper implements ObjectMapper { @singleton() export class EventArrayMapper - implements ObjectMapper + implements ObjectMapper { public constructor(private readonly eventMapper: EventMapper) {} - public mapIn(input: Prisma.JsonValue | undefined): EventData[] { + public mapIn(input: Prisma.JsonValue | undefined): EventDataJson[] { if (input === undefined) return []; if (Array.isArray(input)) { @@ -61,7 +60,7 @@ export class EventArrayMapper return []; } - public mapOut(input: EventData[]): Prisma.JsonValue { + public mapOut(input: EventDataJson[]): Prisma.JsonValue { return input.map((event) => this.eventMapper.mapOut(event) ) as Prisma.JsonArray; diff --git a/packages/persistance/src/services/prisma/mappers/StateTransitionMapper.ts b/packages/persistance/src/services/prisma/mappers/StateTransitionMapper.ts index ed35da7b3..2800427ff 100644 --- a/packages/persistance/src/services/prisma/mappers/StateTransitionMapper.ts +++ b/packages/persistance/src/services/prisma/mappers/StateTransitionMapper.ts @@ -1,7 +1,7 @@ import { singleton } from "tsyringe"; import { - StateTransitionBatch, - UntypedStateTransition, + StateTransitionBatchJson, + UntypedStateTransitionJson, } from "@proto-kit/sequencer"; import { Prisma } from "@prisma/client"; @@ -9,26 +9,29 @@ import { ObjectMapper } from "../../../ObjectMapper"; @singleton() export class StateTransitionMapper - implements ObjectMapper + implements ObjectMapper { - public mapIn(input: Prisma.JsonObject): UntypedStateTransition { - // eslint-disable-next-line @typescript-eslint/no-unsafe-argument - return UntypedStateTransition.fromJSON(input as any); + public mapIn(input: Prisma.JsonObject): UntypedStateTransitionJson { + // + return input as unknown as UntypedStateTransitionJson; } - public mapOut(input: UntypedStateTransition): Prisma.JsonObject { - return input.toJSON(); + public mapOut(input: UntypedStateTransitionJson): Prisma.JsonObject { + // Already JSON-compatible, just cast + return input as unknown as Prisma.JsonObject; } } @singleton() export class StateTransitionArrayMapper implements - ObjectMapper + ObjectMapper { public constructor(private readonly stMapper: StateTransitionMapper) {} - public mapIn(input: Prisma.JsonValue | undefined): UntypedStateTransition[] { + public mapIn( + input: Prisma.JsonValue | undefined + ): UntypedStateTransitionJson[] { if (input === undefined) return []; if (Array.isArray(input)) { @@ -39,20 +42,20 @@ export class StateTransitionArrayMapper return []; } - public mapOut(input: UntypedStateTransition[]): Prisma.JsonValue { + public mapOut(input: UntypedStateTransitionJson[]): Prisma.JsonValue { return input.map((st) => this.stMapper.mapOut(st)) as Prisma.JsonArray; } } @singleton() export class StateTransitionBatchArrayMapper - implements ObjectMapper + implements ObjectMapper { public constructor( private readonly stArrayMapper: StateTransitionArrayMapper ) {} - public mapOut(input: StateTransitionBatch[]): Prisma.JsonValue { + public mapOut(input: StateTransitionBatchJson[]): Prisma.JsonValue { return input.map((st) => ({ stateTransitions: this.stArrayMapper.mapOut( st.stateTransitions @@ -61,7 +64,7 @@ export class StateTransitionBatchArrayMapper })); } - public mapIn(input: Prisma.JsonValue): StateTransitionBatch[] { + public mapIn(input: Prisma.JsonValue): StateTransitionBatchJson[] { if (input === undefined) return []; if (Array.isArray(input)) { diff --git a/packages/persistance/src/services/prisma/mappers/TransactionMapper.ts b/packages/persistance/src/services/prisma/mappers/TransactionMapper.ts index e999e9bc2..2867aed66 100644 --- a/packages/persistance/src/services/prisma/mappers/TransactionMapper.ts +++ b/packages/persistance/src/services/prisma/mappers/TransactionMapper.ts @@ -1,13 +1,12 @@ import { singleton, injectable } from "tsyringe"; import { - PendingTransaction, - TransactionExecutionResult, + TransactionExecutionResultJson, + PendingTransactionJSONType, } from "@proto-kit/sequencer"; import { Transaction as DBTransaction, TransactionExecutionResult as DBTransactionExecutionResult, } from "@prisma/client"; -import { Bool } from "o1js"; import { ObjectMapper } from "../../../ObjectMapper"; @@ -17,30 +16,35 @@ import { EventArrayMapper } from "./EventMapper"; @singleton() @injectable() export class TransactionMapper - implements ObjectMapper + implements ObjectMapper { - public mapIn(input: DBTransaction): PendingTransaction { - return PendingTransaction.fromJSON({ - ...input, + public mapIn(input: DBTransaction): PendingTransactionJSONType { + return { + hash: input.hash, + methodId: input.methodId, + nonce: input.nonce, + sender: input.sender, + argsFields: input.argsFields, + auxiliaryData: input.auxiliaryData, + isMessage: input.isMessage, signature: { r: input.signature_r, s: input.signature_s, }, - }); + }; } - public mapOut(input: PendingTransaction): DBTransaction { - const json = input.toJSON(); + public mapOut(input: PendingTransactionJSONType): DBTransaction { return { - methodId: json.methodId, - nonce: json.nonce, - sender: json.sender, - argsFields: json.argsFields, - auxiliaryData: json.auxiliaryData, - isMessage: json.isMessage, - signature_r: json.signature.r, - signature_s: json.signature.s, - hash: input.hash().toString(), + hash: input.hash, + methodId: input.methodId, + nonce: input.nonce, + sender: input.sender, + argsFields: input.argsFields, + auxiliaryData: input.auxiliaryData, + isMessage: input.isMessage, + signature_r: input.signature.r, + signature_s: input.signature.s, }; } } @@ -49,7 +53,7 @@ export class TransactionMapper export class TransactionExecutionResultMapper implements ObjectMapper< - TransactionExecutionResult, + TransactionExecutionResultJson, [Omit, DBTransaction] > { @@ -61,12 +65,12 @@ export class TransactionExecutionResultMapper public mapIn( input: [Omit, DBTransaction] - ): TransactionExecutionResult { + ): TransactionExecutionResultJson { const executionResult = input[0]; return { tx: this.transactionMapper.mapIn(input[1]), - status: Bool(executionResult.status), - hooksStatus: Bool(executionResult.hooksStatus), + status: executionResult.status, + hooksStatus: executionResult.hooksStatus, statusMessage: executionResult.statusMessage ?? undefined, stateTransitions: this.stBatchMapper.mapIn( executionResult.stateTransitions @@ -76,12 +80,12 @@ export class TransactionExecutionResultMapper } mapOut( - input: TransactionExecutionResult + input: TransactionExecutionResultJson ): [Omit, DBTransaction] { const tx = this.transactionMapper.mapOut(input.tx); const executionResult = { - status: input.status.toBoolean(), - hooksStatus: input.hooksStatus.toBoolean(), + status: input.status, + hooksStatus: input.hooksStatus, statusMessage: input.statusMessage ?? null, stateTransitions: this.stBatchMapper.mapOut(input.stateTransitions), events: this.eventArrayMapper.mapOut(input.events), diff --git a/packages/persistance/test-integration/PrismaBlockProduction.test.ts b/packages/persistance/test-integration/PrismaBlockProduction.test.ts index f6299f6b1..52481d05a 100644 --- a/packages/persistance/test-integration/PrismaBlockProduction.test.ts +++ b/packages/persistance/test-integration/PrismaBlockProduction.test.ts @@ -2,7 +2,6 @@ import "reflect-metadata"; import { afterAll, beforeAll, describe, expect } from "@jest/globals"; import { expectDefined, log } from "@proto-kit/common"; import { BalancesKey, TokenId } from "@proto-kit/library"; -import { NetworkState } from "@proto-kit/protocol"; import { AppChainTransaction } from "@proto-kit/sdk"; import { Block, Batch } from "@proto-kit/sequencer"; import { PrivateKey, PublicKey } from "o1js"; @@ -97,13 +96,11 @@ describe("prisma integration", () => { // Check that transactions match expect(retrievedBlock.transactions).toHaveLength(1); - expect(retrievedBlock.transactions[0].tx.hash().toString()).toStrictEqual( - block.transactions[0].tx.hash().toString() + expect(retrievedBlock.transactions[0].tx.hash).toStrictEqual( + block.transactions[0].tx.hash ); - expect(retrievedBlock.hash.toString()).toStrictEqual( - block.hash.toString() - ); + expect(retrievedBlock.hash).toStrictEqual(block.hash); // Check that events match expect(retrievedBlock.transactions[0].events).toHaveLength(1); @@ -111,23 +108,11 @@ describe("prisma integration", () => { block.transactions[0].events ); - expect( - NetworkState.toFields(retrievedBlock.networkState.before).map((x) => - x.toString() - ) - ).toStrictEqual( - NetworkState.toFields(block.networkState.before).map((x) => - x.toString() - ) + expect(retrievedBlock.networkState.before).toStrictEqual( + block.networkState.before ); - expect( - NetworkState.toFields(retrievedBlock.networkState.during).map((x) => - x.toString() - ) - ).toStrictEqual( - NetworkState.toFields(block.networkState.during).map((x) => - x.toString() - ) + expect(retrievedBlock.networkState.during).toStrictEqual( + block.networkState.during ); }); @@ -240,7 +225,7 @@ describe("prisma integration", () => { expectDefined(transaction.transaction); expect(txs).toHaveLength(1); - expect(txs[0].hash().toString()).toStrictEqual( + expect(txs[0].hash).toStrictEqual( transaction.transaction.hash().toString() ); }); @@ -256,7 +241,7 @@ describe("prisma integration", () => { expectDefined(transaction.transaction); expect(txs).toHaveLength(1); - expect(txs[0].hash().toString()).toStrictEqual( + expect(txs[0].hash).toStrictEqual( transaction.transaction.hash().toString() ); }); diff --git a/packages/processor/src/handlers/HandlersExecutor.ts b/packages/processor/src/handlers/HandlersExecutor.ts index a67612a8a..64d171e77 100644 --- a/packages/processor/src/handlers/HandlersExecutor.ts +++ b/packages/processor/src/handlers/HandlersExecutor.ts @@ -75,7 +75,7 @@ export class HandlersExecutor< ) { await client.block.create({ data: { - height: Number(block.block.height.toBigInt()), + height: Number(block.block.height), }, }); } diff --git a/packages/processor/src/triggers/TimedProcessorTrigger.ts b/packages/processor/src/triggers/TimedProcessorTrigger.ts index 7f3f59454..beee4dfe6 100644 --- a/packages/processor/src/triggers/TimedProcessorTrigger.ts +++ b/packages/processor/src/triggers/TimedProcessorTrigger.ts @@ -40,19 +40,19 @@ export class TimedProcessorTrigger extends ProcessorModule { ) => { // iterate over all transactions for (const tx of block.transactions) { - const methodId = tx.tx.methodId.toBigInt(); + const methodId = tx.tx.methodId.toString(); const methodDescriptor = appChain.runtime.methodIdResolver.getMethodNameFromId(methodId); @@ -66,7 +66,7 @@ describe("HandlersModule", () => { // @ts-expect-error const [, from, to, amount]: [TokenId, PublicKey, PublicKey, Balance] = await parameterDecoder.decode( - tx.tx.argsFields, + tx.tx.argsFields.map(Field), tx.tx.auxiliaryData ); @@ -87,7 +87,7 @@ describe("HandlersModule", () => { await client.balance.create({ data: { address: from.toBase58(), - height: Number(block.height.toString()), + height: Number(block.height), amount: newFromBalance > 0n ? newFromBalance.toString() : "0", }, }); @@ -109,7 +109,7 @@ describe("HandlersModule", () => { await client.balance.create({ data: { address: to.toBase58(), - height: Number(block.height.toString()), + height: Number(block.height), amount: newToBalance > 0n ? newToBalance.toString() : "0", }, }); diff --git a/packages/protocol/src/model/RuntimeLike.ts b/packages/protocol/src/model/RuntimeLike.ts index ec636f5d0..733b1620d 100644 --- a/packages/protocol/src/model/RuntimeLike.ts +++ b/packages/protocol/src/model/RuntimeLike.ts @@ -2,7 +2,7 @@ export type RuntimeMethodInvocationType = "INCOMING_MESSAGE" | "SIGNATURE"; export type RuntimeMethodIdMapping = Record< `${string}.${string}`, - { methodId: bigint; type: RuntimeMethodInvocationType } + { methodId: string; type: RuntimeMethodInvocationType } >; export interface RuntimeLike { diff --git a/packages/protocol/src/model/network/NetworkState.ts b/packages/protocol/src/model/network/NetworkState.ts index b0213ac36..87c8be710 100644 --- a/packages/protocol/src/model/network/NetworkState.ts +++ b/packages/protocol/src/model/network/NetworkState.ts @@ -1,4 +1,4 @@ -import { Field, Poseidon, Struct, UInt64 } from "o1js"; +import { Field, InferJson, Poseidon, Struct, UInt64 } from "o1js"; import { RollupMerkleTree } from "@proto-kit/common"; export class CurrentBlock extends Struct({ @@ -31,3 +31,5 @@ export class NetworkState extends Struct({ }); } } + +export type NetworkStateJson = InferJson; diff --git a/packages/protocol/src/prover/block/accummulators/BlockHashMerkleTree.ts b/packages/protocol/src/prover/block/accummulators/BlockHashMerkleTree.ts index e80d4b6b8..9011686e4 100644 --- a/packages/protocol/src/prover/block/accummulators/BlockHashMerkleTree.ts +++ b/packages/protocol/src/prover/block/accummulators/BlockHashMerkleTree.ts @@ -1,5 +1,5 @@ import { createMerkleTree } from "@proto-kit/common"; -import { Bool, Field, Poseidon, Struct } from "o1js"; +import { Bool, Field, InferJson, Poseidon, Struct } from "o1js"; export class BlockHashMerkleTree extends createMerkleTree(40) {} export class BlockHashMerkleTreeWitness extends BlockHashMerkleTree.WITNESS {} @@ -22,3 +22,6 @@ export class BlockHashTreeEntry extends Struct({ return Poseidon.hash([blockHash, ...this.closed.toFields()]); } } +export type BlockHashMerkleTreeWitnessJson = InferJson< + typeof BlockHashMerkleTreeWitness +>; diff --git a/packages/protocol/src/utils/utils.ts b/packages/protocol/src/utils/utils.ts index 660d8c456..661477742 100644 --- a/packages/protocol/src/utils/utils.ts +++ b/packages/protocol/src/utils/utils.ts @@ -1,6 +1,8 @@ import { Bool, Field, Poseidon, Provable } from "o1js"; import floor from "lodash/floor"; +import { NetworkStateJson } from "../model/network/NetworkState"; + export type ReturnType = FunctionType extends ( ...args: any[] ) => infer Return @@ -33,6 +35,14 @@ export function notInCircuit(): MethodDecorator { }; } +export function networkStateToFields(json: NetworkStateJson): Field[] { + return [Field(json.block.height), Field(json.previous.rootHash)]; +} + +export function hashNetworkState(json: NetworkStateJson): string { + return Poseidon.hash(networkStateToFields(json)).toString(); +} + export function stringToField(value: string) { const fieldSize = Field.sizeInBytes - 1; diff --git a/packages/sdk/src/graphql/GraphqlBlockExplorerTransportModule.ts b/packages/sdk/src/graphql/GraphqlBlockExplorerTransportModule.ts index 3908ab6cd..4537db272 100644 --- a/packages/sdk/src/graphql/GraphqlBlockExplorerTransportModule.ts +++ b/packages/sdk/src/graphql/GraphqlBlockExplorerTransportModule.ts @@ -7,7 +7,6 @@ import { ClientTransaction, InclusionStatus, } from "@proto-kit/sequencer"; -import { Field } from "o1js"; import { GraphqlClient } from "./GraphqlClient"; @@ -106,14 +105,14 @@ export class GraphqlBlockExplorerTransportModule const previousBlockHash = blockData.previousBlockHash != null && blockData.previousBlockHash !== "" - ? Field(blockData.previousBlockHash) + ? blockData.previousBlockHash : undefined; return { - hash: Field(blockData.hash), - height: Field(blockData.height), + hash: blockData.hash, + height: blockData.height.toString(), previousBlockHash, - transactionsHash: Field(blockData.transactionsHash), + transactionsHash: blockData.transactionsHash, transactions: blockData.txs, }; } diff --git a/packages/sdk/src/graphql/GraphqlNetworkStateTransportModule.ts b/packages/sdk/src/graphql/GraphqlNetworkStateTransportModule.ts index eb406a5aa..17b690c74 100644 --- a/packages/sdk/src/graphql/GraphqlNetworkStateTransportModule.ts +++ b/packages/sdk/src/graphql/GraphqlNetworkStateTransportModule.ts @@ -3,7 +3,7 @@ import { NetworkStateTransportModule, AppChainModule, } from "@proto-kit/sequencer"; -import { NetworkState } from "@proto-kit/protocol"; +import { NetworkStateJson } from "@proto-kit/protocol"; import { gql } from "@urql/core"; import { GraphqlClient } from "./GraphqlClient"; @@ -28,7 +28,7 @@ export class GraphqlNetworkStateTransportModule super(); } - private async retrieveNetworkState(path: string): Promise { + private async retrieveNetworkState(path: string): Promise { const query = gql` query NetworkState { network { @@ -57,8 +57,8 @@ export class GraphqlNetworkStateTransportModule } try { - // eslint-disable-next-line @typescript-eslint/no-unsafe-argument - return new NetworkState(NetworkState.fromJSON(json)); + // eslint-disable-next-line @typescript-eslint/consistent-type-assertions + return json as NetworkStateJson; } catch (e) { if (e instanceof Error) { throw errors.receivedResultMalformed(e.message); @@ -69,15 +69,15 @@ export class GraphqlNetworkStateTransportModule throw new Error(queryResult.error.message); } - public async getProvenNetworkState(): Promise { + public async getProvenNetworkState(): Promise { return await this.retrieveNetworkState("proven"); } - public async getStagedNetworkState(): Promise { + public async getStagedNetworkState(): Promise { return await this.retrieveNetworkState("staged"); } - public async getUnprovenNetworkState(): Promise { + public async getUnprovenNetworkState(): Promise { return await this.retrieveNetworkState("unproven"); } } diff --git a/packages/sdk/src/graphql/GraphqlTransactionSender.ts b/packages/sdk/src/graphql/GraphqlTransactionSender.ts index 6bf97b774..d1e649cc7 100644 --- a/packages/sdk/src/graphql/GraphqlTransactionSender.ts +++ b/packages/sdk/src/graphql/GraphqlTransactionSender.ts @@ -1,5 +1,8 @@ import { inject, injectable } from "tsyringe"; -import { PendingTransaction, AppChainModule } from "@proto-kit/sequencer"; +import { + AppChainModule, + PendingTransactionJSONType, +} from "@proto-kit/sequencer"; import { gql } from "@urql/core"; import { TransactionSender } from "../transaction/InMemoryTransactionSender"; @@ -17,13 +20,13 @@ export class GraphqlTransactionSender super(); } - public async send(transaction: PendingTransaction): Promise { + public async send(transaction: PendingTransactionJSONType): Promise { const query = gql` mutation SubmitTx($tx: TransactionObjectInput!) { submitTx(tx: $tx) } `; - const tx = transaction.toJSON(); + const tx = transaction; const queryResult = await this.graphqlClient.client .mutation(query, { tx }) diff --git a/packages/sdk/src/query/BlockStorageNetworkStateModule.ts b/packages/sdk/src/query/BlockStorageNetworkStateModule.ts index 274037c14..a5fcec1d3 100644 --- a/packages/sdk/src/query/BlockStorageNetworkStateModule.ts +++ b/packages/sdk/src/query/BlockStorageNetworkStateModule.ts @@ -6,7 +6,7 @@ import { BatchStorage, AppChainModule, } from "@proto-kit/sequencer"; -import { NetworkState } from "@proto-kit/protocol"; +import { NetworkStateJson } from "@proto-kit/protocol"; import { ModuleContainerLike } from "@proto-kit/common"; @injectable() @@ -37,7 +37,9 @@ export class BlockStorageNetworkStateModule ); } - public async getUnprovenNetworkState(): Promise { + public async getUnprovenNetworkState(): Promise< + NetworkStateJson | undefined + > { const latestBlock = await this.unprovenStorage.getLatestBlock(); return latestBlock?.block.networkState.during; } @@ -46,12 +48,12 @@ export class BlockStorageNetworkStateModule * Staged network state is the networkstate after the latest unproven block * with afterBundle() hooks executed */ - public async getStagedNetworkState(): Promise { + public async getStagedNetworkState(): Promise { const result = await this.unprovenStorage.getLatestBlock(); return result?.result.afterNetworkState; } - public async getProvenNetworkState(): Promise { + public async getProvenNetworkState(): Promise { const batch = await this.provenStorage.getLatestBatch(); if (batch !== undefined) { diff --git a/packages/sdk/src/query/InMemoryBlockExplorer.ts b/packages/sdk/src/query/InMemoryBlockExplorer.ts index 0838eb2ae..96915fe8a 100644 --- a/packages/sdk/src/query/InMemoryBlockExplorer.ts +++ b/packages/sdk/src/query/InMemoryBlockExplorer.ts @@ -64,27 +64,26 @@ export class InMemoryBlockExplorer const clientTransactions: ClientTransaction[] = block.transactions.map( (txResult) => ({ tx: { - hash: txResult.tx.hash().toString(), - methodId: txResult.tx.methodId.toString(), - nonce: txResult.tx.nonce.toString(), - sender: txResult.tx.sender.toBase58(), - argsFields: txResult.tx.argsFields.map((f) => f.toString()), + hash: txResult.tx.hash, + methodId: txResult.tx.methodId, + nonce: txResult.tx.nonce, + sender: txResult.tx.sender, + argsFields: txResult.tx.argsFields, auxiliaryData: txResult.tx.auxiliaryData, - signature: { - r: txResult.tx.signature.r.toString(), - // eslint-disable-next-line @typescript-eslint/no-base-to-string - s: txResult.tx.signature.s.toString(), - }, + signature: txResult.tx.signature, isMessage: txResult.tx.isMessage, }, - status: txResult.status.toBoolean(), + status: txResult.status, statusMessage: txResult.statusMessage, }) ); return { hash: block.hash, - previousBlockHash: block.previousBlockHash, + previousBlockHash: + block.previousBlockHash !== undefined + ? block.previousBlockHash + : undefined, height: block.height, transactions: clientTransactions, transactionsHash: block.transactionsHash, diff --git a/packages/sdk/src/transaction/AppChainTransaction.ts b/packages/sdk/src/transaction/AppChainTransaction.ts index 496772e17..1d9f30d08 100644 --- a/packages/sdk/src/transaction/AppChainTransaction.ts +++ b/packages/sdk/src/transaction/AppChainTransaction.ts @@ -47,6 +47,7 @@ export class AppChainTransaction { public async send() { this.hasPendingTransaction(this.transaction); - await this.transactionSender.send(this.transaction); + // Wait for next commit + await this.transactionSender.send(this.transaction.toJSON()); } } diff --git a/packages/sdk/src/transaction/InMemoryTransactionSender.ts b/packages/sdk/src/transaction/InMemoryTransactionSender.ts index b3bb74981..96dc1ae69 100644 --- a/packages/sdk/src/transaction/InMemoryTransactionSender.ts +++ b/packages/sdk/src/transaction/InMemoryTransactionSender.ts @@ -1,13 +1,13 @@ import { PrivateMempool, - PendingTransaction, AppChainModule, + PendingTransactionJSONType, } from "@proto-kit/sequencer"; import { inject, injectable } from "tsyringe"; import { ModuleContainerLike } from "@proto-kit/common"; export interface TransactionSender extends AppChainModule { - send: (transaction: PendingTransaction) => Promise; + send: (transaction: PendingTransactionJSONType) => Promise; } @injectable() @@ -25,7 +25,7 @@ export class InMemoryTransactionSender this.mempool = this.sequencer.resolveOrFail("Mempool", PrivateMempool); } - public async send(transaction: PendingTransaction) { + public async send(transaction: PendingTransactionJSONType) { await this.mempool.add(transaction); } } diff --git a/packages/sdk/test/TestingAppChain.test.ts b/packages/sdk/test/TestingAppChain.test.ts index f07a6c21c..2f67764a5 100644 --- a/packages/sdk/test/TestingAppChain.test.ts +++ b/packages/sdk/test/TestingAppChain.test.ts @@ -147,7 +147,7 @@ describe("testing app chain", () => { Provable.log("block", block); - expect(block?.transactions[0].status.toBoolean()).toBe(true); + expect(block?.transactions[0].status).toBe(true); /** * Observe new state after the block has been produced diff --git a/packages/sdk/test/blockProof/blockProof.test.ts b/packages/sdk/test/blockProof/blockProof.test.ts index 2bc920780..21fed17b6 100644 --- a/packages/sdk/test/blockProof/blockProof.test.ts +++ b/packages/sdk/test/blockProof/blockProof.test.ts @@ -137,7 +137,7 @@ describe.skip("blockProof", () => { }) ); - expect(block?.transactions[0].status.toBoolean()).toBe(true); + expect(block?.transactions[0].status).toBe(true); expect(aliceBalance?.toBigInt()).toBe(1000n); }, 120_000); }); diff --git a/packages/sdk/test/fee-hook-sts-regression.test.ts b/packages/sdk/test/fee-hook-sts-regression.test.ts index 69a47d39d..9c0a837ea 100644 --- a/packages/sdk/test/fee-hook-sts-regression.test.ts +++ b/packages/sdk/test/fee-hook-sts-regression.test.ts @@ -167,7 +167,7 @@ describe("testing app chain", () => { console.log("block2", block2); - expect(block2?.transactions[0].status.toBoolean()).toBe(true); + expect(block2?.transactions[0].status).toBe(true); /** * Observe new state after the block has been produced diff --git a/packages/sdk/test/networkstate/NetworkState.test.ts b/packages/sdk/test/networkstate/NetworkState.test.ts index f2b812a6e..428dba6ab 100644 --- a/packages/sdk/test/networkstate/NetworkState.test.ts +++ b/packages/sdk/test/networkstate/NetworkState.test.ts @@ -121,7 +121,7 @@ describe.skip("block production", () => { expectDefined(block2); expect(block2.transactions).toHaveLength(1); - expect(block2!.transactions[0].status.toBoolean()).toBe(true); + expect(block2!.transactions[0].status).toBe(true); }, 60000); it("should produce a valid block", async () => { @@ -138,7 +138,7 @@ describe.skip("block production", () => { await tx.send(); const [block, batch] = await blockTrigger.produceBlockAndBatch(); - expect(block!.transactions[0].status.toBoolean()).toBe(true); + expect(block!.transactions[0].status).toBe(true); expectDefined(batch); const publicOutput = BlockProverPublicOutput.fromFields( @@ -155,7 +155,7 @@ describe.skip("block production", () => { await tx2.send(); const [block2, batch2] = await blockTrigger.produceBlockAndBatch(); - expect(block2!.transactions[0].status.toBoolean()).toBe(true); + expect(block2!.transactions[0].status).toBe(true); expectDefined(batch2); const publicOutput2 = BlockProverPublicOutput.fromFields( @@ -172,6 +172,6 @@ describe.skip("block production", () => { await tx3.send(); const [block3] = await blockTrigger.produceBlockAndBatch(); - expect(block3!.transactions[0].status.toBoolean()).toBe(false); + expect(block3!.transactions[0].status).toBe(false); }, 60_000); }); diff --git a/packages/sdk/test/parameters.test.ts b/packages/sdk/test/parameters.test.ts index 9212d8420..938999caf 100644 --- a/packages/sdk/test/parameters.test.ts +++ b/packages/sdk/test/parameters.test.ts @@ -146,6 +146,6 @@ describe("parameters", () => { const block = await appChain.produceBlock(); expectDefined(block); - expect(block.transactions[0].status.toBoolean()).toBe(true); + expect(block.transactions[0].status).toBe(true); }, 60_000); }); diff --git a/packages/sequencer/src/helpers/query/BlockExplorerTransportModule.ts b/packages/sequencer/src/helpers/query/BlockExplorerTransportModule.ts index 67e002cb9..61c1c2538 100644 --- a/packages/sequencer/src/helpers/query/BlockExplorerTransportModule.ts +++ b/packages/sequencer/src/helpers/query/BlockExplorerTransportModule.ts @@ -1,5 +1,3 @@ -import { Field } from "o1js"; - export interface ClientTransaction { tx: { hash: string; @@ -19,11 +17,11 @@ export interface ClientTransaction { } export interface ClientBlock { - hash: Field; - previousBlockHash: Field | undefined; - height: Field; + hash: string; + previousBlockHash: string | undefined; + height: string; transactions: ClientTransaction[]; - transactionsHash: Field; + transactionsHash: string; } export interface BlockExplorerTransportModule { diff --git a/packages/sequencer/src/helpers/query/NetworkStateQuery.ts b/packages/sequencer/src/helpers/query/NetworkStateQuery.ts index c11e2d6f1..878be0916 100644 --- a/packages/sequencer/src/helpers/query/NetworkStateQuery.ts +++ b/packages/sequencer/src/helpers/query/NetworkStateQuery.ts @@ -1,4 +1,4 @@ -import { NetworkState } from "@proto-kit/protocol"; +import { NetworkStateJson } from "@proto-kit/protocol"; import { NetworkStateTransportModule } from "./NetworkStateTransportModule"; @@ -7,15 +7,15 @@ export class NetworkStateQuery { private readonly transportModule: NetworkStateTransportModule ) {} - public get unproven(): Promise { + public get unproven(): Promise { return this.transportModule.getUnprovenNetworkState(); } - public get stagedUnproven(): Promise { + public get stagedUnproven(): Promise { return this.transportModule.getStagedNetworkState(); } - public get proven(): Promise { + public get proven(): Promise { return this.transportModule.getProvenNetworkState(); } } diff --git a/packages/sequencer/src/helpers/query/NetworkStateTransportModule.ts b/packages/sequencer/src/helpers/query/NetworkStateTransportModule.ts index 1a6cd91d4..9e01c5df9 100644 --- a/packages/sequencer/src/helpers/query/NetworkStateTransportModule.ts +++ b/packages/sequencer/src/helpers/query/NetworkStateTransportModule.ts @@ -1,7 +1,7 @@ -import { NetworkState } from "@proto-kit/protocol"; +import { NetworkStateJson } from "@proto-kit/protocol"; export interface NetworkStateTransportModule { - getUnprovenNetworkState: () => Promise; - getStagedNetworkState: () => Promise; - getProvenNetworkState: () => Promise; + getUnprovenNetworkState: () => Promise; + getStagedNetworkState: () => Promise; + getProvenNetworkState: () => Promise; } diff --git a/packages/sequencer/src/helpers/utils.ts b/packages/sequencer/src/helpers/utils.ts index 686613d52..e57c3db1e 100644 --- a/packages/sequencer/src/helpers/utils.ts +++ b/packages/sequencer/src/helpers/utils.ts @@ -194,3 +194,17 @@ export class PairProofTaskSerializer ); } } + +/** + * Type for serialized {@link Field}. + */ +export type FieldString = string; +/** + * + * @param value Value to be converted to {@link Field}. + * @returns + */ +// eslint-disable-next-line @typescript-eslint/no-redeclare +export const FieldString = ( + value: number | string | bigint | Field +): FieldString => String(value); diff --git a/packages/sequencer/src/mempool/Mempool.ts b/packages/sequencer/src/mempool/Mempool.ts index 97401a973..028bdf377 100644 --- a/packages/sequencer/src/mempool/Mempool.ts +++ b/packages/sequencer/src/mempool/Mempool.ts @@ -1,6 +1,9 @@ import { EventEmittingComponent } from "@proto-kit/common"; -import type { PendingTransaction } from "./PendingTransaction"; +import type { + PendingTransaction, + PendingTransactionJSONType, +} from "./PendingTransaction"; export type MempoolEvents = { "mempool-transaction-added": [PendingTransaction]; @@ -12,12 +15,12 @@ export interface Mempool * Add a transaction to the mempool * @returns The new commitment to the mempool */ - add: (tx: PendingTransaction) => Promise; + add: (tx: PendingTransactionJSONType) => Promise; /** * Retrieve all transactions that are currently in the mempool */ - getTxs: (limit?: number) => Promise; + getTxs: (limit?: number) => Promise; removeTxs: (included: string[], dropped: string[]) => Promise; } diff --git a/packages/sequencer/src/mempool/PendingTransaction.ts b/packages/sequencer/src/mempool/PendingTransaction.ts index fd1fedfec..09ac55639 100644 --- a/packages/sequencer/src/mempool/PendingTransaction.ts +++ b/packages/sequencer/src/mempool/PendingTransaction.ts @@ -118,7 +118,7 @@ export class UnsignedTransaction implements UnsignedTransactionBody { } } -interface PendingTransactionJSONType { +export interface PendingTransactionJSONType { hash: string; methodId: string; nonce: string; diff --git a/packages/sequencer/src/mempool/private/PrivateMempool.ts b/packages/sequencer/src/mempool/private/PrivateMempool.ts index e750060ea..fcdaf12da 100644 --- a/packages/sequencer/src/mempool/private/PrivateMempool.ts +++ b/packages/sequencer/src/mempool/private/PrivateMempool.ts @@ -10,6 +10,7 @@ import { BlockHashMerkleTree, MandatoryProtocolModulesRecord, NetworkState, + NetworkStateJson, Protocol, ProvableHookBlockState, RuntimeMethodExecutionContext, @@ -19,7 +20,10 @@ import { import { Field } from "o1js"; import type { Mempool, MempoolEvents } from "../Mempool"; -import type { PendingTransaction } from "../PendingTransaction"; +import { + PendingTransaction, + PendingTransactionJSONType, +} from "../PendingTransaction"; import { sequencerModule, SequencerModule, @@ -34,8 +38,8 @@ import { Tracer } from "../../logging/Tracer"; import { trace } from "../../logging/trace"; type MempoolTransactionPaths = { - transaction: PendingTransaction; - paths: Field[]; + transaction: PendingTransactionJSONType; + paths: string[]; }; interface PrivateMempoolConfig { @@ -73,29 +77,30 @@ export class PrivateMempool return txs.length; } - public async add(tx: PendingTransaction): Promise { + public async add(tx: PendingTransactionJSONType): Promise { const [txValid, error] = this.transactionValidator.validateTx(tx); if (txValid) { const success = await this.transactionStorage.pushUserTransaction(tx); if (success) { - this.events.emit("mempool-transaction-added", tx); - log.trace(`Transaction added to mempool: ${tx.hash().toString()}`); - } else { - log.error( - `Transaction ${tx.hash().toString()} rejected: already exists in mempool` + this.events.emit( + "mempool-transaction-added", + PendingTransaction.fromJSON(tx) ); + log.trace(`Transaction added to mempool: ${tx.hash}`); + } else { + log.error(`Transaction ${tx.hash} rejected: already exists in mempool`); } return success; } log.error( - `Validation of tx ${tx.hash().toString()} failed:`, + `Validation of tx ${tx.hash} failed:`, `${error ?? "unknown error"}` ); throw new Error( - `Validation of tx ${tx.hash().toString()} failed: ${error ?? "unknown error"}` + `Validation of tx ${tx.hash} failed: ${error ?? "unknown error"}` ); } @@ -105,7 +110,7 @@ export class PrivateMempool ); } - public async getStagedNetworkState(): Promise { + public async getStagedNetworkState(): Promise { const result = await this.unprovenQueue.getLatestBlock(); return result?.result.afterNetworkState; } @@ -116,14 +121,17 @@ export class PrivateMempool } @trace("mempool.get_txs") - public async getTxs(limit?: number): Promise { + public async getTxs(limit?: number): Promise { // TODO Add limit to the storage (or do something smarter entirely) const txs = await this.transactionStorage.getPendingUserTransactions(); const baseCachedStateService = new CachedStateService(this.stateService); + // Should provide NetworkState to checkTxValid. + const stagedNetworkState = await this.getStagedNetworkState(); + const networkState = - (await this.getStagedNetworkState()) ?? NetworkState.empty(); + stagedNetworkState || NetworkState.toJSON(NetworkState.empty()); const validationEnabled = this.config.validationEnabled ?? false; const sortedTxs = validationEnabled @@ -149,10 +157,10 @@ export class PrivateMempool @trace("mempool.validate_txs") // eslint-disable-next-line sonarjs/cognitive-complexity private async checkTxValid( - transactions: PendingTransaction[], + transactions: PendingTransactionJSONType[], baseService: CachedStateService, stateServiceProvider: StateServiceProvider, - networkState: NetworkState, + networkState: NetworkStateJson, limit?: number ) { const executionContext = container.resolve( @@ -161,10 +169,10 @@ export class PrivateMempool executionContext.clear(); // Initialize starting state - const sortedTransactions: PendingTransaction[] = []; + const sortedTransactions: PendingTransactionJSONType[] = []; const skippedTransactions: Record = {}; - let queue: PendingTransaction[] = [...transactions]; + let queue: PendingTransactionJSONType[] = [...transactions]; const previousBlock = await this.unprovenQueue.getLatestBlock(); @@ -175,29 +183,44 @@ export class PrivateMempool previousBlock?.result.blockHashRoot ?? BlockHashMerkleTree.EMPTY_ROOT ), eternalTransactionsHash: - previousBlock?.block.toEternalTransactionsHash ?? Field(0), - transactionsHash: previousBlock?.block.transactionsHash ?? Field(0), - incomingMessagesHash: previousBlock?.block.toMessagesHash ?? Field(0), + previousBlock?.block.toEternalTransactionsHash !== undefined + ? Field(previousBlock?.block.toEternalTransactionsHash) + : Field(0), + transactionsHash: + previousBlock?.block.transactionsHash !== undefined + ? Field(previousBlock?.block.transactionsHash) + : Field(0), + incomingMessagesHash: + previousBlock?.block.toMessagesHash !== undefined + ? Field(previousBlock?.block.toMessagesHash) + : Field(0), }; + const provableNetworkState = new NetworkState( + NetworkState.fromJSON(networkState) + ); + + let pendingTransaction: PendingTransaction; + while ( queue.length > 0 && sortedTransactions.length < (limit ?? Number.MAX_VALUE) ) { const [tx] = queue.splice(0, 1); + pendingTransaction = PendingTransaction.fromJSON(tx); const txStateService = new CachedStateService(baseService); stateServiceProvider.setCurrentStateService(txStateService); const contextInputs: RuntimeMethodExecutionData = { - networkState: networkState, - transaction: tx.toProtocolTransaction().transaction, + networkState: provableNetworkState, + transaction: pendingTransaction.toProtocolTransaction().transaction, }; executionContext.setup(contextInputs); - const signedTransaction = tx.toProtocolTransaction(); + const signedTransaction = pendingTransaction.toProtocolTransaction(); // eslint-disable-next-line no-await-in-loop await this.accountStateHook.beforeTransaction({ - networkState: networkState, + networkState: provableNetworkState, transaction: signedTransaction.transaction, signature: signedTransaction.signature, prover: proverState, @@ -206,18 +229,18 @@ export class PrivateMempool executionContext.current().result; if (status.toBoolean()) { - log.trace(`Accepted tx ${tx.hash().toString()}`); + log.trace(`Accepted tx ${tx.hash}`); sortedTransactions.push(tx); // eslint-disable-next-line no-await-in-loop await txStateService.applyStateTransitions(stateTransitions); // eslint-disable-next-line no-await-in-loop await txStateService.mergeIntoParent(); - delete skippedTransactions[tx.hash().toString()]; + delete skippedTransactions[tx.hash]; if (Object.entries(skippedTransactions).length > 0) { // eslint-disable-next-line @typescript-eslint/no-loop-func stateTransitions.forEach((st) => { Object.values(skippedTransactions).forEach((value) => { - if (value.paths.some((x) => x.equals(st.path))) { + if (value.paths.some((x) => x === st.path.toString())) { queue.push(value.transaction); } }); @@ -227,33 +250,29 @@ export class PrivateMempool } else { // eslint-disable-next-line no-await-in-loop const removeTxWhen = await this.accountStateHook.removeTransactionWhen({ - networkState: networkState, + networkState: provableNetworkState, transaction: signedTransaction.transaction, signature: signedTransaction.signature, prover: proverState, }); if (removeTxWhen) { // eslint-disable-next-line no-await-in-loop - await this.transactionStorage.removeTx( - [tx.hash().toString()], - "dropped" - ); + await this.transactionStorage.removeTx([tx.hash], "dropped"); log.trace( - `Deleting tx ${tx.hash().toString()} from mempool because removeTransactionWhen condition is satisfied` + `Deleting tx ${tx.hash} from mempool because removeTransactionWhen condition is satisfied` ); // eslint-disable-next-line no-continue continue; } - log.trace( - `Skipped tx ${tx.hash().toString()} because ${statusMessage}` - ); - if (!(tx.hash().toString() in skippedTransactions)) { - skippedTransactions[tx.hash().toString()] = { + log.trace(`Skipped tx ${tx.hash} because ${statusMessage}`); + if (!(tx.hash in skippedTransactions)) { + skippedTransactions[tx.hash] = { transaction: tx, paths: stateTransitions .map((x) => x.path) - .filter((id, idx, arr) => arr.indexOf(id) === idx), + .filter((id, idx, arr) => arr.indexOf(id) === idx) + .map((f) => f.toString()), }; } stateServiceProvider.popCurrentStateService(); diff --git a/packages/sequencer/src/mempool/verification/TransactionValidator.ts b/packages/sequencer/src/mempool/verification/TransactionValidator.ts index c839ecece..665b99511 100644 --- a/packages/sequencer/src/mempool/verification/TransactionValidator.ts +++ b/packages/sequencer/src/mempool/verification/TransactionValidator.ts @@ -5,7 +5,10 @@ import { RuntimeModulesRecord, } from "@proto-kit/module"; -import { PendingTransaction } from "../PendingTransaction"; +import { + PendingTransaction, + PendingTransactionJSONType, +} from "../PendingTransaction"; @injectable() export class TransactionValidator { @@ -13,14 +16,14 @@ export class TransactionValidator { @inject("Runtime") private readonly runtime: Runtime ) {} - private validateMethod(tx: PendingTransaction): string | undefined { + private validateMethod(tx: PendingTransactionJSONType): string | undefined { // Check if method exists // We don't actually need to use runtime.getMethodById here, bcs the // module name validation happens inside getMethodNameFromId // and also in the next step const methodPath = this.runtime.methodIdResolver.getMethodNameFromId( - tx.methodId.toBigInt() + tx.methodId ); if (methodPath === undefined) { @@ -38,16 +41,20 @@ export class TransactionValidator { return undefined; } - public validateTx(tx: PendingTransaction): [boolean, string | undefined] { + public validateTx( + tx: PendingTransactionJSONType + ): [boolean, string | undefined] { const methodError = this.validateMethod(tx); if (methodError !== undefined) { return [false, methodError]; } - const validSignature = tx.signature.verify( - tx.sender, - tx.getSignatureData() + const transaction = PendingTransaction.fromJSON(tx); + + const validSignature = transaction.signature.verify( + transaction.sender, + transaction.getSignatureData() ); if (!validSignature.toBoolean()) { diff --git a/packages/sequencer/src/protocol/baselayer/NoopBaseLayer.ts b/packages/sequencer/src/protocol/baselayer/NoopBaseLayer.ts index 9e64f6996..cc256a25a 100644 --- a/packages/sequencer/src/protocol/baselayer/NoopBaseLayer.ts +++ b/packages/sequencer/src/protocol/baselayer/NoopBaseLayer.ts @@ -7,7 +7,7 @@ import { sequencerModule, } from "../../sequencer/builder/SequencerModule"; import { IncomingMessageAdapter } from "../../settlement/messages/IncomingMessageAdapter"; -import { PendingTransaction } from "../../mempool/PendingTransaction"; +import { PendingTransactionJSONType } from "../../mempool/PendingTransaction"; import { OutgoingMessageAdapter } from "../../settlement/messages/outgoing/OutgoingMessageCollector"; import { Block } from "../../storage/model/Block"; @@ -23,7 +23,7 @@ class NoopIncomingMessageAdapter implements IncomingMessageAdapter { ): Promise<{ from: string; to: string; - messages: PendingTransaction[]; + messages: PendingTransactionJSONType[]; }> { return { from: "0", diff --git a/packages/sequencer/src/protocol/production/BatchProducerModule.ts b/packages/sequencer/src/protocol/production/BatchProducerModule.ts index 545190624..9208bed58 100644 --- a/packages/sequencer/src/protocol/production/BatchProducerModule.ts +++ b/packages/sequencer/src/protocol/production/BatchProducerModule.ts @@ -2,7 +2,7 @@ import { inject } from "tsyringe"; import { BlockProverPublicInput, BlockProverPublicOutput, - NetworkState, + NetworkStateJson, } from "@proto-kit/protocol"; import { Field, Proof } from "o1js"; import { log, noop } from "@proto-kit/common"; @@ -141,7 +141,7 @@ export class BatchProducerModule extends SequencerModule { ): Promise { const batch = await this.computeBatch(blocks, height); - const blockHashes = blocks.map((bundle) => bundle.block.hash.toString()); + const blockHashes = blocks.map((bundle) => bundle.block.hash); const jsonProof = this.blockProofSerializer .getBlockProofSerializer() @@ -179,8 +179,8 @@ export class BatchProducerModule extends SequencerModule { ): Promise<{ proof: Proof; changes: CachedLinkedLeafStore; - fromNetworkState: NetworkState; - toNetworkState: NetworkState; + fromNetworkState: NetworkStateJson; + toNetworkState: NetworkStateJson; }> { if (blocks.length === 0 || blocks.flat(1).length === 0) { throw errors.blockWithoutTxs(); diff --git a/packages/sequencer/src/protocol/production/helpers/UntypedStateTransition.ts b/packages/sequencer/src/protocol/production/helpers/UntypedStateTransition.ts index 425f01445..36d72332f 100644 --- a/packages/sequencer/src/protocol/production/helpers/UntypedStateTransition.ts +++ b/packages/sequencer/src/protocol/production/helpers/UntypedStateTransition.ts @@ -3,6 +3,20 @@ import { ProvableStateTransition, StateTransition } from "@proto-kit/protocol"; import { UntypedOption } from "./UntypedOption"; +export interface UntypedStateTransitionJson { + path: string; + from: { + isSome: boolean; + isForcedSome: boolean; + value: string[]; + }; + to: { + isSome: boolean; + isForcedSome: boolean; + value: string[]; + }; +} + /** * Generic state transition that constraints the current method circuit * to external state, by providing a state anchor. diff --git a/packages/sequencer/src/protocol/production/sequencing/BlockProducerModule.ts b/packages/sequencer/src/protocol/production/sequencing/BlockProducerModule.ts index 8f035c338..3f15e0366 100644 --- a/packages/sequencer/src/protocol/production/sequencing/BlockProducerModule.ts +++ b/packages/sequencer/src/protocol/production/sequencing/BlockProducerModule.ts @@ -6,7 +6,7 @@ import { Runtime, RuntimeModulesRecord, } from "@proto-kit/module"; -import { Provable } from "o1js"; +import { Field, Provable } from "o1js"; import { Mempool } from "../../../mempool/Mempool"; import { @@ -14,7 +14,7 @@ import { SequencerModule, } from "../../../sequencer/builder/SequencerModule"; import { BlockQueue } from "../../../storage/repositories/BlockStorage"; -import { PendingTransaction } from "../../../mempool/PendingTransaction"; +import { PendingTransactionJSONType } from "../../../mempool/PendingTransaction"; import { AsyncMerkleTreeStore } from "../../../state/async/AsyncMerkleTreeStore"; import { AsyncStateService } from "../../../state/async/AsyncStateService"; import { @@ -74,7 +74,7 @@ export class BlockProducerModule extends SequencerModule { private prettyPrintBlockContents(block: Block) { block.transactions.forEach((tx, i) => { const methodName = this.methodIdResolver.getMethodNameFromId( - tx.tx.methodId.toBigInt() + tx.tx.methodId ); if (!methodName) return; @@ -86,22 +86,20 @@ export class BlockProducerModule extends SequencerModule { log.info("---------------------------------------"); log.info(`Transaction #${i}`); - log.info( - "Sender:", - tx.tx.sender.toBase58(), - "Nonce:", - tx.tx.nonce.toBigInt() - ); + log.info("Sender:", tx.tx.sender, "Nonce:", tx.tx.nonce); log.info(`Method: ${methodName?.join(".")}`); log.info(); if (log.getLevel() <= log.levels.INFO) { Provable.log( "Arguments:", - paramEncoder.decode(tx.tx.argsFields, tx.tx.auxiliaryData) + paramEncoder.decode( + tx.tx.argsFields.map((s) => Field(s)), + tx.tx.auxiliaryData + ) ); } log.info( - `Status: ${tx.status.toBoolean()}`, + `Status: ${tx.status ? "true" : "false"}`, tx.statusMessage !== undefined ? `Reason: ${tx.statusMessage}` : "" ); }); @@ -110,10 +108,10 @@ export class BlockProducerModule extends SequencerModule { } } - @trace("block.result", ([block]) => ({ height: block.height.toString() })) + @trace("block.result", ([block]) => ({ height: block.height })) public async generateMetadata(block: Block): Promise { const traceMetadata = { - height: block.height.toString(), + height: block.height, }; const { result, blockHashTreeStore, treeStore, stateService } = @@ -155,7 +153,7 @@ export class BlockProducerModule extends SequencerModule { } log.info( - `Produced block #${block.height.toBigInt()} (${block.transactions.length} txs)` + `Produced block #${block.height} (${block.transactions.length} txs)` ); this.prettyPrintBlockContents(block); @@ -177,7 +175,7 @@ export class BlockProducerModule extends SequencerModule { // Idea: Create a service that aggregates a bunch of different sources @trace("block.collect_inputs") private async collectProductionData(): Promise<{ - txs: PendingTransaction[]; + txs: PendingTransactionJSONType[]; metadata: BlockWithResult; }> { const txs = await this.mempool.getTxs(this.maximumBlockSize()); @@ -193,7 +191,7 @@ export class BlockProducerModule extends SequencerModule { metadata = BlockWithResult.createEmpty(); } else if (parentBlock.result === undefined) { throw new Error( - `Metadata for block at height ${parentBlock.block.height.toString()} not available` + `Metadata for block at height ${parentBlock.block.height} not available` ); } else { metadata = { @@ -203,7 +201,7 @@ export class BlockProducerModule extends SequencerModule { }; } - let messages: PendingTransaction[] = []; + let messages: PendingTransactionJSONType[] = []; if (this.messageService !== undefined) { messages = await this.messageService.getPendingMessages(); } @@ -248,7 +246,7 @@ export class BlockProducerModule extends SequencerModule { await this.blockQueue.pushBlock(block); }), { - height: block.height.toString(), + height: block.height, } ); diff --git a/packages/sequencer/src/protocol/production/sequencing/BlockProductionService.ts b/packages/sequencer/src/protocol/production/sequencing/BlockProductionService.ts index 18010fa28..f342cfa08 100644 --- a/packages/sequencer/src/protocol/production/sequencing/BlockProductionService.ts +++ b/packages/sequencer/src/protocol/production/sequencing/BlockProductionService.ts @@ -20,14 +20,15 @@ import { match } from "ts-pattern"; import { Block, BlockWithResult, - TransactionExecutionResult, + TransactionExecutionResultJson, } from "../../../storage/model/Block"; import { CachedStateService } from "../../../state/state/CachedStateService"; -import { PendingTransaction } from "../../../mempool/PendingTransaction"; +import { PendingTransactionJSONType } from "../../../mempool/PendingTransaction"; import { AsyncStateService } from "../../../state/async/AsyncStateService"; import { UntypedStateTransition } from "../helpers/UntypedStateTransition"; import { Tracer } from "../../../logging/Tracer"; import { trace } from "../../../logging/trace"; +import { FieldString } from "../../../helpers/utils"; import { BlockTrackers, @@ -38,7 +39,7 @@ import { function isIncludedTxs( x: TransactionExecutionResultStatus -): x is { status: "included"; result: TransactionExecutionResult } { +): x is { status: "included"; result: TransactionExecutionResultJson } { return x.status === "included"; } @@ -98,7 +99,7 @@ export class BlockProductionService { */ public async createBlock( asyncStateService: AsyncStateService, - transactions: PendingTransaction[], + transactions: PendingTransactionJSONType[], lastBlockWithResult: BlockWithResult, allowEmptyBlocks: boolean ): Promise< @@ -120,16 +121,18 @@ export class BlockProductionService { const blockState: BlockTrackers = { blockHashRoot: Field(lastResult.blockHashRoot), eternalTransactionsList: new TransactionHashList( - lastBlock.toEternalTransactionsHash + Field(lastBlock.toEternalTransactionsHash) ), transactionList: new TransactionHashList(), - incomingMessages: new MinaActionsHashList(lastBlock.toMessagesHash), + incomingMessages: new MinaActionsHashList( + Field(lastBlock.toMessagesHash) + ), }; // Get used networkState by executing beforeBlock() hooks const beforeHookResult = await this.executeBeforeBlockHook( toProvableHookBlockState(blockState), - lastResult.afterNetworkState, + new NetworkState(NetworkState.fromJSON(lastResult.afterNetworkState)), stateService ); @@ -149,7 +152,9 @@ export class BlockProductionService { ); const previousBlockHash = - lastResult.blockHash === 0n ? undefined : Field(lastResult.blockHash); + lastResult.blockHash === "0" + ? undefined + : FieldString(lastResult.blockHash); if (executionResults.length === 0 && !allowEmptyBlocks) { log.info( @@ -164,33 +169,37 @@ export class BlockProductionService { const block: Omit = { transactions: includedTransactions, - transactionsHash: newBlockState.transactionList.commitment, + transactionsHash: FieldString(newBlockState.transactionList.commitment), fromEternalTransactionsHash: lastBlock.toEternalTransactionsHash, - toEternalTransactionsHash: - newBlockState.eternalTransactionsList.commitment, + toEternalTransactionsHash: FieldString( + newBlockState.eternalTransactionsList.commitment + ), height: - lastBlock.hash.toBigInt() !== 0n ? lastBlock.height.add(1) : Field(0), - fromBlockHashRoot: Field(lastResult.blockHashRoot), + FieldString(lastBlock.hash) !== "0" + ? FieldString(BigInt(lastBlock.height) + 1n) + : FieldString(0), + fromBlockHashRoot: FieldString(lastResult.blockHashRoot), fromMessagesHash: lastBlock.toMessagesHash, - fromStateRoot: Field(lastResult.stateRoot), - toMessagesHash: newBlockState.incomingMessages.commitment, + fromStateRoot: FieldString(lastResult.stateRoot), + toMessagesHash: FieldString(newBlockState.incomingMessages.commitment), previousBlockHash, networkState: { - before: new NetworkState(lastResult.afterNetworkState), - during: networkState, + before: lastResult.afterNetworkState, + during: NetworkState.toJSON(networkState), }, - beforeBlockStateTransitions, + beforeBlockStateTransitions: beforeBlockStateTransitions.map( + (st: UntypedStateTransition) => st.toJSON() + ), }; - const hash = Block.hash(block); + const hash = Block.hash(block).toString(); const includedTxs = executionResults.map((x) => { const txHash = match(x) .with({ status: "included" }, ({ result }) => result.tx) - .otherwise(({ tx }) => tx) - .hash() - .toString(); + .otherwise(({ tx }) => tx).hash; + return { hash: txHash, type: x.status, diff --git a/packages/sequencer/src/protocol/production/sequencing/BlockResultService.ts b/packages/sequencer/src/protocol/production/sequencing/BlockResultService.ts index 5419344bb..b598a2a46 100644 --- a/packages/sequencer/src/protocol/production/sequencing/BlockResultService.ts +++ b/packages/sequencer/src/protocol/production/sequencing/BlockResultService.ts @@ -3,6 +3,7 @@ import { LinkedMerkleTree } from "@proto-kit/common"; import { AfterBlockHookArguments, BlockHashMerkleTree, + BlockHashMerkleTreeWitness, BlockHashTreeEntry, MandatoryProtocolModulesRecord, NetworkState, @@ -17,11 +18,14 @@ import { inject, injectable, Lifecycle, scoped } from "tsyringe"; import { Block, BlockResult, - TransactionExecutionResult, + TransactionExecutionResultJson, } from "../../../storage/model/Block"; import { AsyncMerkleTreeStore } from "../../../state/async/AsyncMerkleTreeStore"; import { CachedMerkleTreeStore } from "../../../state/merkle/CachedMerkleTreeStore"; -import { UntypedStateTransition } from "../helpers/UntypedStateTransition"; +import { + UntypedStateTransition, + UntypedStateTransitionJson, +} from "../helpers/UntypedStateTransition"; import { CachedStateService } from "../../../state/state/CachedStateService"; import { AsyncStateService } from "../../../state/async/AsyncStateService"; import type { StateRecord } from "../BatchProducerModule"; @@ -29,6 +33,7 @@ import { trace } from "../../../logging/trace"; import { Tracer } from "../../../logging/Tracer"; import { AsyncLinkedLeafStore } from "../../../state/async/AsyncLinkedLeafStore"; import { CachedLinkedLeafStore } from "../../../state/lmt/CachedLinkedLeafStore"; +import { FieldString } from "../../../helpers/utils"; import { executeWithExecutionContext } from "./TransactionExecutionService"; @@ -36,19 +41,16 @@ import { executeWithExecutionContext } from "./TransactionExecutionService"; function collectOrderedStateDiff( stateTransitions: UntypedStateTransition[] ): StateRecord { - return stateTransitions.reduce>( - (state, st) => { - if (st.toValue.isSome.toBoolean()) { - state[st.path.toString()] = st.toValue.value; - } - return state; - }, - {} - ); + return stateTransitions.reduce((state, st) => { + if (st.toValue.isSome.toBoolean()) { + state[st.path.toString()] = st.toValue.value; + } + return state; + }, {}); } function createCombinedOrderedStateDiff( - transactions: TransactionExecutionResult[], + transactions: TransactionExecutionResultJson[], blockHookSTs: UntypedStateTransition[] ) { // Flatten diff list into a single diff by applying them over each other @@ -56,7 +58,9 @@ function createCombinedOrderedStateDiff( .map((tx) => { const transitions = tx.stateTransitions .filter(({ applied }) => applied) - .flatMap(({ stateTransitions }) => stateTransitions); + .flatMap(({ stateTransitions }) => + stateTransitions.map((st) => UntypedStateTransition.fromJSON(st)) + ); transitions.push(...blockHookSTs); @@ -131,21 +135,21 @@ export class BlockResultService { ); // TODO This can be optimized a lot (we are only interested in the root at this step) - await blockHashInMemoryStore.preloadKey(block.height.toBigInt()); + await blockHashInMemoryStore.preloadKey(BigInt(block.height)); const blockHashTree = new BlockHashMerkleTree(blockHashInMemoryStore); blockHashTree.setLeaf( - block.height.toBigInt(), + BigInt(block.height), new BlockHashTreeEntry({ block: { - index: block.height, - transactionListHash: block.transactionsHash, + index: Field(block.height), + transactionListHash: Field(block.transactionsHash), }, closed: Bool(true), }).hash() ); - const blockHashWitness = blockHashTree.getWitness(block.height.toBigInt()); + const blockHashWitness = blockHashTree.getWitness(BigInt(block.height)); const newBlockHashRoot = blockHashTree.getRoot(); return { @@ -184,7 +188,7 @@ export class BlockResultService { } @trace("block.result.generate", ([block]) => ({ - height: block.height.toString(), + height: block.height, })) public async generateMetadataForNextBlock( block: Block, @@ -199,7 +203,9 @@ export class BlockResultService { }> { const combinedDiff = createCombinedOrderedStateDiff( block.transactions, - block.beforeBlockStateTransitions + block.beforeBlockStateTransitions.map((st: UntypedStateTransitionJson) => + UntypedStateTransition.fromJSON(st) + ) ); const inMemoryStore = await CachedLinkedLeafStore.new(merkleTreeStore); @@ -218,11 +224,11 @@ export class BlockResultService { { blockHashRoot, stateRoot: witnessedStateRoot, - incomingMessagesHash: block.toMessagesHash, - transactionsHash: block.transactionsHash, - eternalTransactionsHash: block.toEternalTransactionsHash, + incomingMessagesHash: Field(block.toMessagesHash), + transactionsHash: Field(block.transactionsHash), + eternalTransactionsHash: Field(block.toEternalTransactionsHash), }, - block.networkState.during, + new NetworkState(NetworkState.fromJSON(block.networkState.during)), stateService ); @@ -240,17 +246,17 @@ export class BlockResultService { return { result: { - afterNetworkState: methodResult, + afterNetworkState: NetworkState.toJSON(methodResult), // This is the state root after the last tx and the afterBlock hook - stateRoot: stateRoot.toBigInt(), - witnessedRoots: [witnessedStateRoot.toBigInt()], - blockHashRoot: blockHashRoot.toBigInt(), - blockHashWitness, + stateRoot: FieldString(stateRoot), + witnessedRoots: [FieldString(witnessedStateRoot)], + blockHashRoot: FieldString(blockHashRoot), + blockHashWitness: BlockHashMerkleTreeWitness.toJSON(blockHashWitness), afterBlockStateTransitions: stateTransitions.map((st) => - UntypedStateTransition.fromStateTransition(st) + UntypedStateTransition.fromStateTransition(st).toJSON() ), - blockHash: block.hash.toBigInt(), + blockHash: FieldString(block.hash), }, treeStore: inMemoryStore, blockHashTreeStore: cachedBlockHashTreeStore, diff --git a/packages/sequencer/src/protocol/production/sequencing/TransactionExecutionService.ts b/packages/sequencer/src/protocol/production/sequencing/TransactionExecutionService.ts index 24dc9852a..1aa48733f 100644 --- a/packages/sequencer/src/protocol/production/sequencing/TransactionExecutionService.ts +++ b/packages/sequencer/src/protocol/production/sequencing/TransactionExecutionService.ts @@ -35,11 +35,15 @@ import { // eslint-disable-next-line import/no-extraneous-dependencies import zip from "lodash/zip"; -import { PendingTransaction } from "../../../mempool/PendingTransaction"; +import { + PendingTransaction, + PendingTransactionJSONType, +} from "../../../mempool/PendingTransaction"; import { CachedStateService } from "../../../state/state/CachedStateService"; import { StateTransitionBatch, - TransactionExecutionResult, + TransactionExecutionResultJson, + STBatchToJson, } from "../../../storage/model/Block"; import { UntypedStateTransition } from "../helpers/UntypedStateTransition"; import { trace } from "../../../logging/trace"; @@ -79,7 +83,7 @@ function getAreProofsEnabledFromModule( } async function decodeTransaction( - tx: PendingTransaction, + tx: PendingTransactionJSONType, runtime: Runtime ): Promise<{ method: SomeRuntimeMethod; @@ -87,13 +91,13 @@ async function decodeTransaction( module: RuntimeModule; }> { const methodDescriptors = runtime.methodIdResolver.getMethodNameFromId( - tx.methodId.toBigInt() + tx.methodId ); - const method = runtime.getMethodById(tx.methodId.toBigInt()); + const method = runtime.getMethodById(tx.methodId); if (methodDescriptors === undefined || method === undefined) { - throw errors.methodIdNotFound(tx.methodId.toString()); + throw errors.methodIdNotFound(tx.methodId); } const [moduleName, methodName] = methodDescriptors; @@ -103,7 +107,10 @@ async function decodeTransaction( module, methodName ); - const args = await parameterDecoder.decode(tx.argsFields, tx.auxiliaryData); + const args = await parameterDecoder.decode( + tx.argsFields.map(Field), + tx.auxiliaryData + ); return { method, @@ -198,11 +205,11 @@ function traceLogSTs(msg: string, stateTransitions: StateTransition[]) { export type TransactionExecutionResultStatus = | { - result: TransactionExecutionResult; + result: TransactionExecutionResultJson; status: "included"; } - | { tx: PendingTransaction; status: "skipped" } - | { tx: PendingTransaction; status: "shouldRemove" }; + | { tx: PendingTransactionJSONType; status: "skipped" } + | { tx: PendingTransactionJSONType; status: "shouldRemove" }; @injectable() @scoped(Lifecycle.ContainerScoped) @@ -311,9 +318,10 @@ export class TransactionExecutionService { public addTransactionToBlockProverState( state: BlockTrackers, - tx: PendingTransaction + tx: PendingTransactionJSONType ): BlockTrackers { - const signedTransaction = tx.toProtocolTransaction(); + const signedTransaction = + PendingTransaction.fromJSON(tx).toProtocolTransaction(); // Add tx to commitments return addTransactionToBundle( state, @@ -325,7 +333,7 @@ export class TransactionExecutionService { // eslint-disable-next-line sonarjs/cognitive-complexity public async createExecutionTraces( asyncStateService: CachedStateService, - transactions: PendingTransaction[], + transactions: PendingTransactionJSONType[], networkState: NetworkState, state: BlockTrackers ): Promise<{ @@ -354,10 +362,7 @@ export class TransactionExecutionService { // If the hooks fail AND the tx is not a message (in which case we // have to still execute it), we skip this tx and don't add it to the block - if ( - !executionTrace.hooksStatus.toBoolean() && - !executionTrace.tx.isMessage - ) { + if (!executionTrace.hooksStatus && !executionTrace.tx.isMessage) { const actionMessage = shouldRemove ? "removing as to removeWhen hooks" : "skipping"; @@ -406,14 +411,17 @@ export class TransactionExecutionService { })) public async createExecutionTrace( asyncStateService: CachedStateService, - tx: PendingTransaction, + tx: PendingTransactionJSONType, { networkState, hash: networkStateHash, }: { networkState: NetworkState; hash: Field }, state: BlockTrackers, newState: BlockTrackers - ): Promise<{ result: TransactionExecutionResult; shouldRemove: boolean }> { + ): Promise<{ + result: TransactionExecutionResultJson; + shouldRemove: boolean; + }> { // TODO Use RecordingStateService -> async asProver needed const recordingStateService = new CachedStateService(asyncStateService); @@ -425,7 +433,8 @@ export class TransactionExecutionService { const previousProofsEnabled = appChain.areProofsEnabled; appChain.setProofsEnabled(false); - const signedTransaction = tx.toProtocolTransaction(); + const signedTransaction = + PendingTransaction.fromJSON(tx).toProtocolTransaction(); const runtimeContextInputs = { transaction: signedTransaction.transaction, networkState, @@ -485,7 +494,7 @@ export class TransactionExecutionService { status: runtimeResult.status, networkStateHash: networkStateHash, isMessage: Bool(tx.isMessage), - transactionHash: tx.hash(), + transactionHash: Field(tx.hash), eventsHash, stateTransitionsHash, }) @@ -539,15 +548,21 @@ export class TransactionExecutionService { return { result: { tx, - hooksStatus: Bool(txHooksValid), - status: runtimeResult.status, + hooksStatus: txHooksValid, + status: runtimeResult.status.toBoolean(), statusMessage: beforeTxHookResult.statusMessage ?? afterTxHookResult.statusMessage ?? runtimeResult.statusMessage, - stateTransitions, - events: beforeHookEvents.concat(runtimeResultEvents, afterHookEvents), + stateTransitions: stateTransitions.map(STBatchToJson), + events: beforeHookEvents + .concat(runtimeResultEvents, afterHookEvents) + .map((e) => ({ + eventName: e.eventName, + data: e.data.map((f) => f.toString()), + source: e.source, + })), }, shouldRemove, }; diff --git a/packages/sequencer/src/protocol/production/tasks/NewBlockTask.ts b/packages/sequencer/src/protocol/production/tasks/NewBlockTask.ts index 338ce9514..f49b40f99 100644 --- a/packages/sequencer/src/protocol/production/tasks/NewBlockTask.ts +++ b/packages/sequencer/src/protocol/production/tasks/NewBlockTask.ts @@ -23,7 +23,10 @@ import { Task, TaskSerializer } from "../../../worker/flow/Task"; import { ProofTaskSerializer } from "../../../helpers/utils"; import { TaskWorkerModule } from "../../../worker/worker/TaskWorkerModule"; import { PairingDerivedInput } from "../flow/ReductionTaskFlow"; -import type { TaskStateRecord } from "../tracing/BlockTracingService"; +import { + TaskStateRecordJson, + taskStateRecordFromJson, +} from "../tracing/BlockTracingService"; import { NewBlockProvingParametersSerializer } from "./serializers/NewBlockProvingParametersSerializer"; import { executeWithPrefilledStateService } from "./TransactionProvingTask"; @@ -34,8 +37,8 @@ export interface NewBlockProverParameters { blockWitness: BlockHashMerkleTreeWitness; deferSTProof: Bool; afterBlockRootWitness: WitnessedRootWitness; - startingStateBeforeHook: TaskStateRecord; - startingStateAfterHook: TaskStateRecord; + startingStateBeforeHook: TaskStateRecordJson; + startingStateAfterHook: TaskStateRecordJson; } export type NewBlockProvingParameters = PairingDerivedInput< @@ -113,15 +116,23 @@ export class NewBlockTask input2 ); + // Convert from JSON to provable types at the proving boundary + const startingStateBeforeHookProvable = taskStateRecordFromJson( + startingStateBeforeHook + ); + const startingStateAfterHookProvable = taskStateRecordFromJson( + startingStateAfterHook + ); + await executeWithPrefilledStateService( this.protocol.stateServiceProvider, - [startingStateBeforeHook, startingStateAfterHook], + [startingStateBeforeHookProvable, startingStateAfterHookProvable], async () => {} ); return await executeWithPrefilledStateService( this.protocol.stateServiceProvider, - [startingStateBeforeHook, startingStateAfterHook], + [startingStateBeforeHookProvable, startingStateAfterHookProvable], async () => await this.executionContext.current().result.prove() ); diff --git a/packages/sequencer/src/protocol/production/tasks/RuntimeProvingTask.ts b/packages/sequencer/src/protocol/production/tasks/RuntimeProvingTask.ts index 3fe0756d3..0141301ee 100644 --- a/packages/sequencer/src/protocol/production/tasks/RuntimeProvingTask.ts +++ b/packages/sequencer/src/protocol/production/tasks/RuntimeProvingTask.ts @@ -7,6 +7,7 @@ import { import { MethodPublicOutput, NetworkState, + NetworkStateJson, RuntimeMethodExecutionContext, } from "@proto-kit/protocol"; import { Proof } from "o1js"; @@ -16,8 +17,15 @@ import { Task, TaskSerializer } from "../../../worker/flow/Task"; import { ProofTaskSerializer } from "../../../helpers/utils"; import { TaskWorkerModule } from "../../../worker/worker/TaskWorkerModule"; import { PreFilledStateService } from "../../../state/prefilled/PreFilledStateService"; -import { PendingTransaction } from "../../../mempool/PendingTransaction"; -import { TaskStateRecord } from "../tracing/BlockTracingService"; +import { + PendingTransaction, + PendingTransactionJSONType, +} from "../../../mempool/PendingTransaction"; +import { + TaskStateRecord, + TaskStateRecordJson, + taskStateRecordFromJson, +} from "../tracing/BlockTracingService"; import { RuntimeProofParametersSerializer } from "./serializers/RuntimeProofParametersSerializer"; @@ -29,11 +37,17 @@ export interface RuntimeProofParameters { state: TaskStateRecord; } +export interface RuntimeProofParametersJson { + tx: PendingTransactionJSONType; + networkState: NetworkStateJson; + state: TaskStateRecordJson; +} + @injectable() @scoped(Lifecycle.ContainerScoped) export class RuntimeProvingTask extends TaskWorkerModule - implements Task + implements Task { protected readonly runtimeZkProgrammable = this.runtime.zkProgrammable.zkProgram; @@ -48,7 +62,7 @@ export class RuntimeProvingTask super(); } - public inputSerializer(): TaskSerializer { + public inputSerializer(): TaskSerializer { return new RuntimeProofParametersSerializer(); } @@ -56,15 +70,24 @@ export class RuntimeProvingTask return new ProofTaskSerializer(this.runtimeZkProgrammable[0].Proof); } - public async compute(input: RuntimeProofParameters): Promise { - const method = this.runtime.getMethodById(input.tx.methodId.toBigInt()); + public async compute( + input: RuntimeProofParametersJson + ): Promise { + // Convert from JSON to provable types at the proving boundary + const tx = PendingTransaction.fromJSON(input.tx); + const networkState = new NetworkState( + NetworkState.fromJSON(input.networkState) + ); + const state = taskStateRecordFromJson(input.state); + + const method = this.runtime.getMethodById(tx.methodId.toString()); const methodDescriptors = this.runtime.dependencyContainer .resolve("MethodIdResolver") - .getMethodNameFromId(input.tx.methodId.toBigInt()); + .getMethodNameFromId(tx.methodId.toString()); if (methodDescriptors === undefined || method === undefined) { - throw new Error(`MethodId not found ${input.tx.methodId.toString()}`); + throw new Error(`MethodId not found ${tx.methodId.toString()}`); } const [moduleName, methodName] = methodDescriptors; @@ -74,19 +97,19 @@ export class RuntimeProvingTask methodName ); const decodedArguments = await parameterEncoder.decode( - input.tx.argsFields, - input.tx.auxiliaryData + tx.argsFields, + tx.auxiliaryData ); - const prefilledStateService = new PreFilledStateService(input.state); + const prefilledStateService = new PreFilledStateService(state); this.runtime.stateServiceProvider.setCurrentStateService( prefilledStateService ); // Set network state and transaction for the runtimemodule to access - const { transaction, signature } = input.tx.toProtocolTransaction(); + const { transaction, signature } = tx.toProtocolTransaction(); const contextInputs = { - networkState: input.networkState, + networkState, transaction, signature, }; diff --git a/packages/sequencer/src/protocol/production/tasks/TransactionProvingTask.ts b/packages/sequencer/src/protocol/production/tasks/TransactionProvingTask.ts index de4b80b63..0784973c0 100644 --- a/packages/sequencer/src/protocol/production/tasks/TransactionProvingTask.ts +++ b/packages/sequencer/src/protocol/production/tasks/TransactionProvingTask.ts @@ -18,7 +18,10 @@ import { ProofTaskSerializer } from "../../../helpers/utils"; import { TaskSerializer, Task } from "../../../worker/flow/Task"; import { PreFilledStateService } from "../../../state/prefilled/PreFilledStateService"; import { TaskWorkerModule } from "../../../worker/worker/TaskWorkerModule"; -import type { TaskStateRecord } from "../tracing/BlockTracingService"; +import { + TaskStateRecord, + taskStateRecordFromJson, +} from "../tracing/BlockTracingService"; import { TransactionProvingTaskParameterSerializer } from "./serializers/TransactionProvingTaskParameterSerializer"; import { @@ -93,9 +96,12 @@ export class TransactionProvingTask public async compute( input: TransactionProvingTaskParameters ): Promise { + const startingStateProvable = input.parameters.startingState.map( + taskStateRecordFromJson + ); await executeWithPrefilledStateService( this.protocol.stateServiceProvider, - input.parameters.startingState, + startingStateProvable, async () => { const { type, parameters } = input; @@ -120,7 +126,7 @@ export class TransactionProvingTask return await executeWithPrefilledStateService( this.protocol.stateServiceProvider, - input.parameters.startingState, + startingStateProvable, async () => await this.executionContext.current().result.prove() ); diff --git a/packages/sequencer/src/protocol/production/tasks/serializers/NewBlockProvingParametersSerializer.ts b/packages/sequencer/src/protocol/production/tasks/serializers/NewBlockProvingParametersSerializer.ts index 58771b1df..164f5d474 100644 --- a/packages/sequencer/src/protocol/production/tasks/serializers/NewBlockProvingParametersSerializer.ts +++ b/packages/sequencer/src/protocol/production/tasks/serializers/NewBlockProvingParametersSerializer.ts @@ -17,11 +17,7 @@ import type { NewBlockProverParameters } from "../NewBlockTask"; import { TaskSerializer } from "../../../../worker/flow/Task"; import { ProofTaskSerializer } from "../../../../helpers/utils"; import { PairingDerivedInput } from "../../flow/ReductionTaskFlow"; - -import { - DecodedStateSerializer, - JSONEncodableState, -} from "./DecodedStateSerializer"; +import { TaskStateRecordJson } from "../../tracing/BlockTracingService"; interface JsonType { input1: string; @@ -30,8 +26,8 @@ interface JsonType { publicInput: ReturnType; networkState: ReturnType; blockWitness: ReturnType; - startingStateBeforeHook: JSONEncodableState; - startingStateAfterHook: JSONEncodableState; + startingStateBeforeHook: TaskStateRecordJson; + startingStateAfterHook: TaskStateRecordJson; deferSTProof: boolean; afterBlockRootWitness: ReturnType; }; @@ -71,14 +67,9 @@ export class NewBlockProvingParametersSerializer input.params.blockWitness ), - startingStateBeforeHook: DecodedStateSerializer.toJSON( - input.params.startingStateBeforeHook - ), - - startingStateAfterHook: DecodedStateSerializer.toJSON( - input.params.startingStateAfterHook - ), + startingStateBeforeHook: input.params.startingStateBeforeHook, + startingStateAfterHook: input.params.startingStateAfterHook, deferSTProof: input.params.deferSTProof.toBoolean(), afterBlockRootWitness: WitnessedRootWitness.toJSON( @@ -108,13 +99,9 @@ export class NewBlockProvingParametersSerializer BlockHashMerkleTreeWitness.fromJSON(jsonObject.params.blockWitness) ), - startingStateBeforeHook: DecodedStateSerializer.fromJSON( - jsonObject.params.startingStateBeforeHook - ), + startingStateBeforeHook: jsonObject.params.startingStateBeforeHook, - startingStateAfterHook: DecodedStateSerializer.fromJSON( - jsonObject.params.startingStateBeforeHook - ), + startingStateAfterHook: jsonObject.params.startingStateAfterHook, deferSTProof: Bool(jsonObject.params.deferSTProof), diff --git a/packages/sequencer/src/protocol/production/tasks/serializers/RuntimeProofParametersSerializer.ts b/packages/sequencer/src/protocol/production/tasks/serializers/RuntimeProofParametersSerializer.ts index 102d89c09..73fee13c6 100644 --- a/packages/sequencer/src/protocol/production/tasks/serializers/RuntimeProofParametersSerializer.ts +++ b/packages/sequencer/src/protocol/production/tasks/serializers/RuntimeProofParametersSerializer.ts @@ -1,41 +1,19 @@ -import { NetworkState, ReturnType } from "@proto-kit/protocol"; - import { TaskSerializer } from "../../../../worker/flow/Task"; -import { PendingTransaction } from "../../../../mempool/PendingTransaction"; -import type { RuntimeProofParameters } from "../RuntimeProvingTask"; - -import { - DecodedStateSerializer, - JSONEncodableState, -} from "./DecodedStateSerializer"; +import type { RuntimeProofParametersJson } from "../RuntimeProvingTask"; +/** + * Serializer for RuntimeProofParametersJson. + * Since RuntimeProofParametersJson is already JSON-compatible, this is trivial. + */ export class RuntimeProofParametersSerializer - implements TaskSerializer + implements TaskSerializer { - public toJSON(parameters: RuntimeProofParameters): string { - const jsonReadyObject = { - tx: parameters.tx.toJSON(), - networkState: NetworkState.toJSON(parameters.networkState), - state: DecodedStateSerializer.toJSON(parameters.state), - }; - return JSON.stringify(jsonReadyObject); + public toJSON(parameters: RuntimeProofParametersJson): string { + return JSON.stringify(parameters); } - public fromJSON(json: string): RuntimeProofParameters { - // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment - const jsonReadyObject: { - tx: ReturnType; - networkState: ReturnType<(typeof NetworkState)["toJSON"]>; - state: JSONEncodableState; - } = JSON.parse(json); - return { - tx: PendingTransaction.fromJSON(jsonReadyObject.tx), - - networkState: new NetworkState( - NetworkState.fromJSON(jsonReadyObject.networkState) - ), - - state: DecodedStateSerializer.fromJSON(jsonReadyObject.state), - }; + public fromJSON(json: string): RuntimeProofParametersJson { + // eslint-disable-next-line @typescript-eslint/consistent-type-assertions + return JSON.parse(json) as RuntimeProofParametersJson; } } diff --git a/packages/sequencer/src/protocol/production/tasks/serializers/TransactionProvingTaskParameterSerializer.ts b/packages/sequencer/src/protocol/production/tasks/serializers/TransactionProvingTaskParameterSerializer.ts index 3e08b804e..d057221af 100644 --- a/packages/sequencer/src/protocol/production/tasks/serializers/TransactionProvingTaskParameterSerializer.ts +++ b/packages/sequencer/src/protocol/production/tasks/serializers/TransactionProvingTaskParameterSerializer.ts @@ -10,15 +10,12 @@ import { JsonProof, Signature } from "o1js"; import { TaskSerializer } from "../../../../worker/flow/Task"; import { ProofTaskSerializer } from "../../../../helpers/utils"; +import { TaskStateRecordJson } from "../../tracing/BlockTracingService"; import { TransactionProvingTaskParameters, TransactionProvingType, } from "./types/TransactionProvingTypes"; -import { - DecodedStateSerializer, - JSONEncodableState, -} from "./DecodedStateSerializer"; import { RuntimeVerificationKeyAttestationSerializer } from "./RuntimeVerificationKeyAttestationSerializer"; export type TransactionProverTransactionArgumentsJSON = { @@ -43,7 +40,7 @@ export type MultiExecutionDataJSON = { export type TransactionProverTaskParametersJSON< ExecutionData extends SingleExecutionDataJSON | MultiExecutionDataJSON, > = { - startingState: JSONEncodableState[]; + startingState: TaskStateRecordJson[]; publicInput: ReturnType; executionData: ExecutionData; }; @@ -108,9 +105,7 @@ export class TransactionProvingTaskParameterSerializer const partialParameters = { publicInput: BlockProverPublicInput.toJSON(parameters.publicInput), - startingState: parameters.startingState.map((stateRecord) => - DecodedStateSerializer.toJSON(stateRecord) - ), + startingState: parameters.startingState, }; // The reason we can't just use the structs toJSON is that the VerificationKey @@ -171,9 +166,7 @@ export class TransactionProvingTaskParameterSerializer const partialParameters = { publicInput: BlockProverPublicInput.fromJSON(parameters.publicInput), - startingState: parameters.startingState.map((stateRecord) => - DecodedStateSerializer.fromJSON(stateRecord) - ), + startingState: parameters.startingState, }; if (type === TransactionProvingType.SINGLE) { diff --git a/packages/sequencer/src/protocol/production/tasks/serializers/types/TransactionProvingTypes.ts b/packages/sequencer/src/protocol/production/tasks/serializers/types/TransactionProvingTypes.ts index b091abdc2..ac4c72375 100644 --- a/packages/sequencer/src/protocol/production/tasks/serializers/types/TransactionProvingTypes.ts +++ b/packages/sequencer/src/protocol/production/tasks/serializers/types/TransactionProvingTypes.ts @@ -6,7 +6,7 @@ import { } from "@proto-kit/protocol"; import { Proof } from "o1js"; -import type { TaskStateRecord } from "../../../tracing/BlockTracingService"; +import type { TaskStateRecordJson } from "../../../tracing/BlockTracingService"; export type RuntimeProof = Proof; @@ -22,7 +22,7 @@ export interface TransactionProverTaskParameters< > { publicInput: BlockProverPublicInput; executionData: ExecutionData; - startingState: TaskStateRecord[]; + startingState: TaskStateRecordJson[]; } export type TransactionProvingTaskParameters = diff --git a/packages/sequencer/src/protocol/production/tracing/BatchTracingService.ts b/packages/sequencer/src/protocol/production/tracing/BatchTracingService.ts index f54d52e6f..8b04a8fb6 100644 --- a/packages/sequencer/src/protocol/production/tracing/BatchTracingService.ts +++ b/packages/sequencer/src/protocol/production/tracing/BatchTracingService.ts @@ -2,10 +2,12 @@ import { log, yieldSequential } from "@proto-kit/common"; import { AppliedBatchHashList, MinaActionsHashList, + NetworkState, TransactionHashList, WitnessedRootHashList, } from "@proto-kit/protocol"; import { inject, injectable } from "tsyringe"; +import { Field } from "o1js"; import { StateTransitionProofParameters } from "../tasks/StateTransitionTask"; import { BlockWithResult } from "../../../storage/model/Block"; @@ -40,12 +42,16 @@ export class BatchTracingService { return { pendingSTBatches: new AppliedBatchHashList(), witnessedRoots: new WitnessedRootHashList(), - stateRoot: block.block.fromStateRoot, + stateRoot: Field(block.block.fromStateRoot), eternalTransactionsList: new TransactionHashList( - block.block.fromEternalTransactionsHash + Field(block.block.fromEternalTransactionsHash) + ), + incomingMessages: new MinaActionsHashList( + Field(block.block.fromMessagesHash) + ), + networkState: new NetworkState( + NetworkState.fromJSON(block.block.networkState.before) ), - incomingMessages: new MinaActionsHashList(block.block.fromMessagesHash), - networkState: block.block.networkState.before, }; } diff --git a/packages/sequencer/src/protocol/production/tracing/BlockTracingService.ts b/packages/sequencer/src/protocol/production/tracing/BlockTracingService.ts index 8b6a98bbf..0974ab3c1 100644 --- a/packages/sequencer/src/protocol/production/tracing/BlockTracingService.ts +++ b/packages/sequencer/src/protocol/production/tracing/BlockTracingService.ts @@ -1,6 +1,8 @@ import { + BlockHashMerkleTreeWitness, BlockProverPublicInput, BlockProverState, + NetworkState, WitnessedRootWitness, } from "@proto-kit/protocol"; import { Bool, Field } from "o1js"; @@ -14,6 +16,10 @@ import { BlockWithResult } from "../../../storage/model/Block"; import type { NewBlockProverParameters } from "../tasks/NewBlockTask"; import { Tracer } from "../../../logging/Tracer"; import { trace } from "../../../logging/trace"; +import { + UntypedStateTransition, + UntypedStateTransitionJson, +} from "../helpers/UntypedStateTransition"; import { collectStartingState, @@ -23,6 +29,30 @@ import { export type TaskStateRecord = Record; +export type TaskStateRecordJson = Record; + +export function taskStateRecordToJson( + record: TaskStateRecord +): TaskStateRecordJson { + return Object.fromEntries( + Object.entries(record).map(([key, fields]) => [ + key, + fields.map((f) => f.toString()), + ]) + ); +} + +export function taskStateRecordFromJson( + json: TaskStateRecordJson +): TaskStateRecord { + return Object.fromEntries( + Object.entries(json).map(([key, strings]) => [ + key, + strings.map((s) => Field(s)), + ]) + ); +} + export type BlockTracingState = Pick< BlockProverState, | "witnessedRoots" @@ -50,7 +80,7 @@ export class BlockTracingService { ) {} @trace("batch.trace.block", ([, block]) => ({ - height: block.block.height.toString(), + height: block.block.height, })) public async traceBlock( state: BlockTracingState, @@ -59,35 +89,48 @@ export class BlockTracingService { ): Promise<[BlockTracingState, BlockTrace]> { const publicInput: BlockProverPublicInput = new BlockProverPublicInput({ stateRoot: state.stateRoot, - blockNumber: block.block.height, - blockHashRoot: block.block.fromBlockHashRoot, - eternalTransactionsHash: block.block.fromEternalTransactionsHash, - incomingMessagesHash: block.block.fromMessagesHash, + blockNumber: Field(block.block.height), + blockHashRoot: Field(block.block.fromBlockHashRoot), + eternalTransactionsHash: Field(block.block.fromEternalTransactionsHash), + incomingMessagesHash: Field(block.block.fromMessagesHash), transactionsHash: Field(0), - networkStateHash: block.block.networkState.before.hash(), + networkStateHash: new NetworkState( + NetworkState.fromJSON(block.block.networkState.before) + ).hash(), witnessedRootsHash: state.witnessedRoots.commitment, pendingSTBatchesHash: state.pendingSTBatches.commitment, }); const startingStateBeforeHook = collectStartingState( - block.block.beforeBlockStateTransitions + block.block.beforeBlockStateTransitions.map( + (st: UntypedStateTransitionJson) => UntypedStateTransition.fromJSON(st) + ) ); const blockTrace = { publicInput, - networkState: block.block.networkState.before, + networkState: new NetworkState( + NetworkState.fromJSON(block.block.networkState.before) + ), deferSTProof: Bool(!includeSTProof), - blockWitness: block.result.blockHashWitness, + blockWitness: new BlockHashMerkleTreeWitness( + BlockHashMerkleTreeWitness.fromJSON(block.result.blockHashWitness) + ), startingStateBeforeHook, } satisfies Partial; state.pendingSTBatches.push({ batchHash: toStateTransitionsHash( - block.block.beforeBlockStateTransitions + block.block.beforeBlockStateTransitions.map( + (st: UntypedStateTransitionJson) => + UntypedStateTransition.fromJSON(st) + ) ), applied: Bool(true), }); - state.networkState = block.block.networkState.during; + state.networkState = new NetworkState( + NetworkState.fromJSON(block.block.networkState.during) + ); const [afterState, transactionTraces] = await yieldSequential( chunk(block.block.transactions, 2), @@ -131,9 +174,13 @@ export class BlockTracingService { } const startingStateAfterHook = collectStartingState( - block.result.afterBlockStateTransitions + block.result.afterBlockStateTransitions.map( + (st: UntypedStateTransitionJson) => UntypedStateTransition.fromJSON(st) + ) + ); + state.networkState = new NetworkState( + NetworkState.fromJSON(block.result.afterNetworkState) ); - state.networkState = block.result.afterNetworkState; return [ afterState, @@ -144,7 +191,7 @@ export class BlockTracingService { afterBlockRootWitness, }, transactions: transactionTraces, - height: block.block.height.toString(), + height: block.block.height, }, ]; } diff --git a/packages/sequencer/src/protocol/production/tracing/StateTransitionTracingService.ts b/packages/sequencer/src/protocol/production/tracing/StateTransitionTracingService.ts index 03621892b..a7517f7bd 100644 --- a/packages/sequencer/src/protocol/production/tracing/StateTransitionTracingService.ts +++ b/packages/sequencer/src/protocol/production/tracing/StateTransitionTracingService.ts @@ -18,7 +18,10 @@ import { import { distinctByString } from "../../../helpers/utils"; import { BlockWithResult } from "../../../storage/model/Block"; -import { UntypedStateTransition } from "../helpers/UntypedStateTransition"; +import { + UntypedStateTransition, + UntypedStateTransitionJson, +} from "../helpers/UntypedStateTransition"; import { StateTransitionProofParameters } from "../tasks/StateTransitionTask"; import { trace } from "../../../logging/trace"; import { Tracer } from "../../../logging/Tracer"; @@ -47,10 +50,20 @@ export class StateTransitionTracingService { return blocks.reduce((previous, block) => { const batches = [ { - stateTransitions: block.block.beforeBlockStateTransitions, + stateTransitions: block.block.beforeBlockStateTransitions.map( + (st: UntypedStateTransitionJson) => + UntypedStateTransition.fromJSON(st) + ), applied: true, }, - ...block.block.transactions.flatMap((tx) => tx.stateTransitions), + ...block.block.transactions.flatMap((tx) => + tx.stateTransitions.map((batch) => ({ + stateTransitions: batch.stateTransitions.map((st) => + UntypedStateTransition.fromJSON(st) + ), + applied: batch.applied, + })) + ), ].map((batch) => ({ ...batch, witnessRoot: false })); const batchBeforeWitnessing = previous.concat(batches); @@ -62,7 +75,10 @@ export class StateTransitionTracingService { } return batchBeforeWitnessing.concat({ - stateTransitions: block.result.afterBlockStateTransitions, + stateTransitions: block.result.afterBlockStateTransitions.map( + (st: UntypedStateTransitionJson) => + UntypedStateTransition.fromJSON(st) + ), applied: true, witnessRoot: false, }); diff --git a/packages/sequencer/src/protocol/production/tracing/TransactionTracingService.ts b/packages/sequencer/src/protocol/production/tracing/TransactionTracingService.ts index ee591d282..d7bc60d8a 100644 --- a/packages/sequencer/src/protocol/production/tracing/TransactionTracingService.ts +++ b/packages/sequencer/src/protocol/production/tracing/TransactionTracingService.ts @@ -11,9 +11,12 @@ import { MAX_FIELD } from "@proto-kit/common"; import { toStateTransitionsHash } from "@proto-kit/module"; import { injectable } from "tsyringe"; -import { TransactionExecutionResult } from "../../../storage/model/Block"; +import { + TransactionExecutionResultJson, + STBatchFromJson, +} from "../../../storage/model/Block"; import { PendingTransaction } from "../../../mempool/PendingTransaction"; -import type { RuntimeProofParameters } from "../tasks/RuntimeProvingTask"; +import type { RuntimeProofParametersJson } from "../tasks/RuntimeProvingTask"; import { TransactionProverTaskParameters, TransactionProvingType, @@ -21,23 +24,26 @@ import { import { UntypedStateTransition } from "../helpers/UntypedStateTransition"; import { VerificationKeyService } from "../../runtime/RuntimeVerificationKeyService"; -import type { BlockTracingState, TaskStateRecord } from "./BlockTracingService"; +import type { + BlockTracingState, + TaskStateRecordJson, +} from "./BlockTracingService"; export type TransactionTrace = | { type: TransactionProvingType.SINGLE; transaction: TransactionProverTaskParameters; - runtime: [RuntimeProofParameters]; + runtime: [RuntimeProofParametersJson]; } | { type: TransactionProvingType.MULTI; transaction: TransactionProverTaskParameters; - runtime: [RuntimeProofParameters, RuntimeProofParameters]; + runtime: [RuntimeProofParametersJson, RuntimeProofParametersJson]; }; export function collectStartingState( stateTransitions: UntypedStateTransition[] -): TaskStateRecord { +): TaskStateRecordJson { const stateEntries = stateTransitions // Filter distinct .filter( @@ -49,7 +55,10 @@ export function collectStartingState( // "state hasn't been set before" and has to correlate to a precondition on Field(0) // and for that the state has to be undefined .filter((st) => st.fromValue.isSome.toBoolean()) - .map((st) => [st.path.toString(), st.fromValue.value]); + .map((st) => [ + st.path.toString(), + st.fromValue.value.map((f: Field) => f.toString()), + ]); return Object.fromEntries(stateEntries); } @@ -65,7 +74,7 @@ export class TransactionTracingService { ): Promise { const verificationKeyAttestation = this.verificationKeyService.getAttestation( - transaction.methodId.toBigInt() + transaction.methodId.toString() ); return { @@ -93,16 +102,18 @@ export class TransactionTracingService { private appendTransactionToState( previousState: BlockTracingState, - transaction: TransactionExecutionResult + transaction: TransactionExecutionResultJson ) { + const tx = PendingTransaction.fromJSON(transaction.tx); // TODO Remove this call and instead reuse results from sequencing const newState = addTransactionToBundle( previousState, - Bool(transaction.tx.isMessage), - transaction.tx.toRuntimeTransaction() + Bool(tx.isMessage), + tx.toRuntimeTransaction() ); - transaction.stateTransitions.forEach((batch) => { + const stBatches = transaction.stateTransitions.map(STBatchFromJson); + stBatches.forEach((batch) => { newState.pendingSTBatches.push({ applied: Bool(batch.applied), batchHash: toStateTransitionsHash(batch.stateTransitions), @@ -113,26 +124,27 @@ export class TransactionTracingService { } private createRuntimeProofParams( - tx: TransactionExecutionResult, + tx: TransactionExecutionResultJson, networkState: NetworkState - ): RuntimeProofParameters { - const startingState = collectStartingState( - tx.stateTransitions[1].stateTransitions - ); + ): RuntimeProofParametersJson { + const stBatch = STBatchFromJson(tx.stateTransitions[1]); + const startingState = collectStartingState(stBatch.stateTransitions); return { tx: tx.tx, - networkState, + networkState: NetworkState.toJSON(networkState), state: startingState, }; } private async traceTransaction( previousState: BlockTracingState, - transaction: TransactionExecutionResult + transaction: TransactionExecutionResultJson ) { + const stBatches = transaction.stateTransitions.map(STBatchFromJson); + const beforeHookStartingState = collectStartingState( - transaction.stateTransitions[0].stateTransitions.flat() + stBatches[0].stateTransitions.flat() ); const runtimeTrace1 = this.createRuntimeProofParams( @@ -141,7 +153,7 @@ export class TransactionTracingService { ); const afterHookStartingState = collectStartingState( - transaction.stateTransitions[2].stateTransitions.flat() + stBatches[2].stateTransitions.flat() ); const newState = this.appendTransactionToState(previousState, transaction); @@ -155,7 +167,7 @@ export class TransactionTracingService { public async createSingleTransactionTrace( previousState: BlockTracingState, - transaction: TransactionExecutionResult + transaction: TransactionExecutionResultJson ): Promise<[BlockTracingState, TransactionTrace]> { const publicInput = this.getTransactionProofPublicInput(previousState); @@ -168,7 +180,9 @@ export class TransactionTracingService { const transactionTrace: TransactionProverTaskParameters = { executionData: { - transaction: await this.getTransactionData(transaction.tx), + transaction: await this.getTransactionData( + PendingTransaction.fromJSON(transaction.tx) + ), networkState: previousState.networkState, }, startingState, @@ -187,8 +201,8 @@ export class TransactionTracingService { public async createMultiTransactionTrace( previousState: BlockTracingState, - transaction1: TransactionExecutionResult, - transaction2: TransactionExecutionResult + transaction1: TransactionExecutionResultJson, + transaction2: TransactionExecutionResultJson ): Promise<[BlockTracingState, TransactionTrace]> { const publicInput = this.getTransactionProofPublicInput(previousState); @@ -207,8 +221,12 @@ export class TransactionTracingService { const transactionTrace: TransactionProverTaskParameters = { executionData: { - transaction1: await this.getTransactionData(transaction1.tx), - transaction2: await this.getTransactionData(transaction2.tx), + transaction1: await this.getTransactionData( + PendingTransaction.fromJSON(transaction1.tx) + ), + transaction2: await this.getTransactionData( + PendingTransaction.fromJSON(transaction2.tx) + ), networkState: previousState.networkState, }, startingState: [...startingState1, ...startingState2], diff --git a/packages/sequencer/src/protocol/runtime/RuntimeVerificationKeyService.ts b/packages/sequencer/src/protocol/runtime/RuntimeVerificationKeyService.ts index 5445d6e63..62717cc4a 100644 --- a/packages/sequencer/src/protocol/runtime/RuntimeVerificationKeyService.ts +++ b/packages/sequencer/src/protocol/runtime/RuntimeVerificationKeyService.ts @@ -25,7 +25,7 @@ export type VKRecord = { }; export interface WithGetMethodId { - getMethodId: (moduleName: string, methodName: string) => bigint; + getMethodId: (moduleName: string, methodName: string) => string; } export interface WithZkProgrammableAndGetMethodById { @@ -81,10 +81,10 @@ export class VerificationKeyService extends ConfigurableModule<{}> { methodName ); // eslint-disable-next-line @typescript-eslint/consistent-type-assertions - return [ - methodId.toString(), - new VerificationKey(artifact.verificationKey), - ] as [string, VerificationKey]; + return [methodId, new VerificationKey(artifact.verificationKey)] as [ + string, + VerificationKey, + ]; }); } ); @@ -131,8 +131,8 @@ export class VerificationKeyService extends ConfigurableModule<{}> { return this.persistedVKRecord; } - public getAttestation(methodId: bigint) { - const verificationKey = this.getVkRecord()[methodId.toString()]; + public getAttestation(methodId: string) { + const verificationKey = this.getVkRecord()[methodId]; if (verificationKey === undefined) { throw new Error( `MethodId not registered in VerificationKeyService (${methodId})` @@ -147,9 +147,9 @@ export class VerificationKeyService extends ConfigurableModule<{}> { }); } - public getWitness(methodId: bigint) { + public getWitness(methodId: string) { const vkTree = this.getVKTree(); - return vkTree.tree.getWitness(vkTree.indexes[methodId.toString()]); + return vkTree.tree.getWitness(vkTree.indexes[methodId]); } public getRoot(): bigint { diff --git a/packages/sequencer/src/settlement/interactions/bridging/BridgingSettlementInteraction.ts b/packages/sequencer/src/settlement/interactions/bridging/BridgingSettlementInteraction.ts index ee8b74983..2349b3b85 100644 --- a/packages/sequencer/src/settlement/interactions/bridging/BridgingSettlementInteraction.ts +++ b/packages/sequencer/src/settlement/interactions/bridging/BridgingSettlementInteraction.ts @@ -5,6 +5,7 @@ import { BridgingSettlementModulesRecord, DynamicBlockProof, MandatoryProtocolModulesRecord, + NetworkState, Protocol, SettlementContractModule, } from "@proto-kit/protocol"; @@ -101,8 +102,8 @@ export class BridgingSettlementInteraction implements SettleInteraction { dynamicBlockProof, signature, feepayer, - batch.fromNetworkState, - batch.toNetworkState, + new NetworkState(NetworkState.fromJSON(batch.fromNetworkState)), + new NetworkState(NetworkState.fromJSON(batch.toNetworkState)), latestSequenceStateHash ); } diff --git a/packages/sequencer/src/settlement/interactions/vanilla/VanillaSettlementInteraction.ts b/packages/sequencer/src/settlement/interactions/vanilla/VanillaSettlementInteraction.ts index 096438ab5..665f9e7d0 100644 --- a/packages/sequencer/src/settlement/interactions/vanilla/VanillaSettlementInteraction.ts +++ b/packages/sequencer/src/settlement/interactions/vanilla/VanillaSettlementInteraction.ts @@ -5,6 +5,7 @@ import { BridgingSettlementModulesRecord, DynamicBlockProof, MandatoryProtocolModulesRecord, + NetworkState, Protocol, SettlementContractModule, } from "@proto-kit/protocol"; @@ -93,8 +94,8 @@ export class VanillaSettlementInteraction implements SettleInteraction { dynamicBlockProof, signature, feepayer, - batch.fromNetworkState, - batch.toNetworkState, + new NetworkState(NetworkState.fromJSON(batch.fromNetworkState)), + new NetworkState(NetworkState.fromJSON(batch.toNetworkState)), latestSequenceStateHash ); } diff --git a/packages/sequencer/src/settlement/messages/IncomingMessageAdapter.ts b/packages/sequencer/src/settlement/messages/IncomingMessageAdapter.ts index dffae431b..f552fdc53 100644 --- a/packages/sequencer/src/settlement/messages/IncomingMessageAdapter.ts +++ b/packages/sequencer/src/settlement/messages/IncomingMessageAdapter.ts @@ -1,6 +1,6 @@ import { PublicKey } from "o1js"; -import { PendingTransaction } from "../../mempool/PendingTransaction"; +import { PendingTransactionJSONType } from "../../mempool/PendingTransaction"; /** * An interface provided by the BaseLayer via DependencyFactory, @@ -19,6 +19,6 @@ export interface IncomingMessageAdapter { ) => Promise<{ from: string; to: string; - messages: PendingTransaction[]; + messages: PendingTransactionJSONType[]; }>; } diff --git a/packages/sequencer/src/settlement/messages/IncomingMessagesService.ts b/packages/sequencer/src/settlement/messages/IncomingMessagesService.ts index 6311cb30a..3eec0720a 100644 --- a/packages/sequencer/src/settlement/messages/IncomingMessagesService.ts +++ b/packages/sequencer/src/settlement/messages/IncomingMessagesService.ts @@ -4,7 +4,7 @@ import { ACTIONS_EMPTY_HASH } from "@proto-kit/protocol"; import { SettlementStorage } from "../../storage/repositories/SettlementStorage"; import { MessageStorage } from "../../storage/repositories/MessageStorage"; import { BlockStorage } from "../../storage/repositories/BlockStorage"; -import { PendingTransaction } from "../../mempool/PendingTransaction"; +import { PendingTransactionJSONType } from "../../mempool/PendingTransaction"; import type { BridgingModule } from "../BridgingModule"; import { IncomingMessageAdapter } from "./IncomingMessageAdapter"; @@ -53,7 +53,7 @@ export class IncomingMessagesService { private isComplete( messages: - | { toMessagesHash: string; messages: PendingTransaction[] } + | { toMessagesHash: string; messages: PendingTransactionJSONType[] } | undefined, targetMessagesHash: string ) { @@ -64,7 +64,7 @@ export class IncomingMessagesService { private async ensureMessageCompleteness( messages: - | { toMessagesHash: string; messages: PendingTransaction[] } + | { toMessagesHash: string; messages: PendingTransactionJSONType[] } | undefined, fromMessagesHash: string, targetMessagesHash: string @@ -90,7 +90,7 @@ export class IncomingMessagesService { batches: { fromMessagesHash: string; toMessagesHash: string; - messages: PendingTransaction[]; + messages: PendingTransactionJSONType[]; }[] ) { return { diff --git a/packages/sequencer/src/settlement/messages/MinaIncomingMessageAdapter.ts b/packages/sequencer/src/settlement/messages/MinaIncomingMessageAdapter.ts index 764cfd1ae..ee80e33ff 100644 --- a/packages/sequencer/src/settlement/messages/MinaIncomingMessageAdapter.ts +++ b/packages/sequencer/src/settlement/messages/MinaIncomingMessageAdapter.ts @@ -24,7 +24,10 @@ import { SettlementContractModule, } from "@proto-kit/protocol"; -import { PendingTransaction } from "../../mempool/PendingTransaction"; +import { + PendingTransaction, + PendingTransactionJSONType, +} from "../../mempool/PendingTransaction"; import type { MinaBaseLayer } from "../../protocol/baselayer/MinaBaseLayer"; import { IncomingMessageAdapter } from "./IncomingMessageAdapter"; @@ -67,11 +70,11 @@ export class MinaIncomingMessageAdapter implements IncomingMessageAdapter { private async mapActionToTransactions( tx: RuntimeTransaction, fieldArgs: Field[] - ): Promise { + ): Promise { const { methodId } = tx; const methodPointer = this.runtime.methodIdResolver.getMethodNameFromId( - methodId.toBigInt() + methodId.toString() ); if (methodPointer === undefined) { @@ -96,7 +99,7 @@ export class MinaIncomingMessageAdapter implements IncomingMessageAdapter { argsFields: fields, auxiliaryData: auxiliary, isMessage: true, - }); + }).toJSON(); } public async fetchPendingMessages( @@ -109,7 +112,7 @@ export class MinaIncomingMessageAdapter implements IncomingMessageAdapter { ): Promise<{ from: string; to: string; - messages: PendingTransaction[]; + messages: PendingTransactionJSONType[]; }> { const { network } = this.baseLayer; if (network === undefined) { diff --git a/packages/sequencer/src/settlement/messages/outgoing/DefaultOutgoingMessageAdapter.ts b/packages/sequencer/src/settlement/messages/outgoing/DefaultOutgoingMessageAdapter.ts index 3653d023d..11f3b0207 100644 --- a/packages/sequencer/src/settlement/messages/outgoing/DefaultOutgoingMessageAdapter.ts +++ b/packages/sequencer/src/settlement/messages/outgoing/DefaultOutgoingMessageAdapter.ts @@ -61,7 +61,7 @@ export class DefaultOutgoingMessageAdapter ) .map((event) => { const type = this.outgoingWithdrawalEvents[event.eventName]; - return type.eventType.fromFields(event.data); + return type.eventType.fromFields(event.data.map((d) => Field(d))); }) ); } diff --git a/packages/sequencer/src/storage/inmemory/InMemoryBlockStorage.ts b/packages/sequencer/src/storage/inmemory/InMemoryBlockStorage.ts index 5c5306e7b..bc0e53cf6 100644 --- a/packages/sequencer/src/storage/inmemory/InMemoryBlockStorage.ts +++ b/packages/sequencer/src/storage/inmemory/InMemoryBlockStorage.ts @@ -47,7 +47,7 @@ export class InMemoryBlockStorage implements BlockStorage, BlockQueue { if (result !== undefined) { if (result.result === undefined) { throw new Error( - `Block result for block ${result.block.height.toString()} not found` + `Block result for block ${result.block.height} not found` ); } return { @@ -65,9 +65,7 @@ export class InMemoryBlockStorage implements BlockStorage, BlockQueue { if (latestBatch !== undefined) { cursor = this.blocks.reduce( (c, block, index) => - latestBatch.blockHashes.includes(block.hash.toString()) - ? index + 1 - : c, + latestBatch.blockHashes.includes(block.hash) ? index + 1 : c, 0 ); } @@ -100,6 +98,6 @@ export class InMemoryBlockStorage implements BlockStorage, BlockQueue { } public async getBlock(hash: string): Promise { - return this.blocks.find((block) => block.hash.toString() === hash); + return this.blocks.find((block) => block.hash === hash); } } diff --git a/packages/sequencer/src/storage/inmemory/InMemoryMessageStorage.ts b/packages/sequencer/src/storage/inmemory/InMemoryMessageStorage.ts index 1bf671315..b4f3df4e4 100644 --- a/packages/sequencer/src/storage/inmemory/InMemoryMessageStorage.ts +++ b/packages/sequencer/src/storage/inmemory/InMemoryMessageStorage.ts @@ -1,6 +1,6 @@ import { injectable } from "tsyringe"; -import { PendingTransaction } from "../../mempool/PendingTransaction"; +import { PendingTransactionJSONType } from "../../mempool/PendingTransaction"; import { MessageStorage } from "../repositories/MessageStorage"; @injectable() @@ -8,7 +8,7 @@ export class InMemoryMessageStorage implements MessageStorage { private messages: { [key: string]: { toMessagesHash: string; - messages: PendingTransaction[]; + messages: PendingTransactionJSONType[]; }; } = {}; @@ -16,7 +16,7 @@ export class InMemoryMessageStorage implements MessageStorage { | { fromMessagesHash: string; toMessagesHash: string; - messages: PendingTransaction[]; + messages: PendingTransactionJSONType[]; } | undefined > { @@ -37,7 +37,7 @@ export class InMemoryMessageStorage implements MessageStorage { const batches: { fromMessagesHash: string; toMessagesHash: string; - messages: PendingTransaction[]; + messages: PendingTransactionJSONType[]; }[] = []; let currentHash = fromMessagesHash; @@ -61,7 +61,7 @@ export class InMemoryMessageStorage implements MessageStorage { public async pushMessages( fromMessagesHash: string, toMessagesHash: string, - messages: PendingTransaction[] + messages: PendingTransactionJSONType[] ): Promise { this.messages[fromMessagesHash] = { messages, diff --git a/packages/sequencer/src/storage/inmemory/InMemoryTransactionStorage.ts b/packages/sequencer/src/storage/inmemory/InMemoryTransactionStorage.ts index df0fe7fd7..a8e85cf9b 100644 --- a/packages/sequencer/src/storage/inmemory/InMemoryTransactionStorage.ts +++ b/packages/sequencer/src/storage/inmemory/InMemoryTransactionStorage.ts @@ -1,15 +1,14 @@ import { inject, injectable } from "tsyringe"; -import { Field } from "o1js"; import { TransactionStorage } from "../repositories/TransactionStorage"; -import { PendingTransaction } from "../../mempool/PendingTransaction"; +import { PendingTransactionJSONType } from "../../mempool/PendingTransaction"; import { BlockStorage } from "../repositories/BlockStorage"; import { InMemoryBatchStorage } from "./InMemoryBatchStorage"; @injectable() export class InMemoryTransactionStorage implements TransactionStorage { - private queue: PendingTransaction[] = []; + private queue: PendingTransactionJSONType[] = []; private latestScannedBlock = -1; @@ -22,12 +21,14 @@ export class InMemoryTransactionStorage implements TransactionStorage { public async removeTx(hashes: string[]) { const hashSet = new Set(hashes); this.queue = this.queue.filter((tx) => { - const hash = tx.hash().toString(); + const { hash } = tx; return !hashSet.has(hash); }); } - public async getPendingUserTransactions(): Promise { + public async getPendingUserTransactions(): Promise< + PendingTransactionJSONType[] + > { const nextHeight = await this.blockStorage.getCurrentBlockHeight(); for ( let height = this.latestScannedBlock + 1; @@ -37,10 +38,8 @@ export class InMemoryTransactionStorage implements TransactionStorage { // eslint-disable-next-line no-await-in-loop const block = await this.blockStorage.getBlockAt(height); if (block !== undefined) { - const hashes = block.transactions.map((tx) => tx.tx.hash().toString()); - this.queue = this.queue.filter( - (tx) => !hashes.includes(tx.hash().toString()) - ); + const hashes = block.transactions.map((tx) => tx.tx.hash); + this.queue = this.queue.filter((tx) => !hashes.includes(tx.hash)); } } this.latestScannedBlock = nextHeight - 1; @@ -48,11 +47,11 @@ export class InMemoryTransactionStorage implements TransactionStorage { return this.queue.slice(); } - public async pushUserTransaction(tx: PendingTransaction): Promise { + public async pushUserTransaction( + tx: PendingTransactionJSONType + ): Promise { const notInQueue = - this.queue.find( - (tx2) => tx2.hash().toString() === tx.hash().toString() - ) === undefined; + this.queue.find((tx2) => tx2.hash === tx.hash) === undefined; if (notInQueue) { this.queue.push(tx); } @@ -77,14 +76,14 @@ export class InMemoryTransactionStorage implements TransactionStorage { public async findTransaction(hash: string): Promise< | { - transaction: PendingTransaction; + transaction: PendingTransactionJSONType; block?: string; batch?: number; } | undefined > { const pending = await this.getPendingUserTransactions(); - const pendingResult = pending.find((tx) => tx.hash().toString() === hash); + const pendingResult = pending.find((tx) => tx.hash === hash); if (pendingResult !== undefined) { return { transaction: pendingResult, @@ -92,7 +91,6 @@ export class InMemoryTransactionStorage implements TransactionStorage { } const tipHeight = await this.blockStorage.getCurrentBlockHeight(); - const hashField = Field(hash); for (let height = tipHeight - 1; height >= 0; height--) { // eslint-disable-next-line no-await-in-loop @@ -100,15 +98,13 @@ export class InMemoryTransactionStorage implements TransactionStorage { if (block === undefined) { return undefined; } - const txResult = block.transactions.find((tx) => - tx.tx.hash().equals(hashField).toBoolean() - ); + const txResult = block.transactions.find((tx) => tx.tx.hash === hash); if (txResult !== undefined) { // eslint-disable-next-line no-await-in-loop - const batch = await this.findBatch(block.hash.toString()); + const batch = await this.findBatch(block.hash); return { transaction: txResult.tx, - block: block.transactionsHash.toString(), + block: block.transactionsHash, batch, }; } diff --git a/packages/sequencer/src/storage/model/Batch.ts b/packages/sequencer/src/storage/model/Batch.ts index 731b14718..a14685c42 100644 --- a/packages/sequencer/src/storage/model/Batch.ts +++ b/packages/sequencer/src/storage/model/Batch.ts @@ -1,5 +1,5 @@ import { JsonProof } from "o1js"; -import { NetworkState } from "@proto-kit/protocol"; +import { NetworkStateJson } from "@proto-kit/protocol"; import { PendingTransaction } from "../../mempool/PendingTransaction"; @@ -16,6 +16,6 @@ export interface Batch { } export interface SettleableBatch extends Batch { - fromNetworkState: NetworkState; - toNetworkState: NetworkState; + fromNetworkState: NetworkStateJson; + toNetworkState: NetworkStateJson; } diff --git a/packages/sequencer/src/storage/model/Block.ts b/packages/sequencer/src/storage/model/Block.ts index da2e44802..6db865573 100644 --- a/packages/sequencer/src/storage/model/Block.ts +++ b/packages/sequencer/src/storage/model/Block.ts @@ -3,12 +3,21 @@ import { ACTIONS_EMPTY_HASH, BlockHashMerkleTree, BlockHashMerkleTreeWitness, + BlockHashMerkleTreeWitnessJson, NetworkState, + NetworkStateJson, } from "@proto-kit/protocol"; import { LinkedMerkleTree } from "@proto-kit/common"; -import { PendingTransaction } from "../../mempool/PendingTransaction"; -import { UntypedStateTransition } from "../../protocol/production/helpers/UntypedStateTransition"; +import { + PendingTransaction, + PendingTransactionJSONType, +} from "../../mempool/PendingTransaction"; +import { + UntypedStateTransition, + UntypedStateTransitionJson, +} from "../../protocol/production/helpers/UntypedStateTransition"; +import { FieldString } from "../../helpers/utils"; export interface StateTransitionBatch { stateTransitions: UntypedStateTransition[]; @@ -28,28 +37,46 @@ export interface TransactionExecutionResult { }[]; } +export interface StateTransitionBatchJson { + stateTransitions: UntypedStateTransitionJson[]; + applied: boolean; +} + +export interface TransactionExecutionResultJson { + tx: PendingTransactionJSONType; + stateTransitions: StateTransitionBatchJson[]; + status: boolean; + hooksStatus: boolean; + statusMessage?: string; + events: { + eventName: string; + data: FieldString[]; + source: "afterTxHook" | "beforeTxHook" | "runtime"; + }[]; +} + // TODO Why is Block using Fields, but BlockResult bigints? Align that towards the best option export interface Block { - hash: Field; - previousBlockHash: Field | undefined; - height: Field; + hash: FieldString; + previousBlockHash: FieldString | undefined; + height: FieldString; networkState: { - before: NetworkState; - during: NetworkState; + before: NetworkStateJson; + during: NetworkStateJson; }; - transactions: TransactionExecutionResult[]; - transactionsHash: Field; + transactions: TransactionExecutionResultJson[]; + transactionsHash: FieldString; - fromEternalTransactionsHash: Field; - fromBlockHashRoot: Field; - fromMessagesHash: Field; - fromStateRoot: Field; - toEternalTransactionsHash: Field; - toMessagesHash: Field; + fromEternalTransactionsHash: FieldString; + fromBlockHashRoot: FieldString; + fromMessagesHash: FieldString; + fromStateRoot: FieldString; + toEternalTransactionsHash: FieldString; + toMessagesHash: FieldString; - beforeBlockStateTransitions: UntypedStateTransition[]; + beforeBlockStateTransitions: UntypedStateTransitionJson[]; } // eslint-disable-next-line @typescript-eslint/no-redeclare @@ -59,18 +86,21 @@ export const Block = { }, hash(block: Omit): Field { - return Block.calculateHash(block.height, block.transactionsHash); + return Block.calculateHash( + Field(block.height), + Field(block.transactionsHash) + ); }, }; export interface BlockResult { - blockHash: bigint; - witnessedRoots: [bigint]; - stateRoot: bigint; - blockHashRoot: bigint; - afterNetworkState: NetworkState; - afterBlockStateTransitions: UntypedStateTransition[]; - blockHashWitness: BlockHashMerkleTreeWitness; + blockHash: string; + witnessedRoots: [string]; + stateRoot: string; + blockHashRoot: string; + afterNetworkState: NetworkStateJson; + afterBlockStateTransitions: UntypedStateTransitionJson[]; + blockHashWitness: BlockHashMerkleTreeWitnessJson; } export interface BlockWithResult { @@ -93,33 +123,100 @@ export const BlockWithResult = { createEmpty: () => ({ block: { - hash: Field(0), + hash: FieldString(0), - height: Field(0), - transactionsHash: Field(0), - fromEternalTransactionsHash: Field(0), - toEternalTransactionsHash: Field(0), + height: FieldString(0), + transactionsHash: FieldString(0), + fromEternalTransactionsHash: FieldString(0), + toEternalTransactionsHash: FieldString(0), transactions: [], networkState: { - before: NetworkState.empty(), - during: NetworkState.empty(), + before: NetworkState.toJSON(NetworkState.empty()), + during: NetworkState.toJSON(NetworkState.empty()), }, - fromBlockHashRoot: Field(BlockHashMerkleTree.EMPTY_ROOT), - fromMessagesHash: Field(0), - fromStateRoot: LinkedMerkleTree.EMPTY_ROOT, - toMessagesHash: ACTIONS_EMPTY_HASH, + fromBlockHashRoot: FieldString(BlockHashMerkleTree.EMPTY_ROOT), + fromMessagesHash: FieldString(0), + fromStateRoot: FieldString(LinkedMerkleTree.EMPTY_ROOT), + toMessagesHash: FieldString(ACTIONS_EMPTY_HASH), beforeBlockStateTransitions: [], previousBlockHash: undefined, }, result: { - afterNetworkState: NetworkState.empty(), - stateRoot: LinkedMerkleTree.EMPTY_ROOT.toBigInt(), - blockHashRoot: BlockHashMerkleTree.EMPTY_ROOT, + afterNetworkState: NetworkState.toJSON(NetworkState.empty()), + stateRoot: String(LinkedMerkleTree.EMPTY_ROOT), + blockHashRoot: String(BlockHashMerkleTree.EMPTY_ROOT), afterBlockStateTransitions: [], - blockHashWitness: BlockHashMerkleTree.WITNESS.dummy(), - blockHash: 0n, - witnessedRoots: [LinkedMerkleTree.EMPTY_ROOT.toBigInt()], + blockHashWitness: BlockHashMerkleTreeWitness.toJSON( + BlockHashMerkleTree.WITNESS.dummy() + ), + blockHash: "0", + witnessedRoots: [String(LinkedMerkleTree.EMPTY_ROOT)], }, }) satisfies BlockWithResult, }; + +export function txResultToJson( + txResult: TransactionExecutionResult +): TransactionExecutionResultJson { + return { + tx: txResult.tx.toJSON(), + stateTransitions: txResult.stateTransitions.map((batch) => ({ + stateTransitions: batch.stateTransitions.map((st) => st.toJSON()), + applied: batch.applied, + })), + status: txResult.status.toBoolean(), + hooksStatus: txResult.hooksStatus.toBoolean(), + statusMessage: txResult.statusMessage, + events: txResult.events.map((e) => ({ + eventName: e.eventName, + data: e.data.map((f) => f.toString()), + source: e.source, + })), + }; +} + +export function txResultFromJson( + json: TransactionExecutionResultJson +): TransactionExecutionResult { + return { + tx: PendingTransaction.fromJSON(json.tx), + stateTransitions: json.stateTransitions.map((batch) => ({ + stateTransitions: batch.stateTransitions.map((st) => + UntypedStateTransition.fromJSON(st) + ), + applied: batch.applied, + })), + status: Bool(json.status), + hooksStatus: Bool(json.hooksStatus), + statusMessage: json.statusMessage, + events: json.events.map((e) => ({ + eventName: e.eventName, + data: e.data.map((f) => Field(f)), + source: e.source, + })), + }; +} + +export function STBatchToJson( + stBatch: StateTransitionBatch +): StateTransitionBatchJson { + return { + stateTransitions: stBatch.stateTransitions.map( + (untypedST: UntypedStateTransition) => untypedST.toJSON() + ), + applied: stBatch.applied, + }; +} + +export function STBatchFromJson( + stBatch: StateTransitionBatchJson +): StateTransitionBatch { + return { + stateTransitions: stBatch.stateTransitions.map( + (untypedST: UntypedStateTransitionJson) => + UntypedStateTransition.fromJSON(untypedST) + ), + applied: stBatch.applied, + }; +} diff --git a/packages/sequencer/src/storage/repositories/MessageStorage.ts b/packages/sequencer/src/storage/repositories/MessageStorage.ts index 343e831c8..f137b6c0c 100644 --- a/packages/sequencer/src/storage/repositories/MessageStorage.ts +++ b/packages/sequencer/src/storage/repositories/MessageStorage.ts @@ -1,4 +1,4 @@ -import { PendingTransaction } from "../../mempool/PendingTransaction"; +import { PendingTransactionJSONType } from "../../mempool/PendingTransaction"; /** * Interface to store Messages previously fetched by a IncomingMessageadapter @@ -7,14 +7,14 @@ export interface MessageStorage { pushMessages: ( fromMessagesHash: string, toMessagesHash: string, - messages: PendingTransaction[] + messages: PendingTransactionJSONType[] ) => Promise; getNextMessagesBatch: (fromMessagesHash: string) => Promise< | { fromMessagesHash: string; toMessagesHash: string; - messages: PendingTransaction[]; + messages: PendingTransactionJSONType[]; } | undefined >; @@ -26,7 +26,7 @@ export interface MessageStorage { { fromMessagesHash: string; toMessagesHash: string; - messages: PendingTransaction[]; + messages: PendingTransactionJSONType[]; }[] >; } diff --git a/packages/sequencer/src/storage/repositories/TransactionStorage.ts b/packages/sequencer/src/storage/repositories/TransactionStorage.ts index ef353ad08..7cf87f1d8 100644 --- a/packages/sequencer/src/storage/repositories/TransactionStorage.ts +++ b/packages/sequencer/src/storage/repositories/TransactionStorage.ts @@ -1,9 +1,9 @@ -import { PendingTransaction } from "../../mempool/PendingTransaction"; +import { PendingTransactionJSONType } from "../../mempool/PendingTransaction"; export interface TransactionStorage { - pushUserTransaction: (tx: PendingTransaction) => Promise; + pushUserTransaction: (tx: PendingTransactionJSONType) => Promise; - getPendingUserTransactions: () => Promise; + getPendingUserTransactions: () => Promise; removeTx: (txHashes: string[], type: "included" | "dropped") => Promise; @@ -16,7 +16,7 @@ export interface TransactionStorage { */ findTransaction: (hash: string) => Promise< | { - transaction: PendingTransaction; + transaction: PendingTransactionJSONType; block?: string; batch?: number; } diff --git a/packages/sequencer/test/integration/BlockProduction-test.ts b/packages/sequencer/test/integration/BlockProduction-test.ts index 71c4172f9..3113bd743 100644 --- a/packages/sequencer/test/integration/BlockProduction-test.ts +++ b/packages/sequencer/test/integration/BlockProduction-test.ts @@ -20,6 +20,7 @@ import { Path, Protocol, PROTOKIT_PREFIXES, + hashNetworkState, } from "@proto-kit/protocol"; import { Bool, Field, PrivateKey, PublicKey, Struct, UInt64 } from "o1js"; import "reflect-metadata"; @@ -206,7 +207,7 @@ export function testBlockProduction< expect(block).toBeDefined(); expect(block!.transactions).toHaveLength(1); - expect(block!.transactions[0].status.toBoolean()).toBe(true); + expect(block!.transactions[0].status).toBe(true); expect(block!.transactions[0].statusMessage).toBeUndefined(); expect(block!.transactions[0].stateTransitions).toHaveLength(3); @@ -231,8 +232,8 @@ export function testBlockProduction< expectDefined(latestBlockWithResult); expectDefined(latestBlockWithResult.result); expect( - latestBlockWithResult.result.afterNetworkState.hash().toString() - ).toStrictEqual(batch!.toNetworkState.hash().toString()); + hashNetworkState(latestBlockWithResult.result.afterNetworkState) + ).toStrictEqual(hashNetworkState(batch!.toNetworkState)); // Check if the batchstorage has received the block const batchStorage = sequencer.resolve("BatchStorage") as BatchStorage; @@ -282,7 +283,7 @@ export function testBlockProduction< expect(block).toBeDefined(); expect(block!.transactions).toHaveLength(1); - expect(block!.transactions[0].status.toBoolean()).toBe(true); + expect(block!.transactions[0].status).toBe(true); expect(block!.transactions[0].statusMessage).toBeUndefined(); expect(batch!.blockHashes).toHaveLength(1); @@ -312,7 +313,7 @@ export function testBlockProduction< const [block] = await test.produceBlockAndBatch(); expect(block?.transactions).toHaveLength(1); - expect(block?.transactions[0].status.toBoolean()).toBe(false); + expect(block?.transactions[0].status).toBe(false); expect(block?.transactions[0].statusMessage).toBe("Condition not met"); const balanceModule = runtime.resolve("Balance"); @@ -352,7 +353,7 @@ export function testBlockProduction< expect(block).toBeDefined(); expect(block!.transactions).toHaveLength(1); - expect(block!.transactions[0].status.toBoolean()).toBe(true); + expect(block!.transactions[0].status).toBe(true); expect(block!.transactions[0].statusMessage).toBeUndefined(); expect( @@ -408,7 +409,7 @@ export function testBlockProduction< expect(block2).toBeDefined(); expect(block2!.transactions).toHaveLength(1); - expect(block2!.transactions[0].status.toBoolean()).toBe(true); + expect(block2!.transactions[0].status).toBe(true); expect(block2!.transactions[0].statusMessage).toBeUndefined(); await expect( @@ -442,17 +443,17 @@ export function testBlockProduction< expect(block!.transactions).toHaveLength(numberTxs); range(0, numberTxs).forEach((index) => { - expect(block!.transactions[index].status.toBoolean()).toBe(true); + expect(block!.transactions[index].status).toBe(true); expect(block!.transactions[index].statusMessage).toBe(undefined); const transitions = block!.transactions[index].stateTransitions[1].stateTransitions; const fromBalance = increment * index; - expect(transitions[0].fromValue.value[0].toBigInt()).toStrictEqual( + expect(BigInt(transitions[0].from.value[0])).toStrictEqual( BigInt(fromBalance) ); - expect(transitions[0].toValue.value[0].toBigInt()).toStrictEqual( + expect(BigInt(transitions[0].to.value[0])).toStrictEqual( BigInt(fromBalance + increment) ); }); @@ -584,7 +585,7 @@ export function testBlockProduction< for (let k = 0; k < txsPerBlock; k++) { expect(block!.transactions).toHaveLength(txsPerBlock); - expect(block!.transactions[0].status.toBoolean()).toBe(true); + expect(block!.transactions[0].status).toBe(true); } } @@ -617,7 +618,7 @@ export function testBlockProduction< expect(block!.transactions).toHaveLength(1); - expect(block!.transactions[0].status.toBoolean()).toBe(true); + expect(block!.transactions[0].status).toBe(true); expect(block!.transactions[0].statusMessage).toBe(undefined); expect(batch!.blockHashes).toHaveLength(1); @@ -664,7 +665,7 @@ export function testBlockProduction< expect(block).toBeDefined(); expect(block!.transactions).toHaveLength(1); - expect(block!.transactions[0].status.toBoolean()).toBe(true); + expect(block!.transactions[0].status).toBe(true); expect(block!.transactions[0].statusMessage).toBeUndefined(); expect( @@ -710,13 +711,17 @@ export function testBlockProduction< }; const firstEventReduced = { eventName: firstExpectedEvent.eventName, - data: firstExpectedEvent.eventType.toFields(firstExpectedEvent.event), + data: firstExpectedEvent.eventType + .toFields(firstExpectedEvent.event) + .map((f) => f.toString()), source: "runtime", }; const secondEventReduced = { eventName: secondExpectedEvent.eventName, - data: secondExpectedEvent.eventType.toFields(secondExpectedEvent.event), + data: secondExpectedEvent.eventType + .toFields(secondExpectedEvent.event) + .map((f) => f.toString()), source: "runtime", }; diff --git a/packages/sequencer/test/integration/Mempool.test.ts b/packages/sequencer/test/integration/Mempool.test.ts index d5aec4037..2054b7f86 100644 --- a/packages/sequencer/test/integration/Mempool.test.ts +++ b/packages/sequencer/test/integration/Mempool.test.ts @@ -134,18 +134,18 @@ describe.each([["InMemory", InMemoryDatabase]])( const txs = await mempool.getTxs(); expect(txs).toHaveLength(6); - expect(txs[0].nonce.toBigInt()).toStrictEqual(0n); - expect(txs[0].sender).toStrictEqual(user1PublicKey); - expect(txs[1].nonce.toBigInt()).toStrictEqual(0n); - expect(txs[1].sender).toStrictEqual(user2PublicKey); - expect(txs[2].nonce.toBigInt()).toStrictEqual(0n); - expect(txs[2].sender).toStrictEqual(user3PublicKey); - expect(txs[3].nonce.toBigInt()).toStrictEqual(1n); - expect(txs[3].sender).toStrictEqual(user1PublicKey); - expect(txs[4].nonce.toBigInt()).toStrictEqual(1n); - expect(txs[4].sender).toStrictEqual(user2PublicKey); - expect(txs[5].nonce.toBigInt()).toStrictEqual(1n); - expect(txs[5].sender).toStrictEqual(user3PublicKey); + expect(txs[0].nonce).toStrictEqual("0"); + expect(txs[0].sender).toStrictEqual(user1PublicKey.toBase58()); + expect(txs[1].nonce).toStrictEqual("0"); + expect(txs[1].sender).toStrictEqual(user2PublicKey.toBase58()); + expect(txs[2].nonce).toStrictEqual("0"); + expect(txs[2].sender).toStrictEqual(user3PublicKey.toBase58()); + expect(txs[3].nonce).toStrictEqual("1"); + expect(txs[3].sender).toStrictEqual(user1PublicKey.toBase58()); + expect(txs[4].nonce).toStrictEqual("1"); + expect(txs[4].sender).toStrictEqual(user2PublicKey.toBase58()); + expect(txs[5].nonce).toStrictEqual("1"); + expect(txs[5].sender).toStrictEqual(user3PublicKey.toBase58()); }); it("transactions are returned in right order - medium", async () => { @@ -163,18 +163,18 @@ describe.each([["InMemory", InMemoryDatabase]])( const txs = await mempool.getTxs(); expect(txs).toHaveLength(6); - expect(txs[0].nonce.toBigInt()).toStrictEqual(0n); - expect(txs[0].sender).toStrictEqual(user1PublicKey); - expect(txs[1].nonce.toBigInt()).toStrictEqual(0n); - expect(txs[1].sender).toStrictEqual(user2PublicKey); - expect(txs[2].nonce.toBigInt()).toStrictEqual(1n); - expect(txs[2].sender).toStrictEqual(user1PublicKey); - expect(txs[3].nonce.toBigInt()).toStrictEqual(1n); - expect(txs[3].sender).toStrictEqual(user2PublicKey); - expect(txs[4].nonce.toBigInt()).toStrictEqual(0n); - expect(txs[4].sender).toStrictEqual(user3PublicKey); - expect(txs[5].nonce.toBigInt()).toStrictEqual(1n); - expect(txs[5].sender).toStrictEqual(user3PublicKey); + expect(txs[0].nonce).toStrictEqual("0"); + expect(txs[0].sender).toStrictEqual(user1PublicKey.toBase58()); + expect(txs[1].nonce).toStrictEqual("0"); + expect(txs[1].sender).toStrictEqual(user2PublicKey.toBase58()); + expect(txs[2].nonce).toStrictEqual("1"); + expect(txs[2].sender).toStrictEqual(user1PublicKey.toBase58()); + expect(txs[3].nonce).toStrictEqual("1"); + expect(txs[3].sender).toStrictEqual(user2PublicKey.toBase58()); + expect(txs[4].nonce).toStrictEqual("0"); + expect(txs[4].sender).toStrictEqual(user3PublicKey.toBase58()); + expect(txs[5].nonce).toStrictEqual("1"); + expect(txs[5].sender).toStrictEqual(user3PublicKey.toBase58()); }); it("transactions are returned in right order - harder", async () => { @@ -190,18 +190,18 @@ describe.each([["InMemory", InMemoryDatabase]])( const txs = await mempool.getTxs(); expect(txs).toHaveLength(6); - expect(txs[0].nonce.toBigInt()).toStrictEqual(0n); - expect(txs[0].sender).toStrictEqual(user1PublicKey); - expect(txs[1].nonce.toBigInt()).toStrictEqual(0n); - expect(txs[1].sender).toStrictEqual(user2PublicKey); - expect(txs[2].nonce.toBigInt()).toStrictEqual(0n); - expect(txs[2].sender).toStrictEqual(user3PublicKey); - expect(txs[3].nonce.toBigInt()).toStrictEqual(1n); - expect(txs[3].sender).toStrictEqual(user1PublicKey); - expect(txs[4].nonce.toBigInt()).toStrictEqual(1n); - expect(txs[4].sender).toStrictEqual(user2PublicKey); - expect(txs[5].nonce.toBigInt()).toStrictEqual(1n); - expect(txs[5].sender).toStrictEqual(user3PublicKey); + expect(txs[0].nonce).toStrictEqual("0"); + expect(txs[0].sender).toStrictEqual(user1PublicKey.toBase58()); + expect(txs[1].nonce).toStrictEqual("0"); + expect(txs[1].sender).toStrictEqual(user2PublicKey.toBase58()); + expect(txs[2].nonce).toStrictEqual("0"); + expect(txs[2].sender).toStrictEqual(user3PublicKey.toBase58()); + expect(txs[3].nonce).toStrictEqual("1"); + expect(txs[3].sender).toStrictEqual(user1PublicKey.toBase58()); + expect(txs[4].nonce).toStrictEqual("1"); + expect(txs[4].sender).toStrictEqual(user2PublicKey.toBase58()); + expect(txs[5].nonce).toStrictEqual("1"); + expect(txs[5].sender).toStrictEqual(user3PublicKey.toBase58()); }); it("transactions are returned in right order - hardest", async () => { @@ -219,18 +219,18 @@ describe.each([["InMemory", InMemoryDatabase]])( const txs = await mempool.getTxs(); expect(txs).toHaveLength(6); - expect(txs[0].nonce.toBigInt()).toStrictEqual(0n); - expect(txs[0].sender).toStrictEqual(user1PublicKey); - expect(txs[1].nonce.toBigInt()).toStrictEqual(0n); - expect(txs[1].sender).toStrictEqual(user2PublicKey); - expect(txs[2].nonce.toBigInt()).toStrictEqual(0n); - expect(txs[2].sender).toStrictEqual(user3PublicKey); - expect(txs[3].nonce.toBigInt()).toStrictEqual(1n); - expect(txs[3].sender).toStrictEqual(user1PublicKey); - expect(txs[4].nonce.toBigInt()).toStrictEqual(1n); - expect(txs[4].sender).toStrictEqual(user2PublicKey); - expect(txs[5].nonce.toBigInt()).toStrictEqual(1n); - expect(txs[5].sender).toStrictEqual(user3PublicKey); + expect(txs[0].nonce).toStrictEqual("0"); + expect(txs[0].sender).toStrictEqual(user1PublicKey.toBase58()); + expect(txs[1].nonce).toStrictEqual("0"); + expect(txs[1].sender).toStrictEqual(user2PublicKey.toBase58()); + expect(txs[2].nonce).toStrictEqual("0"); + expect(txs[2].sender).toStrictEqual(user3PublicKey.toBase58()); + expect(txs[3].nonce).toStrictEqual("1"); + expect(txs[3].sender).toStrictEqual(user1PublicKey.toBase58()); + expect(txs[4].nonce).toStrictEqual("1"); + expect(txs[4].sender).toStrictEqual(user2PublicKey.toBase58()); + expect(txs[5].nonce).toStrictEqual("1"); + expect(txs[5].sender).toStrictEqual(user3PublicKey.toBase58()); }); } ); diff --git a/packages/sequencer/test/integration/Proven.test.ts b/packages/sequencer/test/integration/Proven.test.ts index bca7f53af..e8ea6d024 100644 --- a/packages/sequencer/test/integration/Proven.test.ts +++ b/packages/sequencer/test/integration/Proven.test.ts @@ -211,7 +211,7 @@ describe.skip("Proven", () => { expectDefined(block); expect(block.transactions).toHaveLength(1); - expect(block.transactions[0].status.toBoolean()).toBe(true); + expect(block.transactions[0].status).toBe(true); expectDefined(batch); diff --git a/packages/sequencer/test/integration/StorageIntegration.test.ts b/packages/sequencer/test/integration/StorageIntegration.test.ts index 16f17bbe0..215092e4f 100644 --- a/packages/sequencer/test/integration/StorageIntegration.test.ts +++ b/packages/sequencer/test/integration/StorageIntegration.test.ts @@ -17,6 +17,7 @@ import { BlockStorage, VanillaTaskWorkerModules, AppChain, + UntypedStateTransition, } from "../../src"; import { DefaultTestingSequencerModules, @@ -35,7 +36,10 @@ function checkStateDiffEquality(stateDiff: StateRecord, state: StateEntry[]) { return value === undefined; } if (value !== undefined) { - return entry.value.find((v, i) => v !== value[i]) === undefined; + return ( + entry.value.find((v, i) => !v.equals(value[i]).toBoolean()) === + undefined + ); } } return false; @@ -136,25 +140,23 @@ describe.each([["InMemory", InMemoryDatabase]])( const { block } = blocks[0]; - expect(block.hash.toBigInt()).toStrictEqual( - generatedBlock.hash.toBigInt() - ); + expect(block.hash).toStrictEqual(generatedBlock.hash); const blockStorage = sequencer.resolve("BlockStorage") as BlockStorage; const block2 = await blockStorage.getBlockAt( - Number(blocks[0].block.height.toString()) + Number(blocks[0].block.height) ); expectDefined(block2); - expect(block2.hash.toBigInt()).toStrictEqual( - generatedBlock.hash.toBigInt() - ); - - const stateDiff = collectStateDiff( - block.transactions.flatMap((tx) => - tx.stateTransitions.flatMap((batch) => batch.stateTransitions) + expect(block2.hash).toStrictEqual(generatedBlock.hash); + const input = block.transactions.flatMap((tx) => + tx.stateTransitions.flatMap((batch) => + batch.stateTransitions.map((st) => + UntypedStateTransition.fromJSON(st) + ) ) ); + const stateDiff = collectStateDiff(input); const state = await unprovenState.getMany( Object.keys(stateDiff).map(Field) @@ -203,7 +205,7 @@ describe.each([["InMemory", InMemoryDatabase]])( const txs = await txStorage.getPendingUserTransactions(); expect(txs).toHaveLength(1); - expect(txs[0].hash().toString()).toStrictEqual(tx.hash().toString()); + expect(txs[0].hash).toStrictEqual(tx.hash); await sequencer.resolve("BlockTrigger").produceBlock(); diff --git a/packages/sequencer/test/integration/utils.ts b/packages/sequencer/test/integration/utils.ts index f863cf605..877391d83 100644 --- a/packages/sequencer/test/integration/utils.ts +++ b/packages/sequencer/test/integration/utils.ts @@ -7,6 +7,7 @@ import { } from "@proto-kit/module"; import { + PendingTransactionJSONType, StateRecord, UnsignedTransaction, UntypedStateTransition, @@ -18,7 +19,7 @@ export function createTransaction(spec: { method: [string, string]; args: ArgumentTypes; nonce: number; -}) { +}): PendingTransactionJSONType { const methodId = spec.runtime.dependencyContainer .resolve("MethodIdResolver") .getMethodId(spec.method[0], spec.method[1]); @@ -37,7 +38,9 @@ export function createTransaction(spec: { sender: spec.privateKey.toPublicKey(), nonce: UInt64.from(spec.nonce), isMessage: false, - }).sign(spec.privateKey); + }) + .sign(spec.privateKey) + .toJSON(); } export function collectStateDiff( diff --git a/packages/sequencer/test/protocol/production/sequencing/atomic-block-production.test.ts b/packages/sequencer/test/protocol/production/sequencing/atomic-block-production.test.ts index e34b72af6..bcb8ab6ac 100644 --- a/packages/sequencer/test/protocol/production/sequencing/atomic-block-production.test.ts +++ b/packages/sequencer/test/protocol/production/sequencing/atomic-block-production.test.ts @@ -115,6 +115,6 @@ describe("atomic block production", () => { const block = await trigger.produceBlock(); expectDefined(block); - expect(block.height.toString()).toBe("1"); + expect(block.height).toBe("1"); }); }); diff --git a/packages/sequencer/test/settlement/Settlement-only.ts b/packages/sequencer/test/settlement/Settlement-only.ts index c894bfbcf..9ef519695 100644 --- a/packages/sequencer/test/settlement/Settlement-only.ts +++ b/packages/sequencer/test/settlement/Settlement-only.ts @@ -12,6 +12,7 @@ import { import { BlockProverPublicInput, ContractArgsRegistry, + NetworkState, Protocol, SettlementContractModule, } from "@proto-kit/protocol"; @@ -155,6 +156,7 @@ export const settlementOnlyTestFn = ( async function createBatch( withTransactions: boolean, customNonce: number = 0, + // Why is it like this? txs: PendingTransaction[] = [] ) { const mempool = appChain.sequencer.resolve("Mempool") as PrivateMempool; @@ -171,7 +173,7 @@ export const settlementOnlyTestFn = ( await mempool.add(tx); } await mapSequential(txs, async (tx) => { - await mempool.add(tx); + await mempool.add(tx.toJSON()); }); const result = await trigger.produceBlockAndBatch(); @@ -275,10 +277,14 @@ export const settlementOnlyTestFn = ( address: settlementModule.getSettlementContractAddress(), }); const settlement = settlementModule.getSettlementContract(); + + const afterNetworkState = new NetworkState( + NetworkState.fromJSON(lastBlock!.result?.afterNetworkState!) + ); expectDefined(lastBlock); expectDefined(lastBlock.result); expect(settlement.networkStateHash.get().toString()).toStrictEqual( - lastBlock!.result.afterNetworkState.hash().toString() + afterNetworkState.hash().toString() ); expect(settlement.stateRoot.get().toString()).toStrictEqual( lastBlock!.result.stateRoot.toString() diff --git a/packages/sequencer/test/settlement/Settlement.ts b/packages/sequencer/test/settlement/Settlement.ts index 0b89e8871..a071be100 100644 --- a/packages/sequencer/test/settlement/Settlement.ts +++ b/packages/sequencer/test/settlement/Settlement.ts @@ -15,6 +15,7 @@ import { Protocol, ReturnType, SettlementContractModule, + hashNetworkState, } from "@proto-kit/protocol"; import { ClientAppChain, @@ -43,7 +44,6 @@ import { FungibleToken, FungibleTokenAdmin } from "mina-fungible-token"; import { ManualBlockTrigger, - PendingTransaction, PrivateMempool, BlockQueue, SettlementModule, @@ -56,6 +56,7 @@ import { VanillaTaskWorkerModules, Sequencer, InMemoryMinaSigner, + PendingTransactionJSONType, CircuitAnalysisModule, } from "../../src"; import { BlockProofSerializer } from "../../src/protocol/production/tasks/serializers/BlockProofSerializer"; @@ -229,7 +230,7 @@ export const settlementTestFn = ( async function createBatch( withTransactions: boolean, customNonce: number = 0, - txs: PendingTransaction[] = [] + txs: PendingTransactionJSONType[] = [] ) { const mempool = appChain.sequencer.resolve("Mempool") as PrivateMempool; if (withTransactions) { @@ -252,7 +253,7 @@ export const settlementTestFn = ( const [block, batch] = result; console.log( - `block ${block?.height.toString()} ${block?.fromMessagesHash.toString()} -> ${block?.toMessagesHash.toString()}` + `block ${block?.height} ${block?.fromMessagesHash} -> ${block?.toMessagesHash}` ); const proof = await blockSerializer .getBlockProofSerializer() @@ -532,7 +533,7 @@ export const settlementTestFn = ( expectDefined(lastBlock); expectDefined(lastBlock.result); expect(settlement.networkStateHash.get().toString()).toStrictEqual( - lastBlock!.result.afterNetworkState.hash().toString() + hashNetworkState(lastBlock!.result.afterNetworkState).toString() ); expect(settlement.stateRoot.get().toString()).toStrictEqual( lastBlock!.result.stateRoot.toString() @@ -651,11 +652,11 @@ export const settlementTestFn = ( console.log( `Empty Network State ${NetworkState.empty().hash().toString()}` ); - console.log(batch!.toNetworkState.hash().toString()); - console.log(batch2!.fromNetworkState.hash().toString()); + console.log(hashNetworkState(batch!.toNetworkState)); + console.log(hashNetworkState(batch2!.fromNetworkState)); - expect(batch!.toNetworkState.hash().toString()).toStrictEqual( - batch2!.fromNetworkState.hash().toString() + expect(hashNetworkState(batch!.toNetworkState)).toStrictEqual( + hashNetworkState(batch2!.fromNetworkState) ); expect(batch2!.blockHashes).toHaveLength(1); @@ -710,12 +711,12 @@ export const settlementTestFn = ( acc0L2Nonce += 2; expectDefined(block); - expect(block.transactions[0].status.toBoolean()).toBe(true); + expect(block.transactions[0].status).toBe(true); expectDefined(batch); console.log("Test networkstate"); - console.log(NetworkState.toJSON(block.networkState.during)); - console.log(NetworkState.toJSON(batch.toNetworkState)); + console.log(block.networkState.during); + console.log(batch.toNetworkState); const settlementResult = await trigger.settle(batch, { [bridgedTokenId.toString()]: { diff --git a/packages/stack/test/graphql/graphql.test.ts b/packages/stack/test/graphql/graphql.test.ts index 45b29206b..bc8405f14 100644 --- a/packages/stack/test/graphql/graphql.test.ts +++ b/packages/stack/test/graphql/graphql.test.ts @@ -152,7 +152,7 @@ describe("graphql client test", () => { const state = await appChain.query.network.unproven; expect(state).toBeDefined(); - expect(state!.block.height.toBigInt()).toBeGreaterThanOrEqual(0n); + expect(BigInt(state!.block.height)).toBeGreaterThanOrEqual(0n); }); it("should retrieve merkle witness", async () => { @@ -217,8 +217,8 @@ describe("graphql client test", () => { await tx.send(); const block = await trigger.produceBlock(); - const hash = block?.hash.toString()!; - const height = Number(block?.height.toBigInt()); + const hash = block?.hash!; + const height = Number(block?.height); const hashResult = await appChain.query.explorer.getBlock({ hash: hash }); const heightResult = await appChain.query.explorer.getBlock({ @@ -227,7 +227,7 @@ describe("graphql client test", () => { const heightParsedTx = heightResult?.transactions!; - const blockTxHash = block?.transactions[0].tx.toJSON().hash; + const blockTxHash = block?.transactions[0].tx.hash; const queryTxHash = heightParsedTx[0]?.tx?.hash; @@ -235,19 +235,19 @@ describe("graphql client test", () => { expect(blockTxHash).toBe(queryTxHash); // Block hashes should match - expect(block?.hash.toBigInt()).toBe(heightResult?.hash.toBigInt()); + expect(block?.hash).toBe(heightResult?.hash.toString()); // Block heights should match - expect(block?.height.toBigInt()).toBe(heightResult?.height.toBigInt()); + expect(block?.height).toBe(heightResult?.height.toString()); // Previous block hashes should match - expect(block?.previousBlockHash?.toBigInt()).toBe( - heightResult?.previousBlockHash?.toBigInt() + expect(block?.previousBlockHash).toBe( + heightResult?.previousBlockHash?.toString() ); // Transaction hashes should match - expect(block?.transactionsHash.toBigInt()).toBe( - heightResult?.transactionsHash.toBigInt() + expect(block?.transactionsHash).toBe( + heightResult?.transactionsHash.toString() ); // Both query methods should return the same result