diff --git a/packages/sequencer/src/settlement/SettlementModule.ts b/packages/sequencer/src/settlement/SettlementModule.ts index a80cfdb50..7e2b44a7e 100644 --- a/packages/sequencer/src/settlement/SettlementModule.ts +++ b/packages/sequencer/src/settlement/SettlementModule.ts @@ -206,13 +206,15 @@ export class SettlementModule this.utils.signTransaction(tx, [feepayer], this.getContractKeys()); - await this.transactionSender.proveAndSendTransaction(tx, "included"); + const { hash: transactionHash } = + await this.transactionSender.proveAndSendTransaction(tx, "included"); log.info("Settlement transaction send queued"); const settlement = { batches: [batch.height], promisedMessagesHash: latestSequenceStateHash.toString(), + transactionHash, }; await this.settlementStorage.pushSettlement(settlement); diff --git a/packages/sequencer/src/settlement/transactions/MinaTransactionSender.ts b/packages/sequencer/src/settlement/transactions/MinaTransactionSender.ts index 551476616..ac20a8ad1 100644 --- a/packages/sequencer/src/settlement/transactions/MinaTransactionSender.ts +++ b/packages/sequencer/src/settlement/transactions/MinaTransactionSender.ts @@ -25,6 +25,9 @@ export interface TxEvents extends EventsRecord { rejected: [any]; } +export type TxSendResult = + Input extends "none" ? void : { hash: string }; + @injectable() export class MinaTransactionSender { private txStatusEmitters: Record> = {}; @@ -113,10 +116,12 @@ export class MinaTransactionSender { return eventEmitter; } - public async proveAndSendTransaction( + public async proveAndSendTransaction< + Wait extends "sent" | "included" | "none", + >( transaction: Transaction, - waitOnStatus: "sent" | "included" | "none" = "none" - ) { + waitOnStatus: Wait + ): Promise> { const { publicKey, nonce } = transaction.transaction.feePayer.body; log.debug( @@ -167,16 +172,24 @@ export class MinaTransactionSender { const txStatus = await this.sendOrQueue(result.transaction); if (waitOnStatus !== "none") { - await new Promise((resolve, reject) => { - txStatus.on(waitOnStatus, () => { - log.info("Tx included"); - resolve(); - }); - txStatus.on("rejected", (error) => { - reject(error); - }); - }); + const waitInstruction: "sent" | "included" = waitOnStatus; + const hash = await new Promise>( + (resolve, reject) => { + txStatus.on(waitInstruction, (txSendResult) => { + log.info(`Tx ${txSendResult.hash} included`); + resolve(txSendResult); + }); + txStatus.on("rejected", (error) => { + reject(error); + }); + } + ); + + // Yeah that's not super clean, but couldn't figure out a better way tbh + // eslint-disable-next-line @typescript-eslint/consistent-type-assertions + return hash as TxSendResult; } - return txStatus; + // eslint-disable-next-line @typescript-eslint/consistent-type-assertions + return undefined as TxSendResult; } } diff --git a/packages/sequencer/src/settlement/transactions/MinaTransactionSimulator.ts b/packages/sequencer/src/settlement/transactions/MinaTransactionSimulator.ts index babbbc898..00fdee20f 100644 --- a/packages/sequencer/src/settlement/transactions/MinaTransactionSimulator.ts +++ b/packages/sequencer/src/settlement/transactions/MinaTransactionSimulator.ts @@ -11,10 +11,14 @@ import { UInt32, Transaction, } from "o1js"; -import { ReturnType } from "@proto-kit/protocol"; +import { + ACTIONS_EMPTY_HASH, + MINA_EVENT_PREFIXES, + ReturnType, +} from "@proto-kit/protocol"; import { match } from "ts-pattern"; import { inject, injectable } from "tsyringe"; -import { noop } from "@proto-kit/common"; +import { hashWithPrefix, noop, range } from "@proto-kit/common"; import { distinctByPredicate } from "../../helpers/utils"; import type { MinaBaseLayer } from "../../protocol/baselayer/MinaBaseLayer"; @@ -277,6 +281,13 @@ export class MinaTransactionSimulator { }).verificationKey = update.verificationKey.value; } + this.applyZkApp(account, au.body); + } + + private applyZkApp( + account: Account, + { update, actions }: AccountUpdate["body"] + ) { if (account.zkapp !== undefined) { const { appState } = update; for (let i = 0; i < 8; i++) { @@ -284,6 +295,18 @@ export class MinaTransactionSimulator { account.zkapp.appState[i] = appState[i].value; } } + + if (actions.data.length > 0) { + // We don't care about the correct historical array, so we just + // populate the full array with the current value + const previousActionState = + account.zkapp.actionState.at(0) ?? ACTIONS_EMPTY_HASH; + const newActionsHash = hashWithPrefix( + MINA_EVENT_PREFIXES.sequenceEvents, + [previousActionState, actions.hash] + ); + account.zkapp.actionState = range(0, 5).map(() => newActionsHash); + } } } }