diff --git a/biome.json b/biome.json index 75086295..15ddbbac 100644 --- a/biome.json +++ b/biome.json @@ -14,6 +14,7 @@ "!**/tsconfig*.json", "!**/*.compact", "!**/artifacts/**/*", + "!**/test-artifacts/**/*", "!**/coverage/**/*", "!**/dist/**/*", "!**/reports/**/*" diff --git a/contracts/package.json b/contracts/package.json index 7112a1d0..c066ebcb 100644 --- a/contracts/package.json +++ b/contracts/package.json @@ -29,6 +29,7 @@ "@openzeppelin-compact/compact": "workspace:^" }, "devDependencies": { + "@openzeppelin-compact/contracts-simulator": "workspace:^", "@tsconfig/node22": "^22.0.2", "@types/node": "22.18.0", "ts-node": "^10.9.2", diff --git a/contracts/src/access/test/ZOwnablePK.test.ts b/contracts/src/access/test/ZOwnablePK.test.ts index bcf337f1..390f1217 100644 --- a/contracts/src/access/test/ZOwnablePK.test.ts +++ b/contracts/src/access/test/ZOwnablePK.test.ts @@ -3,8 +3,6 @@ import { CompactTypeVector, convert_bigint_to_Uint8Array, persistentHash, - transientHash, - upgradeFromTransient, } from '@midnight-ntwrk/compact-runtime'; import { beforeEach, describe, expect, it } from 'vitest'; import type { ZswapCoinPublicKey } from '../../../artifacts/MockOwnable/contract/index.cjs'; @@ -101,7 +99,7 @@ describe('ZOwnablePK', () => { }); }); - describe('when not deployed and not initialized', () => { + describe('when not initialized correctly', () => { const isNotInit = false; beforeEach(() => { @@ -138,36 +136,6 @@ describe('ZOwnablePK', () => { }); }); - describe('when incorrect hashing algo (not SHA256) is used to generate initial owner id', () => { - // ZOwnablePK only supports sha256 for owner id calculation - // Obviously, using any other algo for the id will not work - const badHashAlgo = (pk: ZswapCoinPublicKey, nonce: Uint8Array) => { - const rt_type = new CompactTypeVector(2, new CompactTypeBytes(32)); - return upgradeFromTransient(transientHash(rt_type, [pk.bytes, nonce])); - }; - const secretNonce = ZOwnablePKPrivateState.generate().secretNonce; - const badOwnerId = badHashAlgo(Z_OWNER, secretNonce); - - beforeEach(() => { - ownable = new ZOwnablePKSimulator(badOwnerId, INSTANCE_SALT, isInit); - }); - // - type FailingCircuits = [method: keyof ZOwnablePKSimulator, args: unknown[]]; - const protectedCircuits: FailingCircuits[] = [ - ['assertOnlyOwner', []], - ['transferOwnership', [badOwnerId]], - ['renounceOwnership', []], - ]; - - it.each(protectedCircuits)('%s should fail', (circuitName, args) => { - ownable.callerCtx.setCaller(OWNER); - - expect(() => { - (ownable[circuitName] as (...args: unknown[]) => unknown)(...args); - }).toThrow('ZOwnablePK: caller is not the owner'); - }); - }); - describe('after initialization', () => { beforeEach(() => { // Create private state object and generate nonce @@ -216,40 +184,34 @@ describe('ZOwnablePK', () => { }); it('should transfer ownership', () => { - ownable.callerCtx.setCaller(OWNER); - ownable.transferOwnership(newIdHash); + ownable.as(OWNER).transferOwnership(newIdHash); expect(ownable.owner()).toEqual(newOwnerCommitment); // Old owner - ownable.callerCtx.setCaller(OWNER); expect(() => { - ownable.assertOnlyOwner(); + ownable.as(OWNER).assertOnlyOwner(); }).toThrow('ZOwnablePK: caller is not the owner'); // Unauthorized - ownable.callerCtx.setCaller(UNAUTHORIZED); expect(() => { - ownable.assertOnlyOwner(); + ownable.as(UNAUTHORIZED).assertOnlyOwner(); }).toThrow('ZOwnablePK: caller is not the owner'); // New owner - ownable.callerCtx.setCaller(NEW_OWNER); ownable.privateState.injectSecretNonce(Buffer.from(newOwnerNonce)); - expect(ownable.assertOnlyOwner()).not.to.throw; + expect(ownable.as(NEW_OWNER).assertOnlyOwner()).not.to.throw; }); it('should fail when transferring to id zero', () => { - ownable.callerCtx.setCaller(OWNER); const badId = new Uint8Array(32).fill(0); expect(() => { - ownable.transferOwnership(badId); + ownable.as(OWNER).transferOwnership(badId); }).toThrow('ZOwnablePK: invalid id'); }); it('should fail when unauthorized transfers ownership', () => { - ownable.callerCtx.setCaller(UNAUTHORIZED); expect(() => { - ownable.transferOwnership(newOwnerCommitment); + ownable.as(UNAUTHORIZED).transferOwnership(newOwnerCommitment); }).toThrow('ZOwnablePK: caller is not the owner'); }); @@ -260,8 +222,7 @@ describe('ZOwnablePK', () => { const beforeInstance = ownable.getPublicState().ZOwnablePK__counter; // Transfer - ownable.callerCtx.setCaller(OWNER); - ownable.transferOwnership(newOwnerCommitment); + ownable.as(OWNER).transferOwnership(newOwnerCommitment); // Check counter const afterInstance = ownable.getPublicState().ZOwnablePK__counter; @@ -280,8 +241,7 @@ describe('ZOwnablePK', () => { expect(initCommitment).toEqual(expInitCommitment); // Transfer ownership to self with the same id -> `H(pk, nonce)` - ownable.callerCtx.setCaller(OWNER); - ownable.transferOwnership(repeatedId); + ownable.as(OWNER).transferOwnership(repeatedId); // Check commitments don't match const newCommitment = ownable.owner(); @@ -297,46 +257,41 @@ describe('ZOwnablePK', () => { expect(newCommitment).toEqual(expNewCommitment); // Check same owner maintains permissions after transfer - ownable.callerCtx.setCaller(OWNER); - expect(ownable.assertOnlyOwner()).not.to.throw; + expect(ownable.as(OWNER).assertOnlyOwner()).not.to.throw; }); }); describe('renounceOwnership', () => { it('should renounce ownership', () => { - ownable.callerCtx.setCaller(OWNER); - ownable.renounceOwnership(); + ownable.as(OWNER).renounceOwnership(); // Check owner is reset expect(ownable.owner()).toEqual(new Uint8Array(32).fill(0)); // Check revoked permissions expect(() => { - ownable.assertOnlyOwner(); + ownable.as(OWNER).assertOnlyOwner(); }).toThrow('ZOwnablePK: caller is not the owner'); }); it('should fail when renouncing from unauthorized', () => { - ownable.callerCtx.setCaller(UNAUTHORIZED); expect(() => { - ownable.renounceOwnership(); - }).toThrow('ZOwnablePK: caller is not the owner'); + ownable.as(UNAUTHORIZED).renounceOwnership(); + }); }); it('should fail when renouncing from authorized with bad nonce', () => { - ownable.callerCtx.setCaller(OWNER); ownable.privateState.injectSecretNonce(BAD_NONCE); expect(() => { - ownable.renounceOwnership(); - }).toThrow('ZOwnablePK: caller is not the owner'); + ownable.as(OWNER).renounceOwnership(); + }); }); it('should fail when renouncing from unauthorized with bad nonce', () => { - ownable.callerCtx.setCaller(UNAUTHORIZED); ownable.privateState.injectSecretNonce(BAD_NONCE); expect(() => { - ownable.renounceOwnership(); - }).toThrow('ZOwnablePK: caller is not the owner'); + ownable.as(UNAUTHORIZED).renounceOwnership(); + }); }); }); @@ -347,8 +302,7 @@ describe('ZOwnablePK', () => { secretNonce, ); - ownable.callerCtx.setCaller(OWNER); - expect(ownable.assertOnlyOwner()).to.not.throw; + expect(ownable.as(OWNER).assertOnlyOwner()).to.not.throw; }); it('should fail when the authorized caller has the wrong nonce', () => { @@ -361,9 +315,8 @@ describe('ZOwnablePK', () => { ); // Set caller and call circuit - ownable.callerCtx.setCaller(OWNER); expect(() => { - ownable.assertOnlyOwner(); + ownable.as(OWNER).assertOnlyOwner(); }).toThrow('ZOwnablePK: caller is not the owner'); }); @@ -373,9 +326,8 @@ describe('ZOwnablePK', () => { secretNonce, ); - ownable.callerCtx.setCaller(UNAUTHORIZED); expect(() => { - ownable.assertOnlyOwner(); + ownable.as(UNAUTHORIZED).assertOnlyOwner(); }).toThrow('ZOwnablePK: caller is not the owner'); }); @@ -389,9 +341,8 @@ describe('ZOwnablePK', () => { ); // Set unauthorized caller and call circuit - ownable.callerCtx.setCaller(UNAUTHORIZED); expect(() => { - ownable.assertOnlyOwner(); + ownable.as(UNAUTHORIZED).assertOnlyOwner(); }).toThrow('ZOwnablePK: caller is not the owner'); }); }); @@ -522,12 +473,9 @@ describe('ZOwnablePK', () => { }); it('should allow anyone to transfer', () => { - ownable.callerCtx.setCaller(OWNER); const id = createIdHash(Z_OWNER, secretNonce); - expect(ownable._transferOwnership(id)).not.to.throw; - - ownable.callerCtx.setCaller(UNAUTHORIZED); - expect(ownable._transferOwnership(id)).not.to.throw; + expect(ownable.as(OWNER)._transferOwnership(id)).not.to.throw; + expect(ownable.as(UNAUTHORIZED)._transferOwnership(id)).not.to.throw; }); }); }); diff --git a/contracts/src/access/test/simulators/ZOwnablePKSimulator.ts b/contracts/src/access/test/simulators/ZOwnablePKSimulator.ts index e6e3f62b..82fcc70e 100644 --- a/contracts/src/access/test/simulators/ZOwnablePKSimulator.ts +++ b/contracts/src/access/test/simulators/ZOwnablePKSimulator.ts @@ -1,13 +1,10 @@ import { - type CircuitContext, - type CoinPublicKey, - emptyZswapLocalState, -} from '@midnight-ntwrk/compact-runtime'; -import { sampleContractAddress } from '@midnight-ntwrk/zswap'; + type BaseSimulatorOptions, + createSimulator, +} from '@openzeppelin-compact/contracts-simulator'; import { type ContractAddress, type Either, - type Ledger, ledger, Contract as MockOwnable, type ZswapCoinPublicKey, @@ -16,190 +13,56 @@ import { ZOwnablePKPrivateState, ZOwnablePKWitnesses, } from '../../witnesses/ZOwnablePKWitnesses.js'; -import type { - ContextlessCircuits, - ExtractImpureCircuits, - ExtractPureCircuits, - SimulatorOptions, -} from '../types/test.js'; -import { AbstractContractSimulator } from '../utils/AbstractContractSimulator.js'; -import { SimulatorStateManager } from '../utils/SimualatorStateManager.js'; -type OwnableSimOptions = SimulatorOptions< - ZOwnablePKPrivateState, - typeof ZOwnablePKWitnesses ->; +/** + * Type constructor args + */ +type ZOwnablePKArgs = readonly [ + owner: Uint8Array, + instanceSalt: Uint8Array, + isInit: boolean, +]; /** - * @description A simulator implementation of a contract for testing purposes. - * @template P - The private state type, fixed to ZOwnablePKPrivateState. - * @template L - The ledger type, fixed to Contract.Ledger. + * Base simulator + * @dev We deliberately use `any` as the base simulator type. + * This workaround is necessary due to type inference and declaration filegen + * in a monorepo environment. Attempting to fully preserve type information + * turns into type gymnastics. + * + * `any` can be safely removed once the contract simulator is consumed + * as a properly packaged dependency (outside the monorepo). */ -export class ZOwnablePKSimulator extends AbstractContractSimulator< +const ZOwnablePKSimulatorBase: any = createSimulator< ZOwnablePKPrivateState, - Ledger -> { - contract: MockOwnable; - readonly contractAddress: string; - private stateManager: SimulatorStateManager; - private callerOverride: CoinPublicKey | null = null; - private _witnesses: ReturnType; - - private _pureCircuitProxy?: ContextlessCircuits< - ExtractPureCircuits>, - ZOwnablePKPrivateState - >; - - private _impureCircuitProxy?: ContextlessCircuits< - ExtractImpureCircuits>, - ZOwnablePKPrivateState - >; + ReturnType, + ReturnType, + ZOwnablePKArgs +>({ + contractFactory: (witnesses) => + new MockOwnable(witnesses), + defaultPrivateState: () => ZOwnablePKPrivateState.generate(), + contractArgs: (owner, instanceSalt, isInit) => { + return [owner, instanceSalt, isInit]; + }, + ledgerExtractor: (state) => ledger(state), + witnessesFactory: () => ZOwnablePKWitnesses(), +}); +/** + * ZOwnablePKSimulator + */ +export class ZOwnablePKSimulator extends ZOwnablePKSimulatorBase { constructor( - initOwner: Uint8Array, + ownerId: Uint8Array, instanceSalt: Uint8Array, isInit: boolean, - options: OwnableSimOptions = {}, - ) { - super(); - - // Setup initial state - const { - privateState = ZOwnablePKPrivateState.generate(), - witnesses = ZOwnablePKWitnesses(), - coinPK = '0'.repeat(64), - address = sampleContractAddress(), - } = options; - const constructorArgs = [initOwner, instanceSalt, isInit]; - - this.contract = new MockOwnable(witnesses); - - this.stateManager = new SimulatorStateManager( - this.contract, - privateState, - coinPK, - address, - ...constructorArgs, - ); - this.contractAddress = this.circuitContext.transactionContext.address; - this._witnesses = witnesses; - this.contract = new MockOwnable(this._witnesses); - } - - get circuitContext() { - return this.stateManager.getContext(); - } - - set circuitContext(ctx) { - this.stateManager.setContext(ctx); - } - - getPublicState(): Ledger { - return ledger(this.circuitContext.transactionContext.state); - } - - /** - * @description Constructs a caller-specific circuit context. - * If a caller override is present, it replaces the current Zswap local state with an empty one - * scoped to the overridden caller. Otherwise, the existing context is reused as-is. - * @returns A circuit context adjusted for the current simulated caller. - */ - protected getCallerContext(): CircuitContext { - return { - ...this.circuitContext, - currentZswapLocalState: this.callerOverride - ? emptyZswapLocalState(this.callerOverride) - : this.circuitContext.currentZswapLocalState, - }; - } - - /** - * @description Initializes and returns a proxy to pure contract circuits. - * The proxy automatically injects the current circuit context into each call, - * and returns only the result portion of each circuit's output. - * @notice The proxy is created only when first accessed a.k.a lazy initialization. - * This approach is efficient in cases where only pure or only impure circuits are used, - * avoiding unnecessary proxy creation. - * @returns A proxy object exposing pure circuit functions without requiring explicit context. - */ - protected get pureCircuit(): ContextlessCircuits< - ExtractPureCircuits>, - ZOwnablePKPrivateState - > { - if (!this._pureCircuitProxy) { - this._pureCircuitProxy = this.createPureCircuitProxy< - MockOwnable['circuits'] - >(this.contract.circuits, () => this.circuitContext); - } - return this._pureCircuitProxy; - } - - /** - * @description Initializes and returns a proxy to impure contract circuits. - * The proxy automatically injects the current (possibly caller-modified) context into each call, - * and updates the circuit context with the one returned by the circuit after execution. - * @notice The proxy is created only when first accessed a.k.a. lazy initialization. - * This approach is efficient in cases where only pure or only impure circuits are used, - * avoiding unnecessary proxy creation. - * @returns A proxy object exposing impure circuit functions without requiring explicit context management. - */ - protected get impureCircuit(): ContextlessCircuits< - ExtractImpureCircuits>, - ZOwnablePKPrivateState - > { - if (!this._impureCircuitProxy) { - this._impureCircuitProxy = this.createImpureCircuitProxy< - MockOwnable['impureCircuits'] - >( - this.contract.impureCircuits, - () => this.getCallerContext(), - (ctx: any) => { - this.circuitContext = ctx; - }, - ); - } - return this._impureCircuitProxy; - } - - /** - * @description Resets the cached circuit proxy instances. - * This is useful if the underlying contract state or circuit context has changed, - * and you want to ensure the proxies are recreated with updated context on next access. - */ - public resetCircuitProxies(): void { - this._pureCircuitProxy = undefined; - this._impureCircuitProxy = undefined; - } - - /** - * @description Helper method that provides access to both pure and impure circuit proxies. - * These proxies automatically inject the appropriate circuit context when invoked. - * @returns An object containing `pure` and `impure` circuit proxy interfaces. - */ - public get circuits() { - return { - pure: this.pureCircuit, - impure: this.impureCircuit, - }; - } - - public get witnesses(): ReturnType { - return this._witnesses; - } - - public set witnesses(newWitnesses: ReturnType) { - this._witnesses = newWitnesses; - this.contract = new MockOwnable(this._witnesses); - } - - public overrideWitness( - key: K, - fn: (typeof this._witnesses)[K], + options: BaseSimulatorOptions< + ZOwnablePKPrivateState, + ReturnType + > = {}, ) { - this.witnesses = { - ...this._witnesses, - [key]: fn, - }; + super([ownerId, instanceSalt, isInit], options); } /** @@ -280,9 +143,10 @@ export class ZOwnablePKSimulator extends AbstractContractSimulator< injectSecretNonce: ( newNonce: Buffer, ): ZOwnablePKPrivateState => { - const currentState = this.stateManager.getContext().currentPrivateState; + const currentState = + this.circuitContextManager.getContext().currentPrivateState; const updatedState = { ...currentState, secretNonce: newNonce }; - this.stateManager.updatePrivateState(updatedState); + this.circuitContextManager.updatePrivateState(updatedState); return updatedState; }, @@ -291,17 +155,8 @@ export class ZOwnablePKSimulator extends AbstractContractSimulator< * @returns The secret nonce. */ getCurrentSecretNonce: (): Uint8Array => { - return this.stateManager.getContext().currentPrivateState.secretNonce; - }, - }; - - public callerCtx = { - /** - * @description Sets the caller context. - * @param caller The caller in context of the proceeding circuit calls. - */ - setCaller: (caller: CoinPublicKey) => { - this.callerOverride = caller; + return this.circuitContextManager.getContext().currentPrivateState + .secretNonce; }, }; } diff --git a/contracts/src/archive/test/simulators/ShieldedTokenSimulator.ts b/contracts/src/archive/test/simulators/ShieldedTokenSimulator.ts index 13f93026..6abf905d 100644 --- a/contracts/src/archive/test/simulators/ShieldedTokenSimulator.ts +++ b/contracts/src/archive/test/simulators/ShieldedTokenSimulator.ts @@ -31,7 +31,8 @@ import type { IContractSimulator } from '../types/test.js'; * @template L - The ledger type, fixed to Contract.Ledger. */ export class ShieldedTokenSimulator - implements IContractSimulator { + implements IContractSimulator +{ /** @description The underlying contract instance managing contract logic. */ readonly contract: MockShielded; diff --git a/contracts/src/token/test/simulators/MultiTokenSimulator.ts b/contracts/src/token/test/simulators/MultiTokenSimulator.ts index b15779ef..34b559ee 100644 --- a/contracts/src/token/test/simulators/MultiTokenSimulator.ts +++ b/contracts/src/token/test/simulators/MultiTokenSimulator.ts @@ -28,7 +28,8 @@ import type { IContractSimulator } from '../types/test.js'; * @template L - The ledger type, fixed to Contract.Ledger. */ export class MultiTokenSimulator - implements IContractSimulator { + implements IContractSimulator +{ /** @description The underlying contract instance managing contract logic. */ readonly contract: MockMultiToken; diff --git a/package.json b/package.json index ebc3ba70..974a7b55 100644 --- a/package.json +++ b/package.json @@ -4,9 +4,9 @@ "private": true, "packageManager": "yarn@4.10.3", "workspaces": [ - "compact/", "contracts/", - "docs/" + "docs/", + "packages/*" ], "scripts": { "docs": "turbo run docs --filter=docs", diff --git a/compact/package.json b/packages/compact/package.json similarity index 100% rename from compact/package.json rename to packages/compact/package.json diff --git a/compact/src/Builder.ts b/packages/compact/src/Builder.ts similarity index 100% rename from compact/src/Builder.ts rename to packages/compact/src/Builder.ts diff --git a/compact/src/Compiler.ts b/packages/compact/src/Compiler.ts similarity index 100% rename from compact/src/Compiler.ts rename to packages/compact/src/Compiler.ts diff --git a/compact/src/runBuilder.ts b/packages/compact/src/runBuilder.ts similarity index 100% rename from compact/src/runBuilder.ts rename to packages/compact/src/runBuilder.ts diff --git a/compact/src/runCompiler.ts b/packages/compact/src/runCompiler.ts similarity index 100% rename from compact/src/runCompiler.ts rename to packages/compact/src/runCompiler.ts diff --git a/compact/src/types/errors.ts b/packages/compact/src/types/errors.ts similarity index 100% rename from compact/src/types/errors.ts rename to packages/compact/src/types/errors.ts diff --git a/compact/test/Compiler.test.ts b/packages/compact/test/Compiler.test.ts similarity index 100% rename from compact/test/Compiler.test.ts rename to packages/compact/test/Compiler.test.ts diff --git a/compact/test/runCompiler.test.ts b/packages/compact/test/runCompiler.test.ts similarity index 100% rename from compact/test/runCompiler.test.ts rename to packages/compact/test/runCompiler.test.ts diff --git a/compact/tsconfig.json b/packages/compact/tsconfig.json similarity index 100% rename from compact/tsconfig.json rename to packages/compact/tsconfig.json diff --git a/compact/turbo.json b/packages/compact/turbo.json similarity index 100% rename from compact/turbo.json rename to packages/compact/turbo.json diff --git a/compact/vitest.config.ts b/packages/compact/vitest.config.ts similarity index 100% rename from compact/vitest.config.ts rename to packages/compact/vitest.config.ts diff --git a/packages/simulator/README.md b/packages/simulator/README.md new file mode 100644 index 00000000..43336b6d --- /dev/null +++ b/packages/simulator/README.md @@ -0,0 +1,381 @@ +# OpenZeppelin Compact Simulator + +OpenZeppelin Compact Simulator provides a testing and development environment for Compact contracts on the Midnight network, +allowing you to simulate contract behavior locally without blockchain deployment. + +## Features + +- ๐Ÿงช **Local Testing** - Test contracts without deployment. +- ๐ŸŽญ **Caller Context Simulation** - Test multi-user interactions. +- ๐Ÿ”ง **Witness Overrides** - Mock and spy on witness functions. +- ๐Ÿ“Š **State Inspection** - Access private and contract state. +- ๐Ÿš€ **Type-Safe** - Full TypeScript support with generics. + +## Quick Start + +```typescript +import { createSimulator } from '@openzeppelin-compact/contracts-simulator'; +import { Contract, ledger } from './artifacts/MyContract/contract/index.cjs'; + +// 1. Define your contract arguments type +type MyContractArgs = readonly [owner: Uint8Array, value: bigint]; + +// 2. Create the simulator +const MySimulator = createSimulator< + MyPrivateState, + ReturnType, + ReturnType, + MyContractArgs +>({ + contractFactory: (witnesses) => new Contract(witnesses), + defaultPrivateState: () => MyPrivateState.generate(), + contractArgs: (owner, value) => [owner, value], + ledgerExtractor: (state) => ledger(state), + witnessesFactory: () => MyWitnesses(), +}); + +// 3. Use it! +const sim = new MySimulator([ownerAddress, 100n], { coinPK: deployerPK }); +``` + +## Core Concepts + +### 1. Creating a Base Simulator + +The base simulator acts as a configuration class that the actual simulator will extend: + +```typescript +import { createSimulator } from '@openzeppelin-compact/contracts-simulator'; +import { Contract as MyContract, ledger } from './artifacts/MyContract/contract/index.cjs'; +import { MyContractWitnesses, MyContractPrivateState } from './MyContractWitnesses.js'; + +// Define contract constructor arguments as a tuple type +type MyContractArgs = readonly [arg1: bigint, arg2: string]; + +// Create the base simulator with full type information +const MyContractSimulatorBase = createSimulator< + MyContractPrivateState, // Private state type + ReturnType, // Ledger state type + ReturnType, // Witnesses type + MyContractArgs // Constructor args type +>({ + contractFactory: (witnesses) => new MyContract(witnesses), + defaultPrivateState: () => MyContractPrivateState.generate(), + contractArgs: (arg1, arg2) => [arg1, arg2], + ledgerExtractor: (state) => ledger(state), + witnessesFactory: () => MyContractWitnesses(), // Note: Must be a function! +}); +``` + +### โš ๏ธ Witness Factory Pattern + +The simulator requires `witnessesFactory` to be a function that returns witnesses, even for empty witnesses. +If the Compact contract has no witnesses: + +```typescript +// Some Compact contract examples use: +export const MyContractWitnesses = {}; + +// But for the simulator, wrap it in a function: +export const MyContractWitnesses = () => ({}); +``` + +This is required because the simulator API expects a factory function for consistency. + +### 2. Extending the Base Simulator + +Create your simulator class with a user-friendly API: + +```typescript +export class MyContractSimulator extends MyContractSimulatorBase { + constructor( + arg1: bigint, + arg2: string, + options: BaseSimulatorOptions< + MyContractPrivateState, + ReturnType + > = {} + ) { + // Bundle args into tuple for parent class + super([arg1, arg2], options); + } + + // Wrap contract's circuits with callable methods + public getValue(): bigint { + return this.circuits.impure.getValue(); + } + + public setValue(val: bigint): void { + this.circuits.impure.setValue(val); + } + + public transfer(to: Uint8Array, amount: bigint): void { + this.circuits.impure.transfer(to, amount); + } +} +``` + +### 3. Circuit Types + +#### Pure Circuits + +Compute outputs from inputs without reading or modifying state: + +```typescript +public add(a: bigint, b: bigint): bigint { + return this.circuits.pure.add(a, b); +} + +public calculateFee(amount: bigint): bigint { + return this.circuits.pure.calculateFee(amount); +} +``` + +#### Impure Circuits + +Read and/or modify the contract state: + +```typescript +public deposit(amount: bigint): void { + this.circuits.impure.deposit(amount); +} + +public getBalance(): bigint { + return this.circuits.impure.getBalance(); +} +``` + +## Advanced Features + +### ๐ŸŽญ Caller Context Management + +Simulate different users interacting with the contract: + +```typescript +// One-off caller context +simulator.as(alice).transfer(zBob, 100n); +simulator.as(bob).withdraw(50n); + +// Persistent caller context +simulator.setPersistentCaller(alice); +simulator.deposit(100n); // Called by alice +simulator.transfer(zBob, 50n); // Called by alice + +// Reset caller context +simulator.resetCaller(); +``` + +### โš ๏ธ Important: Key Encoding for Contracts + +**Same key, different formats:** + +- `alice` = CoinPublicKey (hex string) - for simulator context. +- `zAlice` = ZswapCoinPublicKey (encoded) - for contract parameters. + +```typescript +// For testing, create mock 64-character hex keys +const alice = '0'.repeat(63) + '1'; // CoinPublicKey format +const bob = '0'.repeat(63) + '2'; + +// Encode for contract use +const zAlice = { bytes: encodeCoinPublicKey(alice) }; // ZswapCoinPublicKey format +const zBob = { bytes: encodeCoinPublicKey(bob) }; + +// Incorrect - using wrong format for `bob` +simulator.as(alice).transfer(bob, 100n); // โŒ Contracts need encoded format + +// Correct - encoded as ZswapCoinPublicKey +simulator.as(alice).transfer(zBob, 100n); // โœ… +simulator.as(alice).transferFrom(zAlice, zBob, 100n); // โœ… +``` + +**Remember**: `as()` takes hex strings, contract circuits take encoded keys. + +### ๐Ÿ”ง Witness Overrides + +Perfect for testing edge cases and tracking witness usage: + +```typescript +// Override with fixed value for deterministic testing +const fixedNonce = new Uint8Array(32).fill(42); +simulator.overrideWitness('secretNonce', (context) => { + return [context.privateState, fixedNonce]; +}); + +// Track witness calls +let callCount = 0; +simulator.overrideWitness('secretValue', (context) => { + callCount++; + return [context.privateState, context.privateState.secretValue]; +}); + +simulator.someOperation(); +console.log(`Witness called ${callCount} times`); + +// Test error conditions +simulator.overrideWitness('requiredValue', (context) => { + return [context.privateState, null]; // Return invalid data +}); +``` + +### ๐Ÿ“Š State Inspection + +Access various levels of contract state: + +```typescript +// Get private state +const privateState = simulator.getPrivateState(); +console.log('Secret value:', privateState.secretValue); + +// Get public ledger state +const ledgerState = simulator.getPublicState(); +console.log('Public state:', ledgerState); + +// Get full contract state +const contractState = simulator.getContractState(); + +// Access complete circuit context +const context = simulator.circuitContext; +console.log('Zswap inputs', context.currentZswapLocalState.inputs); +``` + +## Testing Examples + +### Basic Test Structure + +```typescript +import { encodeCoinPublicKey } from '@midnight-ntwrk/compact-runtime'; +import { describe, it, expect, beforeEach } from 'vitest'; +import { MyContractSimulator } from './MyContractSimulator'; + +describe('MyContract', () => { + let simulator: MyContractSimulator; + let owner = '0'.repeat(63) + '1'; + let zOwner = { bytes: encodeCoinPublicKey(owner) }; + + beforeEach(() => { + simulator = new MyContractSimulator( + zOwner, + { privateState: MyPrivateState.generate() } + ); + }); + + it('should transfer ownership', () => { + let newOwner = '0'.repeat(63) + '2'; + let zNewOwner = { bytes: encodeCoinPublicKey(newOwner) }; + + simulator.as(owner).transferOwnership(zNewOwner); + + expect(simulator.getPublicState()._owner).toEqual(zNewOwner); + }); +}); +``` + +### Testing with Witness Overrides + +```typescript +it('should handle custom witness behavior', () => { + const customValue = new Uint8Array(32).fill(99); + let wasCalled = false; + + simulator.overrideWitness('secretValue', (context) => { + wasCalled = true; + return [context.privateState, customValue]; + }); + + simulator.performOperation(); + + expect(wasCalled).toBe(true); +}); +``` + +### Multi-User Interactions + +```typescript +it('should handle multi-user token transfers', () => { + // PKs + const alice = '0'.repeat(63) + '1'; + const zAlice = { bytes: encodeCoinPublicKey(alice) }; + + const bob = '0'.repeat(63) + '2'; + const zBob = { bytes: encodeCoinPublicKey(bob) }; + + const charlie = '0'.repeat(63) + '3'; + const zCharlie = { bytes: encodeCoinPublicKey(charlie) }; + + // Alice deposits + simulator.as(alice).deposit(1000n); + + // Alice transfers to Bob + simulator.as(alice).transfer(zBob, 300n); + + // Bob transfers to Charlie + simulator.as(bob).transfer(zCharlie, 100n); + + const state = simulator.getPublicState(); + expect(state._balances.lookup(zAlice)).toBe(700n); + expect(state._balances.lookup(zBob)).toBe(200n); + expect(state._balances.lookup(zCharlie)).toBe(100n); +}); +``` + +## Special Cases + +### Contracts with No Constructor Arguments + +```typescript +// Define empty args type +type NoArgs = readonly []; + +const SimpleSimulatorBase = createSimulator< + SimplePrivateState, + ReturnType, + ReturnType, + NoArgs // Empty tuple for no arguments +>({ + contractFactory: (witnesses) => new SimpleContract(witnesses), + defaultPrivateState: () => SimplePrivateState.generate(), + contractArgs: () => [], // Return empty array + ledgerExtractor: (state) => ledger(state), + witnessesFactory: () => SimpleWitnesses(), +}); + +export class SimpleSimulator extends SimpleSimulatorBase { + constructor(options: BaseSimulatorOptions<...> = {}) { + super([], options); // Pass empty array + } +} +``` + +## API Reference + +### BaseSimulatorOptions + +```typescript +interface BaseSimulatorOptions { + privateState?: P; // Initial private state + witnesses?: W; // Custom witness implementations + coinPK?: CoinPublicKey; // Coin public key (default: '0'.repeat(64)) + contractAddress?: ContractAddress; // Contract address (default: sampleContractAddress()) +} +``` + +### Core Methods + +| Method | Description | +|--------|-------------| +| `as(caller)` | Execute next operation as specified caller | +| `setPersistentCaller(caller)` | Set persistent caller for all operations | +| `resetCaller()` | Clears the caller context | +| `overrideWitness(key, fn)` | Override a specific witness function | +| `getPrivateState()` | Get current private state | +| `getPublicState()` | Get current public ledger state | +| `getContractState()` | Get full contract state | + +## Tips & Best Practices + +1. **Type Safety**: Always specify generic parameters for full type safety. +2. **Witness Testing**: Use witness overrides to test edge cases without modifying contract code. +3. **Deterministic Tests**: Override witnesses with fixed values for reproducible tests. +4. **State Validation**: Inspect state after operations to ensure correctness. +5. **Multi-User Testing**: Use caller context to simulate realistic multi-user scenarios. diff --git a/packages/simulator/package.json b/packages/simulator/package.json new file mode 100644 index 00000000..37c02b57 --- /dev/null +++ b/packages/simulator/package.json @@ -0,0 +1,43 @@ +{ + "name": "@openzeppelin-compact/contracts-simulator", + "private": true, + "version": "0.0.1", + "keywords": [ + "compact", + "compiler" + ], + "author": "", + "license": "MIT", + "description": "", + "type": "module", + "main": "./dist/index.js", + "types": "./dist/index.d.ts", + "exports": { + ".": { + "types": "./dist/index.d.ts", + "import": "./dist/index.js", + "require": "./dist/index.js" + } + }, + "engines": { + "node": ">=18" + }, + "scripts": { + "build": "tsc -p .", + "types": "tsc -p tsconfig.json --noEmit", + "test": "yarn vitest run", + "clean": "git clean -fXd" + }, + "devDependencies": { + "@midnight-ntwrk/ledger": "^4.0.0", + "@midnight-ntwrk/zswap": "^4.0.0", + "@tsconfig/node22": "^22.0.2", + "@types/node": "22.18.0", + "fast-check": "^3.15.0", + "typescript": "^5.8.2", + "vitest": "^3.1.3" + }, + "dependencies": { + "@midnight-ntwrk/compact-runtime": "^0.8.1" + } +} diff --git a/packages/simulator/src/core/AbstractSimulator.ts b/packages/simulator/src/core/AbstractSimulator.ts new file mode 100644 index 00000000..79258299 --- /dev/null +++ b/packages/simulator/src/core/AbstractSimulator.ts @@ -0,0 +1,179 @@ +import type { + CircuitContext, + CoinPublicKey, + ContractState, +} from '@midnight-ntwrk/compact-runtime'; +import type { + ContextlessCircuits, + IContractSimulator, +} from '../types/index.js'; + +/** + * Abstract base class for simulating contract behavior. + * + * Provides common functionality for managing circuit contexts and creating proxies + * for pure and impure circuit functions. + */ +export abstract class AbstractSimulator + implements IContractSimulator +{ + /** + * Single-use caller override (cleared after each circuit call). + * Set via `as(caller)` for one-time caller context switching. + */ + public callerOverride: CoinPublicKey | null = null; + + /** + * Persistent caller override (until explicitly cleared). + * Set via `setPersistentCaller(caller)` for ongoing caller context. + */ + public persistentCallerOverride: CoinPublicKey | null = null; + + /** The deployed contract's address */ + abstract readonly contractAddress: string; + + /** The current circuit context */ + abstract circuitContext: CircuitContext

; + + /** Retrieves the current public ledger state */ + abstract getPublicState(): L; + + /** + * Sets the caller context for the next circuit call only (auto-resets). + * + * @param caller - The public key to use as the caller for the next circuit execution + * @returns This simulator instance for method chaining + */ + public as(caller: CoinPublicKey): this { + this.callerOverride = caller; + return this; + } + + /** + * Sets a persistent caller that will be used for all subsequent circuit calls. + * + * @param caller - The public key to use as the caller for all future calls, or null to clear + */ + public setPersistentCaller(caller: CoinPublicKey | null): void { + this.persistentCallerOverride = caller; + } + + /** + * Clears persistent caller overrides. + * + * @returns This simulator instance for method chaining + */ + public resetCaller(): this { + this.callerOverride = null; + this.persistentCallerOverride = null; + return this; + } + + /** + * Retrieves the current private state from the circuit context. + * + * @returns The current private state of type P + */ + public getPrivateState(): P { + return this.circuitContext.currentPrivateState; + } + + /** + * Retrieves the original contract state from the circuit context. + * + * @returns The current contract state from the blockchain + */ + public getContractState(): ContractState { + return this.circuitContext.originalState; + } + + /** + * Creates a proxy wrapper around pure circuits. + * Pure circuits do not modify contract state, so only the result is returned. + * + * @param circuits - The pure circuit functions to wrap + * @param context - Function that provides the current circuit context + * @returns A contextless proxy that automatically injects context and extracts results + */ + public createPureCircuitProxy( + circuits: Circuits, + context: () => CircuitContext

, + ): ContextlessCircuits { + return new Proxy(circuits, { + /** + * Proxy getter that wraps circuit functions to handle context injection. + * + * @param target - The original circuits object + * @param prop - The property being accessed + * @param receiver - The proxy object + * @returns The original property or a wrapped function + */ + get: (target, prop, receiver) => { + const original = Reflect.get(target, prop, receiver); + if (typeof original !== 'function') return original; + + return (...args: unknown[]) => { + const fn = original as ( + ctx: CircuitContext

, + ...args: unknown[] + ) => { result: unknown }; + const result = fn(context(), ...args).result; + + // Auto-reset single-use caller override + this.callerOverride = null; + return result; + }; + }, + }) as ContextlessCircuits; + } + + /** + * Creates a proxy wrapper around impure circuits. + * Impure circuits can modify contract state, so the circuit context is updated accordingly. + * + * @param circuits - The impure circuit functions to wrap + * @param context - Function that provides the current circuit context + * @param updateContext - Function to update the circuit context after execution + * @returns A contextless proxy that handles context injection and state updates + */ + public createImpureCircuitProxy( + circuits: Circuits, + context: () => CircuitContext

, + updateContext: (ctx: CircuitContext

) => void, + ): ContextlessCircuits { + return new Proxy(circuits, { + /** + * Proxy getter that wraps circuit functions to handle context injection and updates. + * + * @param target - The original circuits object + * @param prop - The property being accessed + * @param receiver - The proxy object + * @returns The original property or a wrapped function + */ + get: (target, prop, receiver) => { + const original = Reflect.get(target, prop, receiver); + if (typeof original !== 'function') return original; + + return (...args: unknown[]) => { + const fn = original as ( + ctx: CircuitContext

, + ...args: unknown[] + ) => { result: unknown; context: CircuitContext

}; + + const { result, context: newCtx } = fn(context(), ...args); + updateContext(newCtx); + + // Auto-reset single-use caller override + this.callerOverride = null; + return result; + }; + }, + }) as ContextlessCircuits; + } + + /** + * Optional method to reset any cached circuit proxies. + * Implementations can override this to clear cached proxy instances. + */ + public resetCircuitProxies?(): void {} +} diff --git a/packages/simulator/src/core/CircuitContextManager.ts b/packages/simulator/src/core/CircuitContextManager.ts new file mode 100644 index 00000000..88f832a3 --- /dev/null +++ b/packages/simulator/src/core/CircuitContextManager.ts @@ -0,0 +1,92 @@ +import { + type CircuitContext, + type CoinPublicKey, + type ConstructorContext, + type ContractAddress, + type ContractState, + constructorContext, + type EncodedZswapLocalState, + QueryContext, +} from '@midnight-ntwrk/compact-runtime'; + +/** + * A composable utility class for managing Compact contract state in simulations. + * + * Handles initialization and lifecycle management of the `CircuitContext`, + * which includes private state, public (ledger) state, zswap local state, and transaction context. + */ +export class CircuitContextManager

{ + public context: CircuitContext

; + + /** + * Creates an instance of `CircuitContextManager`. + * + * @param contract - A compiled Compact contract instance exposing `initialState()` + * @param contract.initialState - Function that initializes contract state given a constructor context + * @param privateState - The initial private state to inject into the contract + * @param coinPK - The caller's coin public key + * @param contractAddress - Optional override for the contract's address + * @param contractArgs - Additional arguments to pass to the contract constructor + */ + constructor( + contract: { + initialState: ( + ctx: ConstructorContext

, + ...args: any[] + ) => { + currentPrivateState: P; + currentContractState: ContractState; + currentZswapLocalState: EncodedZswapLocalState; + }; + }, + privateState: P, + coinPK: CoinPublicKey, + contractAddress: ContractAddress, + ...contractArgs: any[] + ) { + const initCtx = constructorContext(privateState, coinPK); + + const { + currentPrivateState, + currentContractState, + currentZswapLocalState, + } = contract.initialState(initCtx, ...contractArgs); + + this.context = { + currentPrivateState, + currentZswapLocalState, + originalState: currentContractState, + transactionContext: new QueryContext( + currentContractState.data, + contractAddress, + ), + }; + } + + /** + * Retrieves the current `CircuitContext` + * + * @returns The current circuit context + */ + getContext(): CircuitContext

{ + return this.context; + } + + /** + * Replaces the internal `CircuitContext` with a new one. + * + * @param newContext - The new circuit context to replace the current one + */ + setContext(newContext: CircuitContext

) { + this.context = newContext; + } + + /** + * Updates just the private state inside the existing context. + * + * @param newPrivateState - The new private state to set in the current context + */ + updatePrivateState(newPrivateState: P) { + this.context.currentPrivateState = newPrivateState; + } +} diff --git a/packages/simulator/src/core/ContractSimulator.ts b/packages/simulator/src/core/ContractSimulator.ts new file mode 100644 index 00000000..0c360b44 --- /dev/null +++ b/packages/simulator/src/core/ContractSimulator.ts @@ -0,0 +1,60 @@ +import { + type CircuitContext, + emptyZswapLocalState, +} from '@midnight-ntwrk/compact-runtime'; +import { AbstractSimulator } from './AbstractSimulator.js'; +import type { CircuitContextManager } from './CircuitContextManager.js'; + +/** + * Enhanced base class for simulating Compact contract behavior with state management. + * + * Extends `AbstractSimulator` with: + * - Simplified state management through `CircuitContextManager` + * - Circuit context getters/setters that delegate to the state manager + * - Caller context handling that respects both single-use and persistent overrides + */ +export abstract class ContractSimulator extends AbstractSimulator { + /** + * State manager that handles circuit context, private state, and contract state lifecycle. + * Must be initialized by concrete subclasses in their constructor. + */ + public circuitContextManager!: CircuitContextManager

; + + /** Retrieves the current public ledger state */ + abstract getPublicState(): L; + + /** + * Constructs a circuit context with appropriate caller information. + * + * Checks for caller overrides in priority order: + * 1. Single-use override (set via `as(caller)`) + * 2. Persistent override (set via `setPersistentCaller(caller)`) + * 3. Default caller context + * + * @returns A CircuitContext with the appropriate caller information applied + */ + public getCallerContext(): CircuitContext

{ + const activeCaller = this.callerOverride || this.persistentCallerOverride; + + return { + ...this.circuitContext, + currentZswapLocalState: activeCaller + ? emptyZswapLocalState(activeCaller) + : this.circuitContext.currentZswapLocalState, + }; + } + + /** + * Gets the current circuit context from the state manager + * + * @returns The current circuit context + */ + get circuitContext(): CircuitContext

{ + return this.circuitContextManager.getContext(); + } + + /** Updates the circuit context in the state manager */ + set circuitContext(ctx: CircuitContext

) { + this.circuitContextManager.setContext(ctx); + } +} diff --git a/packages/simulator/src/factory/SimulatorConfig.ts b/packages/simulator/src/factory/SimulatorConfig.ts new file mode 100644 index 00000000..64e852d8 --- /dev/null +++ b/packages/simulator/src/factory/SimulatorConfig.ts @@ -0,0 +1,36 @@ +import type { StateValue } from '@midnight-ntwrk/compact-runtime'; +/** + * Configuration interface for the simulator factory. + * @template P - Private state type + * @template L - Ledger state type + * @template W - Witnesses type + * @template TArgs - Tuple type of contract-specific arguments passed to CircuitContextManager + */ +export interface SimulatorConfig< + P, + L, + W, + TArgs extends readonly any[] = readonly any[], +> { + /** Factory function to create the contract instance */ + contractFactory: (witnesses: W) => any; + /** Function to generate default private state */ + defaultPrivateState: () => P; + /** + * Function to process contract-specific arguments for CircuitContextManager initialization. + * Receives the arguments as spread parameters and returns them as an array + * to be passed to CircuitContextManager after the standard parameters (contract, privateState, coinPK, contractAddress). + * + * @example + * // For a contract with owner and salt arguments: + * contractArgs: (owner, instanceSalt) => [owner, instanceSalt] + * + * // For a contract with no additional arguments: + * contractArgs: () => [] + */ + contractArgs: (...args: TArgs) => any[]; + /** Function to extract ledger state from contract state */ + ledgerExtractor: (state: StateValue) => L; + /** Factory function to create default witnesses */ + witnessesFactory: () => W; +} diff --git a/packages/simulator/src/factory/createSimulator.ts b/packages/simulator/src/factory/createSimulator.ts new file mode 100644 index 00000000..9f1dd249 --- /dev/null +++ b/packages/simulator/src/factory/createSimulator.ts @@ -0,0 +1,177 @@ +import type { WitnessContext } from '@midnight-ntwrk/compact-runtime'; +import { sampleContractAddress } from '@midnight-ntwrk/zswap'; +import { CircuitContextManager } from '../core/CircuitContextManager.js'; +import { ContractSimulator } from '../core/ContractSimulator.js'; +import type { IMinimalContract } from '../types/Contract.js'; +import type { BaseSimulatorOptions } from '../types/Options.js'; +import type { SimulatorConfig } from './SimulatorConfig.js'; + +/** + * Factory function to create simulator classes with consistent boilerplate elimination. + * + * This factory creates a class that extends ContractSimulator with all the common + * functionality needed for contract simulation, including: + * - Witness management + * - State management + * - Circuit proxy creation + * - Options handling + * + * @param config - Configuration object defining how to create and manage the simulator + * @returns A class constructor that can be extended to create specific simulators + */ +export function createSimulator( + config: SimulatorConfig, +) { + return class GeneratedSimulator extends ContractSimulator { + contract: IMinimalContract; + readonly contractAddress: string; + public _witnesses: W; + + /** + * Creates a new simulator instance with explicit contract args and options + */ + constructor( + contractArgs: TArgs = [] as any, + options: BaseSimulatorOptions = {}, + ) { + super(); + + const { + privateState = config.defaultPrivateState(), + witnesses = config.witnessesFactory(), + coinPK = '0'.repeat(64), + contractAddress = sampleContractAddress(), + } = options; + + this._witnesses = witnesses; + this.contract = config.contractFactory(this._witnesses); + + const processedArgs = config.contractArgs(...contractArgs); + + this.circuitContextManager = new CircuitContextManager( + this.contract, + privateState, + coinPK, + contractAddress, + ...processedArgs, + ); + + this.contractAddress = this.circuitContext.transactionContext.address; + } + + public _pureCircuitProxy?: any; + public _impureCircuitProxy?: any; + + /** + * Gets the pure circuit proxy, creating it lazily if it doesn't exist. + * + * @returns The pure circuit proxy for executing read-only contract methods + */ + public get pureCircuit() { + if (!this._pureCircuitProxy) { + this._pureCircuitProxy = this.createPureCircuitProxy( + this.contract.circuits, + () => this.circuitContext, + ); + } + return this._pureCircuitProxy; + } + + /** + * Gets the impure circuit proxy, creating it lazily if it doesn't exist. + * + * @returns The impure circuit proxy for executing state-modifying contract methods + */ + public get impureCircuit() { + if (!this._impureCircuitProxy) { + this._impureCircuitProxy = this.createImpureCircuitProxy( + this.contract.impureCircuits, + () => this.getCallerContext(), + (ctx) => { + this.circuitContext = ctx; + }, + ); + } + return this._impureCircuitProxy; + } + + /** + * Gets both pure and impure circuit proxies. + * + * @returns Object containing both pure and impure circuit proxies + */ + public get circuits() { + return { + pure: this.pureCircuit, + impure: this.impureCircuit, + }; + } + + /** + * Resets cached circuit proxies, forcing re-initialization on next access. + */ + public resetCircuitProxies(): void { + this._pureCircuitProxy = undefined; + this._impureCircuitProxy = undefined; + } + + /** + * Extracts the public ledger state from the current contract state. + * + * @returns The current public state of the contract + */ + getPublicState(): L { + return config.ledgerExtractor( + this.circuitContext.transactionContext.state, + ); + } + + // Common witness management methods + /** + * Gets the current witness functions. + * + * @returns The current witness function implementations + */ + public get witnesses(): W { + return this._witnesses; + } + + /** + * Sets new witness functions and recreates the contract with them. + * + * @param newWitnesses - The new witness function implementations to use + */ + public set witnesses(newWitnesses: W) { + this._witnesses = newWitnesses; + this.contract = config.contractFactory(this._witnesses); + this.resetCircuitProxies(); + } + + /** + * Overrides a specific witness function while keeping others unchanged. + * + * @param key - The key of the witness function to override + * @param fn - The new implementation for the witness function + */ + public overrideWitness(key: K, fn: W[K]) { + this.witnesses = { + ...this._witnesses, + [key]: fn, + } as W; + } + + /** + * Gets the current witness context with the proper structure for witness function calls. + * + * @returns The current witness context that can be passed to witness functions + */ + public getWitnessContext(): WitnessContext { + const circuitCtx = this.circuitContext; + return { + ledger: this.getPublicState(), + privateState: circuitCtx.currentPrivateState, + contractAddress: circuitCtx.transactionContext.address, + }; + } + }; +} diff --git a/packages/simulator/src/index.ts b/packages/simulator/src/index.ts new file mode 100644 index 00000000..0b71a763 --- /dev/null +++ b/packages/simulator/src/index.ts @@ -0,0 +1,13 @@ +// biome-ignore lint/performance/noBarrelFile: entrypoint module +export { AbstractSimulator } from './core/AbstractSimulator.js'; +export { CircuitContextManager } from './core/CircuitContextManager.js'; +export { ContractSimulator } from './core/ContractSimulator.js'; +export { createSimulator } from './factory/createSimulator.js'; +export type { SimulatorConfig } from './factory/SimulatorConfig.js'; +export type { + ContextlessCircuits, + ExtractImpureCircuits, + ExtractPureCircuits, + IContractSimulator, +} from './types/index.js'; +export type { BaseSimulatorOptions } from './types/Options.js'; diff --git a/packages/simulator/src/proxies/CircuitProxies.ts b/packages/simulator/src/proxies/CircuitProxies.ts new file mode 100644 index 00000000..df0c1fe0 --- /dev/null +++ b/packages/simulator/src/proxies/CircuitProxies.ts @@ -0,0 +1,81 @@ +import type { CircuitContext } from '@midnight-ntwrk/compact-runtime'; +import type { + ContextlessCircuits, + ExtractImpureCircuits, + ExtractPureCircuits, +} from '../types/index.js'; + +/** + * Creates lazily-initialized circuit proxies for pure and impure contract functions. + * + * This utility function helps create consistent circuit proxies across different + * simulator implementations while providing lazy initialization for performance. + * @param contract - The contract instance containing circuits and impureCircuits + * @param getContext - Function to retrieve the current circuit context + * @param getCallerContext - Function to retrieve the caller's circuit context + * @param updateContext - Function to update the circuit context after impure operations + * @param createPureProxy - Factory function for creating pure circuit proxies + * @param createImpureProxy - Factory function for creating impure circuit proxies + * @returns Object with lazy circuit proxies and reset functionality + */ +export function createCircuitProxies< + P, + ContractType extends { + circuits: Record; + impureCircuits: Record; + }, +>( + contract: ContractType, + getContext: () => CircuitContext

, + getCallerContext: () => CircuitContext

, + updateContext: (ctx: CircuitContext

) => void, + createPureProxy: >( + circuits: C, + context: () => CircuitContext

, + ) => ContextlessCircuits, + createImpureProxy: >( + circuits: C, + context: () => CircuitContext

, + updateContext: (ctx: CircuitContext

) => void, + ) => ContextlessCircuits, +) { + let pureProxy: + | ContextlessCircuits, P> + | undefined; + let impureProxy: + | ContextlessCircuits, P> + | undefined; + + return { + /** + * Gets the circuit proxies, creating them lazily if they don't exist. + * @returns Object containing pure and impure circuit proxies + */ + get circuits() { + if (!pureProxy) { + pureProxy = createPureProxy( + contract.circuits as ExtractPureCircuits, + getContext, + ); + } + if (!impureProxy) { + impureProxy = createImpureProxy( + contract.impureCircuits as ExtractImpureCircuits, + getCallerContext, + updateContext, + ); + } + return { + pure: pureProxy, + impure: impureProxy, + }; + }, + /** + * Resets the cached circuit proxies, forcing re-initialization on next access. + */ + resetProxies() { + pureProxy = undefined; + impureProxy = undefined; + }, + }; +} diff --git a/packages/simulator/src/types/Circuit.ts b/packages/simulator/src/types/Circuit.ts new file mode 100644 index 00000000..232b28c7 --- /dev/null +++ b/packages/simulator/src/types/Circuit.ts @@ -0,0 +1,40 @@ +import type { CircuitContext } from '@midnight-ntwrk/compact-runtime'; + +/** + * Extracts pure circuits from a contract type. + * Pure circuits are those in `circuits` but not in `impureCircuits`. + */ +export type ExtractPureCircuits = TContract extends { + circuits: infer TCircuits; + impureCircuits: infer TImpureCircuits; +} + ? Omit + : never; + +/** + * Extracts impure circuits from a contract type. + * Impure circuits are those in `impureCircuits`. + */ +export type ExtractImpureCircuits = TContract extends { + impureCircuits: infer TImpureCircuits; +} + ? TImpureCircuits + : never; + +/** + * Transforms circuit functions by removing the explicit `CircuitContext` parameter. + * + * Each original circuit function has signature: + * `(ctx: CircuitContext, ...args) => { result: R; context?: CircuitContext }` + * + * The transformed function takes the same parameters except the context, + * and returns the `result` directly. + */ +export type ContextlessCircuits = { + [K in keyof Circuits]: Circuits[K] extends ( + ctx: CircuitContext, + ...args: infer P + ) => { result: infer R; context?: CircuitContext } + ? (...args: P) => R + : never; +}; diff --git a/packages/simulator/src/types/Contract.ts b/packages/simulator/src/types/Contract.ts new file mode 100644 index 00000000..759188df --- /dev/null +++ b/packages/simulator/src/types/Contract.ts @@ -0,0 +1,45 @@ +import type { + ContractState, + EncodedZswapLocalState, +} from '@midnight-ntwrk/compact-runtime'; + +/** + * Minimal interface representing the expected structure of generated Compact contract artifacts. + * + * This interface defines the bare minimum properties and methods that the simulator + * framework expects from any generated contract, allowing type-safe interaction + * with contracts while acknowledging that the generated artifacts don't provide + * comprehensive TypeScript types. + */ +export interface IMinimalContract { + /** + * Contract initialization method that sets up initial state. + * + * @param ctx - Constructor context containing initial private state and coin public key + * @param args - Additional arguments passed to the contract constructor + * @returns Object containing the initialized contract states + */ + initialState: ( + ctx: any, + ...args: any[] + ) => { + /** The contract's private state after initialization */ + currentPrivateState: any; + /** The contract's on-chain state after initialization */ + currentContractState: ContractState; + /** The Zswap local state for transaction context */ + currentZswapLocalState: EncodedZswapLocalState; + }; + + /** + * Pure circuit functions that don't modify contract state. + * These are read-only operations that can be called without changing the blockchain state. + */ + circuits: Record any>; + + /** + * Impure circuit functions that can modify contract state. + * These operations may change the contract's state and require transaction context. + */ + impureCircuits: Record any>; +} diff --git a/packages/simulator/src/types/Options.ts b/packages/simulator/src/types/Options.ts new file mode 100644 index 00000000..e876530e --- /dev/null +++ b/packages/simulator/src/types/Options.ts @@ -0,0 +1,21 @@ +import type { + CoinPublicKey, + ContractAddress, +} from '@midnight-ntwrk/compact-runtime'; + +/** + * Base configuration options for simulator constructors. + * + * @template P - Private state type + * @template W - Witnesses type + */ +export type BaseSimulatorOptions = { + /** Initial private state (uses default if not provided) */ + privateState?: P; + /** Witness functions (uses default if not provided) */ + witnesses?: W; + /** Coin public key for transactions */ + coinPK?: CoinPublicKey; + /** Contract deployment address */ + contractAddress?: ContractAddress; +}; diff --git a/packages/simulator/src/types/Simulator.ts b/packages/simulator/src/types/Simulator.ts new file mode 100644 index 00000000..5058dd51 --- /dev/null +++ b/packages/simulator/src/types/Simulator.ts @@ -0,0 +1,37 @@ +import type { + CircuitContext, + ContractState, +} from '@midnight-ntwrk/compact-runtime'; + +/** + * Interface defining a generic contract simulator. + * + * @template P - Type representing the private contract state. + * @template L - Type representing the public ledger state. + */ +export interface IContractSimulator { + /** + * The deployed contract's address. + */ + readonly contractAddress: string; + + /** + * The current circuit context holding the contract state. + */ + circuitContext: CircuitContext

; + + /** + * Returns the current public ledger state. + */ + getPublicState(): L; + + /** + * Returns the current private contract state. + */ + getPrivateState(): P; + + /** + * Returns the original contract state. + */ + getContractState(): ContractState; +} diff --git a/packages/simulator/src/types/index.ts b/packages/simulator/src/types/index.ts new file mode 100644 index 00000000..c4fb0509 --- /dev/null +++ b/packages/simulator/src/types/index.ts @@ -0,0 +1,10 @@ +// Re-export all types from type modules + +export type { + ContextlessCircuits, + ExtractImpureCircuits, + ExtractPureCircuits, +} from './Circuit.js'; +export type { IMinimalContract } from './Contract.js'; +export type { BaseSimulatorOptions } from './Options.js'; +export type { IContractSimulator } from './Simulator.js'; diff --git a/packages/simulator/src/utils/CircuitContextUtils.ts b/packages/simulator/src/utils/CircuitContextUtils.ts new file mode 100644 index 00000000..a121d823 --- /dev/null +++ b/packages/simulator/src/utils/CircuitContextUtils.ts @@ -0,0 +1,60 @@ +import { + type CircuitContext, + type CoinPublicKey, + type ContractAddress, + type ContractState, + emptyZswapLocalState, + QueryContext, +} from '@midnight-ntwrk/compact-runtime'; +import type { IContractSimulator } from '../types/index.js'; + +/** + * Constructs a `CircuitContext` from the given state and sender information. + * + * This is typically used at runtime to provide the necessary context + * for executing circuits, including contract state, private state, + * sender identity, and transaction data. + * @param privateState - The private state data specific to the contract + * @param contractState - The current contract state from the blockchain + * @param sender - The public key of the transaction sender + * @param contractAddress - The address of the contract being executed + * @returns A complete CircuitContext ready for circuit execution + */ +export function useCircuitContext

( + privateState: P, + contractState: ContractState, + sender: CoinPublicKey, + contractAddress: ContractAddress, +): CircuitContext

{ + return { + originalState: contractState, + currentPrivateState: privateState, + transactionContext: new QueryContext(contractState.data, contractAddress), + currentZswapLocalState: emptyZswapLocalState(sender), + }; +} + +/** + * Prepares a new `CircuitContext` using the given sender and contract. + * + * Useful for mocking or updating the circuit context with a custom sender. + * @param contract - The contract simulator instance to extract state from + * @param sender - The public key of the new sender to use in the context + * @returns A CircuitContext configured with the contract's current state and the specified sender + */ +export function useCircuitContextSender< + P, + L, + C extends IContractSimulator, +>(contract: C, sender: CoinPublicKey): CircuitContext

{ + const currentPrivateState = contract.getPrivateState(); + const originalState = contract.getContractState(); + const contractAddress = contract.contractAddress; + + return { + originalState, + currentPrivateState, + transactionContext: new QueryContext(originalState.data, contractAddress), + currentZswapLocalState: emptyZswapLocalState(sender), + }; +} diff --git a/packages/simulator/test/README.md b/packages/simulator/test/README.md new file mode 100644 index 00000000..96754a6e --- /dev/null +++ b/packages/simulator/test/README.md @@ -0,0 +1,35 @@ +# Simulator Tests + +All tests live under the `test/` directory, which is organized by type and purpose. +The testing structure ensures consistency, reusability, +and clear separation between unit, integration, and shared test resources. + +## ๐Ÿ“ Structure + +```bash +test/ + โ”œโ”€โ”€ fixtures/ + โ”œโ”€โ”€ integration/ + โ””โ”€โ”€ unit/ +``` + +### ๐Ÿงฉ Fixtures (`test/fixtures`) + +Test fixtures contain reusable components and resources shared across tests. +These help keep test files clean and consistent. + +- `test-contracts/` โ€“ Smart contracts and associated simulators used exclusively for testing. +- `artifacts/` โ€“ Precompiled contract artifacts needed by tests. +- `utils/` โ€“ Helper functions and common utilities for key encoding and keypair generation. + +### ๐Ÿ”— Integration Tests (`test/integration`) + +The `integration/` directory contains tests that verify how multiple components interact as a system. +These tests use simulated dependencies (from `fixtures/`) +to ensure contracts and the simulator package work together as expected in end-to-end scenarios. + +### ๐Ÿงช Unit Tests (`test/unit`) + +The `unit/` directory contains isolated tests focused on individual functions and classes. +These tests mock external dependencies and use lightweight fixtures to validate correctness +and edge cases in a controlled environment. diff --git a/packages/simulator/test/fixtures/sample-contracts/SampleZOwnable.compact b/packages/simulator/test/fixtures/sample-contracts/SampleZOwnable.compact new file mode 100644 index 00000000..8e045ada --- /dev/null +++ b/packages/simulator/test/fixtures/sample-contracts/SampleZOwnable.compact @@ -0,0 +1,72 @@ +// Sample contract for testing +// DO NOT USE IN PRODUCTION!!! + +pragma language_version >= 0.16.0; + +import CompactStandardLibrary; + +export { ZswapCoinPublicKey, ContractAddress, Either }; +export ledger _ownerCommitment: Bytes<32>; +export ledger _counter: Counter; +export sealed ledger _instanceSalt: Bytes<32>; + +witness secretNonce(): Bytes<32>; + +constructor(ownerId: Bytes<32>, instanceSalt: Bytes<32>) { + assert(ownerId != default>, "SampleZOwnable: invalid id"); + _instanceSalt = disclose(instanceSalt); + _transferOwnership(ownerId); +} + +export circuit owner(): Bytes<32> { + return _ownerCommitment; +} + +export circuit transferOwnership(newOwnerId: Bytes<32>): [] { + assertOnlyOwner(); + assert(newOwnerId != default>, "SampleZOwnable: invalid id"); + _transferOwnership(newOwnerId); +} + +export circuit renounceOwnership(): [] { + assertOnlyOwner(); + _ownerCommitment.resetToDefault(); +} + +export circuit assertOnlyOwner(): [] { + const nonce = secretNonce(); + const callerAsEither = Either { + is_left: true, + left: ownPublicKey(), + right: ContractAddress { bytes: pad(32, "") } + }; + const id = _computeOwnerId(callerAsEither, nonce); + assert(_ownerCommitment == _computeOwnerCommitment(id, _counter), "SampleZOwnable: caller is not the owner"); +} + +export circuit _computeOwnerCommitment( + id: Bytes<32>, + counter: Uint<64>, +): Bytes<32> { + return persistentHash>>( + [ + id, + _instanceSalt, + counter as Field as Bytes<32>, + pad(32, "SampleZOwnable:shield:") + ] + ); +} + +export pure circuit _computeOwnerId( + pk: Either, + nonce: Bytes<32> +): Bytes<32> { + assert(pk.is_left, "SampleZOwnable: contract address owners are not yet supported"); + return persistentHash>>([pk.left.bytes, nonce]); +} + +circuit _transferOwnership(newOwnerId: Bytes<32>): [] { + _counter.increment(1); + _ownerCommitment = _computeOwnerCommitment(disclose(newOwnerId), _counter); +} diff --git a/packages/simulator/test/fixtures/sample-contracts/Simple.compact b/packages/simulator/test/fixtures/sample-contracts/Simple.compact new file mode 100644 index 00000000..a0ed9833 --- /dev/null +++ b/packages/simulator/test/fixtures/sample-contracts/Simple.compact @@ -0,0 +1,14 @@ +pragma language_version >= 0.16.0; + +import CompactStandardLibrary; + +export { ZswapCoinPublicKey, ContractAddress, Either, Maybe }; +export ledger _val: Field; + +export circuit setVal(n: Field): [] { + _val = disclose(n); +} + +export circuit getVal(): Field { + return _val; +} diff --git a/packages/simulator/test/fixtures/sample-contracts/Witness.compact b/packages/simulator/test/fixtures/sample-contracts/Witness.compact new file mode 100644 index 00000000..069e8cce --- /dev/null +++ b/packages/simulator/test/fixtures/sample-contracts/Witness.compact @@ -0,0 +1,27 @@ +pragma language_version >= 0.16.0; + +import CompactStandardLibrary; + +export { ZswapCoinPublicKey, ContractAddress, Either, Maybe }; +export ledger _valBytes: Bytes<32>; +export ledger _valField: Field; +export ledger _valUint: Uint<128>; + +witness wit_secretBytes(): Bytes<32>; +witness wit_secretFieldPlusArg(arg1: Field): Field; +witness wit_secretUintPlusArgs(arg1: Uint<128>, arg2: Uint<128>): Uint<128>; + +export circuit setBytes(): [] { + const val = wit_secretBytes(); + _valBytes = disclose(val); +} + +export circuit setField(arg: Field): [] { + const val = wit_secretFieldPlusArg(arg); + _valField = disclose(val); +} + +export circuit setUint(arg1: Uint<128>, arg2: Uint<128>): [] { + const val = wit_secretUintPlusArgs(arg1, arg2); + _valUint = disclose(val); +} diff --git a/packages/simulator/test/fixtures/sample-contracts/witnesses/SampleZOwnableWitnesses.ts b/packages/simulator/test/fixtures/sample-contracts/witnesses/SampleZOwnableWitnesses.ts new file mode 100644 index 00000000..996e9cbe --- /dev/null +++ b/packages/simulator/test/fixtures/sample-contracts/witnesses/SampleZOwnableWitnesses.ts @@ -0,0 +1,68 @@ +import { getRandomValues } from 'node:crypto'; +import type { WitnessContext } from '@midnight-ntwrk/compact-runtime'; +import type { Ledger } from '../../test-artifacts/SampleZOwnable/contract/index.cjs'; + +/** + * @description Interface defining the witness methods for SampleZOwnable operations. + * @template P - The private state type. + */ +export interface ISampleZOwnableWitnesses

{ + /** + * Retrieves the secret nonce from the private state. + * @param context - The witness context containing the private state. + * @returns A tuple of the private state and the secret nonce as a Uint8Array. + */ + secretNonce(context: WitnessContext): [P, Uint8Array]; +} + +/** + * @description Represents the private state of an ownable contract, storing a secret nonce. + */ +export type SampleZOwnablePrivateState = { + /** @description A 32-byte secret nonce used as a privacy additive. */ + secretNonce: Buffer; +}; + +/** + * @description Utility object for managing the private state of an Ownable contract. + */ +export const SampleZOwnablePrivateState = { + /** + * @description Generates a new private state with a random secret nonce. + * @returns A fresh SampleZOwnablePrivateState instance. + */ + generate: (): SampleZOwnablePrivateState => { + return { secretNonce: getRandomValues(Buffer.alloc(32)) }; + }, + + /** + * @description Generates a new private state with a user-defined secret nonce. + * Useful for deterministic nonce generation or advanced use cases. + * + * @param nonce - The 32-byte secret nonce to use. + * @returns A fresh SampleZOwnablePrivateState instance with the provided nonce. + * + * @example + * ```typescript + * // For deterministic nonces (user-defined scheme) + * const deterministicNonce = myDeterministicScheme(...); + * const privateState = SampleZOwnablePrivateState.withNonce(deterministicNonce); + * ``` + */ + withNonce: (nonce: Buffer): SampleZOwnablePrivateState => { + return { secretNonce: nonce }; + }, +}; + +/** + * @description Factory function creating witness implementations for Ownable operations. + * @returns An object implementing the Witnesses interface for SampleZOwnablePrivateState. + */ +export const SampleZOwnableWitnesses = + (): ISampleZOwnableWitnesses => ({ + secretNonce( + context: WitnessContext, + ): [SampleZOwnablePrivateState, Uint8Array] { + return [context.privateState, context.privateState.secretNonce]; + }, + }); diff --git a/packages/simulator/test/fixtures/sample-contracts/witnesses/SimpleWitnesses.ts b/packages/simulator/test/fixtures/sample-contracts/witnesses/SimpleWitnesses.ts new file mode 100644 index 00000000..af5c506d --- /dev/null +++ b/packages/simulator/test/fixtures/sample-contracts/witnesses/SimpleWitnesses.ts @@ -0,0 +1,3 @@ +export type SimplePrivateState = Record; +export const SimplePrivateState: SimplePrivateState = {}; +export const SimpleWitnesses = () => ({}); diff --git a/packages/simulator/test/fixtures/sample-contracts/witnesses/WitnessWitnesses.ts b/packages/simulator/test/fixtures/sample-contracts/witnesses/WitnessWitnesses.ts new file mode 100644 index 00000000..32f12424 --- /dev/null +++ b/packages/simulator/test/fixtures/sample-contracts/witnesses/WitnessWitnesses.ts @@ -0,0 +1,70 @@ +import { getRandomValues } from 'node:crypto'; +import type { WitnessContext } from '@midnight-ntwrk/compact-runtime'; +import type { Ledger } from '../../test-artifacts/Witness/contract/index.cjs'; + +const randomBigInt = (bits: number): bigint => { + const bytes = Math.ceil(bits / 8); + const buffer = new Uint8Array(bytes); + crypto.getRandomValues(buffer); + + let result = 0n; + for (const byte of buffer) { + result = (result << 8n) | BigInt(byte); + } + + const max = 1n << BigInt(bits); + return result % max; +}; + +export interface IWitnessWitnesses

{ + wit_secretBytes(context: WitnessContext): [P, Uint8Array]; + wit_secretFieldPlusArg( + context: WitnessContext, + arg: bigint, + ): [P, bigint]; + wit_secretUintPlusArgs( + context: WitnessContext, + arg1: bigint, + arg2: bigint, + ): [P, bigint]; +} + +export type WitnessPrivateState = { + secretBytes: Buffer; + secretField: bigint; + secretUint: bigint; +}; + +export const WitnessPrivateState = { + generate: (): WitnessPrivateState => { + return { + secretBytes: getRandomValues(Buffer.alloc(32)), + secretField: randomBigInt(222), + secretUint: randomBigInt(128), + }; + }, +}; + +export const WitnessWitnesses = (): IWitnessWitnesses => ({ + wit_secretBytes( + context: WitnessContext, + ): [WitnessPrivateState, Uint8Array] { + return [context.privateState, context.privateState.secretBytes]; + }, + wit_secretFieldPlusArg( + context: WitnessContext, + arg: bigint, + ): [WitnessPrivateState, bigint] { + return [context.privateState, context.privateState.secretField + arg]; + }, + wit_secretUintPlusArgs( + context: WitnessContext, + arg1: bigint, + arg2: bigint, + ): [WitnessPrivateState, bigint] { + return [ + context.privateState, + context.privateState.secretUint + arg1 + arg2, + ]; + }, +}); diff --git a/packages/simulator/test/fixtures/test-artifacts/SampleZOwnable/compiler/contract-info.json b/packages/simulator/test/fixtures/test-artifacts/SampleZOwnable/compiler/contract-info.json new file mode 100644 index 00000000..15dffd51 --- /dev/null +++ b/packages/simulator/test/fixtures/test-artifacts/SampleZOwnable/compiler/contract-info.json @@ -0,0 +1,155 @@ +{ + "circuits": [ + { + "name": "owner", + "pure": false, + "arguments": [ + ], + "result-type": { + "type-name": "Bytes", + "length": 32 + } + }, + { + "name": "transferOwnership", + "pure": false, + "arguments": [ + { + "name": "newOwnerId", + "type": { + "type-name": "Bytes", + "length": 32 + } + } + ], + "result-type": { + "type-name": "Tuple", + "types": [ + ] + } + }, + { + "name": "renounceOwnership", + "pure": false, + "arguments": [ + ], + "result-type": { + "type-name": "Tuple", + "types": [ + ] + } + }, + { + "name": "assertOnlyOwner", + "pure": false, + "arguments": [ + ], + "result-type": { + "type-name": "Tuple", + "types": [ + ] + } + }, + { + "name": "_computeOwnerCommitment", + "pure": false, + "arguments": [ + { + "name": "id", + "type": { + "type-name": "Bytes", + "length": 32 + } + }, + { + "name": "counter", + "type": { + "type-name": "Uint", + "maxval": 18446744073709551615 + } + } + ], + "result-type": { + "type-name": "Bytes", + "length": 32 + } + }, + { + "name": "_computeOwnerId", + "pure": true, + "arguments": [ + { + "name": "pk", + "type": { + "type-name": "Struct", + "name": "Either", + "elements": [ + { + "name": "is_left", + "type": { + "type-name": "Boolean" + } + }, + { + "name": "left", + "type": { + "type-name": "Struct", + "name": "ZswapCoinPublicKey", + "elements": [ + { + "name": "bytes", + "type": { + "type-name": "Bytes", + "length": 32 + } + } + ] + } + }, + { + "name": "right", + "type": { + "type-name": "Struct", + "name": "ContractAddress", + "elements": [ + { + "name": "bytes", + "type": { + "type-name": "Bytes", + "length": 32 + } + } + ] + } + } + ] + } + }, + { + "name": "nonce", + "type": { + "type-name": "Bytes", + "length": 32 + } + } + ], + "result-type": { + "type-name": "Bytes", + "length": 32 + } + } + ], + "witnesses": [ + { + "name": "secretNonce", + "arguments": [ + ], + "result type": { + "type-name": "Bytes", + "length": 32 + } + } + ], + "contracts": [ + ] +} diff --git a/packages/simulator/test/fixtures/test-artifacts/SampleZOwnable/contract/index.cjs b/packages/simulator/test/fixtures/test-artifacts/SampleZOwnable/contract/index.cjs new file mode 100644 index 00000000..f73532a9 --- /dev/null +++ b/packages/simulator/test/fixtures/test-artifacts/SampleZOwnable/contract/index.cjs @@ -0,0 +1,668 @@ +'use strict'; +const __compactRuntime = require('@midnight-ntwrk/compact-runtime'); +const expectedRuntimeVersionString = '0.8.1'; +const expectedRuntimeVersion = expectedRuntimeVersionString.split('-')[0].split('.').map(Number); +const actualRuntimeVersion = __compactRuntime.versionString.split('-')[0].split('.').map(Number); +if (expectedRuntimeVersion[0] != actualRuntimeVersion[0] + || (actualRuntimeVersion[0] == 0 && expectedRuntimeVersion[1] != actualRuntimeVersion[1]) + || expectedRuntimeVersion[1] > actualRuntimeVersion[1] + || (expectedRuntimeVersion[1] == actualRuntimeVersion[1] && expectedRuntimeVersion[2] > actualRuntimeVersion[2])) + throw new __compactRuntime.CompactError(`Version mismatch: compiled code expects ${expectedRuntimeVersionString}, runtime is ${__compactRuntime.versionString}`); +{ const MAX_FIELD = 52435875175126190479447740508185965837690552500527637822603658699938581184512n; + if (__compactRuntime.MAX_FIELD !== MAX_FIELD) + throw new __compactRuntime.CompactError(`compiler thinks maximum field value is ${MAX_FIELD}; run time thinks it is ${__compactRuntime.MAX_FIELD}`) +} + +const _descriptor_0 = new __compactRuntime.CompactTypeBoolean(); + +const _descriptor_1 = new __compactRuntime.CompactTypeBytes(32); + +class _ZswapCoinPublicKey_0 { + alignment() { + return _descriptor_1.alignment(); + } + fromValue(value_0) { + return { + bytes: _descriptor_1.fromValue(value_0) + } + } + toValue(value_0) { + return _descriptor_1.toValue(value_0.bytes); + } +} + +const _descriptor_2 = new _ZswapCoinPublicKey_0(); + +class _ContractAddress_0 { + alignment() { + return _descriptor_1.alignment(); + } + fromValue(value_0) { + return { + bytes: _descriptor_1.fromValue(value_0) + } + } + toValue(value_0) { + return _descriptor_1.toValue(value_0.bytes); + } +} + +const _descriptor_3 = new _ContractAddress_0(); + +class _Either_0 { + alignment() { + return _descriptor_0.alignment().concat(_descriptor_2.alignment().concat(_descriptor_3.alignment())); + } + fromValue(value_0) { + return { + is_left: _descriptor_0.fromValue(value_0), + left: _descriptor_2.fromValue(value_0), + right: _descriptor_3.fromValue(value_0) + } + } + toValue(value_0) { + return _descriptor_0.toValue(value_0.is_left).concat(_descriptor_2.toValue(value_0.left).concat(_descriptor_3.toValue(value_0.right))); + } +} + +const _descriptor_4 = new _Either_0(); + +const _descriptor_5 = new __compactRuntime.CompactTypeUnsignedInteger(65535n, 2); + +const _descriptor_6 = new __compactRuntime.CompactTypeUnsignedInteger(18446744073709551615n, 8); + +const _descriptor_7 = new __compactRuntime.CompactTypeVector(2, _descriptor_1); + +const _descriptor_8 = new __compactRuntime.CompactTypeVector(4, _descriptor_1); + +const _descriptor_9 = new __compactRuntime.CompactTypeUnsignedInteger(255n, 1); + +const _descriptor_10 = new __compactRuntime.CompactTypeUnsignedInteger(340282366920938463463374607431768211455n, 16); + +class Contract { + witnesses; + constructor(...args_0) { + if (args_0.length !== 1) { + throw new __compactRuntime.CompactError(`Contract constructor: expected 1 argument, received ${args_0.length}`); + } + const witnesses_0 = args_0[0]; + if (typeof(witnesses_0) !== 'object') { + throw new __compactRuntime.CompactError('first (witnesses) argument to Contract constructor is not an object'); + } + if (typeof(witnesses_0.secretNonce) !== 'function') { + throw new __compactRuntime.CompactError('first (witnesses) argument to Contract constructor does not contain a function-valued field named secretNonce'); + } + this.witnesses = witnesses_0; + this.circuits = { + owner: (...args_1) => { + if (args_1.length !== 1) { + throw new __compactRuntime.CompactError(`owner: expected 1 argument (as invoked from Typescript), received ${args_1.length}`); + } + const contextOrig_0 = args_1[0]; + if (!(typeof(contextOrig_0) === 'object' && contextOrig_0.originalState != undefined && contextOrig_0.transactionContext != undefined)) { + __compactRuntime.type_error('owner', + 'argument 1 (as invoked from Typescript)', + 'SampleZOwnable.compact line 21 char 1', + 'CircuitContext', + contextOrig_0) + } + const context = { ...contextOrig_0 }; + const partialProofData = { + input: { value: [], alignment: [] }, + output: undefined, + publicTranscript: [], + privateTranscriptOutputs: [] + }; + const result_0 = this._owner_0(context, partialProofData); + partialProofData.output = { value: _descriptor_1.toValue(result_0), alignment: _descriptor_1.alignment() }; + return { result: result_0, context: context, proofData: partialProofData }; + }, + transferOwnership: (...args_1) => { + if (args_1.length !== 2) { + throw new __compactRuntime.CompactError(`transferOwnership: expected 2 arguments (as invoked from Typescript), received ${args_1.length}`); + } + const contextOrig_0 = args_1[0]; + const newOwnerId_0 = args_1[1]; + if (!(typeof(contextOrig_0) === 'object' && contextOrig_0.originalState != undefined && contextOrig_0.transactionContext != undefined)) { + __compactRuntime.type_error('transferOwnership', + 'argument 1 (as invoked from Typescript)', + 'SampleZOwnable.compact line 25 char 1', + 'CircuitContext', + contextOrig_0) + } + if (!(newOwnerId_0.buffer instanceof ArrayBuffer && newOwnerId_0.BYTES_PER_ELEMENT === 1 && newOwnerId_0.length === 32)) { + __compactRuntime.type_error('transferOwnership', + 'argument 1 (argument 2 as invoked from Typescript)', + 'SampleZOwnable.compact line 25 char 1', + 'Bytes<32>', + newOwnerId_0) + } + const context = { ...contextOrig_0 }; + const partialProofData = { + input: { + value: _descriptor_1.toValue(newOwnerId_0), + alignment: _descriptor_1.alignment() + }, + output: undefined, + publicTranscript: [], + privateTranscriptOutputs: [] + }; + const result_0 = this._transferOwnership_0(context, + partialProofData, + newOwnerId_0); + partialProofData.output = { value: [], alignment: [] }; + return { result: result_0, context: context, proofData: partialProofData }; + }, + renounceOwnership: (...args_1) => { + if (args_1.length !== 1) { + throw new __compactRuntime.CompactError(`renounceOwnership: expected 1 argument (as invoked from Typescript), received ${args_1.length}`); + } + const contextOrig_0 = args_1[0]; + if (!(typeof(contextOrig_0) === 'object' && contextOrig_0.originalState != undefined && contextOrig_0.transactionContext != undefined)) { + __compactRuntime.type_error('renounceOwnership', + 'argument 1 (as invoked from Typescript)', + 'SampleZOwnable.compact line 31 char 1', + 'CircuitContext', + contextOrig_0) + } + const context = { ...contextOrig_0 }; + const partialProofData = { + input: { value: [], alignment: [] }, + output: undefined, + publicTranscript: [], + privateTranscriptOutputs: [] + }; + const result_0 = this._renounceOwnership_0(context, partialProofData); + partialProofData.output = { value: [], alignment: [] }; + return { result: result_0, context: context, proofData: partialProofData }; + }, + assertOnlyOwner: (...args_1) => { + if (args_1.length !== 1) { + throw new __compactRuntime.CompactError(`assertOnlyOwner: expected 1 argument (as invoked from Typescript), received ${args_1.length}`); + } + const contextOrig_0 = args_1[0]; + if (!(typeof(contextOrig_0) === 'object' && contextOrig_0.originalState != undefined && contextOrig_0.transactionContext != undefined)) { + __compactRuntime.type_error('assertOnlyOwner', + 'argument 1 (as invoked from Typescript)', + 'SampleZOwnable.compact line 36 char 1', + 'CircuitContext', + contextOrig_0) + } + const context = { ...contextOrig_0 }; + const partialProofData = { + input: { value: [], alignment: [] }, + output: undefined, + publicTranscript: [], + privateTranscriptOutputs: [] + }; + const result_0 = this._assertOnlyOwner_0(context, partialProofData); + partialProofData.output = { value: [], alignment: [] }; + return { result: result_0, context: context, proofData: partialProofData }; + }, + _computeOwnerCommitment: (...args_1) => { + if (args_1.length !== 3) { + throw new __compactRuntime.CompactError(`_computeOwnerCommitment: expected 3 arguments (as invoked from Typescript), received ${args_1.length}`); + } + const contextOrig_0 = args_1[0]; + const id_0 = args_1[1]; + const counter_0 = args_1[2]; + if (!(typeof(contextOrig_0) === 'object' && contextOrig_0.originalState != undefined && contextOrig_0.transactionContext != undefined)) { + __compactRuntime.type_error('_computeOwnerCommitment', + 'argument 1 (as invoked from Typescript)', + 'SampleZOwnable.compact line 47 char 1', + 'CircuitContext', + contextOrig_0) + } + if (!(id_0.buffer instanceof ArrayBuffer && id_0.BYTES_PER_ELEMENT === 1 && id_0.length === 32)) { + __compactRuntime.type_error('_computeOwnerCommitment', + 'argument 1 (argument 2 as invoked from Typescript)', + 'SampleZOwnable.compact line 47 char 1', + 'Bytes<32>', + id_0) + } + if (!(typeof(counter_0) === 'bigint' && counter_0 >= 0n && counter_0 <= 18446744073709551615n)) { + __compactRuntime.type_error('_computeOwnerCommitment', + 'argument 2 (argument 3 as invoked from Typescript)', + 'SampleZOwnable.compact line 47 char 1', + 'Uint<0..18446744073709551615>', + counter_0) + } + const context = { ...contextOrig_0 }; + const partialProofData = { + input: { + value: _descriptor_1.toValue(id_0).concat(_descriptor_6.toValue(counter_0)), + alignment: _descriptor_1.alignment().concat(_descriptor_6.alignment()) + }, + output: undefined, + publicTranscript: [], + privateTranscriptOutputs: [] + }; + const result_0 = this.__computeOwnerCommitment_0(context, + partialProofData, + id_0, + counter_0); + partialProofData.output = { value: _descriptor_1.toValue(result_0), alignment: _descriptor_1.alignment() }; + return { result: result_0, context: context, proofData: partialProofData }; + }, + _computeOwnerId(context, ...args_1) { + return { result: pureCircuits._computeOwnerId(...args_1), context }; + } + }; + this.impureCircuits = { + owner: this.circuits.owner, + transferOwnership: this.circuits.transferOwnership, + renounceOwnership: this.circuits.renounceOwnership, + assertOnlyOwner: this.circuits.assertOnlyOwner, + _computeOwnerCommitment: this.circuits._computeOwnerCommitment + }; + } + initialState(...args_0) { + if (args_0.length !== 3) { + throw new __compactRuntime.CompactError(`Contract state constructor: expected 3 arguments (as invoked from Typescript), received ${args_0.length}`); + } + const constructorContext_0 = args_0[0]; + const ownerId_0 = args_0[1]; + const instanceSalt_0 = args_0[2]; + if (typeof(constructorContext_0) !== 'object') { + throw new __compactRuntime.CompactError(`Contract state constructor: expected 'constructorContext' in argument 1 (as invoked from Typescript) to be an object`); + } + if (!('initialPrivateState' in constructorContext_0)) { + throw new __compactRuntime.CompactError(`Contract state constructor: expected 'initialPrivateState' in argument 1 (as invoked from Typescript)`); + } + if (!('initialZswapLocalState' in constructorContext_0)) { + throw new __compactRuntime.CompactError(`Contract state constructor: expected 'initialZswapLocalState' in argument 1 (as invoked from Typescript)`); + } + if (typeof(constructorContext_0.initialZswapLocalState) !== 'object') { + throw new __compactRuntime.CompactError(`Contract state constructor: expected 'initialZswapLocalState' in argument 1 (as invoked from Typescript) to be an object`); + } + if (!(ownerId_0.buffer instanceof ArrayBuffer && ownerId_0.BYTES_PER_ELEMENT === 1 && ownerId_0.length === 32)) { + __compactRuntime.type_error('Contract state constructor', + 'argument 1 (argument 2 as invoked from Typescript)', + 'SampleZOwnable.compact line 15 char 1', + 'Bytes<32>', + ownerId_0) + } + if (!(instanceSalt_0.buffer instanceof ArrayBuffer && instanceSalt_0.BYTES_PER_ELEMENT === 1 && instanceSalt_0.length === 32)) { + __compactRuntime.type_error('Contract state constructor', + 'argument 2 (argument 3 as invoked from Typescript)', + 'SampleZOwnable.compact line 15 char 1', + 'Bytes<32>', + instanceSalt_0) + } + const state_0 = new __compactRuntime.ContractState(); + let stateValue_0 = __compactRuntime.StateValue.newArray(); + stateValue_0 = stateValue_0.arrayPush(__compactRuntime.StateValue.newNull()); + stateValue_0 = stateValue_0.arrayPush(__compactRuntime.StateValue.newNull()); + stateValue_0 = stateValue_0.arrayPush(__compactRuntime.StateValue.newNull()); + state_0.data = stateValue_0; + state_0.setOperation('owner', new __compactRuntime.ContractOperation()); + state_0.setOperation('transferOwnership', new __compactRuntime.ContractOperation()); + state_0.setOperation('renounceOwnership', new __compactRuntime.ContractOperation()); + state_0.setOperation('assertOnlyOwner', new __compactRuntime.ContractOperation()); + state_0.setOperation('_computeOwnerCommitment', new __compactRuntime.ContractOperation()); + const context = { + originalState: state_0, + currentPrivateState: constructorContext_0.initialPrivateState, + currentZswapLocalState: constructorContext_0.initialZswapLocalState, + transactionContext: new __compactRuntime.QueryContext(state_0.data, __compactRuntime.dummyContractAddress()) + }; + const partialProofData = { + input: { value: [], alignment: [] }, + output: undefined, + publicTranscript: [], + privateTranscriptOutputs: [] + }; + Contract._query(context, + partialProofData, + [ + { push: { storage: false, + value: __compactRuntime.StateValue.newCell({ value: _descriptor_9.toValue(0n), + alignment: _descriptor_9.alignment() }).encode() } }, + { push: { storage: true, + value: __compactRuntime.StateValue.newCell({ value: _descriptor_1.toValue(new Uint8Array(32)), + alignment: _descriptor_1.alignment() }).encode() } }, + { ins: { cached: false, n: 1 } }]); + Contract._query(context, + partialProofData, + [ + { push: { storage: false, + value: __compactRuntime.StateValue.newCell({ value: _descriptor_9.toValue(1n), + alignment: _descriptor_9.alignment() }).encode() } }, + { push: { storage: true, + value: __compactRuntime.StateValue.newCell({ value: _descriptor_6.toValue(0n), + alignment: _descriptor_6.alignment() }).encode() } }, + { ins: { cached: false, n: 1 } }]); + Contract._query(context, + partialProofData, + [ + { push: { storage: false, + value: __compactRuntime.StateValue.newCell({ value: _descriptor_9.toValue(2n), + alignment: _descriptor_9.alignment() }).encode() } }, + { push: { storage: true, + value: __compactRuntime.StateValue.newCell({ value: _descriptor_1.toValue(new Uint8Array(32)), + alignment: _descriptor_1.alignment() }).encode() } }, + { ins: { cached: false, n: 1 } }]); + __compactRuntime.assert(!this._equal_0(ownerId_0, new Uint8Array(32)), + 'SampleZOwnable: invalid id'); + Contract._query(context, + partialProofData, + [ + { push: { storage: false, + value: __compactRuntime.StateValue.newCell({ value: _descriptor_9.toValue(2n), + alignment: _descriptor_9.alignment() }).encode() } }, + { push: { storage: true, + value: __compactRuntime.StateValue.newCell({ value: _descriptor_1.toValue(instanceSalt_0), + alignment: _descriptor_1.alignment() }).encode() } }, + { ins: { cached: false, n: 1 } }]); + this.__transferOwnership_0(context, partialProofData, ownerId_0); + state_0.data = context.transactionContext.state; + return { + currentContractState: state_0, + currentPrivateState: context.currentPrivateState, + currentZswapLocalState: context.currentZswapLocalState + } + } + _persistentHash_0(value_0) { + const result_0 = __compactRuntime.persistentHash(_descriptor_8, value_0); + return result_0; + } + _persistentHash_1(value_0) { + const result_0 = __compactRuntime.persistentHash(_descriptor_7, value_0); + return result_0; + } + _ownPublicKey_0(context, partialProofData) { + const result_0 = __compactRuntime.ownPublicKey(context); + partialProofData.privateTranscriptOutputs.push({ + value: _descriptor_2.toValue(result_0), + alignment: _descriptor_2.alignment() + }); + return result_0; + } + _secretNonce_0(context, partialProofData) { + const witnessContext_0 = __compactRuntime.witnessContext(ledger(context.transactionContext.state), context.currentPrivateState, context.transactionContext.address); + const [nextPrivateState_0, result_0] = this.witnesses.secretNonce(witnessContext_0); + context.currentPrivateState = nextPrivateState_0; + if (!(result_0.buffer instanceof ArrayBuffer && result_0.BYTES_PER_ELEMENT === 1 && result_0.length === 32)) { + __compactRuntime.type_error('secretNonce', + 'return value', + 'SampleZOwnable.compact line 13 char 1', + 'Bytes<32>', + result_0) + } + partialProofData.privateTranscriptOutputs.push({ + value: _descriptor_1.toValue(result_0), + alignment: _descriptor_1.alignment() + }); + return result_0; + } + _owner_0(context, partialProofData) { + return _descriptor_1.fromValue(Contract._query(context, + partialProofData, + [ + { dup: { n: 0 } }, + { idx: { cached: false, + pushPath: false, + path: [ + { tag: 'value', + value: { value: _descriptor_9.toValue(0n), + alignment: _descriptor_9.alignment() } }] } }, + { popeq: { cached: false, + result: undefined } }]).value); + } + _transferOwnership_0(context, partialProofData, newOwnerId_0) { + this._assertOnlyOwner_0(context, partialProofData); + __compactRuntime.assert(!this._equal_1(newOwnerId_0, new Uint8Array(32)), + 'SampleZOwnable: invalid id'); + this.__transferOwnership_0(context, partialProofData, newOwnerId_0); + return []; + } + _renounceOwnership_0(context, partialProofData) { + this._assertOnlyOwner_0(context, partialProofData); + Contract._query(context, + partialProofData, + [ + { push: { storage: false, + value: __compactRuntime.StateValue.newCell({ value: _descriptor_9.toValue(0n), + alignment: _descriptor_9.alignment() }).encode() } }, + { push: { storage: true, + value: __compactRuntime.StateValue.newCell({ value: _descriptor_1.toValue(new Uint8Array(32)), + alignment: _descriptor_1.alignment() }).encode() } }, + { ins: { cached: false, n: 1 } }]); + return []; + } + _assertOnlyOwner_0(context, partialProofData) { + const nonce_0 = this._secretNonce_0(context, partialProofData); + const callerAsEither_0 = { is_left: true, + left: + this._ownPublicKey_0(context, partialProofData), + right: + { bytes: + new Uint8Array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) } }; + const id_0 = this.__computeOwnerId_0(callerAsEither_0, nonce_0); + __compactRuntime.assert(this._equal_2(_descriptor_1.fromValue(Contract._query(context, + partialProofData, + [ + { dup: { n: 0 } }, + { idx: { cached: false, + pushPath: false, + path: [ + { tag: 'value', + value: { value: _descriptor_9.toValue(0n), + alignment: _descriptor_9.alignment() } }] } }, + { popeq: { cached: false, + result: undefined } }]).value), + this.__computeOwnerCommitment_0(context, + partialProofData, + id_0, + _descriptor_6.fromValue(Contract._query(context, + partialProofData, + [ + { dup: { n: 0 } }, + { idx: { cached: false, + pushPath: false, + path: [ + { tag: 'value', + value: { value: _descriptor_9.toValue(1n), + alignment: _descriptor_9.alignment() } }] } }, + { popeq: { cached: true, + result: undefined } }]).value))), + 'SampleZOwnable: caller is not the owner'); + return []; + } + __computeOwnerCommitment_0(context, partialProofData, id_0, counter_0) { + return this._persistentHash_0([id_0, + _descriptor_1.fromValue(Contract._query(context, + partialProofData, + [ + { dup: { n: 0 } }, + { idx: { cached: false, + pushPath: false, + path: [ + { tag: 'value', + value: { value: _descriptor_9.toValue(2n), + alignment: _descriptor_9.alignment() } }] } }, + { popeq: { cached: false, + result: undefined } }]).value), + __compactRuntime.convert_bigint_to_Uint8Array(32, + counter_0), + new Uint8Array([83, 97, 109, 112, 108, 101, 90, 79, 119, 110, 97, 98, 108, 101, 58, 115, 104, 105, 101, 108, 100, 58, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])]); + } + __computeOwnerId_0(pk_0, nonce_0) { + __compactRuntime.assert(pk_0.is_left, + 'SampleZOwnable: contract address owners are not yet supported'); + return this._persistentHash_1([pk_0.left.bytes, nonce_0]); + } + __transferOwnership_0(context, partialProofData, newOwnerId_0) { + const tmp_0 = 1n; + Contract._query(context, + partialProofData, + [ + { idx: { cached: false, + pushPath: true, + path: [ + { tag: 'value', + value: { value: _descriptor_9.toValue(1n), + alignment: _descriptor_9.alignment() } }] } }, + { addi: { immediate: parseInt(__compactRuntime.valueToBigInt( + { value: _descriptor_5.toValue(tmp_0), + alignment: _descriptor_5.alignment() } + .value + )) } }, + { ins: { cached: true, n: 1 } }]); + const tmp_1 = this.__computeOwnerCommitment_0(context, + partialProofData, + newOwnerId_0, + _descriptor_6.fromValue(Contract._query(context, + partialProofData, + [ + { dup: { n: 0 } }, + { idx: { cached: false, + pushPath: false, + path: [ + { tag: 'value', + value: { value: _descriptor_9.toValue(1n), + alignment: _descriptor_9.alignment() } }] } }, + { popeq: { cached: true, + result: undefined } }]).value)); + Contract._query(context, + partialProofData, + [ + { push: { storage: false, + value: __compactRuntime.StateValue.newCell({ value: _descriptor_9.toValue(0n), + alignment: _descriptor_9.alignment() }).encode() } }, + { push: { storage: true, + value: __compactRuntime.StateValue.newCell({ value: _descriptor_1.toValue(tmp_1), + alignment: _descriptor_1.alignment() }).encode() } }, + { ins: { cached: false, n: 1 } }]); + return []; + } + _equal_0(x0, y0) { + if (!x0.every((x, i) => y0[i] === x)) { return false; } + return true; + } + _equal_1(x0, y0) { + if (!x0.every((x, i) => y0[i] === x)) { return false; } + return true; + } + _equal_2(x0, y0) { + if (!x0.every((x, i) => y0[i] === x)) { return false; } + return true; + } + static _query(context, partialProofData, prog) { + var res; + try { + res = context.transactionContext.query(prog, __compactRuntime.CostModel.dummyCostModel()); + } catch (err) { + throw new __compactRuntime.CompactError(err.toString()); + } + context.transactionContext = res.context; + var reads = res.events.filter((e) => e.tag === 'read'); + var i = 0; + partialProofData.publicTranscript = partialProofData.publicTranscript.concat(prog.map((op) => { + if(typeof(op) === 'object' && 'popeq' in op) { + return { popeq: { + ...op.popeq, + result: reads[i++].content, + } }; + } else { + return op; + } + })); + if(res.events.length == 1 && res.events[0].tag === 'read') { + return res.events[0].content; + } else { + return res.events; + } + } +} +function ledger(state) { + const context = { + originalState: state, + transactionContext: new __compactRuntime.QueryContext(state, __compactRuntime.dummyContractAddress()) + }; + const partialProofData = { + input: { value: [], alignment: [] }, + output: undefined, + publicTranscript: [], + privateTranscriptOutputs: [] + }; + return { + get _ownerCommitment() { + return _descriptor_1.fromValue(Contract._query(context, + partialProofData, + [ + { dup: { n: 0 } }, + { idx: { cached: false, + pushPath: false, + path: [ + { tag: 'value', + value: { value: _descriptor_9.toValue(0n), + alignment: _descriptor_9.alignment() } }] } }, + { popeq: { cached: false, + result: undefined } }]).value); + }, + get _counter() { + return _descriptor_6.fromValue(Contract._query(context, + partialProofData, + [ + { dup: { n: 0 } }, + { idx: { cached: false, + pushPath: false, + path: [ + { tag: 'value', + value: { value: _descriptor_9.toValue(1n), + alignment: _descriptor_9.alignment() } }] } }, + { popeq: { cached: true, + result: undefined } }]).value); + }, + get _instanceSalt() { + return _descriptor_1.fromValue(Contract._query(context, + partialProofData, + [ + { dup: { n: 0 } }, + { idx: { cached: false, + pushPath: false, + path: [ + { tag: 'value', + value: { value: _descriptor_9.toValue(2n), + alignment: _descriptor_9.alignment() } }] } }, + { popeq: { cached: false, + result: undefined } }]).value); + } + }; +} +const _emptyContext = { + originalState: new __compactRuntime.ContractState(), + transactionContext: new __compactRuntime.QueryContext(new __compactRuntime.ContractState().data, __compactRuntime.dummyContractAddress()) +}; +const _dummyContract = new Contract({ secretNonce: (...args) => undefined }); +const pureCircuits = { + _computeOwnerId: (...args_0) => { + if (args_0.length !== 2) { + throw new __compactRuntime.CompactError(`_computeOwnerId: expected 2 arguments (as invoked from Typescript), received ${args_0.length}`); + } + const pk_0 = args_0[0]; + const nonce_0 = args_0[1]; + if (!(typeof(pk_0) === 'object' && typeof(pk_0.is_left) === 'boolean' && typeof(pk_0.left) === 'object' && pk_0.left.bytes.buffer instanceof ArrayBuffer && pk_0.left.bytes.BYTES_PER_ELEMENT === 1 && pk_0.left.bytes.length === 32 && typeof(pk_0.right) === 'object' && pk_0.right.bytes.buffer instanceof ArrayBuffer && pk_0.right.bytes.BYTES_PER_ELEMENT === 1 && pk_0.right.bytes.length === 32)) { + __compactRuntime.type_error('_computeOwnerId', + 'argument 1', + 'SampleZOwnable.compact line 61 char 1', + 'struct Either>, right: struct ContractAddress>>', + pk_0) + } + if (!(nonce_0.buffer instanceof ArrayBuffer && nonce_0.BYTES_PER_ELEMENT === 1 && nonce_0.length === 32)) { + __compactRuntime.type_error('_computeOwnerId', + 'argument 2', + 'SampleZOwnable.compact line 61 char 1', + 'Bytes<32>', + nonce_0) + } + return _dummyContract.__computeOwnerId_0(pk_0, nonce_0); + } +}; +const contractReferenceLocations = { tag: 'publicLedgerArray', indices: { } }; +exports.Contract = Contract; +exports.ledger = ledger; +exports.pureCircuits = pureCircuits; +exports.contractReferenceLocations = contractReferenceLocations; +//# sourceMappingURL=index.cjs.map diff --git a/packages/simulator/test/fixtures/test-artifacts/SampleZOwnable/contract/index.cjs.map b/packages/simulator/test/fixtures/test-artifacts/SampleZOwnable/contract/index.cjs.map new file mode 100644 index 00000000..e89fc945 --- /dev/null +++ b/packages/simulator/test/fixtures/test-artifacts/SampleZOwnable/contract/index.cjs.map @@ -0,0 +1,8 @@ +{ + "version": 3, + "file": "index.cjs", + "sourceRoot": "../../../../../", + "sources": ["test/fixtures/sample-contracts/SampleZOwnable.compact", "compiler/standard-library.compact"], + "names": [], + "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAcA;;;;;;;;;;;;;MAMA,AAAA,KAEC;;;;;;;;;;;;;;;;;;;;;;OAAA;MAED,AAAA,iBAIC;;;;;cAJgC,YAAqB;;;;;;;;;;;;;;;;;;yCAArB,YAAqB;;;;;;;;;mDAArB,YAAqB;;;OAIrD;MAED,AAAA,iBAGC;;;;;;;;;;;;;;;;;;;;;;OAAA;MAED,AAAA,eASC;;;;;;;;;;;;;;;;;;;;;;OAAA;MAED,AAAA,uBAYC;;;;;cAXC,IAAa;cACb,SAAiB;;;;;;;;;;;;;;;;;;;;;;;;;yCADjB,IAAa,+BACb,SAAiB;;;;;;;;;yDADjB,IAAa;yDACb,SAAiB;;;OAUlB;MAED,AAAA,eAMC;;OAAA;;;;;;;;;GAhDA;EAJD;;;;;UAAY,SAAkB;UAAE,cAAuB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IANvD;;;;;;;;;uDAA0C;IAC1C;;;;;;;;;uDAAgC;IAChC;;;;;;;;;uDAA8C;2CAKrC,SAAO;;IACd;;;;;;;yGAAyB,cAAY;;uDAAxB;0DACM,SAAO;;;;;;;GAC3B;EC8BD,AAAA,kBAAiC;oEAAA;;;EAAjC,AAAA,kBAAiC;oEAAA;;;EAwFjC,AAAA;;;;;;;;ED5HA,AAAA,cAAiC;;0DAAjC,WAAiC;;;;;;;;;;;;;;GAAA;EAQjC,AAAA,QAEC;mCADQ;;;;;;;;;;;sFAAgB;GACxB;EAED,AAAA,oBAIC,4BAJgC,YAAqB;;2CAE7C,YAAU;;0DACE,YAAU;;GAC9B;EAED,AAAA,oBAGC;;IADC;;;;;;;;;uDAAgB;;GACjB;EAED,AAAA,kBASC;UARO,OAAqB;UACrB,gBAIL;;;;;;UACK,IAA2C,2BAAtB,gBAAc,EAAE,OAAK;kEACzC;;;;;;;;;;;qHAAgB;;;0EAA4B,IAAE;kGAAE;;;;;;;;;;;qJAAQ;;;GAChE;EAED,AAAA,0BAYC,4BAXC,IAAa,EACb,SAAiB;mCAIb,IAAE;2DACF;;;;;;;;;;;8GAAa;;iFACb,SAAO;;GAIZ;EAED,AAAA,kBAMC,CALC,IAA+C,EAC/C,OAAgB;4BAET,IAAE;;mCACoC,IAAE,aAAa,OAAK;GAClE;EAED,AAAA,qBAGC,4BAH0B,YAAqB;UAC9C,KAAQ;IAAR;;;;;;;;;;2EAAA,KAAQ;;;;sDAAA;UACR,KAAgB;;kDAAoC,YAAU;0EAAG;;;;;;;;;;;6HAAQ;IAAzE;;;;;;;yGAAA,KAAgB;;uDAAA;;GACjB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IA/DD;qCAAA;;;;;;;;;;;wFAA0C;KAAA;IAC1C;qCAAA;;;;;;;;;;;wFAAgC;KAAA;IAChC;qCAAA;;;;;;;;;;;wFAA8C;KAAA;;;;;;;;;EAkD9C,AAAA,eAMC;;;;UALC,IAA+C;UAC/C,OAAgB;;;;;;;;;;;;;;;6CADhB,IAA+C,EAC/C,OAAgB;GAIjB;;;;;;;" +} diff --git a/packages/simulator/test/fixtures/test-artifacts/SampleZOwnable/contract/index.d.cts b/packages/simulator/test/fixtures/test-artifacts/SampleZOwnable/contract/index.d.cts new file mode 100644 index 00000000..5404b06f --- /dev/null +++ b/packages/simulator/test/fixtures/test-artifacts/SampleZOwnable/contract/index.d.cts @@ -0,0 +1,64 @@ +import type * as __compactRuntime from '@midnight-ntwrk/compact-runtime'; + +export type ZswapCoinPublicKey = { bytes: Uint8Array }; + +export type ContractAddress = { bytes: Uint8Array }; + +export type Either = { is_left: boolean; left: A; right: B }; + +export type Witnesses = { + secretNonce(context: __compactRuntime.WitnessContext): [T, Uint8Array]; +} + +export type ImpureCircuits = { + owner(context: __compactRuntime.CircuitContext): __compactRuntime.CircuitResults; + transferOwnership(context: __compactRuntime.CircuitContext, + newOwnerId_0: Uint8Array): __compactRuntime.CircuitResults; + renounceOwnership(context: __compactRuntime.CircuitContext): __compactRuntime.CircuitResults; + assertOnlyOwner(context: __compactRuntime.CircuitContext): __compactRuntime.CircuitResults; + _computeOwnerCommitment(context: __compactRuntime.CircuitContext, + id_0: Uint8Array, + counter_0: bigint): __compactRuntime.CircuitResults; +} + +export type PureCircuits = { + _computeOwnerId(pk_0: Either, + nonce_0: Uint8Array): Uint8Array; +} + +export type Circuits = { + owner(context: __compactRuntime.CircuitContext): __compactRuntime.CircuitResults; + transferOwnership(context: __compactRuntime.CircuitContext, + newOwnerId_0: Uint8Array): __compactRuntime.CircuitResults; + renounceOwnership(context: __compactRuntime.CircuitContext): __compactRuntime.CircuitResults; + assertOnlyOwner(context: __compactRuntime.CircuitContext): __compactRuntime.CircuitResults; + _computeOwnerCommitment(context: __compactRuntime.CircuitContext, + id_0: Uint8Array, + counter_0: bigint): __compactRuntime.CircuitResults; + _computeOwnerId(context: __compactRuntime.CircuitContext, + pk_0: Either, + nonce_0: Uint8Array): __compactRuntime.CircuitResults; +} + +export type Ledger = { + readonly _ownerCommitment: Uint8Array; + readonly _counter: bigint; + readonly _instanceSalt: Uint8Array; +} + +export type ContractReferenceLocations = any; + +export declare const contractReferenceLocations : ContractReferenceLocations; + +export declare class Contract = Witnesses> { + witnesses: W; + circuits: Circuits; + impureCircuits: ImpureCircuits; + constructor(witnesses: W); + initialState(context: __compactRuntime.ConstructorContext, + ownerId_0: Uint8Array, + instanceSalt_0: Uint8Array): __compactRuntime.ConstructorResult; +} + +export declare function ledger(state: __compactRuntime.StateValue): Ledger; +export declare const pureCircuits: PureCircuits; diff --git a/packages/simulator/test/fixtures/test-artifacts/SampleZOwnable/keys/_computeOwnerCommitment.prover b/packages/simulator/test/fixtures/test-artifacts/SampleZOwnable/keys/_computeOwnerCommitment.prover new file mode 100644 index 00000000..2c176ba8 Binary files /dev/null and b/packages/simulator/test/fixtures/test-artifacts/SampleZOwnable/keys/_computeOwnerCommitment.prover differ diff --git a/packages/simulator/test/fixtures/test-artifacts/SampleZOwnable/keys/_computeOwnerCommitment.verifier b/packages/simulator/test/fixtures/test-artifacts/SampleZOwnable/keys/_computeOwnerCommitment.verifier new file mode 100644 index 00000000..6ab1053f Binary files /dev/null and b/packages/simulator/test/fixtures/test-artifacts/SampleZOwnable/keys/_computeOwnerCommitment.verifier differ diff --git a/packages/simulator/test/fixtures/test-artifacts/SampleZOwnable/keys/assertOnlyOwner.prover b/packages/simulator/test/fixtures/test-artifacts/SampleZOwnable/keys/assertOnlyOwner.prover new file mode 100644 index 00000000..c9917d3e Binary files /dev/null and b/packages/simulator/test/fixtures/test-artifacts/SampleZOwnable/keys/assertOnlyOwner.prover differ diff --git a/packages/simulator/test/fixtures/test-artifacts/SampleZOwnable/keys/assertOnlyOwner.verifier b/packages/simulator/test/fixtures/test-artifacts/SampleZOwnable/keys/assertOnlyOwner.verifier new file mode 100644 index 00000000..1c73f1d1 Binary files /dev/null and b/packages/simulator/test/fixtures/test-artifacts/SampleZOwnable/keys/assertOnlyOwner.verifier differ diff --git a/packages/simulator/test/fixtures/test-artifacts/SampleZOwnable/keys/owner.prover b/packages/simulator/test/fixtures/test-artifacts/SampleZOwnable/keys/owner.prover new file mode 100644 index 00000000..b82d3265 Binary files /dev/null and b/packages/simulator/test/fixtures/test-artifacts/SampleZOwnable/keys/owner.prover differ diff --git a/packages/simulator/test/fixtures/test-artifacts/SampleZOwnable/keys/owner.verifier b/packages/simulator/test/fixtures/test-artifacts/SampleZOwnable/keys/owner.verifier new file mode 100644 index 00000000..1895d30c Binary files /dev/null and b/packages/simulator/test/fixtures/test-artifacts/SampleZOwnable/keys/owner.verifier differ diff --git a/packages/simulator/test/fixtures/test-artifacts/SampleZOwnable/keys/renounceOwnership.prover b/packages/simulator/test/fixtures/test-artifacts/SampleZOwnable/keys/renounceOwnership.prover new file mode 100644 index 00000000..8f67fc39 Binary files /dev/null and b/packages/simulator/test/fixtures/test-artifacts/SampleZOwnable/keys/renounceOwnership.prover differ diff --git a/packages/simulator/test/fixtures/test-artifacts/SampleZOwnable/keys/renounceOwnership.verifier b/packages/simulator/test/fixtures/test-artifacts/SampleZOwnable/keys/renounceOwnership.verifier new file mode 100644 index 00000000..3c969b0e Binary files /dev/null and b/packages/simulator/test/fixtures/test-artifacts/SampleZOwnable/keys/renounceOwnership.verifier differ diff --git a/packages/simulator/test/fixtures/test-artifacts/SampleZOwnable/keys/transferOwnership.prover b/packages/simulator/test/fixtures/test-artifacts/SampleZOwnable/keys/transferOwnership.prover new file mode 100644 index 00000000..de4f0fb7 Binary files /dev/null and b/packages/simulator/test/fixtures/test-artifacts/SampleZOwnable/keys/transferOwnership.prover differ diff --git a/packages/simulator/test/fixtures/test-artifacts/SampleZOwnable/keys/transferOwnership.verifier b/packages/simulator/test/fixtures/test-artifacts/SampleZOwnable/keys/transferOwnership.verifier new file mode 100644 index 00000000..ad5ab663 Binary files /dev/null and b/packages/simulator/test/fixtures/test-artifacts/SampleZOwnable/keys/transferOwnership.verifier differ diff --git a/packages/simulator/test/fixtures/test-artifacts/SampleZOwnable/zkir/_computeOwnerCommitment.bzkir b/packages/simulator/test/fixtures/test-artifacts/SampleZOwnable/zkir/_computeOwnerCommitment.bzkir new file mode 100644 index 00000000..9395efaf Binary files /dev/null and b/packages/simulator/test/fixtures/test-artifacts/SampleZOwnable/zkir/_computeOwnerCommitment.bzkir differ diff --git a/packages/simulator/test/fixtures/test-artifacts/SampleZOwnable/zkir/_computeOwnerCommitment.zkir b/packages/simulator/test/fixtures/test-artifacts/SampleZOwnable/zkir/_computeOwnerCommitment.zkir new file mode 100644 index 00000000..af60338a --- /dev/null +++ b/packages/simulator/test/fixtures/test-artifacts/SampleZOwnable/zkir/_computeOwnerCommitment.zkir @@ -0,0 +1,37 @@ +{ + "version": { "major": 2, "minor": 0 }, + "do_communications_commitment": true, + "num_inputs": 3, + "instructions": [ + { "op": "constrain_bits", "var": 0, "bits": 8 }, + { "op": "constrain_bits", "var": 1, "bits": 248 }, + { "op": "constrain_bits", "var": 2, "bits": 64 }, + { "op": "load_imm", "imm": "01" }, + { "op": "load_imm", "imm": "30" }, + { "op": "declare_pub_input", "var": 4 }, + { "op": "pi_skip", "guard": 3, "count": 1 }, + { "op": "load_imm", "imm": "50" }, + { "op": "load_imm", "imm": "02" }, + { "op": "declare_pub_input", "var": 5 }, + { "op": "declare_pub_input", "var": 3 }, + { "op": "declare_pub_input", "var": 3 }, + { "op": "declare_pub_input", "var": 6 }, + { "op": "pi_skip", "guard": 3, "count": 4 }, + { "op": "public_input", "guard": null }, + { "op": "public_input", "guard": null }, + { "op": "load_imm", "imm": "20" }, + { "op": "load_imm", "imm": "0C" }, + { "op": "declare_pub_input", "var": 10 }, + { "op": "declare_pub_input", "var": 3 }, + { "op": "declare_pub_input", "var": 9 }, + { "op": "declare_pub_input", "var": 7 }, + { "op": "declare_pub_input", "var": 8 }, + { "op": "pi_skip", "guard": 3, "count": 5 }, + { "op": "div_mod_power_of_two", "var": 2, "bits": 248 }, + { "op": "load_imm", "imm": "00" }, + { "op": "load_imm", "imm": "53616D706C655A4F776E61626C653A736869656C643A" }, + { "op": "persistent_hash", "alignment": [{ "tag": "atom", "value": { "length": 32, "tag": "bytes" } }, { "tag": "atom", "value": { "length": 32, "tag": "bytes" } }, { "tag": "atom", "value": { "length": 32, "tag": "bytes" } }, { "tag": "atom", "value": { "length": 32, "tag": "bytes" } }], "inputs": [0, 1, 7, 8, 11, 12, 13, 14] }, + { "op": "output", "var": 15 }, + { "op": "output", "var": 16 } + ] +} diff --git a/packages/simulator/test/fixtures/test-artifacts/SampleZOwnable/zkir/assertOnlyOwner.bzkir b/packages/simulator/test/fixtures/test-artifacts/SampleZOwnable/zkir/assertOnlyOwner.bzkir new file mode 100644 index 00000000..31503d09 Binary files /dev/null and b/packages/simulator/test/fixtures/test-artifacts/SampleZOwnable/zkir/assertOnlyOwner.bzkir differ diff --git a/packages/simulator/test/fixtures/test-artifacts/SampleZOwnable/zkir/assertOnlyOwner.zkir b/packages/simulator/test/fixtures/test-artifacts/SampleZOwnable/zkir/assertOnlyOwner.zkir new file mode 100644 index 00000000..2d5db59e --- /dev/null +++ b/packages/simulator/test/fixtures/test-artifacts/SampleZOwnable/zkir/assertOnlyOwner.zkir @@ -0,0 +1,75 @@ +{ + "version": { "major": 2, "minor": 0 }, + "do_communications_commitment": true, + "num_inputs": 0, + "instructions": [ + { "op": "load_imm", "imm": "01" }, + { "op": "private_input", "guard": null }, + { "op": "constrain_bits", "var": 1, "bits": 8 }, + { "op": "private_input", "guard": null }, + { "op": "constrain_bits", "var": 2, "bits": 248 }, + { "op": "private_input", "guard": null }, + { "op": "constrain_bits", "var": 3, "bits": 8 }, + { "op": "private_input", "guard": null }, + { "op": "constrain_bits", "var": 4, "bits": 248 }, + { "op": "persistent_hash", "alignment": [{ "tag": "atom", "value": { "length": 32, "tag": "bytes" } }, { "tag": "atom", "value": { "length": 32, "tag": "bytes" } }], "inputs": [3, 4, 1, 2] }, + { "op": "load_imm", "imm": "30" }, + { "op": "declare_pub_input", "var": 7 }, + { "op": "pi_skip", "guard": 0, "count": 1 }, + { "op": "load_imm", "imm": "50" }, + { "op": "load_imm", "imm": "00" }, + { "op": "declare_pub_input", "var": 8 }, + { "op": "declare_pub_input", "var": 0 }, + { "op": "declare_pub_input", "var": 0 }, + { "op": "declare_pub_input", "var": 9 }, + { "op": "pi_skip", "guard": 0, "count": 4 }, + { "op": "public_input", "guard": null }, + { "op": "public_input", "guard": null }, + { "op": "load_imm", "imm": "20" }, + { "op": "load_imm", "imm": "0C" }, + { "op": "declare_pub_input", "var": 13 }, + { "op": "declare_pub_input", "var": 0 }, + { "op": "declare_pub_input", "var": 12 }, + { "op": "declare_pub_input", "var": 10 }, + { "op": "declare_pub_input", "var": 11 }, + { "op": "pi_skip", "guard": 0, "count": 5 }, + { "op": "declare_pub_input", "var": 7 }, + { "op": "pi_skip", "guard": 0, "count": 1 }, + { "op": "declare_pub_input", "var": 8 }, + { "op": "declare_pub_input", "var": 0 }, + { "op": "declare_pub_input", "var": 0 }, + { "op": "declare_pub_input", "var": 0 }, + { "op": "pi_skip", "guard": 0, "count": 4 }, + { "op": "public_input", "guard": null }, + { "op": "load_imm", "imm": "08" }, + { "op": "load_imm", "imm": "0D" }, + { "op": "declare_pub_input", "var": 16 }, + { "op": "declare_pub_input", "var": 0 }, + { "op": "declare_pub_input", "var": 15 }, + { "op": "declare_pub_input", "var": 14 }, + { "op": "pi_skip", "guard": 0, "count": 4 }, + { "op": "declare_pub_input", "var": 7 }, + { "op": "pi_skip", "guard": 0, "count": 1 }, + { "op": "load_imm", "imm": "02" }, + { "op": "declare_pub_input", "var": 8 }, + { "op": "declare_pub_input", "var": 0 }, + { "op": "declare_pub_input", "var": 0 }, + { "op": "declare_pub_input", "var": 17 }, + { "op": "pi_skip", "guard": 0, "count": 4 }, + { "op": "public_input", "guard": null }, + { "op": "public_input", "guard": null }, + { "op": "declare_pub_input", "var": 13 }, + { "op": "declare_pub_input", "var": 0 }, + { "op": "declare_pub_input", "var": 12 }, + { "op": "declare_pub_input", "var": 18 }, + { "op": "declare_pub_input", "var": 19 }, + { "op": "pi_skip", "guard": 0, "count": 5 }, + { "op": "div_mod_power_of_two", "var": 14, "bits": 248 }, + { "op": "load_imm", "imm": "53616D706C655A4F776E61626C653A736869656C643A" }, + { "op": "persistent_hash", "alignment": [{ "tag": "atom", "value": { "length": 32, "tag": "bytes" } }, { "tag": "atom", "value": { "length": 32, "tag": "bytes" } }, { "tag": "atom", "value": { "length": 32, "tag": "bytes" } }, { "tag": "atom", "value": { "length": 32, "tag": "bytes" } }], "inputs": [5, 6, 18, 19, 20, 21, 9, 22] }, + { "op": "test_eq", "a": 10, "b": 23 }, + { "op": "test_eq", "a": 11, "b": 24 }, + { "op": "cond_select", "bit": 25, "a": 26, "b": 9 }, + { "op": "assert", "cond": 27 } + ] +} diff --git a/packages/simulator/test/fixtures/test-artifacts/SampleZOwnable/zkir/owner.bzkir b/packages/simulator/test/fixtures/test-artifacts/SampleZOwnable/zkir/owner.bzkir new file mode 100644 index 00000000..38b14bfd Binary files /dev/null and b/packages/simulator/test/fixtures/test-artifacts/SampleZOwnable/zkir/owner.bzkir differ diff --git a/packages/simulator/test/fixtures/test-artifacts/SampleZOwnable/zkir/owner.zkir b/packages/simulator/test/fixtures/test-artifacts/SampleZOwnable/zkir/owner.zkir new file mode 100644 index 00000000..50ebbfc5 --- /dev/null +++ b/packages/simulator/test/fixtures/test-artifacts/SampleZOwnable/zkir/owner.zkir @@ -0,0 +1,30 @@ +{ + "version": { "major": 2, "minor": 0 }, + "do_communications_commitment": true, + "num_inputs": 0, + "instructions": [ + { "op": "load_imm", "imm": "01" }, + { "op": "load_imm", "imm": "30" }, + { "op": "declare_pub_input", "var": 1 }, + { "op": "pi_skip", "guard": 0, "count": 1 }, + { "op": "load_imm", "imm": "50" }, + { "op": "load_imm", "imm": "00" }, + { "op": "declare_pub_input", "var": 2 }, + { "op": "declare_pub_input", "var": 0 }, + { "op": "declare_pub_input", "var": 0 }, + { "op": "declare_pub_input", "var": 3 }, + { "op": "pi_skip", "guard": 0, "count": 4 }, + { "op": "public_input", "guard": null }, + { "op": "public_input", "guard": null }, + { "op": "load_imm", "imm": "20" }, + { "op": "load_imm", "imm": "0C" }, + { "op": "declare_pub_input", "var": 7 }, + { "op": "declare_pub_input", "var": 0 }, + { "op": "declare_pub_input", "var": 6 }, + { "op": "declare_pub_input", "var": 4 }, + { "op": "declare_pub_input", "var": 5 }, + { "op": "pi_skip", "guard": 0, "count": 5 }, + { "op": "output", "var": 4 }, + { "op": "output", "var": 5 } + ] +} diff --git a/packages/simulator/test/fixtures/test-artifacts/SampleZOwnable/zkir/renounceOwnership.bzkir b/packages/simulator/test/fixtures/test-artifacts/SampleZOwnable/zkir/renounceOwnership.bzkir new file mode 100644 index 00000000..0eb1da1e Binary files /dev/null and b/packages/simulator/test/fixtures/test-artifacts/SampleZOwnable/zkir/renounceOwnership.bzkir differ diff --git a/packages/simulator/test/fixtures/test-artifacts/SampleZOwnable/zkir/renounceOwnership.zkir b/packages/simulator/test/fixtures/test-artifacts/SampleZOwnable/zkir/renounceOwnership.zkir new file mode 100644 index 00000000..ce621b6a --- /dev/null +++ b/packages/simulator/test/fixtures/test-artifacts/SampleZOwnable/zkir/renounceOwnership.zkir @@ -0,0 +1,93 @@ +{ + "version": { "major": 2, "minor": 0 }, + "do_communications_commitment": true, + "num_inputs": 0, + "instructions": [ + { "op": "load_imm", "imm": "01" }, + { "op": "private_input", "guard": null }, + { "op": "constrain_bits", "var": 1, "bits": 8 }, + { "op": "private_input", "guard": null }, + { "op": "constrain_bits", "var": 2, "bits": 248 }, + { "op": "private_input", "guard": null }, + { "op": "constrain_bits", "var": 3, "bits": 8 }, + { "op": "private_input", "guard": null }, + { "op": "constrain_bits", "var": 4, "bits": 248 }, + { "op": "persistent_hash", "alignment": [{ "tag": "atom", "value": { "length": 32, "tag": "bytes" } }, { "tag": "atom", "value": { "length": 32, "tag": "bytes" } }], "inputs": [3, 4, 1, 2] }, + { "op": "load_imm", "imm": "30" }, + { "op": "declare_pub_input", "var": 7 }, + { "op": "pi_skip", "guard": 0, "count": 1 }, + { "op": "load_imm", "imm": "50" }, + { "op": "load_imm", "imm": "00" }, + { "op": "declare_pub_input", "var": 8 }, + { "op": "declare_pub_input", "var": 0 }, + { "op": "declare_pub_input", "var": 0 }, + { "op": "declare_pub_input", "var": 9 }, + { "op": "pi_skip", "guard": 0, "count": 4 }, + { "op": "public_input", "guard": null }, + { "op": "public_input", "guard": null }, + { "op": "load_imm", "imm": "20" }, + { "op": "load_imm", "imm": "0C" }, + { "op": "declare_pub_input", "var": 13 }, + { "op": "declare_pub_input", "var": 0 }, + { "op": "declare_pub_input", "var": 12 }, + { "op": "declare_pub_input", "var": 10 }, + { "op": "declare_pub_input", "var": 11 }, + { "op": "pi_skip", "guard": 0, "count": 5 }, + { "op": "declare_pub_input", "var": 7 }, + { "op": "pi_skip", "guard": 0, "count": 1 }, + { "op": "declare_pub_input", "var": 8 }, + { "op": "declare_pub_input", "var": 0 }, + { "op": "declare_pub_input", "var": 0 }, + { "op": "declare_pub_input", "var": 0 }, + { "op": "pi_skip", "guard": 0, "count": 4 }, + { "op": "public_input", "guard": null }, + { "op": "load_imm", "imm": "08" }, + { "op": "load_imm", "imm": "0D" }, + { "op": "declare_pub_input", "var": 16 }, + { "op": "declare_pub_input", "var": 0 }, + { "op": "declare_pub_input", "var": 15 }, + { "op": "declare_pub_input", "var": 14 }, + { "op": "pi_skip", "guard": 0, "count": 4 }, + { "op": "declare_pub_input", "var": 7 }, + { "op": "pi_skip", "guard": 0, "count": 1 }, + { "op": "load_imm", "imm": "02" }, + { "op": "declare_pub_input", "var": 8 }, + { "op": "declare_pub_input", "var": 0 }, + { "op": "declare_pub_input", "var": 0 }, + { "op": "declare_pub_input", "var": 17 }, + { "op": "pi_skip", "guard": 0, "count": 4 }, + { "op": "public_input", "guard": null }, + { "op": "public_input", "guard": null }, + { "op": "declare_pub_input", "var": 13 }, + { "op": "declare_pub_input", "var": 0 }, + { "op": "declare_pub_input", "var": 12 }, + { "op": "declare_pub_input", "var": 18 }, + { "op": "declare_pub_input", "var": 19 }, + { "op": "pi_skip", "guard": 0, "count": 5 }, + { "op": "div_mod_power_of_two", "var": 14, "bits": 248 }, + { "op": "load_imm", "imm": "53616D706C655A4F776E61626C653A736869656C643A" }, + { "op": "persistent_hash", "alignment": [{ "tag": "atom", "value": { "length": 32, "tag": "bytes" } }, { "tag": "atom", "value": { "length": 32, "tag": "bytes" } }, { "tag": "atom", "value": { "length": 32, "tag": "bytes" } }, { "tag": "atom", "value": { "length": 32, "tag": "bytes" } }], "inputs": [5, 6, 18, 19, 20, 21, 9, 22] }, + { "op": "test_eq", "a": 10, "b": 23 }, + { "op": "test_eq", "a": 11, "b": 24 }, + { "op": "cond_select", "bit": 25, "a": 26, "b": 9 }, + { "op": "assert", "cond": 27 }, + { "op": "load_imm", "imm": "10" }, + { "op": "declare_pub_input", "var": 28 }, + { "op": "declare_pub_input", "var": 0 }, + { "op": "declare_pub_input", "var": 0 }, + { "op": "declare_pub_input", "var": 0 }, + { "op": "declare_pub_input", "var": 9 }, + { "op": "pi_skip", "guard": 0, "count": 5 }, + { "op": "load_imm", "imm": "11" }, + { "op": "declare_pub_input", "var": 29 }, + { "op": "declare_pub_input", "var": 0 }, + { "op": "declare_pub_input", "var": 0 }, + { "op": "declare_pub_input", "var": 12 }, + { "op": "declare_pub_input", "var": 9 }, + { "op": "declare_pub_input", "var": 9 }, + { "op": "pi_skip", "guard": 0, "count": 6 }, + { "op": "load_imm", "imm": "91" }, + { "op": "declare_pub_input", "var": 30 }, + { "op": "pi_skip", "guard": 0, "count": 1 } + ] +} diff --git a/packages/simulator/test/fixtures/test-artifacts/SampleZOwnable/zkir/transferOwnership.bzkir b/packages/simulator/test/fixtures/test-artifacts/SampleZOwnable/zkir/transferOwnership.bzkir new file mode 100644 index 00000000..173464ee Binary files /dev/null and b/packages/simulator/test/fixtures/test-artifacts/SampleZOwnable/zkir/transferOwnership.bzkir differ diff --git a/packages/simulator/test/fixtures/test-artifacts/SampleZOwnable/zkir/transferOwnership.zkir b/packages/simulator/test/fixtures/test-artifacts/SampleZOwnable/zkir/transferOwnership.zkir new file mode 100644 index 00000000..4f31458b --- /dev/null +++ b/packages/simulator/test/fixtures/test-artifacts/SampleZOwnable/zkir/transferOwnership.zkir @@ -0,0 +1,144 @@ +{ + "version": { "major": 2, "minor": 0 }, + "do_communications_commitment": true, + "num_inputs": 2, + "instructions": [ + { "op": "constrain_bits", "var": 0, "bits": 8 }, + { "op": "constrain_bits", "var": 1, "bits": 248 }, + { "op": "load_imm", "imm": "01" }, + { "op": "private_input", "guard": null }, + { "op": "constrain_bits", "var": 3, "bits": 8 }, + { "op": "private_input", "guard": null }, + { "op": "constrain_bits", "var": 4, "bits": 248 }, + { "op": "private_input", "guard": null }, + { "op": "constrain_bits", "var": 5, "bits": 8 }, + { "op": "private_input", "guard": null }, + { "op": "constrain_bits", "var": 6, "bits": 248 }, + { "op": "persistent_hash", "alignment": [{ "tag": "atom", "value": { "length": 32, "tag": "bytes" } }, { "tag": "atom", "value": { "length": 32, "tag": "bytes" } }], "inputs": [5, 6, 3, 4] }, + { "op": "load_imm", "imm": "30" }, + { "op": "declare_pub_input", "var": 9 }, + { "op": "pi_skip", "guard": 2, "count": 1 }, + { "op": "load_imm", "imm": "50" }, + { "op": "load_imm", "imm": "00" }, + { "op": "declare_pub_input", "var": 10 }, + { "op": "declare_pub_input", "var": 2 }, + { "op": "declare_pub_input", "var": 2 }, + { "op": "declare_pub_input", "var": 11 }, + { "op": "pi_skip", "guard": 2, "count": 4 }, + { "op": "public_input", "guard": null }, + { "op": "public_input", "guard": null }, + { "op": "load_imm", "imm": "20" }, + { "op": "load_imm", "imm": "0C" }, + { "op": "declare_pub_input", "var": 15 }, + { "op": "declare_pub_input", "var": 2 }, + { "op": "declare_pub_input", "var": 14 }, + { "op": "declare_pub_input", "var": 12 }, + { "op": "declare_pub_input", "var": 13 }, + { "op": "pi_skip", "guard": 2, "count": 5 }, + { "op": "declare_pub_input", "var": 9 }, + { "op": "pi_skip", "guard": 2, "count": 1 }, + { "op": "declare_pub_input", "var": 10 }, + { "op": "declare_pub_input", "var": 2 }, + { "op": "declare_pub_input", "var": 2 }, + { "op": "declare_pub_input", "var": 2 }, + { "op": "pi_skip", "guard": 2, "count": 4 }, + { "op": "public_input", "guard": null }, + { "op": "load_imm", "imm": "08" }, + { "op": "load_imm", "imm": "0D" }, + { "op": "declare_pub_input", "var": 18 }, + { "op": "declare_pub_input", "var": 2 }, + { "op": "declare_pub_input", "var": 17 }, + { "op": "declare_pub_input", "var": 16 }, + { "op": "pi_skip", "guard": 2, "count": 4 }, + { "op": "declare_pub_input", "var": 9 }, + { "op": "pi_skip", "guard": 2, "count": 1 }, + { "op": "load_imm", "imm": "02" }, + { "op": "declare_pub_input", "var": 10 }, + { "op": "declare_pub_input", "var": 2 }, + { "op": "declare_pub_input", "var": 2 }, + { "op": "declare_pub_input", "var": 19 }, + { "op": "pi_skip", "guard": 2, "count": 4 }, + { "op": "public_input", "guard": null }, + { "op": "public_input", "guard": null }, + { "op": "declare_pub_input", "var": 15 }, + { "op": "declare_pub_input", "var": 2 }, + { "op": "declare_pub_input", "var": 14 }, + { "op": "declare_pub_input", "var": 20 }, + { "op": "declare_pub_input", "var": 21 }, + { "op": "pi_skip", "guard": 2, "count": 5 }, + { "op": "div_mod_power_of_two", "var": 16, "bits": 248 }, + { "op": "load_imm", "imm": "53616D706C655A4F776E61626C653A736869656C643A" }, + { "op": "persistent_hash", "alignment": [{ "tag": "atom", "value": { "length": 32, "tag": "bytes" } }, { "tag": "atom", "value": { "length": 32, "tag": "bytes" } }, { "tag": "atom", "value": { "length": 32, "tag": "bytes" } }, { "tag": "atom", "value": { "length": 32, "tag": "bytes" } }], "inputs": [7, 8, 20, 21, 22, 23, 11, 24] }, + { "op": "test_eq", "a": 12, "b": 25 }, + { "op": "test_eq", "a": 13, "b": 26 }, + { "op": "cond_select", "bit": 27, "a": 28, "b": 11 }, + { "op": "assert", "cond": 29 }, + { "op": "test_eq", "a": 0, "b": 11 }, + { "op": "test_eq", "a": 1, "b": 11 }, + { "op": "cond_select", "bit": 30, "a": 31, "b": 11 }, + { "op": "cond_select", "bit": 32, "a": 11, "b": 2 }, + { "op": "assert", "cond": 33 }, + { "op": "load_imm", "imm": "70" }, + { "op": "declare_pub_input", "var": 34 }, + { "op": "declare_pub_input", "var": 2 }, + { "op": "declare_pub_input", "var": 2 }, + { "op": "declare_pub_input", "var": 2 }, + { "op": "pi_skip", "guard": 2, "count": 4 }, + { "op": "load_imm", "imm": "0E" }, + { "op": "declare_pub_input", "var": 35 }, + { "op": "declare_pub_input", "var": 2 }, + { "op": "pi_skip", "guard": 2, "count": 2 }, + { "op": "load_imm", "imm": "A1" }, + { "op": "declare_pub_input", "var": 36 }, + { "op": "pi_skip", "guard": 2, "count": 1 }, + { "op": "declare_pub_input", "var": 9 }, + { "op": "pi_skip", "guard": 2, "count": 1 }, + { "op": "declare_pub_input", "var": 10 }, + { "op": "declare_pub_input", "var": 2 }, + { "op": "declare_pub_input", "var": 2 }, + { "op": "declare_pub_input", "var": 2 }, + { "op": "pi_skip", "guard": 2, "count": 4 }, + { "op": "public_input", "guard": null }, + { "op": "declare_pub_input", "var": 18 }, + { "op": "declare_pub_input", "var": 2 }, + { "op": "declare_pub_input", "var": 17 }, + { "op": "declare_pub_input", "var": 37 }, + { "op": "pi_skip", "guard": 2, "count": 4 }, + { "op": "declare_pub_input", "var": 9 }, + { "op": "pi_skip", "guard": 2, "count": 1 }, + { "op": "declare_pub_input", "var": 10 }, + { "op": "declare_pub_input", "var": 2 }, + { "op": "declare_pub_input", "var": 2 }, + { "op": "declare_pub_input", "var": 19 }, + { "op": "pi_skip", "guard": 2, "count": 4 }, + { "op": "public_input", "guard": null }, + { "op": "public_input", "guard": null }, + { "op": "declare_pub_input", "var": 15 }, + { "op": "declare_pub_input", "var": 2 }, + { "op": "declare_pub_input", "var": 14 }, + { "op": "declare_pub_input", "var": 38 }, + { "op": "declare_pub_input", "var": 39 }, + { "op": "pi_skip", "guard": 2, "count": 5 }, + { "op": "div_mod_power_of_two", "var": 37, "bits": 248 }, + { "op": "load_imm", "imm": "53616D706C655A4F776E61626C653A736869656C643A" }, + { "op": "persistent_hash", "alignment": [{ "tag": "atom", "value": { "length": 32, "tag": "bytes" } }, { "tag": "atom", "value": { "length": 32, "tag": "bytes" } }, { "tag": "atom", "value": { "length": 32, "tag": "bytes" } }, { "tag": "atom", "value": { "length": 32, "tag": "bytes" } }], "inputs": [0, 1, 38, 39, 40, 41, 11, 42] }, + { "op": "load_imm", "imm": "10" }, + { "op": "declare_pub_input", "var": 45 }, + { "op": "declare_pub_input", "var": 2 }, + { "op": "declare_pub_input", "var": 2 }, + { "op": "declare_pub_input", "var": 2 }, + { "op": "declare_pub_input", "var": 11 }, + { "op": "pi_skip", "guard": 2, "count": 5 }, + { "op": "load_imm", "imm": "11" }, + { "op": "declare_pub_input", "var": 46 }, + { "op": "declare_pub_input", "var": 2 }, + { "op": "declare_pub_input", "var": 2 }, + { "op": "declare_pub_input", "var": 14 }, + { "op": "declare_pub_input", "var": 43 }, + { "op": "declare_pub_input", "var": 44 }, + { "op": "pi_skip", "guard": 2, "count": 6 }, + { "op": "load_imm", "imm": "91" }, + { "op": "declare_pub_input", "var": 47 }, + { "op": "pi_skip", "guard": 2, "count": 1 } + ] +} diff --git a/packages/simulator/test/fixtures/test-artifacts/Simple/compiler/contract-info.json b/packages/simulator/test/fixtures/test-artifacts/Simple/compiler/contract-info.json new file mode 100644 index 00000000..8191238b --- /dev/null +++ b/packages/simulator/test/fixtures/test-artifacts/Simple/compiler/contract-info.json @@ -0,0 +1,34 @@ +{ + "circuits": [ + { + "name": "setVal", + "pure": false, + "arguments": [ + { + "name": "n", + "type": { + "type-name": "Field" + } + } + ], + "result-type": { + "type-name": "Tuple", + "types": [ + ] + } + }, + { + "name": "getVal", + "pure": false, + "arguments": [ + ], + "result-type": { + "type-name": "Field" + } + } + ], + "witnesses": [ + ], + "contracts": [ + ] +} diff --git a/packages/simulator/test/fixtures/test-artifacts/Simple/contract/index.cjs b/packages/simulator/test/fixtures/test-artifacts/Simple/contract/index.cjs new file mode 100644 index 00000000..9ce3beba --- /dev/null +++ b/packages/simulator/test/fixtures/test-artifacts/Simple/contract/index.cjs @@ -0,0 +1,261 @@ +'use strict'; +const __compactRuntime = require('@midnight-ntwrk/compact-runtime'); +const expectedRuntimeVersionString = '0.8.1'; +const expectedRuntimeVersion = expectedRuntimeVersionString.split('-')[0].split('.').map(Number); +const actualRuntimeVersion = __compactRuntime.versionString.split('-')[0].split('.').map(Number); +if (expectedRuntimeVersion[0] != actualRuntimeVersion[0] + || (actualRuntimeVersion[0] == 0 && expectedRuntimeVersion[1] != actualRuntimeVersion[1]) + || expectedRuntimeVersion[1] > actualRuntimeVersion[1] + || (expectedRuntimeVersion[1] == actualRuntimeVersion[1] && expectedRuntimeVersion[2] > actualRuntimeVersion[2])) + throw new __compactRuntime.CompactError(`Version mismatch: compiled code expects ${expectedRuntimeVersionString}, runtime is ${__compactRuntime.versionString}`); +{ const MAX_FIELD = 52435875175126190479447740508185965837690552500527637822603658699938581184512n; + if (__compactRuntime.MAX_FIELD !== MAX_FIELD) + throw new __compactRuntime.CompactError(`compiler thinks maximum field value is ${MAX_FIELD}; run time thinks it is ${__compactRuntime.MAX_FIELD}`) +} + +const _descriptor_0 = new __compactRuntime.CompactTypeField(); + +const _descriptor_1 = new __compactRuntime.CompactTypeUnsignedInteger(18446744073709551615n, 8); + +const _descriptor_2 = new __compactRuntime.CompactTypeBoolean(); + +const _descriptor_3 = new __compactRuntime.CompactTypeBytes(32); + +class _ContractAddress_0 { + alignment() { + return _descriptor_3.alignment(); + } + fromValue(value_0) { + return { + bytes: _descriptor_3.fromValue(value_0) + } + } + toValue(value_0) { + return _descriptor_3.toValue(value_0.bytes); + } +} + +const _descriptor_4 = new _ContractAddress_0(); + +const _descriptor_5 = new __compactRuntime.CompactTypeUnsignedInteger(255n, 1); + +const _descriptor_6 = new __compactRuntime.CompactTypeUnsignedInteger(340282366920938463463374607431768211455n, 16); + +class Contract { + witnesses; + constructor(...args_0) { + if (args_0.length !== 1) { + throw new __compactRuntime.CompactError(`Contract constructor: expected 1 argument, received ${args_0.length}`); + } + const witnesses_0 = args_0[0]; + if (typeof(witnesses_0) !== 'object') { + throw new __compactRuntime.CompactError('first (witnesses) argument to Contract constructor is not an object'); + } + this.witnesses = witnesses_0; + this.circuits = { + setVal: (...args_1) => { + if (args_1.length !== 2) { + throw new __compactRuntime.CompactError(`setVal: expected 2 arguments (as invoked from Typescript), received ${args_1.length}`); + } + const contextOrig_0 = args_1[0]; + const n_0 = args_1[1]; + if (!(typeof(contextOrig_0) === 'object' && contextOrig_0.originalState != undefined && contextOrig_0.transactionContext != undefined)) { + __compactRuntime.type_error('setVal', + 'argument 1 (as invoked from Typescript)', + 'Simple.compact line 8 char 1', + 'CircuitContext', + contextOrig_0) + } + if (!(typeof(n_0) === 'bigint' && n_0 >= 0 && n_0 <= __compactRuntime.MAX_FIELD)) { + __compactRuntime.type_error('setVal', + 'argument 1 (argument 2 as invoked from Typescript)', + 'Simple.compact line 8 char 1', + 'Field', + n_0) + } + const context = { ...contextOrig_0 }; + const partialProofData = { + input: { + value: _descriptor_0.toValue(n_0), + alignment: _descriptor_0.alignment() + }, + output: undefined, + publicTranscript: [], + privateTranscriptOutputs: [] + }; + const result_0 = this._setVal_0(context, partialProofData, n_0); + partialProofData.output = { value: [], alignment: [] }; + return { result: result_0, context: context, proofData: partialProofData }; + }, + getVal: (...args_1) => { + if (args_1.length !== 1) { + throw new __compactRuntime.CompactError(`getVal: expected 1 argument (as invoked from Typescript), received ${args_1.length}`); + } + const contextOrig_0 = args_1[0]; + if (!(typeof(contextOrig_0) === 'object' && contextOrig_0.originalState != undefined && contextOrig_0.transactionContext != undefined)) { + __compactRuntime.type_error('getVal', + 'argument 1 (as invoked from Typescript)', + 'Simple.compact line 12 char 1', + 'CircuitContext', + contextOrig_0) + } + const context = { ...contextOrig_0 }; + const partialProofData = { + input: { value: [], alignment: [] }, + output: undefined, + publicTranscript: [], + privateTranscriptOutputs: [] + }; + const result_0 = this._getVal_0(context, partialProofData); + partialProofData.output = { value: _descriptor_0.toValue(result_0), alignment: _descriptor_0.alignment() }; + return { result: result_0, context: context, proofData: partialProofData }; + } + }; + this.impureCircuits = { + setVal: this.circuits.setVal, + getVal: this.circuits.getVal + }; + } + initialState(...args_0) { + if (args_0.length !== 1) { + throw new __compactRuntime.CompactError(`Contract state constructor: expected 1 argument (as invoked from Typescript), received ${args_0.length}`); + } + const constructorContext_0 = args_0[0]; + if (typeof(constructorContext_0) !== 'object') { + throw new __compactRuntime.CompactError(`Contract state constructor: expected 'constructorContext' in argument 1 (as invoked from Typescript) to be an object`); + } + if (!('initialZswapLocalState' in constructorContext_0)) { + throw new __compactRuntime.CompactError(`Contract state constructor: expected 'initialZswapLocalState' in argument 1 (as invoked from Typescript)`); + } + if (typeof(constructorContext_0.initialZswapLocalState) !== 'object') { + throw new __compactRuntime.CompactError(`Contract state constructor: expected 'initialZswapLocalState' in argument 1 (as invoked from Typescript) to be an object`); + } + const state_0 = new __compactRuntime.ContractState(); + let stateValue_0 = __compactRuntime.StateValue.newArray(); + stateValue_0 = stateValue_0.arrayPush(__compactRuntime.StateValue.newNull()); + state_0.data = stateValue_0; + state_0.setOperation('setVal', new __compactRuntime.ContractOperation()); + state_0.setOperation('getVal', new __compactRuntime.ContractOperation()); + const context = { + originalState: state_0, + currentPrivateState: constructorContext_0.initialPrivateState, + currentZswapLocalState: constructorContext_0.initialZswapLocalState, + transactionContext: new __compactRuntime.QueryContext(state_0.data, __compactRuntime.dummyContractAddress()) + }; + const partialProofData = { + input: { value: [], alignment: [] }, + output: undefined, + publicTranscript: [], + privateTranscriptOutputs: [] + }; + Contract._query(context, + partialProofData, + [ + { push: { storage: false, + value: __compactRuntime.StateValue.newCell({ value: _descriptor_5.toValue(0n), + alignment: _descriptor_5.alignment() }).encode() } }, + { push: { storage: true, + value: __compactRuntime.StateValue.newCell({ value: _descriptor_0.toValue(0n), + alignment: _descriptor_0.alignment() }).encode() } }, + { ins: { cached: false, n: 1 } }]); + state_0.data = context.transactionContext.state; + return { + currentContractState: state_0, + currentPrivateState: context.currentPrivateState, + currentZswapLocalState: context.currentZswapLocalState + } + } + _setVal_0(context, partialProofData, n_0) { + Contract._query(context, + partialProofData, + [ + { push: { storage: false, + value: __compactRuntime.StateValue.newCell({ value: _descriptor_5.toValue(0n), + alignment: _descriptor_5.alignment() }).encode() } }, + { push: { storage: true, + value: __compactRuntime.StateValue.newCell({ value: _descriptor_0.toValue(n_0), + alignment: _descriptor_0.alignment() }).encode() } }, + { ins: { cached: false, n: 1 } }]); + return []; + } + _getVal_0(context, partialProofData) { + return _descriptor_0.fromValue(Contract._query(context, + partialProofData, + [ + { dup: { n: 0 } }, + { idx: { cached: false, + pushPath: false, + path: [ + { tag: 'value', + value: { value: _descriptor_5.toValue(0n), + alignment: _descriptor_5.alignment() } }] } }, + { popeq: { cached: false, + result: undefined } }]).value); + } + static _query(context, partialProofData, prog) { + var res; + try { + res = context.transactionContext.query(prog, __compactRuntime.CostModel.dummyCostModel()); + } catch (err) { + throw new __compactRuntime.CompactError(err.toString()); + } + context.transactionContext = res.context; + var reads = res.events.filter((e) => e.tag === 'read'); + var i = 0; + partialProofData.publicTranscript = partialProofData.publicTranscript.concat(prog.map((op) => { + if(typeof(op) === 'object' && 'popeq' in op) { + return { popeq: { + ...op.popeq, + result: reads[i++].content, + } }; + } else { + return op; + } + })); + if(res.events.length == 1 && res.events[0].tag === 'read') { + return res.events[0].content; + } else { + return res.events; + } + } +} +function ledger(state) { + const context = { + originalState: state, + transactionContext: new __compactRuntime.QueryContext(state, __compactRuntime.dummyContractAddress()) + }; + const partialProofData = { + input: { value: [], alignment: [] }, + output: undefined, + publicTranscript: [], + privateTranscriptOutputs: [] + }; + return { + get _val() { + return _descriptor_0.fromValue(Contract._query(context, + partialProofData, + [ + { dup: { n: 0 } }, + { idx: { cached: false, + pushPath: false, + path: [ + { tag: 'value', + value: { value: _descriptor_5.toValue(0n), + alignment: _descriptor_5.alignment() } }] } }, + { popeq: { cached: false, + result: undefined } }]).value); + } + }; +} +const _emptyContext = { + originalState: new __compactRuntime.ContractState(), + transactionContext: new __compactRuntime.QueryContext(new __compactRuntime.ContractState().data, __compactRuntime.dummyContractAddress()) +}; +const _dummyContract = new Contract({ }); +const pureCircuits = {}; +const contractReferenceLocations = { tag: 'publicLedgerArray', indices: { } }; +exports.Contract = Contract; +exports.ledger = ledger; +exports.pureCircuits = pureCircuits; +exports.contractReferenceLocations = contractReferenceLocations; +//# sourceMappingURL=index.cjs.map diff --git a/packages/simulator/test/fixtures/test-artifacts/Simple/contract/index.cjs.map b/packages/simulator/test/fixtures/test-artifacts/Simple/contract/index.cjs.map new file mode 100644 index 00000000..5b3e8d27 --- /dev/null +++ b/packages/simulator/test/fixtures/test-artifacts/Simple/contract/index.cjs.map @@ -0,0 +1,8 @@ +{ + "version": 3, + "file": "index.cjs", + "sourceRoot": "../../../../../", + "sources": ["test/fixtures/sample-contracts/Simple.compact"], + "names": [], + "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAAA;;;;;;;;;;MAOA,AAAA,MAEC;;;;;cAFqB,GAAQ;;;;;;;;;;;;;;;;;;yCAAR,GAAQ;;;;;;;mEAAR,GAAQ;;;OAE7B;MAED,AAAA,MAEC;;;;;;;;;;;;;;;;;;;;;;OAAA;;;;;;GACA;EAdD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAKA;;;;;;;;;uDAA0B;;;;;;;GASzB;EAPD,AAAA,SAEC,4BAFqB,GAAQ;IAC5B;;;;;;;yGAAgB,GAAC;;uDAAb;;GACL;EAED,AAAA,SAEC;mCADQ;;;;;;;;;;;sFAAI;GACZ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IARD;qCAAA;;;;;;;;;;;wFAA0B;KAAA;;;;;;;;;;;;;;" +} diff --git a/packages/simulator/test/fixtures/test-artifacts/Simple/contract/index.d.cts b/packages/simulator/test/fixtures/test-artifacts/Simple/contract/index.d.cts new file mode 100644 index 00000000..b9e089a9 --- /dev/null +++ b/packages/simulator/test/fixtures/test-artifacts/Simple/contract/index.d.cts @@ -0,0 +1,44 @@ +import type * as __compactRuntime from '@midnight-ntwrk/compact-runtime'; + +export type ZswapCoinPublicKey = { bytes: Uint8Array }; + +export type ContractAddress = { bytes: Uint8Array }; + +export type Either = { is_left: boolean; left: A; right: B }; + +export type Maybe = { is_some: boolean; value: T }; + +export type Witnesses = { +} + +export type ImpureCircuits = { + setVal(context: __compactRuntime.CircuitContext, n_0: bigint): __compactRuntime.CircuitResults; + getVal(context: __compactRuntime.CircuitContext): __compactRuntime.CircuitResults; +} + +export type PureCircuits = { +} + +export type Circuits = { + setVal(context: __compactRuntime.CircuitContext, n_0: bigint): __compactRuntime.CircuitResults; + getVal(context: __compactRuntime.CircuitContext): __compactRuntime.CircuitResults; +} + +export type Ledger = { + readonly _val: bigint; +} + +export type ContractReferenceLocations = any; + +export declare const contractReferenceLocations : ContractReferenceLocations; + +export declare class Contract = Witnesses> { + witnesses: W; + circuits: Circuits; + impureCircuits: ImpureCircuits; + constructor(witnesses: W); + initialState(context: __compactRuntime.ConstructorContext): __compactRuntime.ConstructorResult; +} + +export declare function ledger(state: __compactRuntime.StateValue): Ledger; +export declare const pureCircuits: PureCircuits; diff --git a/packages/simulator/test/fixtures/test-artifacts/Simple/keys/getVal.prover b/packages/simulator/test/fixtures/test-artifacts/Simple/keys/getVal.prover new file mode 100644 index 00000000..84ddcc6f Binary files /dev/null and b/packages/simulator/test/fixtures/test-artifacts/Simple/keys/getVal.prover differ diff --git a/packages/simulator/test/fixtures/test-artifacts/Simple/keys/getVal.verifier b/packages/simulator/test/fixtures/test-artifacts/Simple/keys/getVal.verifier new file mode 100644 index 00000000..5a60c135 Binary files /dev/null and b/packages/simulator/test/fixtures/test-artifacts/Simple/keys/getVal.verifier differ diff --git a/packages/simulator/test/fixtures/test-artifacts/Simple/keys/setVal.prover b/packages/simulator/test/fixtures/test-artifacts/Simple/keys/setVal.prover new file mode 100644 index 00000000..b0636044 Binary files /dev/null and b/packages/simulator/test/fixtures/test-artifacts/Simple/keys/setVal.prover differ diff --git a/packages/simulator/test/fixtures/test-artifacts/Simple/keys/setVal.verifier b/packages/simulator/test/fixtures/test-artifacts/Simple/keys/setVal.verifier new file mode 100644 index 00000000..e205835e Binary files /dev/null and b/packages/simulator/test/fixtures/test-artifacts/Simple/keys/setVal.verifier differ diff --git a/packages/simulator/test/fixtures/test-artifacts/Simple/zkir/getVal.bzkir b/packages/simulator/test/fixtures/test-artifacts/Simple/zkir/getVal.bzkir new file mode 100644 index 00000000..74acd352 Binary files /dev/null and b/packages/simulator/test/fixtures/test-artifacts/Simple/zkir/getVal.bzkir differ diff --git a/packages/simulator/test/fixtures/test-artifacts/Simple/zkir/getVal.zkir b/packages/simulator/test/fixtures/test-artifacts/Simple/zkir/getVal.zkir new file mode 100644 index 00000000..cad66eb4 --- /dev/null +++ b/packages/simulator/test/fixtures/test-artifacts/Simple/zkir/getVal.zkir @@ -0,0 +1,27 @@ +{ + "version": { "major": 2, "minor": 0 }, + "do_communications_commitment": true, + "num_inputs": 0, + "instructions": [ + { "op": "load_imm", "imm": "01" }, + { "op": "load_imm", "imm": "30" }, + { "op": "declare_pub_input", "var": 1 }, + { "op": "pi_skip", "guard": 0, "count": 1 }, + { "op": "load_imm", "imm": "50" }, + { "op": "load_imm", "imm": "00" }, + { "op": "declare_pub_input", "var": 2 }, + { "op": "declare_pub_input", "var": 0 }, + { "op": "declare_pub_input", "var": 0 }, + { "op": "declare_pub_input", "var": 3 }, + { "op": "pi_skip", "guard": 0, "count": 4 }, + { "op": "public_input", "guard": null }, + { "op": "load_imm", "imm": "-02" }, + { "op": "load_imm", "imm": "0C" }, + { "op": "declare_pub_input", "var": 6 }, + { "op": "declare_pub_input", "var": 0 }, + { "op": "declare_pub_input", "var": 5 }, + { "op": "declare_pub_input", "var": 4 }, + { "op": "pi_skip", "guard": 0, "count": 4 }, + { "op": "output", "var": 4 } + ] +} diff --git a/packages/simulator/test/fixtures/test-artifacts/Simple/zkir/setVal.bzkir b/packages/simulator/test/fixtures/test-artifacts/Simple/zkir/setVal.bzkir new file mode 100644 index 00000000..8a9d024d Binary files /dev/null and b/packages/simulator/test/fixtures/test-artifacts/Simple/zkir/setVal.bzkir differ diff --git a/packages/simulator/test/fixtures/test-artifacts/Simple/zkir/setVal.zkir b/packages/simulator/test/fixtures/test-artifacts/Simple/zkir/setVal.zkir new file mode 100644 index 00000000..2dd05a6e --- /dev/null +++ b/packages/simulator/test/fixtures/test-artifacts/Simple/zkir/setVal.zkir @@ -0,0 +1,27 @@ +{ + "version": { "major": 2, "minor": 0 }, + "do_communications_commitment": true, + "num_inputs": 1, + "instructions": [ + { "op": "load_imm", "imm": "01" }, + { "op": "load_imm", "imm": "10" }, + { "op": "load_imm", "imm": "00" }, + { "op": "declare_pub_input", "var": 2 }, + { "op": "declare_pub_input", "var": 1 }, + { "op": "declare_pub_input", "var": 1 }, + { "op": "declare_pub_input", "var": 1 }, + { "op": "declare_pub_input", "var": 3 }, + { "op": "pi_skip", "guard": 1, "count": 5 }, + { "op": "load_imm", "imm": "11" }, + { "op": "load_imm", "imm": "-02" }, + { "op": "declare_pub_input", "var": 4 }, + { "op": "declare_pub_input", "var": 1 }, + { "op": "declare_pub_input", "var": 1 }, + { "op": "declare_pub_input", "var": 5 }, + { "op": "declare_pub_input", "var": 0 }, + { "op": "pi_skip", "guard": 1, "count": 5 }, + { "op": "load_imm", "imm": "91" }, + { "op": "declare_pub_input", "var": 6 }, + { "op": "pi_skip", "guard": 1, "count": 1 } + ] +} diff --git a/packages/simulator/test/fixtures/test-artifacts/Witness/compiler/contract-info.json b/packages/simulator/test/fixtures/test-artifacts/Witness/compiler/contract-info.json new file mode 100644 index 00000000..3bbe4e42 --- /dev/null +++ b/packages/simulator/test/fixtures/test-artifacts/Witness/compiler/contract-info.json @@ -0,0 +1,107 @@ +{ + "circuits": [ + { + "name": "setBytes", + "pure": false, + "arguments": [ + ], + "result-type": { + "type-name": "Tuple", + "types": [ + ] + } + }, + { + "name": "setField", + "pure": false, + "arguments": [ + { + "name": "arg", + "type": { + "type-name": "Field" + } + } + ], + "result-type": { + "type-name": "Tuple", + "types": [ + ] + } + }, + { + "name": "setUint", + "pure": false, + "arguments": [ + { + "name": "arg1", + "type": { + "type-name": "Uint", + "maxval": 340282366920938463463374607431768211455 + } + }, + { + "name": "arg2", + "type": { + "type-name": "Uint", + "maxval": 340282366920938463463374607431768211455 + } + } + ], + "result-type": { + "type-name": "Tuple", + "types": [ + ] + } + } + ], + "witnesses": [ + { + "name": "wit_secretBytes", + "arguments": [ + ], + "result type": { + "type-name": "Bytes", + "length": 32 + } + }, + { + "name": "wit_secretFieldPlusArg", + "arguments": [ + { + "name": "arg1", + "type": { + "type-name": "Field" + } + } + ], + "result type": { + "type-name": "Field" + } + }, + { + "name": "wit_secretUintPlusArgs", + "arguments": [ + { + "name": "arg1", + "type": { + "type-name": "Uint", + "maxval": 340282366920938463463374607431768211455 + } + }, + { + "name": "arg2", + "type": { + "type-name": "Uint", + "maxval": 340282366920938463463374607431768211455 + } + } + ], + "result type": { + "type-name": "Uint", + "maxval": 340282366920938463463374607431768211455 + } + } + ], + "contracts": [ + ] +} diff --git a/packages/simulator/test/fixtures/test-artifacts/Witness/contract/index.cjs b/packages/simulator/test/fixtures/test-artifacts/Witness/contract/index.cjs new file mode 100644 index 00000000..1b1b0708 --- /dev/null +++ b/packages/simulator/test/fixtures/test-artifacts/Witness/contract/index.cjs @@ -0,0 +1,448 @@ +'use strict'; +const __compactRuntime = require('@midnight-ntwrk/compact-runtime'); +const expectedRuntimeVersionString = '0.8.1'; +const expectedRuntimeVersion = expectedRuntimeVersionString.split('-')[0].split('.').map(Number); +const actualRuntimeVersion = __compactRuntime.versionString.split('-')[0].split('.').map(Number); +if (expectedRuntimeVersion[0] != actualRuntimeVersion[0] + || (actualRuntimeVersion[0] == 0 && expectedRuntimeVersion[1] != actualRuntimeVersion[1]) + || expectedRuntimeVersion[1] > actualRuntimeVersion[1] + || (expectedRuntimeVersion[1] == actualRuntimeVersion[1] && expectedRuntimeVersion[2] > actualRuntimeVersion[2])) + throw new __compactRuntime.CompactError(`Version mismatch: compiled code expects ${expectedRuntimeVersionString}, runtime is ${__compactRuntime.versionString}`); +{ const MAX_FIELD = 52435875175126190479447740508185965837690552500527637822603658699938581184512n; + if (__compactRuntime.MAX_FIELD !== MAX_FIELD) + throw new __compactRuntime.CompactError(`compiler thinks maximum field value is ${MAX_FIELD}; run time thinks it is ${__compactRuntime.MAX_FIELD}`) +} + +const _descriptor_0 = new __compactRuntime.CompactTypeField(); + +const _descriptor_1 = new __compactRuntime.CompactTypeUnsignedInteger(340282366920938463463374607431768211455n, 16); + +const _descriptor_2 = new __compactRuntime.CompactTypeBytes(32); + +const _descriptor_3 = new __compactRuntime.CompactTypeUnsignedInteger(18446744073709551615n, 8); + +const _descriptor_4 = new __compactRuntime.CompactTypeBoolean(); + +class _ContractAddress_0 { + alignment() { + return _descriptor_2.alignment(); + } + fromValue(value_0) { + return { + bytes: _descriptor_2.fromValue(value_0) + } + } + toValue(value_0) { + return _descriptor_2.toValue(value_0.bytes); + } +} + +const _descriptor_5 = new _ContractAddress_0(); + +const _descriptor_6 = new __compactRuntime.CompactTypeUnsignedInteger(255n, 1); + +class Contract { + witnesses; + constructor(...args_0) { + if (args_0.length !== 1) { + throw new __compactRuntime.CompactError(`Contract constructor: expected 1 argument, received ${args_0.length}`); + } + const witnesses_0 = args_0[0]; + if (typeof(witnesses_0) !== 'object') { + throw new __compactRuntime.CompactError('first (witnesses) argument to Contract constructor is not an object'); + } + if (typeof(witnesses_0.wit_secretBytes) !== 'function') { + throw new __compactRuntime.CompactError('first (witnesses) argument to Contract constructor does not contain a function-valued field named wit_secretBytes'); + } + if (typeof(witnesses_0.wit_secretFieldPlusArg) !== 'function') { + throw new __compactRuntime.CompactError('first (witnesses) argument to Contract constructor does not contain a function-valued field named wit_secretFieldPlusArg'); + } + if (typeof(witnesses_0.wit_secretUintPlusArgs) !== 'function') { + throw new __compactRuntime.CompactError('first (witnesses) argument to Contract constructor does not contain a function-valued field named wit_secretUintPlusArgs'); + } + this.witnesses = witnesses_0; + this.circuits = { + setBytes: (...args_1) => { + if (args_1.length !== 1) { + throw new __compactRuntime.CompactError(`setBytes: expected 1 argument (as invoked from Typescript), received ${args_1.length}`); + } + const contextOrig_0 = args_1[0]; + if (!(typeof(contextOrig_0) === 'object' && contextOrig_0.originalState != undefined && contextOrig_0.transactionContext != undefined)) { + __compactRuntime.type_error('setBytes', + 'argument 1 (as invoked from Typescript)', + 'Witness.compact line 14 char 1', + 'CircuitContext', + contextOrig_0) + } + const context = { ...contextOrig_0 }; + const partialProofData = { + input: { value: [], alignment: [] }, + output: undefined, + publicTranscript: [], + privateTranscriptOutputs: [] + }; + const result_0 = this._setBytes_0(context, partialProofData); + partialProofData.output = { value: [], alignment: [] }; + return { result: result_0, context: context, proofData: partialProofData }; + }, + setField: (...args_1) => { + if (args_1.length !== 2) { + throw new __compactRuntime.CompactError(`setField: expected 2 arguments (as invoked from Typescript), received ${args_1.length}`); + } + const contextOrig_0 = args_1[0]; + const arg_0 = args_1[1]; + if (!(typeof(contextOrig_0) === 'object' && contextOrig_0.originalState != undefined && contextOrig_0.transactionContext != undefined)) { + __compactRuntime.type_error('setField', + 'argument 1 (as invoked from Typescript)', + 'Witness.compact line 19 char 1', + 'CircuitContext', + contextOrig_0) + } + if (!(typeof(arg_0) === 'bigint' && arg_0 >= 0 && arg_0 <= __compactRuntime.MAX_FIELD)) { + __compactRuntime.type_error('setField', + 'argument 1 (argument 2 as invoked from Typescript)', + 'Witness.compact line 19 char 1', + 'Field', + arg_0) + } + const context = { ...contextOrig_0 }; + const partialProofData = { + input: { + value: _descriptor_0.toValue(arg_0), + alignment: _descriptor_0.alignment() + }, + output: undefined, + publicTranscript: [], + privateTranscriptOutputs: [] + }; + const result_0 = this._setField_0(context, partialProofData, arg_0); + partialProofData.output = { value: [], alignment: [] }; + return { result: result_0, context: context, proofData: partialProofData }; + }, + setUint: (...args_1) => { + if (args_1.length !== 3) { + throw new __compactRuntime.CompactError(`setUint: expected 3 arguments (as invoked from Typescript), received ${args_1.length}`); + } + const contextOrig_0 = args_1[0]; + const arg1_0 = args_1[1]; + const arg2_0 = args_1[2]; + if (!(typeof(contextOrig_0) === 'object' && contextOrig_0.originalState != undefined && contextOrig_0.transactionContext != undefined)) { + __compactRuntime.type_error('setUint', + 'argument 1 (as invoked from Typescript)', + 'Witness.compact line 24 char 1', + 'CircuitContext', + contextOrig_0) + } + if (!(typeof(arg1_0) === 'bigint' && arg1_0 >= 0n && arg1_0 <= 340282366920938463463374607431768211455n)) { + __compactRuntime.type_error('setUint', + 'argument 1 (argument 2 as invoked from Typescript)', + 'Witness.compact line 24 char 1', + 'Uint<0..340282366920938463463374607431768211455>', + arg1_0) + } + if (!(typeof(arg2_0) === 'bigint' && arg2_0 >= 0n && arg2_0 <= 340282366920938463463374607431768211455n)) { + __compactRuntime.type_error('setUint', + 'argument 2 (argument 3 as invoked from Typescript)', + 'Witness.compact line 24 char 1', + 'Uint<0..340282366920938463463374607431768211455>', + arg2_0) + } + const context = { ...contextOrig_0 }; + const partialProofData = { + input: { + value: _descriptor_1.toValue(arg1_0).concat(_descriptor_1.toValue(arg2_0)), + alignment: _descriptor_1.alignment().concat(_descriptor_1.alignment()) + }, + output: undefined, + publicTranscript: [], + privateTranscriptOutputs: [] + }; + const result_0 = this._setUint_0(context, + partialProofData, + arg1_0, + arg2_0); + partialProofData.output = { value: [], alignment: [] }; + return { result: result_0, context: context, proofData: partialProofData }; + } + }; + this.impureCircuits = { + setBytes: this.circuits.setBytes, + setField: this.circuits.setField, + setUint: this.circuits.setUint + }; + } + initialState(...args_0) { + if (args_0.length !== 1) { + throw new __compactRuntime.CompactError(`Contract state constructor: expected 1 argument (as invoked from Typescript), received ${args_0.length}`); + } + const constructorContext_0 = args_0[0]; + if (typeof(constructorContext_0) !== 'object') { + throw new __compactRuntime.CompactError(`Contract state constructor: expected 'constructorContext' in argument 1 (as invoked from Typescript) to be an object`); + } + if (!('initialPrivateState' in constructorContext_0)) { + throw new __compactRuntime.CompactError(`Contract state constructor: expected 'initialPrivateState' in argument 1 (as invoked from Typescript)`); + } + if (!('initialZswapLocalState' in constructorContext_0)) { + throw new __compactRuntime.CompactError(`Contract state constructor: expected 'initialZswapLocalState' in argument 1 (as invoked from Typescript)`); + } + if (typeof(constructorContext_0.initialZswapLocalState) !== 'object') { + throw new __compactRuntime.CompactError(`Contract state constructor: expected 'initialZswapLocalState' in argument 1 (as invoked from Typescript) to be an object`); + } + const state_0 = new __compactRuntime.ContractState(); + let stateValue_0 = __compactRuntime.StateValue.newArray(); + stateValue_0 = stateValue_0.arrayPush(__compactRuntime.StateValue.newNull()); + stateValue_0 = stateValue_0.arrayPush(__compactRuntime.StateValue.newNull()); + stateValue_0 = stateValue_0.arrayPush(__compactRuntime.StateValue.newNull()); + state_0.data = stateValue_0; + state_0.setOperation('setBytes', new __compactRuntime.ContractOperation()); + state_0.setOperation('setField', new __compactRuntime.ContractOperation()); + state_0.setOperation('setUint', new __compactRuntime.ContractOperation()); + const context = { + originalState: state_0, + currentPrivateState: constructorContext_0.initialPrivateState, + currentZswapLocalState: constructorContext_0.initialZswapLocalState, + transactionContext: new __compactRuntime.QueryContext(state_0.data, __compactRuntime.dummyContractAddress()) + }; + const partialProofData = { + input: { value: [], alignment: [] }, + output: undefined, + publicTranscript: [], + privateTranscriptOutputs: [] + }; + Contract._query(context, + partialProofData, + [ + { push: { storage: false, + value: __compactRuntime.StateValue.newCell({ value: _descriptor_6.toValue(0n), + alignment: _descriptor_6.alignment() }).encode() } }, + { push: { storage: true, + value: __compactRuntime.StateValue.newCell({ value: _descriptor_2.toValue(new Uint8Array(32)), + alignment: _descriptor_2.alignment() }).encode() } }, + { ins: { cached: false, n: 1 } }]); + Contract._query(context, + partialProofData, + [ + { push: { storage: false, + value: __compactRuntime.StateValue.newCell({ value: _descriptor_6.toValue(1n), + alignment: _descriptor_6.alignment() }).encode() } }, + { push: { storage: true, + value: __compactRuntime.StateValue.newCell({ value: _descriptor_0.toValue(0n), + alignment: _descriptor_0.alignment() }).encode() } }, + { ins: { cached: false, n: 1 } }]); + Contract._query(context, + partialProofData, + [ + { push: { storage: false, + value: __compactRuntime.StateValue.newCell({ value: _descriptor_6.toValue(2n), + alignment: _descriptor_6.alignment() }).encode() } }, + { push: { storage: true, + value: __compactRuntime.StateValue.newCell({ value: _descriptor_1.toValue(0n), + alignment: _descriptor_1.alignment() }).encode() } }, + { ins: { cached: false, n: 1 } }]); + state_0.data = context.transactionContext.state; + return { + currentContractState: state_0, + currentPrivateState: context.currentPrivateState, + currentZswapLocalState: context.currentZswapLocalState + } + } + _wit_secretBytes_0(context, partialProofData) { + const witnessContext_0 = __compactRuntime.witnessContext(ledger(context.transactionContext.state), context.currentPrivateState, context.transactionContext.address); + const [nextPrivateState_0, result_0] = this.witnesses.wit_secretBytes(witnessContext_0); + context.currentPrivateState = nextPrivateState_0; + if (!(result_0.buffer instanceof ArrayBuffer && result_0.BYTES_PER_ELEMENT === 1 && result_0.length === 32)) { + __compactRuntime.type_error('wit_secretBytes', + 'return value', + 'Witness.compact line 10 char 1', + 'Bytes<32>', + result_0) + } + partialProofData.privateTranscriptOutputs.push({ + value: _descriptor_2.toValue(result_0), + alignment: _descriptor_2.alignment() + }); + return result_0; + } + _wit_secretFieldPlusArg_0(context, partialProofData, arg1_0) { + const witnessContext_0 = __compactRuntime.witnessContext(ledger(context.transactionContext.state), context.currentPrivateState, context.transactionContext.address); + const [nextPrivateState_0, result_0] = this.witnesses.wit_secretFieldPlusArg(witnessContext_0, + arg1_0); + context.currentPrivateState = nextPrivateState_0; + if (!(typeof(result_0) === 'bigint' && result_0 >= 0 && result_0 <= __compactRuntime.MAX_FIELD)) { + __compactRuntime.type_error('wit_secretFieldPlusArg', + 'return value', + 'Witness.compact line 11 char 1', + 'Field', + result_0) + } + partialProofData.privateTranscriptOutputs.push({ + value: _descriptor_0.toValue(result_0), + alignment: _descriptor_0.alignment() + }); + return result_0; + } + _wit_secretUintPlusArgs_0(context, partialProofData, arg1_0, arg2_0) { + const witnessContext_0 = __compactRuntime.witnessContext(ledger(context.transactionContext.state), context.currentPrivateState, context.transactionContext.address); + const [nextPrivateState_0, result_0] = this.witnesses.wit_secretUintPlusArgs(witnessContext_0, + arg1_0, + arg2_0); + context.currentPrivateState = nextPrivateState_0; + if (!(typeof(result_0) === 'bigint' && result_0 >= 0n && result_0 <= 340282366920938463463374607431768211455n)) { + __compactRuntime.type_error('wit_secretUintPlusArgs', + 'return value', + 'Witness.compact line 12 char 1', + 'Uint<0..340282366920938463463374607431768211455>', + result_0) + } + partialProofData.privateTranscriptOutputs.push({ + value: _descriptor_1.toValue(result_0), + alignment: _descriptor_1.alignment() + }); + return result_0; + } + _setBytes_0(context, partialProofData) { + const val_0 = this._wit_secretBytes_0(context, partialProofData); + Contract._query(context, + partialProofData, + [ + { push: { storage: false, + value: __compactRuntime.StateValue.newCell({ value: _descriptor_6.toValue(0n), + alignment: _descriptor_6.alignment() }).encode() } }, + { push: { storage: true, + value: __compactRuntime.StateValue.newCell({ value: _descriptor_2.toValue(val_0), + alignment: _descriptor_2.alignment() }).encode() } }, + { ins: { cached: false, n: 1 } }]); + return []; + } + _setField_0(context, partialProofData, arg_0) { + const val_0 = this._wit_secretFieldPlusArg_0(context, + partialProofData, + arg_0); + Contract._query(context, + partialProofData, + [ + { push: { storage: false, + value: __compactRuntime.StateValue.newCell({ value: _descriptor_6.toValue(1n), + alignment: _descriptor_6.alignment() }).encode() } }, + { push: { storage: true, + value: __compactRuntime.StateValue.newCell({ value: _descriptor_0.toValue(val_0), + alignment: _descriptor_0.alignment() }).encode() } }, + { ins: { cached: false, n: 1 } }]); + return []; + } + _setUint_0(context, partialProofData, arg1_0, arg2_0) { + const val_0 = this._wit_secretUintPlusArgs_0(context, + partialProofData, + arg1_0, + arg2_0); + Contract._query(context, + partialProofData, + [ + { push: { storage: false, + value: __compactRuntime.StateValue.newCell({ value: _descriptor_6.toValue(2n), + alignment: _descriptor_6.alignment() }).encode() } }, + { push: { storage: true, + value: __compactRuntime.StateValue.newCell({ value: _descriptor_1.toValue(val_0), + alignment: _descriptor_1.alignment() }).encode() } }, + { ins: { cached: false, n: 1 } }]); + return []; + } + static _query(context, partialProofData, prog) { + var res; + try { + res = context.transactionContext.query(prog, __compactRuntime.CostModel.dummyCostModel()); + } catch (err) { + throw new __compactRuntime.CompactError(err.toString()); + } + context.transactionContext = res.context; + var reads = res.events.filter((e) => e.tag === 'read'); + var i = 0; + partialProofData.publicTranscript = partialProofData.publicTranscript.concat(prog.map((op) => { + if(typeof(op) === 'object' && 'popeq' in op) { + return { popeq: { + ...op.popeq, + result: reads[i++].content, + } }; + } else { + return op; + } + })); + if(res.events.length == 1 && res.events[0].tag === 'read') { + return res.events[0].content; + } else { + return res.events; + } + } +} +function ledger(state) { + const context = { + originalState: state, + transactionContext: new __compactRuntime.QueryContext(state, __compactRuntime.dummyContractAddress()) + }; + const partialProofData = { + input: { value: [], alignment: [] }, + output: undefined, + publicTranscript: [], + privateTranscriptOutputs: [] + }; + return { + get _valBytes() { + return _descriptor_2.fromValue(Contract._query(context, + partialProofData, + [ + { dup: { n: 0 } }, + { idx: { cached: false, + pushPath: false, + path: [ + { tag: 'value', + value: { value: _descriptor_6.toValue(0n), + alignment: _descriptor_6.alignment() } }] } }, + { popeq: { cached: false, + result: undefined } }]).value); + }, + get _valField() { + return _descriptor_0.fromValue(Contract._query(context, + partialProofData, + [ + { dup: { n: 0 } }, + { idx: { cached: false, + pushPath: false, + path: [ + { tag: 'value', + value: { value: _descriptor_6.toValue(1n), + alignment: _descriptor_6.alignment() } }] } }, + { popeq: { cached: false, + result: undefined } }]).value); + }, + get _valUint() { + return _descriptor_1.fromValue(Contract._query(context, + partialProofData, + [ + { dup: { n: 0 } }, + { idx: { cached: false, + pushPath: false, + path: [ + { tag: 'value', + value: { value: _descriptor_6.toValue(2n), + alignment: _descriptor_6.alignment() } }] } }, + { popeq: { cached: false, + result: undefined } }]).value); + } + }; +} +const _emptyContext = { + originalState: new __compactRuntime.ContractState(), + transactionContext: new __compactRuntime.QueryContext(new __compactRuntime.ContractState().data, __compactRuntime.dummyContractAddress()) +}; +const _dummyContract = new Contract({ + wit_secretBytes: (...args) => undefined, + wit_secretFieldPlusArg: (...args) => undefined, + wit_secretUintPlusArgs: (...args) => undefined +}); +const pureCircuits = {}; +const contractReferenceLocations = { tag: 'publicLedgerArray', indices: { } }; +exports.Contract = Contract; +exports.ledger = ledger; +exports.pureCircuits = pureCircuits; +exports.contractReferenceLocations = contractReferenceLocations; +//# sourceMappingURL=index.cjs.map diff --git a/packages/simulator/test/fixtures/test-artifacts/Witness/contract/index.cjs.map b/packages/simulator/test/fixtures/test-artifacts/Witness/contract/index.cjs.map new file mode 100644 index 00000000..dcf28b21 --- /dev/null +++ b/packages/simulator/test/fixtures/test-artifacts/Witness/contract/index.cjs.map @@ -0,0 +1,8 @@ +{ + "version": 3, + "file": "index.cjs", + "sourceRoot": "../../../../../", + "sources": ["test/fixtures/sample-contracts/Witness.compact"], + "names": [], + "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAAA;;;;;;;;;;;;;;;;;;;MAaA,AAAA,QAGC;;;;;;;;;;;;;;;;;;;;;;OAAA;MAED,AAAA,QAGC;;;;;cAHuB,KAAU;;;;;;;;;;;;;;;;;;yCAAV,KAAU;;;;;;;qEAAV,KAAU;;;OAGjC;MAED,AAAA,OAGC;;;;;cAHsB,MAAe;cAAE,MAAe;;;;;;;;;;;;;;;;;;;;;;;;;yCAAhC,MAAe,+BAAE,MAAe;;;;;;;;;yCAAhC,MAAe;yCAAE,MAAe;;;OAGtD;;;;;;;GACA;EA3BD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAKA;;;;;;;;;uDAAmC;IACnC;;;;;;;;;uDAA+B;IAC/B;;;;;;;;;uDAAkC;;;;;;;GAoBjC;EAlBD,AAAA,kBAAqC;;0DAArC,eAAqC;;;;;;;;;;;;;;GAAA;EACrC,AAAA,yBAAmD,4BAApB,MAAW;;0DAA1C,sBAAmD;iFAApB,MAAW;;;;;;;;;;;;;;GAAS;EACnD,AAAA,yBAA4E,4BAA7C,MAAe,EAAE,MAAe;;0DAA/D,sBAA4E;iFAA7C,MAAe;iFAAE,MAAe;;;;;;;;;;;;;;GAAa;EAE5E,AAAA,WAGC;UAFO,KAAuB;IAC7B;;;;;;;yGAAqB,KAAG;;uDAAf;;GACV;EAED,AAAA,WAGC,4BAHuB,KAAU;UAC1B,KAAiC;;iDAAJ,KAAG;IACtC;;;;;;;yGAAqB,KAAG;;uDAAf;;GACV;EAED,AAAA,UAGC,4BAHsB,MAAe,EAAE,MAAe;UAC/C,KAAwC;;iDAAX,MAAI;iDAAE,MAAI;IAC7C;;;;;;;yGAAoB,KAAG;;uDAAf;;GACT;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IArBD;qCAAA;;;;;;;;;;;wFAAmC;KAAA;IACnC;qCAAA;;;;;;;;;;;wFAA+B;KAAA;IAC/B;qCAAA;;;;;;;;;;;wFAAkC;KAAA;;;;;;;;;;;;;;;;;;" +} diff --git a/packages/simulator/test/fixtures/test-artifacts/Witness/contract/index.d.cts b/packages/simulator/test/fixtures/test-artifacts/Witness/contract/index.d.cts new file mode 100644 index 00000000..ea270f84 --- /dev/null +++ b/packages/simulator/test/fixtures/test-artifacts/Witness/contract/index.d.cts @@ -0,0 +1,58 @@ +import type * as __compactRuntime from '@midnight-ntwrk/compact-runtime'; + +export type ZswapCoinPublicKey = { bytes: Uint8Array }; + +export type ContractAddress = { bytes: Uint8Array }; + +export type Either = { is_left: boolean; left: A; right: B }; + +export type Maybe = { is_some: boolean; value: T }; + +export type Witnesses = { + wit_secretBytes(context: __compactRuntime.WitnessContext): [T, Uint8Array]; + wit_secretFieldPlusArg(context: __compactRuntime.WitnessContext, + arg1_0: bigint): [T, bigint]; + wit_secretUintPlusArgs(context: __compactRuntime.WitnessContext, + arg1_0: bigint, + arg2_0: bigint): [T, bigint]; +} + +export type ImpureCircuits = { + setBytes(context: __compactRuntime.CircuitContext): __compactRuntime.CircuitResults; + setField(context: __compactRuntime.CircuitContext, arg_0: bigint): __compactRuntime.CircuitResults; + setUint(context: __compactRuntime.CircuitContext, + arg1_0: bigint, + arg2_0: bigint): __compactRuntime.CircuitResults; +} + +export type PureCircuits = { +} + +export type Circuits = { + setBytes(context: __compactRuntime.CircuitContext): __compactRuntime.CircuitResults; + setField(context: __compactRuntime.CircuitContext, arg_0: bigint): __compactRuntime.CircuitResults; + setUint(context: __compactRuntime.CircuitContext, + arg1_0: bigint, + arg2_0: bigint): __compactRuntime.CircuitResults; +} + +export type Ledger = { + readonly _valBytes: Uint8Array; + readonly _valField: bigint; + readonly _valUint: bigint; +} + +export type ContractReferenceLocations = any; + +export declare const contractReferenceLocations : ContractReferenceLocations; + +export declare class Contract = Witnesses> { + witnesses: W; + circuits: Circuits; + impureCircuits: ImpureCircuits; + constructor(witnesses: W); + initialState(context: __compactRuntime.ConstructorContext): __compactRuntime.ConstructorResult; +} + +export declare function ledger(state: __compactRuntime.StateValue): Ledger; +export declare const pureCircuits: PureCircuits; diff --git a/packages/simulator/test/fixtures/test-artifacts/Witness/keys/setBytes.prover b/packages/simulator/test/fixtures/test-artifacts/Witness/keys/setBytes.prover new file mode 100644 index 00000000..6d0aa34e Binary files /dev/null and b/packages/simulator/test/fixtures/test-artifacts/Witness/keys/setBytes.prover differ diff --git a/packages/simulator/test/fixtures/test-artifacts/Witness/keys/setBytes.verifier b/packages/simulator/test/fixtures/test-artifacts/Witness/keys/setBytes.verifier new file mode 100644 index 00000000..1c6c4550 Binary files /dev/null and b/packages/simulator/test/fixtures/test-artifacts/Witness/keys/setBytes.verifier differ diff --git a/packages/simulator/test/fixtures/test-artifacts/Witness/keys/setField.prover b/packages/simulator/test/fixtures/test-artifacts/Witness/keys/setField.prover new file mode 100644 index 00000000..ceeb847d Binary files /dev/null and b/packages/simulator/test/fixtures/test-artifacts/Witness/keys/setField.prover differ diff --git a/packages/simulator/test/fixtures/test-artifacts/Witness/keys/setField.verifier b/packages/simulator/test/fixtures/test-artifacts/Witness/keys/setField.verifier new file mode 100644 index 00000000..90ca30e8 Binary files /dev/null and b/packages/simulator/test/fixtures/test-artifacts/Witness/keys/setField.verifier differ diff --git a/packages/simulator/test/fixtures/test-artifacts/Witness/keys/setUint.prover b/packages/simulator/test/fixtures/test-artifacts/Witness/keys/setUint.prover new file mode 100644 index 00000000..c0f1c9a9 Binary files /dev/null and b/packages/simulator/test/fixtures/test-artifacts/Witness/keys/setUint.prover differ diff --git a/packages/simulator/test/fixtures/test-artifacts/Witness/keys/setUint.verifier b/packages/simulator/test/fixtures/test-artifacts/Witness/keys/setUint.verifier new file mode 100644 index 00000000..91b2ba2d Binary files /dev/null and b/packages/simulator/test/fixtures/test-artifacts/Witness/keys/setUint.verifier differ diff --git a/packages/simulator/test/fixtures/test-artifacts/Witness/zkir/setBytes.bzkir b/packages/simulator/test/fixtures/test-artifacts/Witness/zkir/setBytes.bzkir new file mode 100644 index 00000000..2fb735f1 Binary files /dev/null and b/packages/simulator/test/fixtures/test-artifacts/Witness/zkir/setBytes.bzkir differ diff --git a/packages/simulator/test/fixtures/test-artifacts/Witness/zkir/setBytes.zkir b/packages/simulator/test/fixtures/test-artifacts/Witness/zkir/setBytes.zkir new file mode 100644 index 00000000..fcfb7286 --- /dev/null +++ b/packages/simulator/test/fixtures/test-artifacts/Witness/zkir/setBytes.zkir @@ -0,0 +1,32 @@ +{ + "version": { "major": 2, "minor": 0 }, + "do_communications_commitment": true, + "num_inputs": 0, + "instructions": [ + { "op": "load_imm", "imm": "01" }, + { "op": "private_input", "guard": null }, + { "op": "constrain_bits", "var": 1, "bits": 8 }, + { "op": "private_input", "guard": null }, + { "op": "constrain_bits", "var": 2, "bits": 248 }, + { "op": "load_imm", "imm": "10" }, + { "op": "load_imm", "imm": "00" }, + { "op": "declare_pub_input", "var": 3 }, + { "op": "declare_pub_input", "var": 0 }, + { "op": "declare_pub_input", "var": 0 }, + { "op": "declare_pub_input", "var": 0 }, + { "op": "declare_pub_input", "var": 4 }, + { "op": "pi_skip", "guard": 0, "count": 5 }, + { "op": "load_imm", "imm": "11" }, + { "op": "load_imm", "imm": "20" }, + { "op": "declare_pub_input", "var": 5 }, + { "op": "declare_pub_input", "var": 0 }, + { "op": "declare_pub_input", "var": 0 }, + { "op": "declare_pub_input", "var": 6 }, + { "op": "declare_pub_input", "var": 1 }, + { "op": "declare_pub_input", "var": 2 }, + { "op": "pi_skip", "guard": 0, "count": 6 }, + { "op": "load_imm", "imm": "91" }, + { "op": "declare_pub_input", "var": 7 }, + { "op": "pi_skip", "guard": 0, "count": 1 } + ] +} diff --git a/packages/simulator/test/fixtures/test-artifacts/Witness/zkir/setField.bzkir b/packages/simulator/test/fixtures/test-artifacts/Witness/zkir/setField.bzkir new file mode 100644 index 00000000..7acd4c9a Binary files /dev/null and b/packages/simulator/test/fixtures/test-artifacts/Witness/zkir/setField.bzkir differ diff --git a/packages/simulator/test/fixtures/test-artifacts/Witness/zkir/setField.zkir b/packages/simulator/test/fixtures/test-artifacts/Witness/zkir/setField.zkir new file mode 100644 index 00000000..c01bbe24 --- /dev/null +++ b/packages/simulator/test/fixtures/test-artifacts/Witness/zkir/setField.zkir @@ -0,0 +1,27 @@ +{ + "version": { "major": 2, "minor": 0 }, + "do_communications_commitment": true, + "num_inputs": 1, + "instructions": [ + { "op": "load_imm", "imm": "01" }, + { "op": "private_input", "guard": null }, + { "op": "load_imm", "imm": "10" }, + { "op": "declare_pub_input", "var": 3 }, + { "op": "declare_pub_input", "var": 1 }, + { "op": "declare_pub_input", "var": 1 }, + { "op": "declare_pub_input", "var": 1 }, + { "op": "declare_pub_input", "var": 1 }, + { "op": "pi_skip", "guard": 1, "count": 5 }, + { "op": "load_imm", "imm": "11" }, + { "op": "load_imm", "imm": "-02" }, + { "op": "declare_pub_input", "var": 4 }, + { "op": "declare_pub_input", "var": 1 }, + { "op": "declare_pub_input", "var": 1 }, + { "op": "declare_pub_input", "var": 5 }, + { "op": "declare_pub_input", "var": 2 }, + { "op": "pi_skip", "guard": 1, "count": 5 }, + { "op": "load_imm", "imm": "91" }, + { "op": "declare_pub_input", "var": 6 }, + { "op": "pi_skip", "guard": 1, "count": 1 } + ] +} diff --git a/packages/simulator/test/fixtures/test-artifacts/Witness/zkir/setUint.bzkir b/packages/simulator/test/fixtures/test-artifacts/Witness/zkir/setUint.bzkir new file mode 100644 index 00000000..848bc6e0 Binary files /dev/null and b/packages/simulator/test/fixtures/test-artifacts/Witness/zkir/setUint.bzkir differ diff --git a/packages/simulator/test/fixtures/test-artifacts/Witness/zkir/setUint.zkir b/packages/simulator/test/fixtures/test-artifacts/Witness/zkir/setUint.zkir new file mode 100644 index 00000000..d6b52312 --- /dev/null +++ b/packages/simulator/test/fixtures/test-artifacts/Witness/zkir/setUint.zkir @@ -0,0 +1,30 @@ +{ + "version": { "major": 2, "minor": 0 }, + "do_communications_commitment": true, + "num_inputs": 2, + "instructions": [ + { "op": "constrain_bits", "var": 0, "bits": 128 }, + { "op": "constrain_bits", "var": 1, "bits": 128 }, + { "op": "load_imm", "imm": "01" }, + { "op": "private_input", "guard": null }, + { "op": "constrain_bits", "var": 3, "bits": 128 }, + { "op": "load_imm", "imm": "10" }, + { "op": "load_imm", "imm": "02" }, + { "op": "declare_pub_input", "var": 4 }, + { "op": "declare_pub_input", "var": 2 }, + { "op": "declare_pub_input", "var": 2 }, + { "op": "declare_pub_input", "var": 2 }, + { "op": "declare_pub_input", "var": 5 }, + { "op": "pi_skip", "guard": 2, "count": 5 }, + { "op": "load_imm", "imm": "11" }, + { "op": "declare_pub_input", "var": 6 }, + { "op": "declare_pub_input", "var": 2 }, + { "op": "declare_pub_input", "var": 2 }, + { "op": "declare_pub_input", "var": 4 }, + { "op": "declare_pub_input", "var": 3 }, + { "op": "pi_skip", "guard": 2, "count": 5 }, + { "op": "load_imm", "imm": "91" }, + { "op": "declare_pub_input", "var": 7 }, + { "op": "pi_skip", "guard": 2, "count": 1 } + ] +} diff --git a/packages/simulator/test/fixtures/utils/address.ts b/packages/simulator/test/fixtures/utils/address.ts new file mode 100644 index 00000000..53de4c9a --- /dev/null +++ b/packages/simulator/test/fixtures/utils/address.ts @@ -0,0 +1,101 @@ +import { + convert_bigint_to_Uint8Array, + encodeCoinPublicKey, +} from '@midnight-ntwrk/compact-runtime'; +import { encodeContractAddress } from '@midnight-ntwrk/ledger'; +import type * as Compact from '../test-artifacts/SampleZOwnable/contract/index.cjs'; + +const PREFIX_ADDRESS = '0200'; + +/** + * @description Converts an ASCII string to its hexadecimal representation, + * left-padded with zeros to a specified length. Useful for generating + * fixed-size hex strings for encoding. + * @param str ASCII string to convert. + * @param len Total desired length of the resulting hex string. Defaults to 64. + * @returns Hexadecimal string representation of `str`, padded to `length` characters. + */ +export const toHexPadded = (str: string, len = 64) => + Buffer.from(str, 'ascii').toString('hex').padStart(len, '0'); + +/** + * @description Generates ZswapCoinPublicKey from `str` for testing purposes. + * @param str String to hexify and encode. + * @returns Encoded `ZswapCoinPublicKey`. + */ +export const encodeToPK = (str: string): Compact.ZswapCoinPublicKey => ({ + bytes: encodeCoinPublicKey(toHexPadded(str)), +}); + +/** + * @description Generates ContractAddress from `str` for testing purposes. + * Prepends 32-byte hex with PREFIX_ADDRESS before encoding. + * @param str String to hexify and encode. + * @returns Encoded `ZswapCoinPublicKey`. + */ +export const encodeToAddress = (str: string): Compact.ContractAddress => ({ + bytes: encodeContractAddress(PREFIX_ADDRESS + toHexPadded(str)), +}); + +/** + * @description Generates an Either object for ZswapCoinPublicKey for testing. + * For use when an Either argument is expected. + * @param str String to hexify and encode. + * @returns Defined Either object for ZswapCoinPublicKey. + */ +export const createEitherTestUser = (str: string) => ({ + is_left: true, + left: encodeToPK(str), + right: encodeToAddress(''), +}); + +/** + * @description Generates an Either object for ContractAddress for testing. + * For use when an Either argument is expected. + * @param str String to hexify and encode. + * @returns Defined Either object for ContractAddress. + */ +export const createEitherTestContractAddress = (str: string) => ({ + is_left: false, + left: encodeToPK(''), + right: encodeToAddress(str), +}); + +const baseGeneratePubKeyPair = ( + str: string, + asEither: boolean, +): [ + string, + ( + | Compact.ZswapCoinPublicKey + | Compact.Either + ), +] => { + const pk = toHexPadded(str); + const zpk = asEither ? createEitherTestUser(str) : encodeToPK(str); + return [pk, zpk]; +}; + +export const generatePubKeyPair = (str: string) => + baseGeneratePubKeyPair(str, false) as [string, Compact.ZswapCoinPublicKey]; + +export const generateEitherPubKeyPair = (str: string) => + baseGeneratePubKeyPair(str, true) as [ + string, + Compact.Either, + ]; + +export const zeroUint8Array = (length = 32) => + convert_bigint_to_Uint8Array(length, 0n); + +export const ZERO_KEY = { + is_left: true, + left: { bytes: zeroUint8Array() }, + right: encodeToAddress(''), +}; + +export const ZERO_ADDRESS = { + is_left: false, + left: encodeToPK(''), + right: { bytes: zeroUint8Array() }, +}; diff --git a/packages/simulator/test/integration/SampleZOwnable.test.ts b/packages/simulator/test/integration/SampleZOwnable.test.ts new file mode 100644 index 00000000..ed24b097 --- /dev/null +++ b/packages/simulator/test/integration/SampleZOwnable.test.ts @@ -0,0 +1,419 @@ +import { + CompactTypeBytes, + CompactTypeVector, + convert_bigint_to_Uint8Array, + persistentHash, +} from '@midnight-ntwrk/compact-runtime'; +import { beforeEach, describe, expect, it } from 'vitest'; +import { SampleZOwnablePrivateState } from '../fixtures/sample-contracts/witnesses/SampleZOwnableWitnesses.js'; +import type { ZswapCoinPublicKey } from '../fixtures/test-artifacts/SampleZOwnable/contract/index.cjs'; +import * as utils from '../fixtures/utils/address.js'; +import { SampleZOwnableSimulator } from './SampleZOwnableSimulator.js'; + +// PKs +const [OWNER, Z_OWNER] = utils.generatePubKeyPair('OWNER'); +const [NEW_OWNER, Z_NEW_OWNER] = utils.generatePubKeyPair('NEW_OWNER'); +const [UNAUTHORIZED, _] = utils.generatePubKeyPair('UNAUTHORIZED'); + +const INSTANCE_SALT = new Uint8Array(32).fill(8675309); +const BAD_NONCE = Buffer.from(Buffer.alloc(32, 'BAD_NONCE')); +const DOMAIN = 'SampleZOwnable:shield:'; +const INIT_COUNTER = 1n; + +let secretNonce: Uint8Array; +let ownable: SampleZOwnableSimulator; + +// Helpers +/** + * Create the id hash. + * @param pk User's public key. + * @param nonce Unique secret nonce. + * @returns id hash. + */ +const createIdHash = ( + pk: ZswapCoinPublicKey, + nonce: Uint8Array, +): Uint8Array => { + const rt_type = new CompactTypeVector(2, new CompactTypeBytes(32)); + + const bPK = pk.bytes; + return persistentHash(rt_type, [bPK, nonce]); +}; + +/** + * Create the stored commitment. + * @param id User's unique id. + * @param instanceSalt Unique value generated for the contract instance. + * @param counter Counter. + * @returns Commitment. + */ +const buildCommitmentFromId = ( + id: Uint8Array, + instanceSalt: Uint8Array, + counter: bigint, +): Uint8Array => { + const rt_type = new CompactTypeVector(4, new CompactTypeBytes(32)); + const bCounter = convert_bigint_to_Uint8Array(32, counter); + const bDomain = new TextEncoder().encode(DOMAIN); + + const commitment = persistentHash(rt_type, [ + id, + instanceSalt, + bCounter, + bDomain, + ]); + return commitment; +}; + +/** + * Builds commitment. + * @param pk User's public key. + * @param nonce User's secret nonce. + * @param instanceSalt Unique value generated for the contract instance. + * @param counter Counter. + * @param domain Contract domain. + * @returns Commitment + */ +const buildCommitment = ( + pk: ZswapCoinPublicKey, + nonce: Uint8Array, + instanceSalt: Uint8Array, + counter: bigint, + domain: string, +): Uint8Array => { + const id = createIdHash(pk, nonce); + + const rt_type = new CompactTypeVector(4, new CompactTypeBytes(32)); + const bCounter = convert_bigint_to_Uint8Array(32, counter); + const bDomain = new TextEncoder().encode(domain); + + const commitment = persistentHash(rt_type, [ + id, + instanceSalt, + bCounter, + bDomain, + ]); + return commitment; +}; + +describe('SampleZOwnable', () => { + describe('before initialize', () => { + it('should fail when setting owner commitment as 0', () => { + expect(() => { + const badId = new Uint8Array(32).fill(0); + new SampleZOwnableSimulator(badId, INSTANCE_SALT); + }).toThrow('SampleZOwnable: invalid id'); + }); + + it('should initialize with non-zero commitment', () => { + const notZeroPK = utils.encodeToPK('NOT_ZERO'); + const notZeroNonce = new Uint8Array(32).fill(1); + const nonZeroId = createIdHash(notZeroPK, notZeroNonce); + ownable = new SampleZOwnableSimulator(nonZeroId, INSTANCE_SALT); + + const nonZeroCommitment = buildCommitmentFromId( + nonZeroId, + INSTANCE_SALT, + INIT_COUNTER, + ); + expect(ownable.owner()).toEqual(nonZeroCommitment); + }); + }); + + describe('after initialization', () => { + beforeEach(() => { + // Create private state object and generate nonce + const PS = SampleZOwnablePrivateState.generate(); + // Bind nonce for convenience + secretNonce = PS.secretNonce; + // Prepare owner ID with gen nonce + const ownerId = createIdHash(Z_OWNER, secretNonce); + // Deploy contract with derived owner commitment and PS + ownable = new SampleZOwnableSimulator(ownerId, INSTANCE_SALT, { + privateState: PS, + }); + }); + + describe('owner', () => { + it('should return the correct owner commitment', () => { + const expCommitment = buildCommitment( + Z_OWNER, + secretNonce, + INSTANCE_SALT, + INIT_COUNTER, + DOMAIN, + ); + expect(ownable.owner()).toEqual(expCommitment); + }); + }); + + describe('transferOwnership', () => { + let newOwnerCommitment: Uint8Array; + let newOwnerNonce: Uint8Array; + let newIdHash: Uint8Array; + let newCounter: bigint; + + beforeEach(() => { + // Prepare new owner commitment + newOwnerNonce = SampleZOwnablePrivateState.generate().secretNonce; + newCounter = INIT_COUNTER + 1n; + newIdHash = createIdHash(Z_NEW_OWNER, newOwnerNonce); + newOwnerCommitment = buildCommitment( + Z_NEW_OWNER, + newOwnerNonce, + INSTANCE_SALT, + newCounter, + DOMAIN, + ); + }); + + it('should transfer ownership', () => { + ownable.as(OWNER).transferOwnership(newIdHash); + expect(ownable.owner()).toEqual(newOwnerCommitment); + + // Old owner + expect(() => { + ownable.as(OWNER).assertOnlyOwner(); + }).toThrow('SampleZOwnable: caller is not the owner'); + + // Unauthorized + expect(() => { + ownable.as(UNAUTHORIZED).assertOnlyOwner(); + }).toThrow('SampleZOwnable: caller is not the owner'); + + // New owner + ownable.privateState.injectSecretNonce(Buffer.from(newOwnerNonce)); + expect(ownable.as(NEW_OWNER).assertOnlyOwner()).not.to.throw; + }); + + it('should fail when transferring to id zero', () => { + const badId = new Uint8Array(32).fill(0); + expect(() => { + ownable.as(OWNER).transferOwnership(badId); + }).toThrow('SampleZOwnable: invalid id'); + }); + + it('should fail when unauthorized transfers ownership', () => { + expect(() => { + ownable.as(UNAUTHORIZED).transferOwnership(newOwnerCommitment); + }).toThrow('SampleZOwnable: caller is not the owner'); + }); + + /** + * @description More thoroughly tested in `_transferOwnership` + * */ + it('should bump instance after transfer', () => { + const beforeInstance = ownable.getPublicState()._counter; + + // Transfer + ownable.as(OWNER).transferOwnership(newOwnerCommitment); + + // Check counter + const afterInstance = ownable.getPublicState()._counter; + expect(afterInstance).toEqual(beforeInstance + 1n); + }); + + it('should change commitment when transferring ownership to self with same pk + nonce)', () => { + // Confirm current commitment + const repeatedId = createIdHash(Z_OWNER, secretNonce); + const initCommitment = ownable.owner(); + const expInitCommitment = buildCommitmentFromId( + repeatedId, + INSTANCE_SALT, + INIT_COUNTER, + ); + expect(initCommitment).toEqual(expInitCommitment); + + // Transfer ownership to self with the same id -> `H(pk, nonce)` + ownable.as(OWNER).transferOwnership(repeatedId); + + // Check commitments don't match + const newCommitment = ownable.owner(); + expect(initCommitment).not.toEqual(newCommitment); + + // Build commitment locally and validate new commitment == expected + const bumpedCounter = INIT_COUNTER + 1n; + const expNewCommitment = buildCommitmentFromId( + repeatedId, + INSTANCE_SALT, + bumpedCounter, + ); + expect(newCommitment).toEqual(expNewCommitment); + + // Check same owner maintains permissions after transfer + expect(ownable.as(OWNER).assertOnlyOwner()).not.to.throw; + }); + }); + + describe('renounceOwnership', () => { + it('should renounce ownership', () => { + ownable.as(OWNER).renounceOwnership(); + + // Check owner is reset + expect(ownable.owner()).toEqual(new Uint8Array(32).fill(0)); + + // Check revoked permissions + expect(() => { + ownable.assertOnlyOwner(); + }).toThrow('SampleZOwnable: caller is not the owner'); + }); + + it('should fail when renouncing from unauthorized', () => { + expect(() => { + ownable.as(UNAUTHORIZED).renounceOwnership(); + }).toThrow('SampleZOwnable: caller is not the owner'); + }); + + it('should fail when renouncing from authorized with bad nonce', () => { + ownable.privateState.injectSecretNonce(BAD_NONCE); + expect(() => { + ownable.as(OWNER).renounceOwnership(); + }).toThrow('SampleZOwnable: caller is not the owner'); + }); + + it('should fail when renouncing from unauthorized with bad nonce', () => { + ownable.privateState.injectSecretNonce(BAD_NONCE); + expect(() => { + ownable.as(UNAUTHORIZED).renounceOwnership(); + }); + }); + }); + + describe('assertOnlyOwner', () => { + it('should allow authorized caller with correct nonce to call', () => { + // Check nonce is correct + expect(ownable.privateState.getCurrentSecretNonce()).toEqual( + secretNonce, + ); + + expect(ownable.as(OWNER).assertOnlyOwner()).to.not.throw; + }); + + it('should fail when the authorized caller has the wrong nonce', () => { + // Inject bad nonce + ownable.privateState.injectSecretNonce(BAD_NONCE); + + // Check nonce does not match + expect(ownable.privateState.getCurrentSecretNonce()).not.toEqual( + secretNonce, + ); + + // Set caller and call circuit + expect(() => { + ownable.as(OWNER).assertOnlyOwner(); + }).toThrow('SampleZOwnable: caller is not the owner'); + }); + + it('should fail when unauthorized caller has the correct nonce', () => { + // Check nonce is correct + expect(ownable.privateState.getCurrentSecretNonce()).toEqual( + secretNonce, + ); + + expect(() => { + ownable.as(UNAUTHORIZED).assertOnlyOwner(); + }).toThrow('SampleZOwnable: caller is not the owner'); + }); + + it('should fail when unauthorized caller has the wrong nonce', () => { + // Inject bad nonce + ownable.privateState.injectSecretNonce(BAD_NONCE); + + // Check nonce does not match + expect(ownable.privateState.getCurrentSecretNonce()).not.toEqual( + secretNonce, + ); + + // Set unauthorized caller and call circuit + expect(() => { + ownable.as(UNAUTHORIZED).assertOnlyOwner(); + }).toThrow('SampleZOwnable: caller is not the owner'); + }); + }); + + describe('_computeOwnerCommitment', () => { + const MAX_U64 = 2n ** 64n - 1n; + const testCases = [ + ...Array.from({ length: 10 }, (_, i) => ({ + label: `User${i}`, + ownerPK: utils.encodeToPK(`User${i}`), + counter: BigInt(Math.floor(Math.random() * 2 ** 64 - 1)), + })), + { + label: 'ZeroCounter', + ownerPK: utils.encodeToPK('ZeroCounter'), + counter: 0n, + }, + { + label: 'MaxCounter', + ownerPK: utils.encodeToPK('MaxUser'), + counter: MAX_U64, + }, + ]; + it.each(testCases)( + 'should match commitment for $label with counter $counter', + ({ ownerPK, counter }) => { + const id = createIdHash(ownerPK, secretNonce); + + // Check buildCommitmentFromId + const hashFromContract = ownable._computeOwnerCommitment(id, counter); + const hashFromHelper1 = buildCommitmentFromId( + id, + INSTANCE_SALT, + counter, + ); + expect(hashFromContract).toEqual(hashFromHelper1); + + // Check buildCommitment + const hashFromHelper2 = buildCommitment( + ownerPK, + secretNonce, + INSTANCE_SALT, + counter, + DOMAIN, + ); + expect(hashFromHelper1).toEqual(hashFromHelper2); + }, + ); + }); + + describe('_computeOwnerId', () => { + const testCases = [ + ...Array.from({ length: 10 }, (_, i) => ({ + label: `User${i}`, + eitherOwner: utils.createEitherTestUser(`User${i}`), + nonce: new Uint8Array(32).fill(i), + })), + { + label: 'All-zero nonce', + eitherOwner: utils.createEitherTestUser('ZeroUser'), + nonce: new Uint8Array(32).fill(0), + }, + { + label: 'Max nonce', + eitherOwner: utils.createEitherTestUser('MaxUser'), + nonce: new Uint8Array(32).fill(255), + }, + ]; + + it.each(testCases)( + 'should match local and contract owner id for $label', + ({ eitherOwner, nonce }) => { + const ownerId = ownable._computeOwnerId(eitherOwner, nonce); + const expId = createIdHash(eitherOwner.left, nonce); + expect(ownerId).toEqual(expId); + }, + ); + + it('should fail to compute ContractAddress id', () => { + const eitherContract = + utils.createEitherTestContractAddress('CONTRACT'); + expect(() => { + ownable._computeOwnerId(eitherContract, secretNonce); + }).toThrow( + 'SampleZOwnable: contract address owners are not yet supported', + ); + }); + }); + }); +}); diff --git a/packages/simulator/test/integration/SampleZOwnableSimulator.ts b/packages/simulator/test/integration/SampleZOwnableSimulator.ts new file mode 100644 index 00000000..7b22f12c --- /dev/null +++ b/packages/simulator/test/integration/SampleZOwnableSimulator.ts @@ -0,0 +1,150 @@ +import { type BaseSimulatorOptions, createSimulator } from '../../src/index'; +import { + SampleZOwnablePrivateState, + SampleZOwnableWitnesses, +} from '../fixtures/sample-contracts/witnesses/SampleZOwnableWitnesses'; +import { + type ContractAddress, + type Either, + ledger, + Contract as SampleZOwnable, + type ZswapCoinPublicKey, +} from '../fixtures/test-artifacts/SampleZOwnable/contract/index.cjs'; + +/** + * Type constructor args + */ +type SampleZOwnableArgs = readonly [ + owner: Uint8Array, + instanceSalt: Uint8Array, +]; + +/** + * Base simulator + */ +const SampleZOwnableSimulatorBase = createSimulator< + SampleZOwnablePrivateState, + ReturnType, + ReturnType, + SampleZOwnableArgs +>({ + contractFactory: (witnesses) => + new SampleZOwnable(witnesses), + defaultPrivateState: () => SampleZOwnablePrivateState.generate(), + contractArgs: (owner, instanceSalt) => { + return [owner, instanceSalt]; + }, + ledgerExtractor: (state) => ledger(state), + witnessesFactory: () => SampleZOwnableWitnesses(), +}); + +/** + * SampleZOwnable Simulator + */ +export class SampleZOwnableSimulator extends SampleZOwnableSimulatorBase { + constructor( + ownerId: Uint8Array, + instanceSalt: Uint8Array, + options: BaseSimulatorOptions< + SampleZOwnablePrivateState, + ReturnType + > = {}, + ) { + super([ownerId, instanceSalt], options); + } + + /** + * @description Returns the current commitment representing the contract owner. + * The full commitment is: `SHA256(SHA256(pk, nonce), instanceSalt, counter, domain)`. + * @returns The current owner's commitment. + */ + public owner(): Uint8Array { + return this.circuits.impure.owner(); + } + + /** + * @description Transfers ownership to `newOwnerId`. + * `newOwnerId` must be precalculated and given to the current owner off chain. + * @param newOwnerId The new owner's unique identifier (`SHA256(pk, nonce)`). + */ + public transferOwnership(newOwnerId: Uint8Array) { + this.circuits.impure.transferOwnership(newOwnerId); + } + + /** + * @description Leaves the contract without an owner. + * It will not be possible to call `assertOnlyOnwer` circuits anymore. + * Can only be called by the current owner. + */ + public renounceOwnership() { + this.circuits.impure.renounceOwnership(); + } + + /** + * @description Throws if called by any account whose id hash `SHA256(pk, nonce)` does not match + * the stored owner commitment. Use this to only allow the owner to call specific circuits. + */ + public assertOnlyOwner() { + this.circuits.impure.assertOnlyOwner(); + } + + /** + * @description Computes the owner commitment from the given `id` and `counter`. + * @param id - The unique identifier of the owner calculated by `SHA256(pk, nonce)`. + * @param counter - The current counter or round. This increments by `1` + * after every transfer to prevent duplicate commitments given the same `id`. + * @returns The commitment derived from `id` and `counter`. + */ + public _computeOwnerCommitment(id: Uint8Array, counter: bigint): Uint8Array { + return this.circuits.impure._computeOwnerCommitment(id, counter); + } + + /** + * @description Computes the unique identifier (`id`) of the owner from their + * public key and a secret nonce. + * @param pk - The public key of the identity being committed. + * @param nonce - A private nonce to scope the commitment. + * @returns The computed owner ID. + */ + public _computeOwnerId( + pk: Either, + nonce: Uint8Array, + ): Uint8Array { + return this.circuits.pure._computeOwnerId(pk, nonce); + } + + /** + * @description Transfers ownership to owner id `newOwnerId` without + * enforcing permission checks on the caller. + * @param newOwnerId - The unique identifier of the new owner calculated by `SHA256(pk, nonce)`. + */ + public _transferOwnership(newOwnerId: Uint8Array) { + this.circuits.impure._transferOwnership(newOwnerId); + } + + public readonly privateState = { + /** + * @description Contextually sets a new nonce into the private state. + * @param newNonce The secret nonce. + * @returns The SampleZOwnable private state after setting the new nonce. + */ + injectSecretNonce: ( + newNonce: Buffer, + ): SampleZOwnablePrivateState => { + const currentState = + this.circuitContextManager.getContext().currentPrivateState; + const updatedState = { ...currentState, secretNonce: newNonce }; + this.circuitContextManager.updatePrivateState(updatedState); + return updatedState; + }, + + /** + * @description Returns the secret nonce given the context. + * @returns The secret nonce. + */ + getCurrentSecretNonce: (): Uint8Array => { + return this.circuitContextManager.getContext().currentPrivateState + .secretNonce; + }, + }; +} diff --git a/packages/simulator/test/integration/Simple.test.ts b/packages/simulator/test/integration/Simple.test.ts new file mode 100644 index 00000000..d174d450 --- /dev/null +++ b/packages/simulator/test/integration/Simple.test.ts @@ -0,0 +1,20 @@ +import { beforeEach, describe, expect, it } from 'vitest'; +import { SimpleSimulator } from './SimpleSimulator'; + +let simple: SimpleSimulator; + +describe('Simple test', () => { + beforeEach(() => { + simple = new SimpleSimulator(); + }); + + it('sanity check', () => { + expect(1).toEqual(1); + }); + + it('should set val', () => { + const VAL = 123n; + simple.setVal(VAL); + expect(simple.getVal()).toEqual(VAL); + }); +}); diff --git a/packages/simulator/test/integration/SimpleSimulator.ts b/packages/simulator/test/integration/SimpleSimulator.ts new file mode 100644 index 00000000..980b1457 --- /dev/null +++ b/packages/simulator/test/integration/SimpleSimulator.ts @@ -0,0 +1,43 @@ +import { type BaseSimulatorOptions, createSimulator } from '../../src/index'; +import { + SimplePrivateState, + SimpleWitnesses, +} from '../fixtures/sample-contracts/witnesses/SimpleWitnesses'; +import { + ledger, + Contract as SimpleContract, +} from '../fixtures/test-artifacts/Simple/contract/index.cjs'; + +/** + * Base simulator + */ +const SimpleSimulatorBase = createSimulator({ + contractFactory: (witnesses) => + new SimpleContract(witnesses), + defaultPrivateState: () => SimplePrivateState, + contractArgs: () => [], + ledgerExtractor: (state) => ledger(state), + witnessesFactory: () => SimpleWitnesses(), +}); + +/** + * Simple Simulator + */ +export class SimpleSimulator extends SimpleSimulatorBase { + constructor( + options: BaseSimulatorOptions< + SimplePrivateState, + ReturnType + > = {}, + ) { + super([], options); + } + + public setVal(n: bigint) { + this.circuits.impure.setVal(n); + } + + public getVal(): bigint { + return this.circuits.impure.getVal(); + } +} diff --git a/packages/simulator/test/integration/Witness.test.ts b/packages/simulator/test/integration/Witness.test.ts new file mode 100644 index 00000000..c2e05198 --- /dev/null +++ b/packages/simulator/test/integration/Witness.test.ts @@ -0,0 +1,189 @@ +import { beforeEach, describe, expect, it } from 'vitest'; +import type { + IWitnessWitnesses, + WitnessPrivateState, +} from '../fixtures/sample-contracts/witnesses/WitnessWitnesses'; +import { WitnessSimulator } from './WitnessSimulator'; + +const VAL1 = 3n; +const VAL2 = 7n; +const BYTES_OVERRIDE = new Uint8Array(32).fill(1); +const FIELD_OVERRIDE = 222n; +const UINT_OVERRIDE = 333n; + +const overrideWitnesses = (): IWitnessWitnesses => ({ + wit_secretBytes(ctx) { + return [ctx.privateState, BYTES_OVERRIDE]; + }, + wit_secretFieldPlusArg(ctx) { + return [ctx.privateState, FIELD_OVERRIDE]; + }, + wit_secretUintPlusArgs(ctx) { + return [ctx.privateState, UINT_OVERRIDE]; + }, +}); + +let contract: WitnessSimulator; + +describe('witness/private state overrides', () => { + beforeEach(() => { + contract = new WitnessSimulator(); + }); + + describe('witness overrides', () => { + it('should have default public state values', () => { + expect(contract.getPublicState()._valBytes).toEqual( + new Uint8Array(32).fill(0), + ); + expect(contract.getPublicState()._valField).toEqual(0n); + expect(contract.getPublicState()._valUint).toEqual(0n); + }); + + it('should set values according to witness logic', () => { + // Private state + const psBytes = contract.getPrivateState().secretBytes; + const psField = contract.getPrivateState().secretField; + const psUint = contract.getPrivateState().secretUint; + + // Set values + contract.setBytes(); + contract.setField(VAL1); + contract.setUint(VAL1, VAL2); + + // Check values + expect(contract.getPublicState()._valBytes).toEqual( + new Uint8Array(psBytes), + ); + expect(contract.getPublicState()._valField).toEqual(psField + VAL1); + expect(contract.getPublicState()._valUint).toEqual(psUint + VAL1 + VAL2); + }); + + it('should override all witnesses', () => { + // Private state + const psBytes = contract.getPrivateState().secretBytes; + const psField = contract.getPrivateState().secretField; + const psUint = contract.getPrivateState().secretUint; + + // Override entire object + contract.witnesses = overrideWitnesses(); + + // Set values + contract.setBytes(); + contract.setField(VAL1); + contract.setUint(VAL1, VAL2); + + // Check bytes + expect(contract.getPublicState()._valBytes).toEqual(BYTES_OVERRIDE); + expect(contract.getPublicState()._valBytes).not.toEqual( + new Uint8Array(psBytes), + ); + + // Check field + expect(contract.getPublicState()._valField).toEqual(FIELD_OVERRIDE); + expect(contract.getPublicState()._valField).not.toEqual(psField + VAL1); + + // Check uint + expect(contract.getPublicState()._valUint).toEqual(UINT_OVERRIDE); + expect(contract.getPublicState()._valUint).not.toEqual( + psUint + VAL1 + VAL2, + ); + }); + + describe('when overriding individual witnesses', () => { + it('should override wit_secretBytes', () => { + // Private state + const psBytes = contract.getPrivateState().secretBytes; + const psField = contract.getPrivateState().secretField; + const psUint = contract.getPrivateState().secretUint; + + contract.overrideWitness('wit_secretBytes', (ctx) => { + return [ctx.privateState, BYTES_OVERRIDE]; + }); + + // Set all values + contract.setBytes(); + contract.setField(VAL1); + contract.setUint(VAL1, VAL2); + + // Check bytes override + expect(contract.getPublicState()._valBytes).toEqual(BYTES_OVERRIDE); + expect(contract.getPublicState()._valBytes).not.toEqual( + new Uint8Array(psBytes), + ); + + // Check other witnesses remain unchanged + expect(contract.getPublicState()._valField).toEqual(psField + VAL1); + expect(contract.getPublicState()._valUint).toEqual( + psUint + VAL1 + VAL2, + ); + }); + + it('should override wit_secretFieldPlusArg', () => { + // Private state + const psBytes = contract.getPrivateState().secretBytes; + const _psField = contract.getPrivateState().secretField; + const psUint = contract.getPrivateState().secretUint; + + contract.overrideWitness('wit_secretFieldPlusArg', (ctx) => { + return [ctx.privateState, FIELD_OVERRIDE]; + }); + + // Set all values + contract.setBytes(); + contract.setField(VAL1); + contract.setUint(VAL1, VAL2); + + // Check field override + expect(contract.getPublicState()._valField).toEqual(FIELD_OVERRIDE); + expect(contract.getPublicState()._valField).not.toEqual(VAL1); + + // Check other witnesses remain unchanged + expect(contract.getPublicState()._valBytes).toEqual( + new Uint8Array(psBytes), + ); + expect(contract.getPublicState()._valUint).toEqual( + psUint + VAL1 + VAL2, + ); + }); + + it('should override wit_secretUintPlusArgs', () => { + // Private state + const psBytes = contract.getPrivateState().secretBytes; + const psField = contract.getPrivateState().secretField; + const psUint = contract.getPrivateState().secretUint; + + contract.overrideWitness('wit_secretUintPlusArgs', (ctx) => { + return [ctx.privateState, UINT_OVERRIDE]; + }); + + // Set all values + contract.setBytes(); + contract.setField(VAL1); + contract.setUint(VAL1, VAL2); + + // Check uint override + expect(contract.getPublicState()._valUint).toEqual(UINT_OVERRIDE); + expect(contract.getPublicState()._valUint).not.toEqual( + psUint + VAL1 + VAL2, + ); + + // Check other witnesses remain unchanged + expect(contract.getPublicState()._valBytes).toEqual( + new Uint8Array(psBytes), + ); + expect(contract.getPublicState()._valField).toEqual(psField + VAL1); + }); + }); + }); + + describe('private state overrides', () => { + it('should match ps ', () => { + // Private state + const _psBytes = contract.getPrivateState().secretBytes; + const _psField = contract.getPrivateState().secretField; + const _psUint = contract.getPrivateState().secretUint; + }); + + it('should override the entire private state', () => {}); + }); +}); diff --git a/packages/simulator/test/integration/WitnessSimulator.ts b/packages/simulator/test/integration/WitnessSimulator.ts new file mode 100644 index 00000000..3bc09536 --- /dev/null +++ b/packages/simulator/test/integration/WitnessSimulator.ts @@ -0,0 +1,85 @@ +import { type BaseSimulatorOptions, createSimulator } from '../../src/index'; +import { + WitnessPrivateState, + WitnessWitnesses, +} from '../fixtures/sample-contracts/witnesses/WitnessWitnesses'; +import { + ledger, + Contract as SampleZOwnable, +} from '../fixtures/test-artifacts/Witness/contract/index.cjs'; + +/** + * Type constructor args + */ +type WitnessArgs = readonly []; + +/** + * Base simulator + */ +const WitnessSimulatorBase = createSimulator< + WitnessPrivateState, + ReturnType, + ReturnType, + WitnessArgs +>({ + contractFactory: (witnesses) => + new SampleZOwnable(witnesses), + defaultPrivateState: () => WitnessPrivateState.generate(), + contractArgs: () => { + return []; + }, + ledgerExtractor: (state) => ledger(state), + witnessesFactory: () => WitnessWitnesses(), +}); + +/** + * SampleZOwnable Simulator + */ +export class WitnessSimulator extends WitnessSimulatorBase { + constructor( + options: BaseSimulatorOptions< + WitnessPrivateState, + ReturnType + > = {}, + ) { + super([], options); + } + + public setBytes() { + this.circuits.impure.setBytes(); + } + + public setField(arg: bigint) { + this.circuits.impure.setField(arg); + } + + public setUint(arg1: bigint, arg2: bigint) { + this.circuits.impure.setUint(arg1, arg2); + } + + public readonly privateState = { + injectSecretBytes: ( + newBytes: Buffer, + ): WitnessPrivateState => { + const currentState = + this.circuitContextManager.getContext().currentPrivateState; + const updatedState = { ...currentState, secretBytes: newBytes }; + this.circuitContextManager.updatePrivateState(updatedState); + return updatedState; + }, + injectSecretField: (newField: bigint): WitnessPrivateState => { + const currentState = + this.circuitContextManager.getContext().currentPrivateState; + const updatedState = { ...currentState, secretField: newField }; + this.circuitContextManager.updatePrivateState(updatedState); + return updatedState; + }, + injectSecretUint: (newUint: bigint): WitnessPrivateState => { + const currentState = + this.circuitContextManager.getContext().currentPrivateState; + const updatedState = { ...currentState, secretUint: newUint }; + this.circuitContextManager.updatePrivateState(updatedState); + return updatedState; + }, + }; +} diff --git a/packages/simulator/test/unit/core/StateManager.test.ts b/packages/simulator/test/unit/core/StateManager.test.ts new file mode 100644 index 00000000..871d2b41 --- /dev/null +++ b/packages/simulator/test/unit/core/StateManager.test.ts @@ -0,0 +1,144 @@ +import { + type CircuitContext, + ContractState, + dummyContractAddress, + type EncodedQualifiedCoinInfo, + type EncodedZswapLocalState, + encodeQualifiedCoinInfo, + type QualifiedCoinInfo, + QueryContext, + StateValue, + sampleTokenType, +} from '@midnight-ntwrk/compact-runtime'; +import { beforeEach, describe, expect, it } from 'vitest'; +import { CircuitContextManager } from '../../../src/core/CircuitContextManager'; +import { + type SimplePrivateState, + SimpleWitnesses, +} from '../../fixtures/sample-contracts/witnesses/SimpleWitnesses'; +import { Contract as MockSimple } from '../../fixtures/test-artifacts/Simple/contract/index.cjs'; +import { encodeToAddress, toHexPadded } from '../../fixtures/utils/address'; + +// Constants +const DEPLOYER = 'DEPLOYER'; +const deployer = toHexPadded(DEPLOYER); + +// Mut vars +let mockContract: MockSimple; +let initialPrivateState: SimplePrivateState; +let circuitCtxManager: CircuitContextManager; +let ctx: CircuitContext; + +describe('CircuitContextManager', () => { + /** + * Parametrize me! + */ + describe('constructor', () => { + beforeEach(() => { + mockContract = new MockSimple(SimpleWitnesses()); + initialPrivateState = {}; + + circuitCtxManager = new CircuitContextManager( + mockContract, + initialPrivateState, + deployer, + dummyContractAddress(), + ); + + ctx = circuitCtxManager.getContext(); + }); + + it('should set private state', () => { + expect(ctx.currentPrivateState).toEqual(initialPrivateState); + }); + + it('should set zswap local state', () => { + const expectedZswapState: EncodedZswapLocalState = { + coinPublicKey: { + bytes: Uint8Array.from(Buffer.from(toHexPadded(DEPLOYER), 'hex')), + }, + currentIndex: 0n, + inputs: [], + outputs: [], + }; + expect(ctx.currentZswapLocalState).toEqual(expectedZswapState); + }); + + it('should set original state', () => { + expect(ctx.originalState).toBeInstanceOf(ContractState); + expect(ctx.originalState).toHaveProperty('__wbg_ptr'); + expect((ctx.originalState as any).__wbg_ptr).toBeTypeOf('number'); + }); + + it('should set tx ctx', () => { + // Need to go deeper + expect(ctx.transactionContext).toBeInstanceOf(QueryContext); + expect(ctx.transactionContext.address).toEqual(dummyContractAddress()); + expect(ctx.transactionContext.state).toBeInstanceOf(StateValue); + expect(ctx.transactionContext.state).toHaveProperty('__wbg_ptr'); + }); + }); + + describe('setContext', () => { + beforeEach(() => { + mockContract = new MockSimple(SimpleWitnesses()); + initialPrivateState = {}; + + circuitCtxManager = new CircuitContextManager( + mockContract, + initialPrivateState, + deployer, + dummyContractAddress(), + ); + + ctx = circuitCtxManager.getContext(); + }); + + /** + * Improve me + */ + it('should set new ctx', () => { + const oldCtx = circuitCtxManager.getContext(); + + const qualCoin: QualifiedCoinInfo = { + type: sampleTokenType(), + nonce: toHexPadded('nonce'), + value: 123n, + mt_index: 987n, + }; + const encQualCoin: EncodedQualifiedCoinInfo = + encodeQualifiedCoinInfo(qualCoin); + + // zswap local state + const zswapLocalState_1: EncodedZswapLocalState = { + coinPublicKey: { + bytes: Uint8Array.from(Buffer.from(toHexPadded('goldenFace'), 'hex')), + }, + currentIndex: 555n, + inputs: [encQualCoin], + outputs: [], + }; + + // OG state + const NEW_OG_STATE: ContractState = new ContractState(); + + // Query ctx + const modifiedTxCtx: QueryContext = { + ...ctx.transactionContext, + address: encodeToAddress('otherAddress'), + } as unknown as QueryContext; + + // Build new ctx + const newCtx: CircuitContext = { + originalState: NEW_OG_STATE, + currentPrivateState: initialPrivateState, + currentZswapLocalState: zswapLocalState_1, + transactionContext: modifiedTxCtx, + }; + + circuitCtxManager.setContext(newCtx); + expect(circuitCtxManager.getContext()).toEqual(newCtx); + expect(circuitCtxManager.getContext()).not.toEqual(oldCtx); + }); + }); +}); diff --git a/packages/simulator/tsconfig.json b/packages/simulator/tsconfig.json new file mode 100644 index 00000000..db5e5e63 --- /dev/null +++ b/packages/simulator/tsconfig.json @@ -0,0 +1,15 @@ +{ + "extends": "@tsconfig/node22/tsconfig.json", + "compilerOptions": { + "rootDir": "./src", + "outDir": "./dist", + "declaration": true, + "skipLibCheck": true, + "sourceMap": true, + "rewriteRelativeImportExtensions": true, + "erasableSyntaxOnly": true, + "verbatimModuleSyntax": true + }, + "include": ["src/**/*"], + "exclude": ["node_modules", "dist", "tests"] +} diff --git a/packages/simulator/vitest.config.ts b/packages/simulator/vitest.config.ts new file mode 100644 index 00000000..d57e53a7 --- /dev/null +++ b/packages/simulator/vitest.config.ts @@ -0,0 +1,10 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + globals: true, + environment: 'node', + include: ['test/**/*.test.ts'], + reporters: 'verbose', + }, +}); diff --git a/yarn.lock b/yarn.lock index 082ea687..d695bc0f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -105,184 +105,184 @@ __metadata: languageName: node linkType: hard -"@esbuild/aix-ppc64@npm:0.25.9": - version: 0.25.9 - resolution: "@esbuild/aix-ppc64@npm:0.25.9" +"@esbuild/aix-ppc64@npm:0.25.10": + version: 0.25.10 + resolution: "@esbuild/aix-ppc64@npm:0.25.10" conditions: os=aix & cpu=ppc64 languageName: node linkType: hard -"@esbuild/android-arm64@npm:0.25.9": - version: 0.25.9 - resolution: "@esbuild/android-arm64@npm:0.25.9" +"@esbuild/android-arm64@npm:0.25.10": + version: 0.25.10 + resolution: "@esbuild/android-arm64@npm:0.25.10" conditions: os=android & cpu=arm64 languageName: node linkType: hard -"@esbuild/android-arm@npm:0.25.9": - version: 0.25.9 - resolution: "@esbuild/android-arm@npm:0.25.9" +"@esbuild/android-arm@npm:0.25.10": + version: 0.25.10 + resolution: "@esbuild/android-arm@npm:0.25.10" conditions: os=android & cpu=arm languageName: node linkType: hard -"@esbuild/android-x64@npm:0.25.9": - version: 0.25.9 - resolution: "@esbuild/android-x64@npm:0.25.9" +"@esbuild/android-x64@npm:0.25.10": + version: 0.25.10 + resolution: "@esbuild/android-x64@npm:0.25.10" conditions: os=android & cpu=x64 languageName: node linkType: hard -"@esbuild/darwin-arm64@npm:0.25.9": - version: 0.25.9 - resolution: "@esbuild/darwin-arm64@npm:0.25.9" +"@esbuild/darwin-arm64@npm:0.25.10": + version: 0.25.10 + resolution: "@esbuild/darwin-arm64@npm:0.25.10" conditions: os=darwin & cpu=arm64 languageName: node linkType: hard -"@esbuild/darwin-x64@npm:0.25.9": - version: 0.25.9 - resolution: "@esbuild/darwin-x64@npm:0.25.9" +"@esbuild/darwin-x64@npm:0.25.10": + version: 0.25.10 + resolution: "@esbuild/darwin-x64@npm:0.25.10" conditions: os=darwin & cpu=x64 languageName: node linkType: hard -"@esbuild/freebsd-arm64@npm:0.25.9": - version: 0.25.9 - resolution: "@esbuild/freebsd-arm64@npm:0.25.9" +"@esbuild/freebsd-arm64@npm:0.25.10": + version: 0.25.10 + resolution: "@esbuild/freebsd-arm64@npm:0.25.10" conditions: os=freebsd & cpu=arm64 languageName: node linkType: hard -"@esbuild/freebsd-x64@npm:0.25.9": - version: 0.25.9 - resolution: "@esbuild/freebsd-x64@npm:0.25.9" +"@esbuild/freebsd-x64@npm:0.25.10": + version: 0.25.10 + resolution: "@esbuild/freebsd-x64@npm:0.25.10" conditions: os=freebsd & cpu=x64 languageName: node linkType: hard -"@esbuild/linux-arm64@npm:0.25.9": - version: 0.25.9 - resolution: "@esbuild/linux-arm64@npm:0.25.9" +"@esbuild/linux-arm64@npm:0.25.10": + version: 0.25.10 + resolution: "@esbuild/linux-arm64@npm:0.25.10" conditions: os=linux & cpu=arm64 languageName: node linkType: hard -"@esbuild/linux-arm@npm:0.25.9": - version: 0.25.9 - resolution: "@esbuild/linux-arm@npm:0.25.9" +"@esbuild/linux-arm@npm:0.25.10": + version: 0.25.10 + resolution: "@esbuild/linux-arm@npm:0.25.10" conditions: os=linux & cpu=arm languageName: node linkType: hard -"@esbuild/linux-ia32@npm:0.25.9": - version: 0.25.9 - resolution: "@esbuild/linux-ia32@npm:0.25.9" +"@esbuild/linux-ia32@npm:0.25.10": + version: 0.25.10 + resolution: "@esbuild/linux-ia32@npm:0.25.10" conditions: os=linux & cpu=ia32 languageName: node linkType: hard -"@esbuild/linux-loong64@npm:0.25.9": - version: 0.25.9 - resolution: "@esbuild/linux-loong64@npm:0.25.9" +"@esbuild/linux-loong64@npm:0.25.10": + version: 0.25.10 + resolution: "@esbuild/linux-loong64@npm:0.25.10" conditions: os=linux & cpu=loong64 languageName: node linkType: hard -"@esbuild/linux-mips64el@npm:0.25.9": - version: 0.25.9 - resolution: "@esbuild/linux-mips64el@npm:0.25.9" +"@esbuild/linux-mips64el@npm:0.25.10": + version: 0.25.10 + resolution: "@esbuild/linux-mips64el@npm:0.25.10" conditions: os=linux & cpu=mips64el languageName: node linkType: hard -"@esbuild/linux-ppc64@npm:0.25.9": - version: 0.25.9 - resolution: "@esbuild/linux-ppc64@npm:0.25.9" +"@esbuild/linux-ppc64@npm:0.25.10": + version: 0.25.10 + resolution: "@esbuild/linux-ppc64@npm:0.25.10" conditions: os=linux & cpu=ppc64 languageName: node linkType: hard -"@esbuild/linux-riscv64@npm:0.25.9": - version: 0.25.9 - resolution: "@esbuild/linux-riscv64@npm:0.25.9" +"@esbuild/linux-riscv64@npm:0.25.10": + version: 0.25.10 + resolution: "@esbuild/linux-riscv64@npm:0.25.10" conditions: os=linux & cpu=riscv64 languageName: node linkType: hard -"@esbuild/linux-s390x@npm:0.25.9": - version: 0.25.9 - resolution: "@esbuild/linux-s390x@npm:0.25.9" +"@esbuild/linux-s390x@npm:0.25.10": + version: 0.25.10 + resolution: "@esbuild/linux-s390x@npm:0.25.10" conditions: os=linux & cpu=s390x languageName: node linkType: hard -"@esbuild/linux-x64@npm:0.25.9": - version: 0.25.9 - resolution: "@esbuild/linux-x64@npm:0.25.9" +"@esbuild/linux-x64@npm:0.25.10": + version: 0.25.10 + resolution: "@esbuild/linux-x64@npm:0.25.10" conditions: os=linux & cpu=x64 languageName: node linkType: hard -"@esbuild/netbsd-arm64@npm:0.25.9": - version: 0.25.9 - resolution: "@esbuild/netbsd-arm64@npm:0.25.9" +"@esbuild/netbsd-arm64@npm:0.25.10": + version: 0.25.10 + resolution: "@esbuild/netbsd-arm64@npm:0.25.10" conditions: os=netbsd & cpu=arm64 languageName: node linkType: hard -"@esbuild/netbsd-x64@npm:0.25.9": - version: 0.25.9 - resolution: "@esbuild/netbsd-x64@npm:0.25.9" +"@esbuild/netbsd-x64@npm:0.25.10": + version: 0.25.10 + resolution: "@esbuild/netbsd-x64@npm:0.25.10" conditions: os=netbsd & cpu=x64 languageName: node linkType: hard -"@esbuild/openbsd-arm64@npm:0.25.9": - version: 0.25.9 - resolution: "@esbuild/openbsd-arm64@npm:0.25.9" +"@esbuild/openbsd-arm64@npm:0.25.10": + version: 0.25.10 + resolution: "@esbuild/openbsd-arm64@npm:0.25.10" conditions: os=openbsd & cpu=arm64 languageName: node linkType: hard -"@esbuild/openbsd-x64@npm:0.25.9": - version: 0.25.9 - resolution: "@esbuild/openbsd-x64@npm:0.25.9" +"@esbuild/openbsd-x64@npm:0.25.10": + version: 0.25.10 + resolution: "@esbuild/openbsd-x64@npm:0.25.10" conditions: os=openbsd & cpu=x64 languageName: node linkType: hard -"@esbuild/openharmony-arm64@npm:0.25.9": - version: 0.25.9 - resolution: "@esbuild/openharmony-arm64@npm:0.25.9" +"@esbuild/openharmony-arm64@npm:0.25.10": + version: 0.25.10 + resolution: "@esbuild/openharmony-arm64@npm:0.25.10" conditions: os=openharmony & cpu=arm64 languageName: node linkType: hard -"@esbuild/sunos-x64@npm:0.25.9": - version: 0.25.9 - resolution: "@esbuild/sunos-x64@npm:0.25.9" +"@esbuild/sunos-x64@npm:0.25.10": + version: 0.25.10 + resolution: "@esbuild/sunos-x64@npm:0.25.10" conditions: os=sunos & cpu=x64 languageName: node linkType: hard -"@esbuild/win32-arm64@npm:0.25.9": - version: 0.25.9 - resolution: "@esbuild/win32-arm64@npm:0.25.9" +"@esbuild/win32-arm64@npm:0.25.10": + version: 0.25.10 + resolution: "@esbuild/win32-arm64@npm:0.25.10" conditions: os=win32 & cpu=arm64 languageName: node linkType: hard -"@esbuild/win32-ia32@npm:0.25.9": - version: 0.25.9 - resolution: "@esbuild/win32-ia32@npm:0.25.9" +"@esbuild/win32-ia32@npm:0.25.10": + version: 0.25.10 + resolution: "@esbuild/win32-ia32@npm:0.25.10" conditions: os=win32 & cpu=ia32 languageName: node linkType: hard -"@esbuild/win32-x64@npm:0.25.9": - version: 0.25.9 - resolution: "@esbuild/win32-x64@npm:0.25.9" +"@esbuild/win32-x64@npm:0.25.10": + version: 0.25.10 + resolution: "@esbuild/win32-x64@npm:0.25.10" conditions: os=win32 & cpu=x64 languageName: node linkType: hard @@ -395,9 +395,9 @@ __metadata: languageName: node linkType: hard -"@openzeppelin-compact/compact@workspace:^, @openzeppelin-compact/compact@workspace:compact": +"@openzeppelin-compact/compact@workspace:^, @openzeppelin-compact/compact@workspace:packages/compact": version: 0.0.0-use.local - resolution: "@openzeppelin-compact/compact@workspace:compact" + resolution: "@openzeppelin-compact/compact@workspace:packages/compact" dependencies: "@tsconfig/node22": "npm:^22.0.2" "@types/node": "npm:22.18.0" @@ -412,11 +412,27 @@ __metadata: languageName: unknown linkType: soft +"@openzeppelin-compact/contracts-simulator@workspace:^, @openzeppelin-compact/contracts-simulator@workspace:packages/simulator": + version: 0.0.0-use.local + resolution: "@openzeppelin-compact/contracts-simulator@workspace:packages/simulator" + dependencies: + "@midnight-ntwrk/compact-runtime": "npm:^0.8.1" + "@midnight-ntwrk/ledger": "npm:^4.0.0" + "@midnight-ntwrk/zswap": "npm:^4.0.0" + "@tsconfig/node22": "npm:^22.0.2" + "@types/node": "npm:22.18.0" + fast-check: "npm:^3.15.0" + typescript: "npm:^5.8.2" + vitest: "npm:^3.1.3" + languageName: unknown + linkType: soft + "@openzeppelin-compact/contracts@workspace:contracts": version: 0.0.0-use.local resolution: "@openzeppelin-compact/contracts@workspace:contracts" dependencies: "@openzeppelin-compact/compact": "workspace:^" + "@openzeppelin-compact/contracts-simulator": "workspace:^" "@tsconfig/node22": "npm:^22.0.2" "@types/node": "npm:22.18.0" ts-node: "npm:^10.9.2" @@ -451,149 +467,156 @@ __metadata: languageName: node linkType: hard -"@rollup/rollup-android-arm-eabi@npm:4.50.2": - version: 4.50.2 - resolution: "@rollup/rollup-android-arm-eabi@npm:4.50.2" +"@rollup/rollup-android-arm-eabi@npm:4.52.4": + version: 4.52.4 + resolution: "@rollup/rollup-android-arm-eabi@npm:4.52.4" conditions: os=android & cpu=arm languageName: node linkType: hard -"@rollup/rollup-android-arm64@npm:4.50.2": - version: 4.50.2 - resolution: "@rollup/rollup-android-arm64@npm:4.50.2" +"@rollup/rollup-android-arm64@npm:4.52.4": + version: 4.52.4 + resolution: "@rollup/rollup-android-arm64@npm:4.52.4" conditions: os=android & cpu=arm64 languageName: node linkType: hard -"@rollup/rollup-darwin-arm64@npm:4.50.2": - version: 4.50.2 - resolution: "@rollup/rollup-darwin-arm64@npm:4.50.2" +"@rollup/rollup-darwin-arm64@npm:4.52.4": + version: 4.52.4 + resolution: "@rollup/rollup-darwin-arm64@npm:4.52.4" conditions: os=darwin & cpu=arm64 languageName: node linkType: hard -"@rollup/rollup-darwin-x64@npm:4.50.2": - version: 4.50.2 - resolution: "@rollup/rollup-darwin-x64@npm:4.50.2" +"@rollup/rollup-darwin-x64@npm:4.52.4": + version: 4.52.4 + resolution: "@rollup/rollup-darwin-x64@npm:4.52.4" conditions: os=darwin & cpu=x64 languageName: node linkType: hard -"@rollup/rollup-freebsd-arm64@npm:4.50.2": - version: 4.50.2 - resolution: "@rollup/rollup-freebsd-arm64@npm:4.50.2" +"@rollup/rollup-freebsd-arm64@npm:4.52.4": + version: 4.52.4 + resolution: "@rollup/rollup-freebsd-arm64@npm:4.52.4" conditions: os=freebsd & cpu=arm64 languageName: node linkType: hard -"@rollup/rollup-freebsd-x64@npm:4.50.2": - version: 4.50.2 - resolution: "@rollup/rollup-freebsd-x64@npm:4.50.2" +"@rollup/rollup-freebsd-x64@npm:4.52.4": + version: 4.52.4 + resolution: "@rollup/rollup-freebsd-x64@npm:4.52.4" conditions: os=freebsd & cpu=x64 languageName: node linkType: hard -"@rollup/rollup-linux-arm-gnueabihf@npm:4.50.2": - version: 4.50.2 - resolution: "@rollup/rollup-linux-arm-gnueabihf@npm:4.50.2" +"@rollup/rollup-linux-arm-gnueabihf@npm:4.52.4": + version: 4.52.4 + resolution: "@rollup/rollup-linux-arm-gnueabihf@npm:4.52.4" conditions: os=linux & cpu=arm & libc=glibc languageName: node linkType: hard -"@rollup/rollup-linux-arm-musleabihf@npm:4.50.2": - version: 4.50.2 - resolution: "@rollup/rollup-linux-arm-musleabihf@npm:4.50.2" +"@rollup/rollup-linux-arm-musleabihf@npm:4.52.4": + version: 4.52.4 + resolution: "@rollup/rollup-linux-arm-musleabihf@npm:4.52.4" conditions: os=linux & cpu=arm & libc=musl languageName: node linkType: hard -"@rollup/rollup-linux-arm64-gnu@npm:4.50.2": - version: 4.50.2 - resolution: "@rollup/rollup-linux-arm64-gnu@npm:4.50.2" +"@rollup/rollup-linux-arm64-gnu@npm:4.52.4": + version: 4.52.4 + resolution: "@rollup/rollup-linux-arm64-gnu@npm:4.52.4" conditions: os=linux & cpu=arm64 & libc=glibc languageName: node linkType: hard -"@rollup/rollup-linux-arm64-musl@npm:4.50.2": - version: 4.50.2 - resolution: "@rollup/rollup-linux-arm64-musl@npm:4.50.2" +"@rollup/rollup-linux-arm64-musl@npm:4.52.4": + version: 4.52.4 + resolution: "@rollup/rollup-linux-arm64-musl@npm:4.52.4" conditions: os=linux & cpu=arm64 & libc=musl languageName: node linkType: hard -"@rollup/rollup-linux-loong64-gnu@npm:4.50.2": - version: 4.50.2 - resolution: "@rollup/rollup-linux-loong64-gnu@npm:4.50.2" +"@rollup/rollup-linux-loong64-gnu@npm:4.52.4": + version: 4.52.4 + resolution: "@rollup/rollup-linux-loong64-gnu@npm:4.52.4" conditions: os=linux & cpu=loong64 & libc=glibc languageName: node linkType: hard -"@rollup/rollup-linux-ppc64-gnu@npm:4.50.2": - version: 4.50.2 - resolution: "@rollup/rollup-linux-ppc64-gnu@npm:4.50.2" +"@rollup/rollup-linux-ppc64-gnu@npm:4.52.4": + version: 4.52.4 + resolution: "@rollup/rollup-linux-ppc64-gnu@npm:4.52.4" conditions: os=linux & cpu=ppc64 & libc=glibc languageName: node linkType: hard -"@rollup/rollup-linux-riscv64-gnu@npm:4.50.2": - version: 4.50.2 - resolution: "@rollup/rollup-linux-riscv64-gnu@npm:4.50.2" +"@rollup/rollup-linux-riscv64-gnu@npm:4.52.4": + version: 4.52.4 + resolution: "@rollup/rollup-linux-riscv64-gnu@npm:4.52.4" conditions: os=linux & cpu=riscv64 & libc=glibc languageName: node linkType: hard -"@rollup/rollup-linux-riscv64-musl@npm:4.50.2": - version: 4.50.2 - resolution: "@rollup/rollup-linux-riscv64-musl@npm:4.50.2" +"@rollup/rollup-linux-riscv64-musl@npm:4.52.4": + version: 4.52.4 + resolution: "@rollup/rollup-linux-riscv64-musl@npm:4.52.4" conditions: os=linux & cpu=riscv64 & libc=musl languageName: node linkType: hard -"@rollup/rollup-linux-s390x-gnu@npm:4.50.2": - version: 4.50.2 - resolution: "@rollup/rollup-linux-s390x-gnu@npm:4.50.2" +"@rollup/rollup-linux-s390x-gnu@npm:4.52.4": + version: 4.52.4 + resolution: "@rollup/rollup-linux-s390x-gnu@npm:4.52.4" conditions: os=linux & cpu=s390x & libc=glibc languageName: node linkType: hard -"@rollup/rollup-linux-x64-gnu@npm:4.50.2": - version: 4.50.2 - resolution: "@rollup/rollup-linux-x64-gnu@npm:4.50.2" +"@rollup/rollup-linux-x64-gnu@npm:4.52.4": + version: 4.52.4 + resolution: "@rollup/rollup-linux-x64-gnu@npm:4.52.4" conditions: os=linux & cpu=x64 & libc=glibc languageName: node linkType: hard -"@rollup/rollup-linux-x64-musl@npm:4.50.2": - version: 4.50.2 - resolution: "@rollup/rollup-linux-x64-musl@npm:4.50.2" +"@rollup/rollup-linux-x64-musl@npm:4.52.4": + version: 4.52.4 + resolution: "@rollup/rollup-linux-x64-musl@npm:4.52.4" conditions: os=linux & cpu=x64 & libc=musl languageName: node linkType: hard -"@rollup/rollup-openharmony-arm64@npm:4.50.2": - version: 4.50.2 - resolution: "@rollup/rollup-openharmony-arm64@npm:4.50.2" +"@rollup/rollup-openharmony-arm64@npm:4.52.4": + version: 4.52.4 + resolution: "@rollup/rollup-openharmony-arm64@npm:4.52.4" conditions: os=openharmony & cpu=arm64 languageName: node linkType: hard -"@rollup/rollup-win32-arm64-msvc@npm:4.50.2": - version: 4.50.2 - resolution: "@rollup/rollup-win32-arm64-msvc@npm:4.50.2" +"@rollup/rollup-win32-arm64-msvc@npm:4.52.4": + version: 4.52.4 + resolution: "@rollup/rollup-win32-arm64-msvc@npm:4.52.4" conditions: os=win32 & cpu=arm64 languageName: node linkType: hard -"@rollup/rollup-win32-ia32-msvc@npm:4.50.2": - version: 4.50.2 - resolution: "@rollup/rollup-win32-ia32-msvc@npm:4.50.2" +"@rollup/rollup-win32-ia32-msvc@npm:4.52.4": + version: 4.52.4 + resolution: "@rollup/rollup-win32-ia32-msvc@npm:4.52.4" conditions: os=win32 & cpu=ia32 languageName: node linkType: hard -"@rollup/rollup-win32-x64-msvc@npm:4.50.2": - version: 4.50.2 - resolution: "@rollup/rollup-win32-x64-msvc@npm:4.50.2" +"@rollup/rollup-win32-x64-gnu@npm:4.52.4": + version: 4.52.4 + resolution: "@rollup/rollup-win32-x64-gnu@npm:4.52.4" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + +"@rollup/rollup-win32-x64-msvc@npm:4.52.4": + version: 4.52.4 + resolution: "@rollup/rollup-win32-x64-msvc@npm:4.52.4" conditions: os=win32 & cpu=x64 languageName: node linkType: hard @@ -982,9 +1005,9 @@ __metadata: linkType: hard "cli-spinners@npm:^3.2.0": - version: 3.2.0 - resolution: "cli-spinners@npm:3.2.0" - checksum: 10/6612d3880c87ad1749556ff463c41499ebeab4024ee4afc41a8731d0bcd1679b18bb67a98df7e647cfa49adcff1ce86c049e141a4da028bb12831d7f13111d89 + version: 3.3.0 + resolution: "cli-spinners@npm:3.3.0" + checksum: 10/d95f69f4a6a4efab2104ca5d4723c9f6fae9a4006df7fdcc1f79ea6539324e274b85bf6f5931146d84296b0f71814f4c1ff1acc158f2e1107c0c9797c1291bcc languageName: node linkType: hard @@ -1108,35 +1131,35 @@ __metadata: linkType: hard "esbuild@npm:^0.25.0": - version: 0.25.9 - resolution: "esbuild@npm:0.25.9" - dependencies: - "@esbuild/aix-ppc64": "npm:0.25.9" - "@esbuild/android-arm": "npm:0.25.9" - "@esbuild/android-arm64": "npm:0.25.9" - "@esbuild/android-x64": "npm:0.25.9" - "@esbuild/darwin-arm64": "npm:0.25.9" - "@esbuild/darwin-x64": "npm:0.25.9" - "@esbuild/freebsd-arm64": "npm:0.25.9" - "@esbuild/freebsd-x64": "npm:0.25.9" - "@esbuild/linux-arm": "npm:0.25.9" - "@esbuild/linux-arm64": "npm:0.25.9" - "@esbuild/linux-ia32": "npm:0.25.9" - "@esbuild/linux-loong64": "npm:0.25.9" - "@esbuild/linux-mips64el": "npm:0.25.9" - "@esbuild/linux-ppc64": "npm:0.25.9" - "@esbuild/linux-riscv64": "npm:0.25.9" - "@esbuild/linux-s390x": "npm:0.25.9" - "@esbuild/linux-x64": "npm:0.25.9" - "@esbuild/netbsd-arm64": "npm:0.25.9" - "@esbuild/netbsd-x64": "npm:0.25.9" - "@esbuild/openbsd-arm64": "npm:0.25.9" - "@esbuild/openbsd-x64": "npm:0.25.9" - "@esbuild/openharmony-arm64": "npm:0.25.9" - "@esbuild/sunos-x64": "npm:0.25.9" - "@esbuild/win32-arm64": "npm:0.25.9" - "@esbuild/win32-ia32": "npm:0.25.9" - "@esbuild/win32-x64": "npm:0.25.9" + version: 0.25.10 + resolution: "esbuild@npm:0.25.10" + dependencies: + "@esbuild/aix-ppc64": "npm:0.25.10" + "@esbuild/android-arm": "npm:0.25.10" + "@esbuild/android-arm64": "npm:0.25.10" + "@esbuild/android-x64": "npm:0.25.10" + "@esbuild/darwin-arm64": "npm:0.25.10" + "@esbuild/darwin-x64": "npm:0.25.10" + "@esbuild/freebsd-arm64": "npm:0.25.10" + "@esbuild/freebsd-x64": "npm:0.25.10" + "@esbuild/linux-arm": "npm:0.25.10" + "@esbuild/linux-arm64": "npm:0.25.10" + "@esbuild/linux-ia32": "npm:0.25.10" + "@esbuild/linux-loong64": "npm:0.25.10" + "@esbuild/linux-mips64el": "npm:0.25.10" + "@esbuild/linux-ppc64": "npm:0.25.10" + "@esbuild/linux-riscv64": "npm:0.25.10" + "@esbuild/linux-s390x": "npm:0.25.10" + "@esbuild/linux-x64": "npm:0.25.10" + "@esbuild/netbsd-arm64": "npm:0.25.10" + "@esbuild/netbsd-x64": "npm:0.25.10" + "@esbuild/openbsd-arm64": "npm:0.25.10" + "@esbuild/openbsd-x64": "npm:0.25.10" + "@esbuild/openharmony-arm64": "npm:0.25.10" + "@esbuild/sunos-x64": "npm:0.25.10" + "@esbuild/win32-arm64": "npm:0.25.10" + "@esbuild/win32-ia32": "npm:0.25.10" + "@esbuild/win32-x64": "npm:0.25.10" dependenciesMeta: "@esbuild/aix-ppc64": optional: true @@ -1192,7 +1215,7 @@ __metadata: optional: true bin: esbuild: bin/esbuild - checksum: 10/fc174ae7f646ad413adb641c7e46f16be575e462ed209866b55d5954d382e5da839e3f3f89a8e42e2b71d48895cc636ba43523011249fe5ff9c63d8d39d3a364 + checksum: 10/a8e4d33d7e785b7c8e1255d2ef532a53d1406659dbf2d0d3cdeb95c4760f51f86683e42974643b4f1dbe58381b6c7ce1217d4c8325f84353fbfc40be7b326358 languageName: node linkType: hard @@ -1229,6 +1252,15 @@ __metadata: languageName: node linkType: hard +"fast-check@npm:^3.15.0": + version: 3.23.2 + resolution: "fast-check@npm:3.23.2" + dependencies: + pure-rand: "npm:^6.1.0" + checksum: 10/dab344146b778e8bc2973366ea55528d1b58d3e3037270262b877c54241e800c4d744957722c24705c787020d702aece11e57c9e3dbd5ea19c3e10926bf1f3fe + languageName: node + linkType: hard + "fdir@npm:^6.5.0": version: 6.5.0 resolution: "fdir@npm:6.5.0" @@ -1665,21 +1697,12 @@ __metadata: languageName: node linkType: hard -"minizlib@npm:^3.0.1": - version: 3.0.2 - resolution: "minizlib@npm:3.0.2" +"minizlib@npm:^3.0.1, minizlib@npm:^3.1.0": + version: 3.1.0 + resolution: "minizlib@npm:3.1.0" dependencies: minipass: "npm:^7.1.2" - checksum: 10/c075bed1594f68dcc8c35122333520112daefd4d070e5d0a228bd4cf5580e9eed3981b96c0ae1d62488e204e80fd27b2b9d0068ca9a5ef3993e9565faf63ca41 - languageName: node - linkType: hard - -"mkdirp@npm:^3.0.1": - version: 3.0.1 - resolution: "mkdirp@npm:3.0.1" - bin: - mkdirp: dist/cjs/src/bin.js - checksum: 10/16fd79c28645759505914561e249b9a1f5fe3362279ad95487a4501e4467abeb714fd35b95307326b8fd03f3c7719065ef11a6f97b7285d7888306d1bd2232ba + checksum: 10/f47365cc2cb7f078cbe7e046eb52655e2e7e97f8c0a9a674f4da60d94fb0624edfcec9b5db32e8ba5a99a5f036f595680ae6fe02a262beaa73026e505cc52f99 languageName: node linkType: hard @@ -1919,6 +1942,13 @@ __metadata: languageName: node linkType: hard +"pure-rand@npm:^6.1.0": + version: 6.1.0 + resolution: "pure-rand@npm:6.1.0" + checksum: 10/256aa4bcaf9297256f552914e03cbdb0039c8fe1db11fa1e6d3f80790e16e563eb0a859a1e61082a95e224fc0c608661839439f8ecc6a3db4e48d46d99216ee4 + languageName: node + linkType: hard + "readdirp@npm:~3.6.0": version: 3.6.0 resolution: "readdirp@npm:3.6.0" @@ -1946,30 +1976,31 @@ __metadata: linkType: hard "rollup@npm:^4.43.0": - version: 4.50.2 - resolution: "rollup@npm:4.50.2" - dependencies: - "@rollup/rollup-android-arm-eabi": "npm:4.50.2" - "@rollup/rollup-android-arm64": "npm:4.50.2" - "@rollup/rollup-darwin-arm64": "npm:4.50.2" - "@rollup/rollup-darwin-x64": "npm:4.50.2" - "@rollup/rollup-freebsd-arm64": "npm:4.50.2" - "@rollup/rollup-freebsd-x64": "npm:4.50.2" - "@rollup/rollup-linux-arm-gnueabihf": "npm:4.50.2" - "@rollup/rollup-linux-arm-musleabihf": "npm:4.50.2" - "@rollup/rollup-linux-arm64-gnu": "npm:4.50.2" - "@rollup/rollup-linux-arm64-musl": "npm:4.50.2" - "@rollup/rollup-linux-loong64-gnu": "npm:4.50.2" - "@rollup/rollup-linux-ppc64-gnu": "npm:4.50.2" - "@rollup/rollup-linux-riscv64-gnu": "npm:4.50.2" - "@rollup/rollup-linux-riscv64-musl": "npm:4.50.2" - "@rollup/rollup-linux-s390x-gnu": "npm:4.50.2" - "@rollup/rollup-linux-x64-gnu": "npm:4.50.2" - "@rollup/rollup-linux-x64-musl": "npm:4.50.2" - "@rollup/rollup-openharmony-arm64": "npm:4.50.2" - "@rollup/rollup-win32-arm64-msvc": "npm:4.50.2" - "@rollup/rollup-win32-ia32-msvc": "npm:4.50.2" - "@rollup/rollup-win32-x64-msvc": "npm:4.50.2" + version: 4.52.4 + resolution: "rollup@npm:4.52.4" + dependencies: + "@rollup/rollup-android-arm-eabi": "npm:4.52.4" + "@rollup/rollup-android-arm64": "npm:4.52.4" + "@rollup/rollup-darwin-arm64": "npm:4.52.4" + "@rollup/rollup-darwin-x64": "npm:4.52.4" + "@rollup/rollup-freebsd-arm64": "npm:4.52.4" + "@rollup/rollup-freebsd-x64": "npm:4.52.4" + "@rollup/rollup-linux-arm-gnueabihf": "npm:4.52.4" + "@rollup/rollup-linux-arm-musleabihf": "npm:4.52.4" + "@rollup/rollup-linux-arm64-gnu": "npm:4.52.4" + "@rollup/rollup-linux-arm64-musl": "npm:4.52.4" + "@rollup/rollup-linux-loong64-gnu": "npm:4.52.4" + "@rollup/rollup-linux-ppc64-gnu": "npm:4.52.4" + "@rollup/rollup-linux-riscv64-gnu": "npm:4.52.4" + "@rollup/rollup-linux-riscv64-musl": "npm:4.52.4" + "@rollup/rollup-linux-s390x-gnu": "npm:4.52.4" + "@rollup/rollup-linux-x64-gnu": "npm:4.52.4" + "@rollup/rollup-linux-x64-musl": "npm:4.52.4" + "@rollup/rollup-openharmony-arm64": "npm:4.52.4" + "@rollup/rollup-win32-arm64-msvc": "npm:4.52.4" + "@rollup/rollup-win32-ia32-msvc": "npm:4.52.4" + "@rollup/rollup-win32-x64-gnu": "npm:4.52.4" + "@rollup/rollup-win32-x64-msvc": "npm:4.52.4" "@types/estree": "npm:1.0.8" fsevents: "npm:~2.3.2" dependenciesMeta: @@ -2013,13 +2044,15 @@ __metadata: optional: true "@rollup/rollup-win32-ia32-msvc": optional: true + "@rollup/rollup-win32-x64-gnu": + optional: true "@rollup/rollup-win32-x64-msvc": optional: true fsevents: optional: true bin: rollup: dist/bin/rollup - checksum: 10/2461be4ae1c8aa65cad1fdda4e2681ed14e65e19cf0ba74347746e92a4519d6cf4cf005e609b17a42bc628da30dbd1aa195e5b2e91d9fdc49c7a8b7a4ab5c274 + checksum: 10/ca5e2ba511d29accc0a3e02546d777a95748b757040f907e8c58c236b507f8791ab18dbe7a86085c1e7746c8c246a94a1f1895d9e29404e30be46cf1a7405dce languageName: node linkType: hard @@ -2031,11 +2064,11 @@ __metadata: linkType: hard "semver@npm:^7.3.5": - version: 7.7.2 - resolution: "semver@npm:7.7.2" + version: 7.7.3 + resolution: "semver@npm:7.7.3" bin: semver: bin/semver.js - checksum: 10/7a24cffcaa13f53c09ce55e05efe25cd41328730b2308678624f8b9f5fc3093fc4d189f47950f0b811ff8f3c3039c24a2c36717ba7961615c682045bf03e1dda + checksum: 10/8dbc3168e057a38fc322af909c7f5617483c50caddba135439ff09a754b20bdd6482a5123ff543dad4affa488ecf46ec5fb56d61312ad20bb140199b88dfaea9 languageName: node linkType: hard @@ -2192,11 +2225,11 @@ __metadata: linkType: hard "strip-literal@npm:^3.0.0": - version: 3.0.0 - resolution: "strip-literal@npm:3.0.0" + version: 3.1.0 + resolution: "strip-literal@npm:3.1.0" dependencies: js-tokens: "npm:^9.0.1" - checksum: 10/da1616f654f3ff481e078597b4565373a5eeed78b83de4a11a1a1b98292a9036f2474e528eff19b6eed93370428ff957a473827057c117495086436725d7efad + checksum: 10/6eb00906a1c343a1050579d1d6023e067a2d72152edb92e64cad49535115beb2e77905ace24aa459f29b66e75edba75ef9d8eca90575b0322640d64a5d37e131 languageName: node linkType: hard @@ -2210,16 +2243,15 @@ __metadata: linkType: hard "tar@npm:^7.4.3": - version: 7.4.3 - resolution: "tar@npm:7.4.3" + version: 7.5.1 + resolution: "tar@npm:7.5.1" dependencies: "@isaacs/fs-minipass": "npm:^4.0.0" chownr: "npm:^3.0.0" minipass: "npm:^7.1.2" - minizlib: "npm:^3.0.1" - mkdirp: "npm:^3.0.1" + minizlib: "npm:^3.1.0" yallist: "npm:^5.0.0" - checksum: 10/12a2a4fc6dee23e07cc47f1aeb3a14a1afd3f16397e1350036a8f4cdfee8dcac7ef5978337a4e7b2ac2c27a9a6d46388fc2088ea7c80cb6878c814b1425f8ecf + checksum: 10/4848cd2fa2fcaf0734cf54e14bc685056eb43a74d7cc7f954c3ac88fea88c85d95b1d7896619f91aab6f2234c5eec731c18aaa201a78fcf86985bdc824ed7a00 languageName: node linkType: hard @@ -2262,9 +2294,9 @@ __metadata: linkType: hard "tinyspy@npm:^4.0.3": - version: 4.0.3 - resolution: "tinyspy@npm:4.0.3" - checksum: 10/b6a3ed40dd76a2b3c020250cf1401506b456509d1fb9dba0c7b0e644d258dac722843b85c57ccc36c8687db1e7978cb6adcc43e3b71c475910c085b96d41cb53 + version: 4.0.4 + resolution: "tinyspy@npm:4.0.4" + checksum: 10/858a99e3ded2fba8fe7c243099d9e58e926d6525af03d19cdf86c1a9a30398161fb830b4f77890d266bcc1c69df08fa6f4baf29d089385e4cdaa98d7b6296e7c languageName: node linkType: hard @@ -2386,7 +2418,7 @@ __metadata: languageName: node linkType: hard -"typescript@npm:^5.9.3": +"typescript@npm:^5.8.2, typescript@npm:^5.9.3": version: 5.9.3 resolution: "typescript@npm:5.9.3" bin: @@ -2396,7 +2428,7 @@ __metadata: languageName: node linkType: hard -"typescript@patch:typescript@npm%3A^5.9.3#optional!builtin": +"typescript@patch:typescript@npm%3A^5.8.2#optional!builtin, typescript@patch:typescript@npm%3A^5.9.3#optional!builtin": version: 5.9.3 resolution: "typescript@patch:typescript@npm%3A5.9.3#optional!builtin::version=5.9.3&hash=5786d5" bin: @@ -2454,8 +2486,8 @@ __metadata: linkType: hard "vite@npm:^5.0.0 || ^6.0.0 || ^7.0.0-0": - version: 7.1.5 - resolution: "vite@npm:7.1.5" + version: 7.1.9 + resolution: "vite@npm:7.1.9" dependencies: esbuild: "npm:^0.25.0" fdir: "npm:^6.5.0" @@ -2504,7 +2536,7 @@ __metadata: optional: true bin: vite: bin/vite.js - checksum: 10/59edeef7e98757a668b2ad8a1731a5657fa83e22a165a36b7359225ea98a9be39b2f486710c0cf5085edb85daee7c8b6b6b0bd85d0ef32a1aa84aef71aabd0f0 + checksum: 10/361b8dfea414233927761fdb63ec38c2cc4630007efc33a7a02ef9a5c033552c41b2e95f7fcc9acf427da5a3cf194d62dc5f0853bc31e940a13528d0fb180132 languageName: node linkType: hard