From 02f81660a77341ed86c8a1c4772cd1fc202a7dae Mon Sep 17 00:00:00 2001 From: Alex Date: Sat, 26 Apr 2025 13:38:01 +0300 Subject: [PATCH] add: mutex calls --- .../mutex/calls/constructExecOpsCall.ts | 70 ++++++++ .../mutex/calls/constructGetAccessCall.ts | 26 --- .../onchain/mutex/calls/constructLockCall.ts | 4 +- .../onchain/mutex/calls/constructTryOpCall.ts | 23 --- .../mutex/calls/constructUnlockCall.ts | 4 +- .../cwait/src/onchain/mutex/calls/index.ts | 3 +- .../cwait/src/onchain/mutex/claims/index.ts | 2 +- .../onchain/mutex/claims/mutexBlockAccess.ts | 41 ----- .../src/onchain/mutex/claims/mutexExecOps.ts | 43 +++++ packages/cwait/src/onchain/mutex/index.ts | 13 +- .../src/onchain/mutex/methods/execOps.ts | 117 +++++++++++++ .../src/onchain/mutex/methods/getAccess.ts | 32 ---- .../cwait/src/onchain/mutex/methods/index.ts | 4 +- .../cwait/src/onchain/mutex/methods/names.ts | 5 +- .../mutex/methods/preReadExecTakeOps.ts | 49 ++++++ .../onchain/mutex/methods/saveExecOpResult.ts | 33 ++++ .../cwait/src/onchain/mutex/methods/tryOp.ts | 30 ---- packages/cwait/src/onchain/mutex/types.ts | 29 ++-- .../src/onchain/promisifiedOps/ops/store.ts | 4 +- .../onchain/utils/constructTx/constructTx.ts | 4 +- .../utils/constructTx/prepareInThreadTxs.ts | 155 ++++++++++++------ .../utils/constructTx/prepareOutThreadTxs.ts | 149 ++++++++++------- .../onchain/utils/constructTx/prepareTxs.ts | 38 +++-- .../onchain/utils/constructTx/splitTasks.ts | 64 +++----- .../cwait/src/onchain/utils/typeGuards.ts | 44 +++++ packages/cwait/src/types.ts | 33 +++- 26 files changed, 672 insertions(+), 347 deletions(-) create mode 100644 packages/cwait/src/onchain/mutex/calls/constructExecOpsCall.ts delete mode 100644 packages/cwait/src/onchain/mutex/calls/constructGetAccessCall.ts delete mode 100644 packages/cwait/src/onchain/mutex/calls/constructTryOpCall.ts delete mode 100644 packages/cwait/src/onchain/mutex/claims/mutexBlockAccess.ts create mode 100644 packages/cwait/src/onchain/mutex/claims/mutexExecOps.ts create mode 100644 packages/cwait/src/onchain/mutex/methods/execOps.ts delete mode 100644 packages/cwait/src/onchain/mutex/methods/getAccess.ts create mode 100644 packages/cwait/src/onchain/mutex/methods/preReadExecTakeOps.ts create mode 100644 packages/cwait/src/onchain/mutex/methods/saveExecOpResult.ts delete mode 100644 packages/cwait/src/onchain/mutex/methods/tryOp.ts diff --git a/packages/cwait/src/onchain/mutex/calls/constructExecOpsCall.ts b/packages/cwait/src/onchain/mutex/calls/constructExecOpsCall.ts new file mode 100644 index 0000000..af1d981 --- /dev/null +++ b/packages/cwait/src/onchain/mutex/calls/constructExecOpsCall.ts @@ -0,0 +1,70 @@ +import { constructContractRef, ContractIssuer, CwebStore, GStore, GTake } from '@coinweb/contract-kit'; +import { CwebTake } from '@coinweb/contract-kit/dist/types/operations/take'; + +import { PreparedExtendedStoreOp } from '../../../types'; +import { + constructMutexExecOpsBlock, + constructMutexExecOpsClaimTake, + constructMutexLockClaimRangeRead, +} from '../claims'; +import { execOpsMethodName } from '../methods'; +import { MutexExecOpArgs } from '../types'; + +export const constructExecOpsCall = ({ + issuer, + ops, + processId, + execId, +}: { + issuer: ContractIssuer; + ops: (GTake | PreparedExtendedStoreOp)[]; + processId: string; + execId?: string; +}) => { + const opsFee = BigInt(ops.length) * 100n; + const execCallInnerFee = 700n; + + const takeOpsAdditionalFee = BigInt(ops.filter((op) => 'TakeOp' in op).length) * 100n; + const prepareTakeOpsInnerFee = takeOpsAdditionalFee > 0n ? 700n : 0n; + + const storedCweb = ops + .filter((op) => 'StoreOp' in op) + .reduce((total, op) => { + if (op.providedCweb) { + return total + op.providedCweb; + } + return total + BigInt(op.StoreOp.fees_stored); + }, 0n); + + const callFee = 700n; + + const providedCweb = opsFee + execCallInnerFee + takeOpsAdditionalFee + prepareTakeOpsInnerFee + storedCweb + callFee; + const contractArgsFee = 100n; + + const preparedOps: (GTake | GStore)[] = ops.map((op) => { + if ('StoreOp' in op) { + const preparedStoreOp = { ...op }; + delete preparedStoreOp.providedCweb; + return preparedStoreOp; + } + + return op; + }); + + return { + callInfo: { + ref: constructContractRef(issuer, []), + methodInfo: { + methodName: execOpsMethodName, + methodArgs: [preparedOps, processId, execId] satisfies MutexExecOpArgs, + }, + contractInfo: { + providedCweb, + authenticated: null, + }, + contractArgs: [constructMutexLockClaimRangeRead(issuer)], + }, + fee: providedCweb + contractArgsFee, + ops: execId ? ([constructMutexExecOpsBlock(execId, issuer), constructMutexExecOpsClaimTake(execId)] as const) : [], + }; +}; diff --git a/packages/cwait/src/onchain/mutex/calls/constructGetAccessCall.ts b/packages/cwait/src/onchain/mutex/calls/constructGetAccessCall.ts deleted file mode 100644 index 88eec8b..0000000 --- a/packages/cwait/src/onchain/mutex/calls/constructGetAccessCall.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { ContractIssuer, constructContractRef } from '@coinweb/contract-kit'; - -import { constructMutexLockClaimRangeRead } from '../claims'; -import { getAccessMethodName } from '../methods'; -import { MutexGetAccessArgs } from '../types'; - -export const constructGetAccessCall = ( - issuer: ContractIssuer, - ...[claimKey, processId, uniqueId]: MutexGetAccessArgs -) => { - return { - callInfo: { - ref: constructContractRef(issuer, []), - methodInfo: { - methodName: getAccessMethodName, - methodArgs: [claimKey, processId, uniqueId] satisfies MutexGetAccessArgs, - }, - contractInfo: { - providedCweb: 800n, - authenticated: null, - }, - contractArgs: [constructMutexLockClaimRangeRead(issuer)], - }, - fee: 900n, - }; -}; diff --git a/packages/cwait/src/onchain/mutex/calls/constructLockCall.ts b/packages/cwait/src/onchain/mutex/calls/constructLockCall.ts index 4c2a69a..aee89c0 100644 --- a/packages/cwait/src/onchain/mutex/calls/constructLockCall.ts +++ b/packages/cwait/src/onchain/mutex/calls/constructLockCall.ts @@ -1,6 +1,6 @@ import { ContractIssuer, constructContractRef } from '@coinweb/contract-kit'; -import { constructMutexBlockAccessClaimTake, constructMutexLockBlock } from '../claims'; +import { constructMutexBlockLockClaimTake, constructMutexLockBlock } from '../claims'; import { lockMethodName } from '../methods'; import { lockFee } from '../settings'; @@ -18,7 +18,7 @@ export const constructLockCall = (issuer: ContractIssuer, lockId: string) => { }, contractArgs: [], }, - ops: [constructMutexLockBlock(lockId, issuer), constructMutexBlockAccessClaimTake(lockId)], + ops: [constructMutexLockBlock(lockId, issuer), constructMutexBlockLockClaimTake(lockId)] as const, fee: lockFee + 200n, }; }; diff --git a/packages/cwait/src/onchain/mutex/calls/constructTryOpCall.ts b/packages/cwait/src/onchain/mutex/calls/constructTryOpCall.ts deleted file mode 100644 index 04d6078..0000000 --- a/packages/cwait/src/onchain/mutex/calls/constructTryOpCall.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { constructContractRef, ContractIssuer } from '@coinweb/contract-kit'; - -import { constructMutexLockClaimRangeRead } from '../claims'; -import { tryOpMethodName } from '../methods'; -import { MutexTryOpArgs } from '../types'; - -export const constructTryOpCall = (issuer: ContractIssuer, ...[op, processId]: MutexTryOpArgs) => { - return { - callInfo: { - ref: constructContractRef(issuer, []), - methodInfo: { - methodName: tryOpMethodName, - methodArgs: [op, processId] satisfies MutexTryOpArgs, - }, - contractInfo: { - providedCweb: 800n, - authenticated: null, - }, - contractArgs: [constructMutexLockClaimRangeRead(issuer)], - }, - fee: 900n, - }; -}; diff --git a/packages/cwait/src/onchain/mutex/calls/constructUnlockCall.ts b/packages/cwait/src/onchain/mutex/calls/constructUnlockCall.ts index 4ce421d..72b6ec6 100644 --- a/packages/cwait/src/onchain/mutex/calls/constructUnlockCall.ts +++ b/packages/cwait/src/onchain/mutex/calls/constructUnlockCall.ts @@ -18,7 +18,9 @@ export const constructUnlockCall = (issuer: ContractIssuer, ...[lockId, timestam }, contractArgs: [], }, - ops: notify ? [constructMutexUnlockBlock(lockId, issuer), constructMutexBlockUnlockClaimTake(lockId)] : [], + ops: notify + ? ([constructMutexUnlockBlock(lockId, issuer), constructMutexBlockUnlockClaimTake(lockId)] as const) + : [], fee: 1200n, }; }; diff --git a/packages/cwait/src/onchain/mutex/calls/index.ts b/packages/cwait/src/onchain/mutex/calls/index.ts index 357971b..04c5a4f 100644 --- a/packages/cwait/src/onchain/mutex/calls/index.ts +++ b/packages/cwait/src/onchain/mutex/calls/index.ts @@ -1,4 +1,3 @@ -export * from './constructGetAccessCall'; export * from './constructLockCall'; -export * from './constructTryOpCall'; +export * from './constructExecOpsCall'; export * from './constructUnlockCall'; diff --git a/packages/cwait/src/onchain/mutex/claims/index.ts b/packages/cwait/src/onchain/mutex/claims/index.ts index c1f0350..588d5db 100644 --- a/packages/cwait/src/onchain/mutex/claims/index.ts +++ b/packages/cwait/src/onchain/mutex/claims/index.ts @@ -1,4 +1,4 @@ -export * from './mutexBlockAccess'; +export * from './mutexExecOps'; export * from './mutexBlockLock'; export * from './mutexBlockUnlock'; export * from './mutexLock'; diff --git a/packages/cwait/src/onchain/mutex/claims/mutexBlockAccess.ts b/packages/cwait/src/onchain/mutex/claims/mutexBlockAccess.ts deleted file mode 100644 index 8c02e2e..0000000 --- a/packages/cwait/src/onchain/mutex/claims/mutexBlockAccess.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { - BlockFilter, - constructBlock, - constructClaim, - constructClaimKey, - constructStore, - constructTake, - ContractIssuer, - CwebStore, - GStore, -} from '@coinweb/contract-kit'; - -import { MutexAccessResult, MutexAccessStatus } from '../types'; - -export const mutexBlockAccessKey = 'mutex_block_access'; - -export const constructMutexBlockAccessClaimKey = (uniqueId: string) => - constructClaimKey([mutexBlockAccessKey], [uniqueId]); - -export const constructMutexBlockAccessClaim = (uniqueId: string, status: MutexAccessStatus) => - constructClaim(constructMutexBlockAccessClaimKey(uniqueId), { status } satisfies MutexAccessResult, '0x0'); - -export const constructMutexBlockAccessClaimStore = (uniqueId: string, status: MutexAccessStatus): GStore => - constructStore(constructMutexBlockAccessClaim(uniqueId, status)); - -export const constructMutexBlockAccessClaimTake = (uniqueId: string) => - constructTake(constructMutexBlockAccessClaimKey(uniqueId)); - -export const constructMutexBlockAccessBlockFilter = (lockId: string, issuer: ContractIssuer): BlockFilter => { - const { first_part: first, second_part: second } = constructMutexBlockAccessClaimKey(lockId); - - return { - issuer, - first, - second, - }; -}; - -export const constructMutexAccessBlock = (lockId: string, issuer: ContractIssuer) => { - return constructBlock([constructMutexBlockAccessBlockFilter(lockId, issuer)]); -}; diff --git a/packages/cwait/src/onchain/mutex/claims/mutexExecOps.ts b/packages/cwait/src/onchain/mutex/claims/mutexExecOps.ts new file mode 100644 index 0000000..e3b4d47 --- /dev/null +++ b/packages/cwait/src/onchain/mutex/claims/mutexExecOps.ts @@ -0,0 +1,43 @@ +import { + BlockFilter, + constructBlock, + constructClaim, + constructClaimKey, + constructStore, + constructTake, + ContractIssuer, + CwebStore, + GStore, +} from '@coinweb/contract-kit'; +import { toHex } from 'lib/shared'; + +import { MutexExecOpsResult } from '../types'; + +export const mutexExecOpsKey = 'mutex_exec_ops'; + +export const constructMutexExecOpsClaimKey = (execId: string) => constructClaimKey([mutexExecOpsKey], [execId]); + +export const constructMutexExecOpsClaim = (execId: string, result: MutexExecOpsResult, storeCweb: bigint) => + constructClaim(constructMutexExecOpsClaimKey(execId), result, toHex(storeCweb)); + +export const constructMutexExecOpsClaimStore = ( + execId: string, + result: MutexExecOpsResult, + storeCweb: bigint = 0n +): GStore => constructStore(constructMutexExecOpsClaim(execId, result, storeCweb)); + +export const constructMutexExecOpsClaimTake = (execId: string) => constructTake(constructMutexExecOpsClaimKey(execId)); + +export const constructMutexExecOpsFilter = (lockId: string, issuer: ContractIssuer): BlockFilter => { + const { first_part: first, second_part: second } = constructMutexExecOpsClaimKey(lockId); + + return { + issuer, + first, + second, + }; +}; + +export const constructMutexExecOpsBlock = (lockId: string, issuer: ContractIssuer) => { + return constructBlock([constructMutexExecOpsFilter(lockId, issuer)]); +}; diff --git a/packages/cwait/src/onchain/mutex/index.ts b/packages/cwait/src/onchain/mutex/index.ts index c5a0114..004e773 100644 --- a/packages/cwait/src/onchain/mutex/index.ts +++ b/packages/cwait/src/onchain/mutex/index.ts @@ -1,20 +1,27 @@ import { execLockMethodName, - getAccessMethodName, + execOps, + execOpsMethodName, lockMethodName, mutexExecLock, - mutexGetAccess, mutexLock, mutexUnlock, + preReadExecTakeOpsMethodName, + saveExecOpResult, + saveExecOpResultMethodName, unlockMethodName, } from './methods'; +import { preReadExecTakeOps } from './methods/preReadExecTakeOps'; export * from './claims'; export * from './types'; +export * from './calls'; export const mutexMethods = { [execLockMethodName]: mutexExecLock, - [getAccessMethodName]: mutexGetAccess, [lockMethodName]: mutexLock, [unlockMethodName]: mutexUnlock, + [execOpsMethodName]: execOps, + [preReadExecTakeOpsMethodName]: preReadExecTakeOps, + [saveExecOpResultMethodName]: saveExecOpResult, }; diff --git a/packages/cwait/src/onchain/mutex/methods/execOps.ts b/packages/cwait/src/onchain/mutex/methods/execOps.ts new file mode 100644 index 0000000..31e2842 --- /dev/null +++ b/packages/cwait/src/onchain/mutex/methods/execOps.ts @@ -0,0 +1,117 @@ +import { + constructContinueTx, + constructContractRef, + constructRead, + Context, + extractContractArgs, + extractRead, + PreparedOperation, +} from '@coinweb/contract-kit'; +import { getCallParameters, getContractArguments, getContractIssuer } from 'lib/onchain'; +import { TypedClaim } from 'lib/shared'; + +import { constructMutexExecOpsClaimStore } from '../claims'; +import { MutexLockState, MutexExecOpArgs, MutexSaveExecOpResultArgs, MutexPreReadTakeOpsArgs } from '../types'; +import { isMatchLockKeys } from '../utils'; + +import { saveExecOpResultMethodName } from './names'; + +export const execOps = (context: Context) => { + const { availableCweb } = getCallParameters(context); + const issuer = getContractIssuer(context); + + const [ops, processId, execId] = getContractArguments(context); + + const lockQueue = extractRead(extractContractArgs(context.tx)[0])?.map( + ({ content }) => content as TypedClaim + ); + + let availableOps: PreparedOperation[] = []; + const unavailableIndexes: number[] = []; + + if (!lockQueue) { + availableOps = ops; + } else { + ops.forEach((op, i) => { + const claimKey = 'StoreOp' in op ? op.StoreOp.key : op.TakeOp.key; + const isLockedByOtherProcess = lockQueue.some( + ({ body }) => + body.locked && body.processId !== processId && body.keys.some((key) => isMatchLockKeys(key, claimKey)) + ); + + if (isLockedByOtherProcess) { + unavailableIndexes.push(i); + } else { + availableOps.push(op); + } + }); + } + + if (availableOps.length === 0) { + return execId + ? [ + constructContinueTx(context, [ + constructMutexExecOpsClaimStore(execId, new Array(ops.length).fill({ ok: false })), + ]), + ] + : []; + } + + if (!execId) { + return [constructContinueTx(context, availableOps)]; + } + + const preReadTakeOps = availableOps.filter((op) => 'TakeOp' in op).map((op) => constructRead(issuer, op.TakeOp.key)); + + if (preReadTakeOps.length > 0) { + const fee = 700n + BigInt(preReadTakeOps.length) * 100n; + + return [ + constructContinueTx( + context, + [], + [ + { + callInfo: { + ref: constructContractRef(issuer, []), + methodInfo: { + methodName: saveExecOpResultMethodName, + methodArgs: [execId, unavailableIndexes, availableOps] satisfies MutexPreReadTakeOpsArgs, + }, + contractInfo: { + providedCweb: availableCweb - fee, + authenticated: null, + }, + contractArgs: preReadTakeOps, + }, + }, + ] + ), + ]; + } + + const fee = 700n + BigInt(availableOps.length) * 100n; + + return [ + constructContinueTx( + context, + [], + [ + { + callInfo: { + ref: constructContractRef(issuer, []), + methodInfo: { + methodName: saveExecOpResultMethodName, + methodArgs: [execId, unavailableIndexes] satisfies MutexSaveExecOpResultArgs, + }, + contractInfo: { + providedCweb: availableCweb - fee, + authenticated: null, + }, + contractArgs: availableOps, + }, + }, + ] + ), + ]; +}; diff --git a/packages/cwait/src/onchain/mutex/methods/getAccess.ts b/packages/cwait/src/onchain/mutex/methods/getAccess.ts deleted file mode 100644 index a3365ad..0000000 --- a/packages/cwait/src/onchain/mutex/methods/getAccess.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { constructContinueTx, Context, extractContractArgs, extractRead } from '@coinweb/contract-kit'; -import { getContractArguments } from 'lib/onchain'; -import { TypedClaim } from 'lib/shared'; - -import { constructMutexBlockAccessClaimStore } from '../claims'; -import { MutexAccessStatus, MutexGetAccessArgs, MutexLockState } from '../types'; -import { isMatchLockKeys } from '../utils'; - -export const mutexGetAccess = (context: Context) => { - const [claimKey, processId, uniqueId] = getContractArguments(context); - - const lockQueue = extractRead(extractContractArgs(context.tx)[0])?.map( - ({ content }) => content as TypedClaim - ); - - if (!lockQueue) { - throw new Error('No lock queue found'); - } - - const isLockedByOtherProcess = lockQueue.some( - ({ body }) => body.locked && body.processId !== processId && body.keys.some((key) => isMatchLockKeys(key, claimKey)) - ); - - return [ - constructContinueTx(context, [ - constructMutexBlockAccessClaimStore( - uniqueId, - isLockedByOtherProcess ? MutexAccessStatus.DENIED : MutexAccessStatus.GRANTED - ), - ]), - ]; -}; diff --git a/packages/cwait/src/onchain/mutex/methods/index.ts b/packages/cwait/src/onchain/mutex/methods/index.ts index 78f9c05..3cbc6ba 100644 --- a/packages/cwait/src/onchain/mutex/methods/index.ts +++ b/packages/cwait/src/onchain/mutex/methods/index.ts @@ -1,7 +1,7 @@ export * from './execLock'; -export * from './getAccess'; export * from './lock'; export * from './names'; export * from './notifyLock'; -export * from './tryOp'; +export * from './execOps'; export * from './unlock'; +export * from './saveExecOpResult'; diff --git a/packages/cwait/src/onchain/mutex/methods/names.ts b/packages/cwait/src/onchain/mutex/methods/names.ts index b430203..ce0c305 100644 --- a/packages/cwait/src/onchain/mutex/methods/names.ts +++ b/packages/cwait/src/onchain/mutex/methods/names.ts @@ -1,6 +1,7 @@ export const lockMethodName = '_mutex_lock'; export const execLockMethodName = '_mutex_execLock'; -export const getAccessMethodName = '_mutex_get_access'; export const unlockMethodName = '_mutex_unlock'; -export const tryOpMethodName = '_mutex_try_op'; export const notifyLockMethodName = '_mutex_notify_lock'; +export const execOpsMethodName = '_mutex_execOps'; +export const saveExecOpResultMethodName = '_mutex_save_exec_op_result'; +export const preReadExecTakeOpsMethodName = '_mutex_pre_read_exec_take_ops'; diff --git a/packages/cwait/src/onchain/mutex/methods/preReadExecTakeOps.ts b/packages/cwait/src/onchain/mutex/methods/preReadExecTakeOps.ts new file mode 100644 index 0000000..6e4fb0c --- /dev/null +++ b/packages/cwait/src/onchain/mutex/methods/preReadExecTakeOps.ts @@ -0,0 +1,49 @@ +import { + constructContinueTx, + constructContractRef, + Context, + extractContractArgs, + extractRead, + passCwebFrom, +} from '@coinweb/contract-kit'; +import { getCallParameters, getContractArguments, getContractIssuer } from 'lib/onchain'; + +import { MutexPreReadTakeOpsArgs, MutexSaveExecOpResultArgs } from '../types'; + +import { saveExecOpResultMethodName } from './names'; + +export const preReadExecTakeOps = (context: Context) => { + const { availableCweb } = getCallParameters(context); + const issuer = getContractIssuer(context); + + const [execId, unavailableIndexes, ops] = getContractArguments(context); + + const storedCweb = extractContractArgs(context.tx) + .map((op) => BigInt(extractRead(op)?.[0]?.content.fees_stored ?? 0)) + .reduce((total, op) => total + op, 0n); + + const fee = 700n + BigInt(ops.length) * 100n; + + return [ + constructContinueTx( + context, + [passCwebFrom(issuer, availableCweb)], + [ + { + callInfo: { + ref: constructContractRef(issuer, []), + methodInfo: { + methodName: saveExecOpResultMethodName, + methodArgs: [execId, unavailableIndexes] satisfies MutexSaveExecOpResultArgs, + }, + contractInfo: { + providedCweb: availableCweb + storedCweb - fee, + authenticated: null, + }, + contractArgs: ops, + }, + }, + ] + ), + ]; +}; diff --git a/packages/cwait/src/onchain/mutex/methods/saveExecOpResult.ts b/packages/cwait/src/onchain/mutex/methods/saveExecOpResult.ts new file mode 100644 index 0000000..a531c9a --- /dev/null +++ b/packages/cwait/src/onchain/mutex/methods/saveExecOpResult.ts @@ -0,0 +1,33 @@ +import { constructContinueTx, Context, extractContractArgs } from '@coinweb/contract-kit'; +import { getCallParameters, getContractArguments } from 'lib/onchain'; + +import { constructMutexExecOpsClaimStore } from '../claims'; +import { MutexExecOpsResult, MutexSaveExecOpResultArgs } from '../types'; + +export const saveExecOpResult = (context: Context) => { + const { availableCweb } = getCallParameters(context); + + const [execId, unavailableIndexes] = getContractArguments(context); + const resolvedOps = extractContractArgs(context.tx); + + const result = new Array(resolvedOps.length + unavailableIndexes.length) + .fill({ ok: false, error: 'Resource is busy' }) + .map((value: MutexExecOpsResult[number], i) => { + if (unavailableIndexes.includes(i)) { + return value; + } + + const resolvedOp = resolvedOps.shift(); + + if (!resolvedOp) { + throw new Error('An error occurred while saving the exec op result'); + } + + return { + ok: true as const, + resolved: resolvedOp, + }; + }); + + return [constructContinueTx(context, [constructMutexExecOpsClaimStore(execId, result, availableCweb - 200n)])]; +}; diff --git a/packages/cwait/src/onchain/mutex/methods/tryOp.ts b/packages/cwait/src/onchain/mutex/methods/tryOp.ts deleted file mode 100644 index ce986d2..0000000 --- a/packages/cwait/src/onchain/mutex/methods/tryOp.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { constructContinueTx, Context, extractContractArgs, extractRead } from '@coinweb/contract-kit'; -import { getContractArguments } from 'lib/onchain'; -import { TypedClaim } from 'lib/shared'; - -import { MutexLockState, MutexTryOpArgs } from '../types'; -import { isMatchLockKeys } from '../utils'; - -export const tryOp = (context: Context) => { - const [op, processId] = getContractArguments(context); - - const claimKey = 'StoreOp' in op ? op.StoreOp.key : op.TakeOp.key; - - const lockQueue = extractRead(extractContractArgs(context.tx)[0])?.map( - ({ content }) => content as TypedClaim - ); - - if (!lockQueue) { - throw new Error('No lock queue found'); - } - - const isLockedByOtherProcess = lockQueue.some( - ({ body }) => body.locked && body.processId !== processId && body.keys.some((key) => isMatchLockKeys(key, claimKey)) - ); - - if (isLockedByOtherProcess) { - return []; - } - - return [constructContinueTx(context, [op])]; -}; diff --git a/packages/cwait/src/onchain/mutex/types.ts b/packages/cwait/src/onchain/mutex/types.ts index 9a54e83..7116b84 100644 --- a/packages/cwait/src/onchain/mutex/types.ts +++ b/packages/cwait/src/onchain/mutex/types.ts @@ -1,4 +1,5 @@ -import { Claim, ClaimKey, GStore, GTake } from '@coinweb/contract-kit'; +import { ClaimKey, CwebStore, GStore, GTake, PreparedOperation, ResolvedOperation } from '@coinweb/contract-kit'; +import { CwebTake } from '@coinweb/contract-kit/dist/types/operations/take'; export type LockedKey = Omit & Partial>; @@ -8,19 +9,23 @@ export type MutexLockState = { processId: string; }; -export enum MutexAccessStatus { - GRANTED = 'granted', - DENIED = 'denied', -} - -export type MutexAccessResult = { - status: MutexAccessStatus; -}; - -export type MutexGetAccessArgs = [claimKey: ClaimKey, processId: string, uniqueId: string]; +export type MutexExecOpsResult = ( + | { + ok: true; + resolved: ResolvedOperation; + } + | { + ok: false; + error: string; + } +)[]; export type MutexUnlockArgs = [lockId: string, timestamp: number, notify?: boolean]; -export type MutexTryOpArgs = [op: GTake | GStore, processId: string]; +export type MutexExecOpArgs = [ops: (GTake | GStore)[], processId: string, execId?: string]; export type MutexNotifyLockArgs = [lockId: string]; + +export type MutexPreReadTakeOpsArgs = [execId: string, unavailableIndexes: number[], ops: PreparedOperation[]]; + +export type MutexSaveExecOpResultArgs = [execId: string, unavailableIndexes: number[]]; diff --git a/packages/cwait/src/onchain/promisifiedOps/ops/store.ts b/packages/cwait/src/onchain/promisifiedOps/ops/store.ts index 57b35d7..15857ec 100644 --- a/packages/cwait/src/onchain/promisifiedOps/ops/store.ts +++ b/packages/cwait/src/onchain/promisifiedOps/ops/store.ts @@ -6,7 +6,7 @@ import { isResolvedSlotOp, isResolvedStoreOp } from '../../utils'; import { pushAwaitedTask } from '../awaited'; import { shiftResolvedOp } from '../resolved'; -export const storeOp = (claim: Claim) => { +export const storeOp = (claim: Claim, storeCweb?: bigint) => { console.log('storeOp'); let opMarkerValue = false; @@ -15,7 +15,7 @@ export const storeOp = (claim: Claim) => { const { op, isOp } = shiftResolvedOp(); if (!isOp) { - pushAwaitedTask(constructStore(claim)); + pushAwaitedTask({ ...constructStore(claim), providedCweb: storeCweb }); opMarkerValue = true; } else { if (isResolvedSlotOp(op)) { diff --git a/packages/cwait/src/onchain/utils/constructTx/constructTx.ts b/packages/cwait/src/onchain/utils/constructTx/constructTx.ts index bf5ae39..df863c8 100644 --- a/packages/cwait/src/onchain/utils/constructTx/constructTx.ts +++ b/packages/cwait/src/onchain/utils/constructTx/constructTx.ts @@ -1,9 +1,9 @@ import { prepareTx } from './prepareTxs'; export const constructTx = (isFullyExecuted: boolean) => { - const { calls } = prepareTx(isFullyExecuted); + const { calls, txFee } = prepareTx(isFullyExecuted, 0n); - const { txs } = prepareTx(isFullyExecuted, calls); + const { txs } = prepareTx(isFullyExecuted, txFee, calls); return txs; }; diff --git a/packages/cwait/src/onchain/utils/constructTx/prepareInThreadTxs.ts b/packages/cwait/src/onchain/utils/constructTx/prepareInThreadTxs.ts index 0458c90..a7633ff 100644 --- a/packages/cwait/src/onchain/utils/constructTx/prepareInThreadTxs.ts +++ b/packages/cwait/src/onchain/utils/constructTx/prepareInThreadTxs.ts @@ -3,42 +3,52 @@ import { constructContinueTx, constructContractRef, FullCallInfo, + GTake, NewTx, passCwebFrom, PreparedOperation, sendCwebInterface, } from '@coinweb/contract-kit'; -import { CwebBlock } from '@coinweb/contract-kit/dist/esm/operations/block'; -import { GBlock } from '@coinweb/contract-kit/dist/types/operations/generics'; +import { CwebTake } from '@coinweb/contract-kit/dist/types/operations/take'; -import { ExecutorMethodArgs, ResolvedOp, ResolvedSlotOp, Task } from '../../../types'; +import { ExecutorMethodArgs, PreparedExtendedStoreOp, PreparedOp, ResolvedOp, ResolvedSlotOp } from '../../../types'; import { constructFundsClaimRangRead, constructFundsClaimStore } from '../../claims/funds'; import { constructResultBlockFilter, constructResultClaimTake, resultKey } from '../../claims/result'; import { context, getRawContext } from '../../context'; -import { isPreparedBlockOp, isPreparedExecOp } from '../typeGuards'; +import { constructExecOpsCall, constructLockCall, constructUnlockCall } from '../../mutex'; +import { + isPreparedExecOp, + isPreparedLockOp, + isPreparedStoreOp, + isPreparedTakeOp, + isPreparedUnlockOp, +} from '../typeGuards'; +import { uuid } from '../uuid'; export const prepareInThreadTxs = ({ cwebPerCall, outThreadTasksCount, outThreadFee, - tasks, + ops, }: { - tasks: Task[]; + ops: PreparedOp[]; cwebPerCall: bigint; outThreadTasksCount: number; outThreadFee: bigint; }): { txs: NewTx[]; calls: number; + txFee: bigint; } => { - if (!tasks.length) { + if (!ops.length) { const { constructSendCweb } = sendCwebInterface(); const { availableCweb, takeOps, storedCweb } = context.funds; const { user } = context; const restOfAvailableCweb = availableCweb - outThreadFee; - const restOfCweb = restOfAvailableCweb + storedCweb - BigInt(takeOps.length) * 100n - 3000n; + const fee = BigInt(takeOps.length) * 100n - 3000n; + const restOfCweb = restOfAvailableCweb + storedCweb - fee; const txs = restOfCweb > 0n @@ -51,12 +61,15 @@ export const prepareInThreadTxs = ({ ] : []; - return { txs, calls: 0 }; + return { txs, calls: 0, txFee: fee }; } let txFee = 0n; let callsPrepared = 0; - const resolvedSlotOps = new Array(outThreadTasksCount).fill({ SlotOp: 0 }) satisfies ResolvedSlotOp[]; + const resolvedSlotOps = new Array(outThreadTasksCount).fill({ SlotOp: { ok: true } }) satisfies ResolvedSlotOp[]; + + const preparedExecOps: (PreparedExtendedStoreOp | GTake)[] = []; + const excOpsIndexes: number[] = []; const resolvedChildOps: ResolvedOp[] = [...context.ops, ...resolvedSlotOps]; @@ -66,48 +79,77 @@ export const prepareInThreadTxs = ({ //Info for separate child call const childCalls: FullCallInfo[] = []; - //Block ops for separate child call - const childBlocks = tasks - .filter((task): task is { op: GBlock; batchId: number } => isPreparedBlockOp(task.op)) - .map(({ op }) => op); + ops.forEach((op, i) => { + switch (true) { + case isPreparedExecOp(op): { + console.log('Child call info'); + const id = op.ExecOp.id; - tasks.forEach((task) => { - if (isPreparedExecOp(task.op)) { - console.log('Child call info'); - const id = task.op.ExecOp.id; + callArgs.push(constructBlock([constructResultBlockFilter(id)]), constructResultClaimTake(id)); + txFee += 200n; - callArgs.push(constructBlock([constructResultBlockFilter(id)]), constructResultClaimTake(id)); - txFee += 200n; - - childCalls.push({ - callInfo: { - ref: constructContractRef(context.issuer, []), - methodInfo: { - methodName: context.methodName, - methodArgs: [ - context.initialArgs, - [...resolvedChildOps, { ExecOp: { id } }], - context.user, - id, - context.thisId, - true, - ] satisfies ExecutorMethodArgs, + childCalls.push({ + callInfo: { + ref: constructContractRef(context.issuer, []), + methodInfo: { + methodName: context.methodName, + methodArgs: [ + context.initialArgs, + [...resolvedChildOps, { ExecOp: { id } }], + context.user, + id, + context.thisId, + true, + ] satisfies ExecutorMethodArgs, + }, + contractInfo: { + providedCweb: cwebPerCall, + authenticated: null, + }, + contractArgs: [], }, - contractInfo: { - providedCweb: cwebPerCall - 700n, - authenticated: null, - }, - contractArgs: [], - }, - }); + }); - callsPrepared++; - } else { - callArgs.push(task.op); - txFee += 100n; + callsPrepared++; + txFee += 700n; + + break; + } + case isPreparedStoreOp(op): + case isPreparedTakeOp(op): { + preparedExecOps.push(op); + excOpsIndexes.push(i); + break; + } + case isPreparedLockOp(op): { + const { callInfo, fee, ops } = constructLockCall(context.issuer, op.LockOp.lockId); + + childCalls.push({ callInfo }); + callArgs.push(...ops); + txFee += fee; + + break; + } + case isPreparedUnlockOp(op): { + const { callInfo, fee, ops } = constructUnlockCall( + context.issuer, + op.UnlockOp.lockId, + op.UnlockOp.timestamp, + false + ); + + childCalls.push({ callInfo }); + callArgs.push(...ops); + txFee += fee; + + break; + } + default: + callArgs.push(op); + txFee += 100n; } - resolvedChildOps.push({ SlotOp: 0 }); + resolvedChildOps.push({ SlotOp: { ok: true } }); }); const returnTxs: NewTx[] = []; @@ -168,6 +210,7 @@ export const prepareInThreadTxs = ({ context.parentId, context.needSaveResult, takeOps.map((op) => (op.TakeOp.key.second_part as [string])[0]), + excOpsIndexes, ] satisfies ExecutorMethodArgs, }, contractInfo: { @@ -182,10 +225,26 @@ export const prepareInThreadTxs = ({ ); if (childCalls.length) { - returnTxs.push(constructContinueTx(getRawContext(), childBlocks, childCalls)); + returnTxs.push(constructContinueTx(getRawContext(), [], childCalls)); } } } - return { txs: returnTxs, calls: callsPrepared }; + if (preparedExecOps.length > 0) { + const execId = uuid(); + + const { callInfo, fee, ops } = constructExecOpsCall({ + issuer: context.issuer, + ops: preparedExecOps, + processId: context.thisId, + execId, + }); + + childCalls.push({ callInfo }); + txFee += fee; + + callArgs.push(...ops); + } + + return { txs: returnTxs, calls: callsPrepared, txFee }; }; diff --git a/packages/cwait/src/onchain/utils/constructTx/prepareOutThreadTxs.ts b/packages/cwait/src/onchain/utils/constructTx/prepareOutThreadTxs.ts index 390b5af..d04bd3a 100644 --- a/packages/cwait/src/onchain/utils/constructTx/prepareOutThreadTxs.ts +++ b/packages/cwait/src/onchain/utils/constructTx/prepareOutThreadTxs.ts @@ -2,96 +2,125 @@ import { constructContinueTx, constructContractRef, FullCallInfo, + GTake, NewTx, PreparedOperation, } from '@coinweb/contract-kit'; +import { CwebTake } from '@coinweb/contract-kit/dist/types/operations/take'; -import { ExecutorMethodArgs, Task } from '../../../types'; +import { ExecutorMethodArgs, PreparedExtendedStoreOp, PreparedOp } from '../../../types'; import { context, getRawContext } from '../../context'; -import { isPreparedExecOp } from '../typeGuards'; +import { constructLockCall, constructExecOpsCall, constructUnlockCall } from '../../mutex'; +import { + isPreparedBlockOp, + isPreparedExecOp, + isPreparedLockOp, + isPreparedStoreOp, + isPreparedTakeOp, + isPreparedUnlockOp, +} from '../typeGuards'; export const prepareOutThreadTxs = ({ - tasks, + ops, cwebPerCall, }: { - tasks: Task[]; + ops: PreparedOp[]; cwebPerCall: bigint; }): { txs: NewTx[]; - fee: bigint; + txFee: bigint; calls: number; } => { const siblingCallResolvedOps = [...context.ops]; - const siblingTxInfo: { - [batchId: number]: { - calls: FullCallInfo[]; - ops: PreparedOperation[]; - }; - } = {}; + const preparedCalls: FullCallInfo[] = []; + const preparedOps: PreparedOperation[] = []; + const preparedStoreAndTakes: (PreparedExtendedStoreOp | GTake)[] = []; let txFee = 0n; let callsPrepared = 0; - tasks.forEach((task) => { - if (isPreparedExecOp(task.op)) { - console.log('Sibling call info'); + ops.forEach((op) => { + switch (true) { + case isPreparedExecOp(op): { + console.log('Sibling call info'); - const id = task.op.ExecOp.id; + const id = op.ExecOp.id; - const callInfo = { - callInfo: { - ref: constructContractRef(context.issuer, []), - methodInfo: { - methodName: context.methodName, - methodArgs: [ - context.initialArgs, - [...siblingCallResolvedOps, { ExecOp: { id } }], - context.user, - id, - context.parentId ?? context.thisId, - false, - ] satisfies ExecutorMethodArgs, + const callInfo = { + callInfo: { + ref: constructContractRef(context.issuer, []), + methodInfo: { + methodName: context.methodName, + methodArgs: [ + context.initialArgs, + [...siblingCallResolvedOps, { ExecOp: { id } }], + context.user, + id, + context.parentId ?? context.thisId, + false, + ] satisfies ExecutorMethodArgs, + }, + contractInfo: { + providedCweb: cwebPerCall, + authenticated: null, + }, + contractArgs: [], }, - contractInfo: { - providedCweb: cwebPerCall - 700n, - authenticated: null, - }, - contractArgs: [], - }, - }; - - if (siblingTxInfo[task.batchId]) { - siblingTxInfo[task.batchId].calls.push(callInfo); - } else { - siblingTxInfo[task.batchId] = { - calls: [callInfo], - ops: [], }; - } - callsPrepared++; - } else { - if (siblingTxInfo[task.batchId]) { - siblingTxInfo[task.batchId].ops.push(task.op); - } else { - siblingTxInfo[task.batchId] = { - calls: [], - ops: [task.op], - }; - } + preparedCalls.push(callInfo); + txFee += 700n; - txFee += 100n; + callsPrepared++; + break; + } + case isPreparedStoreOp(op): + case isPreparedTakeOp(op): { + preparedStoreAndTakes.push(op); + break; + } + case isPreparedLockOp(op): { + const { callInfo, fee } = constructLockCall(context.issuer, op.LockOp.lockId); + + preparedCalls.push({ callInfo }); + txFee += fee; + + break; + } + case isPreparedUnlockOp(op): { + const { callInfo, fee } = constructUnlockCall(context.issuer, op.UnlockOp.lockId, op.UnlockOp.timestamp, false); + + preparedCalls.push({ callInfo }); + txFee += fee; + + break; + } + case isPreparedBlockOp(op): { + break; + } + default: + preparedOps.push(op); + txFee += 100n; } - siblingCallResolvedOps.push({ SlotOp: 0 }); + siblingCallResolvedOps.push({ SlotOp: { ok: true } }); }); - const siblingTxs = Object.values(siblingTxInfo).map(({ calls, ops }) => - constructContinueTx(getRawContext(), ops, calls) - ); + if (preparedStoreAndTakes.length > 0) { + const { callInfo, fee } = constructExecOpsCall({ + issuer: context.issuer, + ops: preparedStoreAndTakes, + processId: context.thisId, + }); - txFee += BigInt(siblingTxs.length) * 100n; + preparedCalls.push({ callInfo }); + txFee += fee; + } - return { txs: siblingTxs, fee: txFee, calls: callsPrepared }; + return { + txs: [constructContinueTx(getRawContext(), preparedOps, preparedCalls)], + txFee, + calls: callsPrepared, + }; }; diff --git a/packages/cwait/src/onchain/utils/constructTx/prepareTxs.ts b/packages/cwait/src/onchain/utils/constructTx/prepareTxs.ts index cc0ddf4..7e6ec95 100644 --- a/packages/cwait/src/onchain/utils/constructTx/prepareTxs.ts +++ b/packages/cwait/src/onchain/utils/constructTx/prepareTxs.ts @@ -7,7 +7,11 @@ import { prepareInThreadTxs } from './prepareInThreadTxs'; import { prepareOutThreadTxs } from './prepareOutThreadTxs'; import { splitTasks } from './splitTasks'; -export const prepareTx = (isFullyExecuted: boolean, callsCount?: number): { txs: NewTx[]; calls: number } => { +export const prepareTx = ( + isFullyExecuted: boolean, + txFee: bigint, + callsCount?: number +): { txs: NewTx[]; calls: number; txFee: bigint } => { console.log('Calls Count: ', callsCount); const awaitedTasks = getAwaitedTasks(); @@ -16,14 +20,15 @@ export const prepareTx = (isFullyExecuted: boolean, callsCount?: number): { txs: if (!awaitedTasks.length) { if (context.isChild) { - return { txs: [], calls: 0 }; + return { txs: [], calls: 0, txFee: 0n }; } const { constructSendCweb } = sendCwebInterface(); const { availableCweb, takeOps, storedCweb } = context.funds; const { user } = context; - const restOfCweb = availableCweb + storedCweb - BigInt(takeOps.length) * 100n - 3000n; + const fee = BigInt(takeOps.length) * 100n - 3000n; + const restOfCweb = availableCweb + storedCweb - fee; const txs = restOfCweb > 0n @@ -36,26 +41,35 @@ export const prepareTx = (isFullyExecuted: boolean, callsCount?: number): { txs: ] : []; - return { txs, calls: 0 }; + return { txs, calls: 0, txFee: fee }; } - const { inThreadTasks, outThreadTasks } = splitTasks(awaitedTasks, isFullyExecuted); + const { inThreadOps, outThreadOps } = splitTasks(awaitedTasks, isFullyExecuted); const { availableCweb } = context.funds; - const cwebPerCall = availableCweb / BigInt(callsCount || 1); + const cwebPerCall = (availableCweb - txFee) / BigInt(callsCount || 1); + const { txs: outThreadTxs, - fee: outThreadFee, calls: outThreadCallsPrepared, - } = prepareOutThreadTxs({ tasks: outThreadTasks, cwebPerCall }); + txFee: outThreadFee, + } = prepareOutThreadTxs({ ops: outThreadOps, cwebPerCall }); - const { txs: inThreadTxs, calls: inThreadCallsPrepared } = prepareInThreadTxs({ - tasks: inThreadTasks, + const { + txs: inThreadTxs, + calls: inThreadCallsPrepared, + txFee: inThreadFee, + } = prepareInThreadTxs({ + ops: inThreadOps, cwebPerCall, - outThreadTasksCount: outThreadTasks.length, + outThreadTasksCount: outThreadOps.length, outThreadFee, }); - return { txs: [...inThreadTxs, ...outThreadTxs], calls: inThreadCallsPrepared + outThreadCallsPrepared }; + return { + txs: [...inThreadTxs, ...outThreadTxs], + calls: inThreadCallsPrepared + outThreadCallsPrepared, + txFee: inThreadFee + outThreadFee, + }; }; diff --git a/packages/cwait/src/onchain/utils/constructTx/splitTasks.ts b/packages/cwait/src/onchain/utils/constructTx/splitTasks.ts index ee90bf1..2db8275 100644 --- a/packages/cwait/src/onchain/utils/constructTx/splitTasks.ts +++ b/packages/cwait/src/onchain/utils/constructTx/splitTasks.ts @@ -1,48 +1,26 @@ -import { Task } from '../../../types'; -import { isPreparedBlockOp } from '../typeGuards'; +import { PreparedOp, Task } from '../../../types'; export const splitTasks = (awaitedTasks: Task[], isFullyExecuted: boolean) => { - const awaitedTasksBatched: (Task | Task[])[] = []; - const preparedTasks: (Task | Task[])[] = []; - - awaitedTasks.forEach((task) => { - if (task.batchId === -1) { - awaitedTasksBatched.push(task); - return; - } - - const latestTask = awaitedTasksBatched.at(-1); - - if (Array.isArray(latestTask) && latestTask.at(-1)?.batchId === task.batchId) { - latestTask.push(task); - return; - } - - awaitedTasksBatched.push([task]); - }); - - awaitedTasksBatched.forEach((task, i) => { - if ( - i === awaitedTasksBatched.length - 1 || - !Array.isArray(task) || - (task.length > 1 && task.some(({ op }) => isPreparedBlockOp(op))) - ) { - preparedTasks.push(task); - } else { - preparedTasks.push(...task.map((task) => ({ ...task, batchId: -1 }))); - } - }); - - let inThreadTasks: Task[] = []; - let outThreadTasks: Task[] = []; - - if (!isFullyExecuted) { - const preparedCallTasks = preparedTasks.at(-1)!; - inThreadTasks = [...(Array.isArray(preparedCallTasks) ? preparedCallTasks : [preparedCallTasks])]; - outThreadTasks = preparedTasks.slice(0, -1).flat(); - } else { - outThreadTasks = preparedTasks.flat(); + if (awaitedTasks.length === 0) { + return { inThreadOps: [], outThreadOps: [] }; } - return { inThreadTasks, outThreadTasks }; + const inThreadOps: PreparedOp[] = []; + let outThreadOps: PreparedOp[] = []; + + if (!isFullyExecuted) { + const latestTaskBatchId = awaitedTasks.at(-1)!.batchId ?? -1; + + awaitedTasks.forEach((task) => { + if (task.batchId === latestTaskBatchId) { + inThreadOps.push(task.op); + } else { + outThreadOps.push(task.op); + } + }); + } else { + outThreadOps = awaitedTasks.map((task) => task.op); + } + + return { inThreadOps, outThreadOps }; }; diff --git a/packages/cwait/src/onchain/utils/typeGuards.ts b/packages/cwait/src/onchain/utils/typeGuards.ts index ff95a00..b35ae5b 100644 --- a/packages/cwait/src/onchain/utils/typeGuards.ts +++ b/packages/cwait/src/onchain/utils/typeGuards.ts @@ -12,10 +12,14 @@ import { CwebBlock } from '@coinweb/contract-kit/dist/esm/operations/block'; import { CwebCallRefResolved, isResolvedCall } from '@coinweb/contract-kit/dist/esm/operations/call'; import { ResolvedBlock } from '@coinweb/contract-kit/dist/types/operations/block'; import { GBlock, GCall, GRead, GStore, GTake } from '@coinweb/contract-kit/dist/types/operations/generics'; +import { CwebTake } from '@coinweb/contract-kit/dist/types/operations/take'; import { PreparedExecOp, + PreparedExtendedStoreOp, + PreparedLockOp, PreparedOp, + PreparedUnlockOp, ResolvedChildOp, ResolvedExecOp, ResolvedLockOp, @@ -119,3 +123,43 @@ export const isPreparedBlockOp = (op?: PreparedOp | null): op is GBlock { + if (op && 'LockOp' in op) { + console.log('isPreparedLockOp >>> ', JSON.stringify(op)); + } + + return !!(op && 'LockOp' in op); +}; + +export const isPreparedUnlockOp = (op?: PreparedOp | null): op is PreparedUnlockOp => { + if (op && 'UnlockOp' in op) { + console.log('isPreparedUnlockOp >>> ', JSON.stringify(op)); + } + + return !!(op && 'UnlockOp' in op); +}; + +export const isPreparedStoreOp = (op?: PreparedOp | null): op is GStore => { + if (op && 'StoreOp' in op) { + console.log('isPreparedStoreOp >>> ', JSON.stringify(op)); + } + + return !!(op && 'StoreOp' in op); +}; + +export const isPreparedExtendedStoreOp = (op?: PreparedOp | null): op is PreparedExtendedStoreOp => { + if (op && 'StoreOp' in op) { + console.log('isPreparedExtendedStoreOp >>> ', JSON.stringify(op)); + } + + return !!(op && 'StoreOp' in op); +}; + +export const isPreparedTakeOp = (op?: PreparedOp | null): op is GTake => { + if (op && 'TakeOp' in op) { + console.log('isPreparedTakeOp >>> ', JSON.stringify(op)); + } + + return !!(op && 'TakeOp' in op); +}; diff --git a/packages/cwait/src/types.ts b/packages/cwait/src/types.ts index c0601b9..45fc95c 100644 --- a/packages/cwait/src/types.ts +++ b/packages/cwait/src/types.ts @@ -1,4 +1,13 @@ -import { Claim, ClaimKey, OrdJson, PreparedOperation, ResolvedOperation, User } from '@coinweb/contract-kit'; +import { + Claim, + ClaimKey, + CwebStore, + GStore, + OrdJson, + PreparedOperation, + ResolvedOperation, + User, +} from '@coinweb/contract-kit'; import { LockedKey } from './onchain/mutex'; @@ -11,7 +20,14 @@ export type TypedClaim & { providedCweb?: bigint }; + +export type PreparedOp = + | PreparedOperation + | PreparedExecOp + | PreparedLockOp + | PreparedUnlockOp + | PreparedExtendedStoreOp; export type Task = { op: PreparedOp; @@ -76,4 +102,5 @@ export type ExecutorMethodArgs = [ parentId?: string, saveResult?: boolean, takenFundsIds?: string[], + execOpsIndexes?: number[], ];