From 99c541a0619cd844cc246161ccadc152bd3e0d36 Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 13 Jun 2025 09:00:37 +0300 Subject: [PATCH] fix: fix indeterministic --- .cweb-config/dapp-ecosystem-test.yaml | 9 +- .env.yarn | 2 +- package.json | 2 +- .../src/offchain/shared/constants.ts | 2 +- .../src/onchain/__tests__/handlers.test.ts | 94 +++++++++++++++++++ .../src/onchain/__tests__/helpers.ts | 35 +++++++ packages/contract.cm/vitest.config.ts | 14 +++ packages/cwait/src/onchain/context/context.ts | 4 +- .../cwait/src/onchain/context/extractOps.ts | 8 +- .../constructTx/prepareInThreadTxs.ts | 47 ++++++---- .../constructTx/prepareOutThreadTxs.ts | 1 + .../cwait/src/onchain/executor/executor.ts | 6 +- .../cwait/src/onchain/features/cwait/cwait.ts | 37 +++----- .../cwait/src/onchain/features/mutex/lock.ts | 4 +- .../cwait/src/onchain/features/ops/read.ts | 6 +- .../mutex/calls/constructUnlockCall.ts | 5 +- .../cwait/src/onchain/mutex/methods/unlock.ts | 8 +- .../cwait/src/onchain/runtime/awaitedTasks.ts | 13 --- .../cwait/src/onchain/runtime/execLoop.ts | 21 ++++- .../cwait/src/onchain/runtime/resolvedOps.ts | 8 +- packages/cwait/src/types.ts | 6 +- packages/ui/.env | 2 +- yarn.lock | 10 +- 23 files changed, 256 insertions(+), 88 deletions(-) create mode 100644 packages/contract.cm/src/onchain/__tests__/handlers.test.ts create mode 100644 packages/contract.cm/src/onchain/__tests__/helpers.ts create mode 100644 packages/contract.cm/vitest.config.ts diff --git a/.cweb-config/dapp-ecosystem-test.yaml b/.cweb-config/dapp-ecosystem-test.yaml index 779766e..dd5f4a7 100644 --- a/.cweb-config/dapp-ecosystem-test.yaml +++ b/.cweb-config/dapp-ecosystem-test.yaml @@ -14,9 +14,11 @@ includes: blake3: 321b1f88930aead7fe47961c8664005b3441f6502824769bf21eb06c3e5aaba4 use: - - dex-app.cm v0.0.67+test - - eth_offer_maker 0.0.67+devnet - - jump-listener.cm v0.0.5 + - jump-listener.cm v0.1.8 + - jump_listener_devnet v0.1.8 + - jump-forwarder.cm v0.1.5 + - jump_forwarder_devnet v0.1.5 + interpreters: {} @@ -31,3 +33,4 @@ contract_instances: template: contract.cm v0.0.1 parameters: content: [] + diff --git a/.env.yarn b/.env.yarn index 0e3587e..0efe5b1 100644 --- a/.env.yarn +++ b/.env.yarn @@ -4,4 +4,4 @@ NPM_PASSWORD= # This is needed when working with WASM modules (nodeLinker: node-modules is mandatory) NODE_OPTIONS="--experimental-wasm-modules --no-warnings=ExperimentalWarning --max-old-space-size=8192" -REGISTRATION_PROFILE=devnet +REGISTRATION_PROFILE=test diff --git a/package.json b/package.json index 0e00e51..0a9303c 100644 --- a/package.json +++ b/package.json @@ -47,7 +47,7 @@ "@coinweb/self-register": "0.1.3", "@coinweb/claims-client": "0.1.6-debug", "@coinweb/contract-kit": "0.2.6", - "@coinweb/testing-sdk": "0.0.9-remote", + "@coinweb/testing-sdk": "0.0.10-mutex", "@coinweb/minimal-sdk": "1.2.21" }, "devDependencies": { diff --git a/packages/contract.cm/src/offchain/shared/constants.ts b/packages/contract.cm/src/offchain/shared/constants.ts index ddb5d35..c28e3bb 100644 --- a/packages/contract.cm/src/offchain/shared/constants.ts +++ b/packages/contract.cm/src/offchain/shared/constants.ts @@ -9,5 +9,5 @@ export enum PUBLIC_METHODS { } export const FEE = { - ADD_WORD: 100000n, + ADD_WORD: 10000000n, }; diff --git a/packages/contract.cm/src/onchain/__tests__/handlers.test.ts b/packages/contract.cm/src/onchain/__tests__/handlers.test.ts new file mode 100644 index 0000000..b8cd823 --- /dev/null +++ b/packages/contract.cm/src/onchain/__tests__/handlers.test.ts @@ -0,0 +1,94 @@ +import path from 'node:path'; + +import { constructContractIssuer, constructContractRef, type PreparedCallInfo } from '@coinweb/contract-kit'; +import { UnitTest, type ExecInfo, type RoundInfo, type UnitTestContext, type DbWriteOp } from '@coinweb/testing-sdk'; +import { describe, it, expect, beforeAll } from 'vitest'; + +import { AddWordArgs, FEE, PUBLIC_METHODS } from '../../offchain/shared'; + +import { CONTRACT_INSTANCE_ID, TEST_PRIVATE_KEY, TEST_PUBLIC_KEY, waitRoundsCount } from './helpers'; + +const addWordRound = (creatorAccount: Buffer): RoundInfo => { + const self = constructContractIssuer(CONTRACT_INSTANCE_ID); + + const kvDeleteCallInfo: PreparedCallInfo = { + ref: constructContractRef(self, []), + methodInfo: { methodName: PUBLIC_METHODS.ADD_WORD, methodArgs: ['TEST_HELLO'] satisfies AddWordArgs }, + contractArgs: [], + contractInfo: { providedCweb: FEE.ADD_WORD, authenticated: null }, // null = authenticated account in tests + }; + + const withFunds = { type: { privateKey: creatorAccount } }; + + return { + txsInfo: { + // @ts-ignore + txs: [{ callInfo: kvDeleteCallInfo, withFunds }], + l1_events: [], + }, + claims: [], + blocks_on: [], + }; +}; + +const runTest = async (context: UnitTestContext, input: ExecInfo, checkFn: (results: DbWriteOp[]) => void) => { + const test = new UnitTest(context); + await test.load(path.join(import.meta.dirname, '../../../tests_data/state.json')); + await test.run(input, { checkFn }); +}; + +describe('PUBLIC_METHODS.ADD_WORD Tests', () => { + let creatorAccount: { publicKey: Buffer; privateKey: Buffer }; + + beforeAll(() => { + creatorAccount = { + privateKey: Buffer.from(TEST_PRIVATE_KEY, 'hex'), + publicKey: Buffer.from(TEST_PUBLIC_KEY, 'hex'), + }; + }); + + it('Should successfully execute', async () => { + const input: ExecInfo = { + rounds: [...waitRoundsCount(50), addWordRound(creatorAccount.privateKey), ...waitRoundsCount(2)], + }; + + const checkFn = (results: DbWriteOp[]) => { + expect(results).toBeDefined(); + + // @ts-ignore + // const errors = findAllValuesRecursivelyByKey<{ ContractError: ContractError }>(results, 'Invalid'); + // expect(errors.length).toBe(0); + + // @ts-ignore + // const storeOps = findAllValuesRecursivelyByKey(results, 'StoreOp'); + // expect(storeOps.length).toBeGreaterThan(0); + + // const existingPayload = storeOps.find((s) => isEqual(s.key.first_part, createFirstPartDefault())); + + // expect(existingPayload).toBeDefined(); + // expect(existingPayload?.key).toStrictEqual(createDefaultKey(Number.MAX_SAFE_INTEGER)); + // expect(existingPayload?.key.first_part).toStrictEqual(createFirstPartDefault()); + + // const { id, data, owner, tx } = existingPayload?.body as StoreKvPayload; + // expect(id).toBeDefined(); + // expect(data).toBeDefined(); + // expect(owner).toBeDefined(); + // expect(tx).toBeDefined(); + // expect(tx?.timestamp).toBeDefined(); + // expect(tx?.coinwebTxId).toBeDefined(); + // expect(Number(id)).toStrictEqual(Number.MAX_SAFE_INTEGER); + // expect(owner).toStrictEqual({ + // auth: 'EcdsaContract', + // payload: TEST_PUBLIC_KEY, + // }); + }; + + const context: UnitTestContext = { + name: 'add_word', + testPath: path.join(import.meta.dirname, './tests_data/add_word'), + verbose: true, + }; + + await runTest(context, input, checkFn); + }); +}); diff --git a/packages/contract.cm/src/onchain/__tests__/helpers.ts b/packages/contract.cm/src/onchain/__tests__/helpers.ts new file mode 100644 index 0000000..47473e4 --- /dev/null +++ b/packages/contract.cm/src/onchain/__tests__/helpers.ts @@ -0,0 +1,35 @@ +import path from 'node:path'; + +import { getInstanceFromIndex, type RoundInfo } from '@coinweb/testing-sdk'; + +const onchainPackage = (await import('../../../dist/out/package.json')) as { + name: string; +}; + +export const CONTRACT_INSTANCE = await getInstanceFromIndex({ + path: path.resolve(import.meta.dirname, '../../../tests_data/index.yaml'), + instance_alias: 'cwait-contract 0.0.1-test', +}); + +export const CONTRACT_INSTANCE_ID = '0x'.concat(CONTRACT_INSTANCE.instance_id.replace(/0x/, '')); + +export const CONTRACT_TEMPLATE_ID = `0x${onchainPackage.name.substring(5)}`; + +export const TEST_PRIVATE_KEY = '31c70848e4e3aaffcf91f134853ec966e913aa9a813115bcb81512e7625f46a9'; +export const TEST_PUBLIC_KEY = '03951f89fe78e13f295d96eb7afa1e0da726df7d58f9c84f7144e5febc30efeec4'; + +export const waitRoundsCount = (roundsCount: number): RoundInfo[] => { + const emptyRound: RoundInfo = { + txsInfo: { + txs: [], + l1_events: [], + }, + claims: [], + blocks_on: [], + }; + const rounds: RoundInfo[] = []; + for (let i = 0; i < roundsCount; i++) { + rounds.push(emptyRound); + } + return rounds; +}; diff --git a/packages/contract.cm/vitest.config.ts b/packages/contract.cm/vitest.config.ts new file mode 100644 index 0000000..a7b2399 --- /dev/null +++ b/packages/contract.cm/vitest.config.ts @@ -0,0 +1,14 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + globals: true, + environment: 'node', + reporters: 'default', + silent: false, + testTimeout: 240000, + teardownTimeout: 240000, + hookTimeout: 240000, + pool: 'forks', + }, +}); diff --git a/packages/cwait/src/onchain/context/context.ts b/packages/cwait/src/onchain/context/context.ts index d140bc0..8cedf7e 100644 --- a/packages/cwait/src/onchain/context/context.ts +++ b/packages/cwait/src/onchain/context/context.ts @@ -58,7 +58,7 @@ export const handleContext = (ctx: Context) => { caller, thisId, parentId, - saveResult, + shouldSaveResult, takenFundsIds = [], execOpsIndexes = [], ] = getMethodArgs(); @@ -70,7 +70,7 @@ export const handleContext = (ctx: Context) => { initialContext.parentId = parentId; initialContext.methodName = methodName; initialContext.initialArgs = initialArgs ?? []; - initialContext.needSaveResult = saveResult ?? false; + initialContext.needSaveResult = shouldSaveResult ?? false; const { authInfo } = getCallParameters(getRawContext()); initialContext.user = (authInfo && extractUser(authInfo)) ?? caller ?? null; diff --git a/packages/cwait/src/onchain/context/extractOps.ts b/packages/cwait/src/onchain/context/extractOps.ts index 1d3d302..2bb917e 100644 --- a/packages/cwait/src/onchain/context/extractOps.ts +++ b/packages/cwait/src/onchain/context/extractOps.ts @@ -86,7 +86,11 @@ export const extractOps = ({ throw new Error('Wrong subcall result'); } - extractedOps.push({ ChildOp: 0 }, ...(nextAfterBlock.TakeOp.result as TypedClaim).body); + extractedOps.push({ + ChildOp: { + ops: (nextAfterBlock.TakeOp.result as TypedClaim).body, + }, + }); i += 2; continue; @@ -112,7 +116,7 @@ export const extractOps = ({ throw new Error('Wrong mutex unlock result'); } - extractedOps.push({ SlotOp: { ok: true } }); + extractedOps.push({ UnlockOp: 0 }); i += 2; continue; diff --git a/packages/cwait/src/onchain/executor/constructTx/prepareInThreadTxs.ts b/packages/cwait/src/onchain/executor/constructTx/prepareInThreadTxs.ts index 5f6c179..af304d7 100644 --- a/packages/cwait/src/onchain/executor/constructTx/prepareInThreadTxs.ts +++ b/packages/cwait/src/onchain/executor/constructTx/prepareInThreadTxs.ts @@ -11,7 +11,7 @@ import { } from '@coinweb/contract-kit'; import { CwebTake } from '@coinweb/contract-kit/dist/types/operations/take'; -import { ExecutorMethodArgs, PreparedExtendedStoreOp, PreparedOp, ResolvedOp, ResolvedSlotOp } from '../../../types'; +import { ExecutorMethodArgs, PreparedExtendedStoreOp, PreparedOp, ResolvedSlotOp } from '../../../types'; import { constructFundsClaimRangRead, constructFundsClaimStore } from '../../claims/funds'; import { constructResultBlockFilter, constructResultClaimTake, resultKey } from '../../claims/result'; import { context, getRawContext } from '../../context'; @@ -68,16 +68,18 @@ export const prepareInThreadTxs = ({ let callsPrepared = 0; const resolvedSlotOps = new Array(outThreadTasksCount).fill({ SlotOp: { ok: true } }) satisfies ResolvedSlotOp[]; + //Mutex exec ops const preparedExecOps: (PreparedExtendedStoreOp | GTake)[] = []; const excOpsIndexes: number[] = []; - const resolvedChildOps: ResolvedOp[] = [...context.ops, ...resolvedSlotOps]; + // const resolvedChildOps: ResolvedOp[] = [...context.ops, ...resolvedSlotOps]; + //Children //Arg for the main call const callArgs: PreparedOperation[] = []; - //Info for separate child call - const childCalls: FullCallInfo[] = []; + //Info for separate parallel calls + const parallelCalls: FullCallInfo[] = []; const outThreadOps: PreparedOperation[] = []; @@ -89,14 +91,20 @@ export const prepareInThreadTxs = ({ callArgs.push(constructBlock([constructResultBlockFilter(id)]), constructResultClaimTake(id)); txFee += 200n; - childCalls.push({ + const childOps = [ + ...context.ops, + ...resolvedSlotOps, + ...ops.map((_, j) => (i === j ? { ExecOp: { id } } : { SlotOp: { ok: true } })), + ]; + + parallelCalls.push({ callInfo: { ref: constructContractRef(context.issuer, []), methodInfo: { methodName: context.methodName, methodArgs: [ context.initialArgs, - [...resolvedChildOps, { ExecOp: { id } }], + childOps, context.user, id, context.thisId, @@ -151,7 +159,7 @@ export const prepareInThreadTxs = ({ processId: context.thisId, }); - childCalls.push({ callInfo }); + parallelCalls.push({ callInfo }); callArgs.push(...inThreadOps); txFee += fee; @@ -160,14 +168,15 @@ export const prepareInThreadTxs = ({ break; } case isPreparedUnlockOp(op): { + console.log('prepareInThreadTxs >>> unlockOp'); const { callInfo, fee, ops } = constructUnlockCall( context.issuer, op.UnlockOp.lockId, op.UnlockOp.timestamp, - false + true ); - childCalls.push({ callInfo }); + parallelCalls.push({ callInfo }); callArgs.push(...ops); txFee += fee; @@ -177,8 +186,6 @@ export const prepareInThreadTxs = ({ callArgs.push(op); txFee += 100n; } - - resolvedChildOps.push({ SlotOp: { ok: true } }); }); if (preparedExecOps.length > 0) { @@ -191,9 +198,12 @@ export const prepareInThreadTxs = ({ execId, }); - childCalls.push({ callInfo }); + parallelCalls.push({ callInfo }); txFee += fee; + console.log(txFee); + console.log(context.funds.availableCweb); + callArgs.push(...ops); } @@ -206,6 +216,7 @@ export const prepareInThreadTxs = ({ if ('StoreOp' in latestCallArg && (latestCallArg.StoreOp.key.first_part as [string])[0] === resultKey) { //SAVE RESULT CLAIMS + console.log('SAVE RESULT CLAIMS'); if (callArgs.length > 1) { throw new Error('Unexpected count of result ops'); } @@ -221,10 +232,12 @@ export const prepareInThreadTxs = ({ const cwebToStore = availableCweb + storedCweb - BigInt(takeOps.length) * 100n - 500n; + console.log('cwebToStore: ', cwebToStore); + resultOps.push( - constructFundsClaimStore(context.parentId, cwebToStore), + passCwebFrom(context.issuer, availableCweb), ...takeOps, - passCwebFrom(context.issuer, availableCweb) + constructFundsClaimStore(context.parentId, cwebToStore) ); } @@ -235,6 +248,8 @@ export const prepareInThreadTxs = ({ txFee += 800n + outThreadFee + BigInt(takeOps.length) * 100n; callsPrepared++; + console.log('provided cweb: ', cwebPerCall - txFee + storedCweb); + returnTxs.push( constructContinueTx( getRawContext(), @@ -267,8 +282,8 @@ export const prepareInThreadTxs = ({ ) ); - if (childCalls.length || outThreadOps.length) { - returnTxs.push(constructContinueTx(getRawContext(), outThreadOps, childCalls)); + if (parallelCalls.length || outThreadOps.length) { + returnTxs.push(constructContinueTx(getRawContext(), outThreadOps, parallelCalls)); } } } diff --git a/packages/cwait/src/onchain/executor/constructTx/prepareOutThreadTxs.ts b/packages/cwait/src/onchain/executor/constructTx/prepareOutThreadTxs.ts index d3096f3..419285e 100644 --- a/packages/cwait/src/onchain/executor/constructTx/prepareOutThreadTxs.ts +++ b/packages/cwait/src/onchain/executor/constructTx/prepareOutThreadTxs.ts @@ -95,6 +95,7 @@ export const prepareOutThreadTxs = ({ break; } case isPreparedUnlockOp(op): { + console.log('prepareOutThreadTxs >>> unlockOp'); const { callInfo, fee } = constructUnlockCall(context.issuer, op.UnlockOp.lockId, op.UnlockOp.timestamp, false); preparedCalls.push({ callInfo }); diff --git a/packages/cwait/src/onchain/executor/executor.ts b/packages/cwait/src/onchain/executor/executor.ts index 6a31392..8c396f8 100644 --- a/packages/cwait/src/onchain/executor/executor.ts +++ b/packages/cwait/src/onchain/executor/executor.ts @@ -18,7 +18,7 @@ export const executor = (method: (...args: any[]) => Promise) => { } if (shouldRestart) { - console.log('<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< executor-finish'); + console.log('<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< executor-finish-restart'); return constructTx(false); } @@ -29,9 +29,9 @@ export const executor = (method: (...args: any[]) => Promise) => { }, 0); try { - setNextExec(() => method(...context.initialArgs)); + setNextExec(() => method(...context.initialArgs), []); const isFullyExecuted = await execLoop(); - console.log('<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< executor-finish'); + console.log('<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< executor-finish-end'); return constructTx(isFullyExecuted); } catch (error) { console.log((error as Error).message); diff --git a/packages/cwait/src/onchain/features/cwait/cwait.ts b/packages/cwait/src/onchain/features/cwait/cwait.ts index 93ee765..e8f1870 100644 --- a/packages/cwait/src/onchain/features/cwait/cwait.ts +++ b/packages/cwait/src/onchain/features/cwait/cwait.ts @@ -3,19 +3,8 @@ import { constructStore } from '@coinweb/contract-kit/dist/esm/operations/store' import { constructResultClaim } from '../../claims/result'; import { opMarker } from '../../globals/promise'; import { setNextExec, stopExecution } from '../../runtime'; -import { - freezeAwaitedTasks, - getAwaitedTasksCount, - pushAwaitedTask, - unfreezeAwaitedTasks, -} from '../../runtime/awaitedTasks'; -import { - freezeResolvedOps, - getUsedOps, - saveUsedOps, - shiftResolvedOp, - unfreezeResolvedOps, -} from '../../runtime/resolvedOps'; +import { getAwaitedTasksCount, pushAwaitedTask } from '../../runtime/awaitedTasks'; +import { getUsedOps, startSavingUsedOps, stopSavingUsedOps, shiftResolvedOp } from '../../runtime/resolvedOps'; import { isResolvedChildOp, isResolvedExecOp, isResolvedSlotOp } from '../../utils'; import { uuid } from '../../utils'; @@ -55,32 +44,28 @@ export const cwait = Promise { - unfreezeAwaitedTasks(); - unfreezeResolvedOps(); - - saveUsedOps(); - + startSavingUsedOps(); await asyncCallback(...args); + stopSavingUsedOps(); if (!getAwaitedTasksCount()) { + console.log('push result claim'); + pushAwaitedTask(constructStore(constructResultClaim(op.ExecOp.id, getUsedOps()))); } - }); + }, []); - stopExecution(); + stopExecution(); //Check: maybe does no affect - return new Promise(() => null); + return; } if (isResolvedChildOp(op)) { - return asyncCallback(...args); + return setNextExec(() => asyncCallback(...args), op.ChildOp.ops); } - throw new Error('Read operation not found'); + throw new Error('Exec or Child operation not found'); } }) as TAsyncCallback; }; diff --git a/packages/cwait/src/onchain/features/mutex/lock.ts b/packages/cwait/src/onchain/features/mutex/lock.ts index fccbe2a..75f99d6 100644 --- a/packages/cwait/src/onchain/features/mutex/lock.ts +++ b/packages/cwait/src/onchain/features/mutex/lock.ts @@ -2,11 +2,11 @@ import { getTime } from 'lib/onchain'; import { opMarker } from '../../globals/promise'; import { LockedKey } from '../../mutex'; -import { isResolvedLockOp, isResolvedSlotOp, isResolvedUnlockOp, uuid } from '../../utils'; import { pushAwaitedTask, shiftResolvedOp } from '../../runtime'; +import { isResolvedLockOp, isResolvedSlotOp, isResolvedUnlockOp, uuid } from '../../utils'; const unlock = (lockId: string, timestamp: number) => { - console.log('lockOp'); + console.log('unlockOp'); let opMarkerValue = false; const result = new Promise((resolve, reject) => { diff --git a/packages/cwait/src/onchain/features/ops/read.ts b/packages/cwait/src/onchain/features/ops/read.ts index d5477b8..0b07821 100644 --- a/packages/cwait/src/onchain/features/ops/read.ts +++ b/packages/cwait/src/onchain/features/ops/read.ts @@ -3,8 +3,8 @@ import { Claim, ClaimKey, constructRead, extractRead } from '@coinweb/contract-k import { TypedClaim } from '../../../types'; import { context } from '../../context'; import { opMarker } from '../../globals/promise'; -import { isResolvedReadOp, isResolvedSlotOp } from '../../utils'; import { pushAwaitedTask, shiftResolvedOp } from '../../runtime'; +import { isResolvedReadOp, isResolvedSlotOp } from '../../utils'; export const readOp = (key: ClaimKey) => { let opMarkerValue = false; @@ -22,10 +22,14 @@ export const readOp = (key: ClaimKey) => { } if (!isResolvedReadOp(op)) { + console.log(JSON.stringify(op)); + throw new Error('Read operation not found'); } const claim = (extractRead(op)?.[0]?.content ?? null) as TClaim | null; + + console.log('ResolveRead claim: ', claim); resolve(claim); } } catch (error) { diff --git a/packages/cwait/src/onchain/mutex/calls/constructUnlockCall.ts b/packages/cwait/src/onchain/mutex/calls/constructUnlockCall.ts index 72b6ec6..48d102c 100644 --- a/packages/cwait/src/onchain/mutex/calls/constructUnlockCall.ts +++ b/packages/cwait/src/onchain/mutex/calls/constructUnlockCall.ts @@ -2,6 +2,7 @@ import { constructContractRef, ContractIssuer } from '@coinweb/contract-kit'; import { constructMutexBlockUnlockClaimTake, constructMutexUnlockBlock } from '../claims'; import { unlockMethodName } from '../methods'; +import { unlockFee } from '../settings'; import { MutexUnlockArgs } from '../types'; export const constructUnlockCall = (issuer: ContractIssuer, ...[lockId, timestamp, notify]: MutexUnlockArgs) => { @@ -13,7 +14,7 @@ export const constructUnlockCall = (issuer: ContractIssuer, ...[lockId, timestam methodArgs: [lockId, timestamp] satisfies MutexUnlockArgs, }, contractInfo: { - providedCweb: 1000n, + providedCweb: unlockFee, authenticated: null, }, contractArgs: [], @@ -21,6 +22,6 @@ export const constructUnlockCall = (issuer: ContractIssuer, ...[lockId, timestam ops: notify ? ([constructMutexUnlockBlock(lockId, issuer), constructMutexBlockUnlockClaimTake(lockId)] as const) : [], - fee: 1200n, + fee: unlockFee + (notify ? 1000n : 800n), }; }; diff --git a/packages/cwait/src/onchain/mutex/methods/unlock.ts b/packages/cwait/src/onchain/mutex/methods/unlock.ts index c98abfe..9b6ab01 100644 --- a/packages/cwait/src/onchain/mutex/methods/unlock.ts +++ b/packages/cwait/src/onchain/mutex/methods/unlock.ts @@ -1,7 +1,7 @@ import { constructContinueTx, constructContractRef, constructTake, Context, passCwebFrom } from '@coinweb/contract-kit'; import { getCallParameters, getContractIssuer, getMethodArguments } from 'lib/onchain'; -import { constructMutexLockClaimKey } from '../claims'; +import { constructMutexBlockUnlockClaimStore, constructMutexLockClaimKey } from '../claims'; import { lockFee } from '../settings'; import { MutexUnlockArgs } from '../types'; @@ -16,7 +16,11 @@ export const mutexUnlock = (context: Context) => { return [ constructContinueTx( context, - [passCwebFrom(issuer, availableCweb), constructTake(constructMutexLockClaimKey(lockId, timestamp))], + [ + passCwebFrom(issuer, availableCweb), + constructTake(constructMutexLockClaimKey(lockId, timestamp)), + constructMutexBlockUnlockClaimStore(lockId), + ], [ { callInfo: { diff --git a/packages/cwait/src/onchain/runtime/awaitedTasks.ts b/packages/cwait/src/onchain/runtime/awaitedTasks.ts index d848ccc..f2b3934 100644 --- a/packages/cwait/src/onchain/runtime/awaitedTasks.ts +++ b/packages/cwait/src/onchain/runtime/awaitedTasks.ts @@ -1,13 +1,8 @@ import { PreparedOp, Task } from '../../types'; const awaitedTasks: Task[] = []; -let isFreezed = false; export const pushAwaitedTask = (op: PreparedOp) => { - if (isFreezed) { - return; - } - awaitedTasks.push({ op, batchId: -1 }); }; @@ -20,11 +15,3 @@ export const markTaskBatch = (count: number, batchId: number) => { }; export const getAwaitedTasksCount = () => awaitedTasks.length; - -export const freezeAwaitedTasks = () => { - isFreezed = true; -}; - -export const unfreezeAwaitedTasks = () => { - isFreezed = false; -}; diff --git a/packages/cwait/src/onchain/runtime/execLoop.ts b/packages/cwait/src/onchain/runtime/execLoop.ts index da06ae4..6630337 100644 --- a/packages/cwait/src/onchain/runtime/execLoop.ts +++ b/packages/cwait/src/onchain/runtime/execLoop.ts @@ -1,3 +1,7 @@ +import { ResolvedOp } from '../../types'; + +import { pushResolvedOp } from './resolvedOps'; + let abortExecution: ((isFullyExecuted: boolean) => void) | null = null; export const stopExecution = (isFullyExecuted = false) => { @@ -5,10 +9,18 @@ export const stopExecution = (isFullyExecuted = false) => { abortExecution?.(isFullyExecuted); }; -type ExecTask = () => Promise; +type ExecTask = () => Promise; +type Exec = { task: ExecTask; ops: ResolvedOp[] }; -let execQueue: ExecTask[] = []; -export const setNextExec = (task: ExecTask) => (execQueue = [task]); +const execQueue: Exec[] = []; +export const setNextExec = (task: ExecTask, ops: ResolvedOp[]) => { + return new Promise((resolve, reject) => { + execQueue.push({ + task: () => task().then(resolve, reject), + ops, + }); + }); +}; export const execLoop = async (): Promise => { const nextExec = execQueue.pop(); @@ -17,7 +29,8 @@ export const execLoop = async (): Promise => { const execution = new Promise((resolve, reject) => { abortExecution = resolve; - nextExec().then( + pushResolvedOp(nextExec.ops); + nextExec.task().then( () => { resolve(true); }, diff --git a/packages/cwait/src/onchain/runtime/resolvedOps.ts b/packages/cwait/src/onchain/runtime/resolvedOps.ts index 1cb6a4f..96db33c 100644 --- a/packages/cwait/src/onchain/runtime/resolvedOps.ts +++ b/packages/cwait/src/onchain/runtime/resolvedOps.ts @@ -36,16 +36,22 @@ export const shiftResolvedOp = () => { usedOps.push(result.op); } + console.log('shiftResolvedOp: ', JSON.stringify(result)); + return result; }; export const getUsedOps = () => usedOps; -export const saveUsedOps = () => { +export const startSavingUsedOps = () => { usedOps = []; isSavingUsed = true; }; +export const stopSavingUsedOps = () => { + isSavingUsed = false; +}; + export const freezeResolvedOps = () => { isFreezed = true; }; diff --git a/packages/cwait/src/types.ts b/packages/cwait/src/types.ts index 45fc95c..ca22d77 100644 --- a/packages/cwait/src/types.ts +++ b/packages/cwait/src/types.ts @@ -37,7 +37,9 @@ export type ResolvedExecOp = { }; export type ResolvedChildOp = { - ChildOp: 0; + ChildOp: { + ops: ResolvedOp[]; + }; }; export type ResolvedLockOp = { @@ -100,7 +102,7 @@ export type ExecutorMethodArgs = [ caller?: User, thisId?: string, parentId?: string, - saveResult?: boolean, + shouldSaveResult?: boolean, takenFundsIds?: string[], execOpsIndexes?: number[], ]; diff --git a/packages/ui/.env b/packages/ui/.env index fc545dd..2b76c00 100644 --- a/packages/ui/.env +++ b/packages/ui/.env @@ -1,4 +1,4 @@ VITE_API_URL='https://api-cloud.coinweb.io/wallet' VITE_EXPLORER_URL='https://explorer.coinweb.io' -VITE_CONTRACT_ADDRESS="0xedca91a29553fa2466e89eef02ac06dc49ab68be5e81fa91d05765cab79ec02a" +VITE_CONTRACT_ADDRESS="0xc599810e4861b7b1ae25695d58eeef10556fc84f87f72fafa31c0921e86e92be" diff --git a/yarn.lock b/yarn.lock index 0e862fd..2bbc788 100644 --- a/yarn.lock +++ b/yarn.lock @@ -379,16 +379,16 @@ __metadata: languageName: node linkType: hard -"@coinweb/testing-sdk@npm:0.0.9-remote": - version: 0.0.9-remote - resolution: "@coinweb/testing-sdk@npm:0.0.9-remote" +"@coinweb/testing-sdk@npm:0.0.10-mutex": + version: 0.0.10-mutex + resolution: "@coinweb/testing-sdk@npm:0.0.10-mutex" dependencies: "@coinweb/contract-kit": "npm:0.2.0" - "@coinweb/minimal-sdk": "npm:1.2.19" + "@coinweb/minimal-sdk": "npm:1.2.18" json-stable-stringify: "npm:^1.1.1" lodash.isequal: "npm:^4.5.0" secp256k1: "npm:^5.0.0" - checksum: 10c0/80212780455d4bc2c1082d62ce382c6d07c6b5a107dc372f630c2257de686ec7e072c5d7a0b966e611b73f8b49ef9d6d8b68fbd66de1d968992a6b18444f218b + checksum: 10c0/84785ecc631510aa39189792826cff454700cba96d13d6c9f93f4166ee035fb1c8a8c0f2eab0f51d7d5fe127c661c7964792f8dfacffb9bec5eaf3a6da7fc744 languageName: node linkType: hard