diff --git a/packages/contract.cm/src/onchain/contract.ts b/packages/contract.cm/src/onchain/contract.ts index 7cad51e..800500b 100644 --- a/packages/contract.cm/src/onchain/contract.ts +++ b/packages/contract.cm/src/onchain/contract.ts @@ -1,31 +1,9 @@ -import { SELF_REGISTER_HANDLER_NAME } from '@coinweb/contract-kit'; -import { selfRegisterHandler } from '@coinweb/self-register'; -import { addMethodHandler, ContractHandlers, executeHandler, executor } from 'cwait'; +import { constructCwebMain } from 'cwait'; import { PUBLIC_METHODS } from '../offchain/shared'; import { addWord } from './addWord'; -import { mutexLock } from './mutex/lock'; -import { mutexUnlock, mutexUnlockExec } from './mutex/unlock'; -const createModule = (): ContractHandlers => { - const module: ContractHandlers = { handlers: {} }; - - addMethodHandler(module, PUBLIC_METHODS.ADD_WORD, executor(addWord)); - - addMethodHandler(module, '_lock', mutexLock); - addMethodHandler(module, '_unlock', mutexUnlock); - addMethodHandler(module, '_unlock_exec', mutexUnlockExec); - - addMethodHandler(module, SELF_REGISTER_HANDLER_NAME, selfRegisterHandler); - - return module; -}; - -export const cwebMain = () => { - return (async () => { - const module = createModule(); - - await executeHandler(module); - })(); -}; +export const cwebMain = constructCwebMain({ + [PUBLIC_METHODS.ADD_WORD]: addWord, +}); diff --git a/packages/contract.cm/src/onchain/mutex/access.ts b/packages/contract.cm/src/onchain/mutex/access.ts deleted file mode 100644 index c308620..0000000 --- a/packages/contract.cm/src/onchain/mutex/access.ts +++ /dev/null @@ -1 +0,0 @@ -export const mutexAccess = () => {}; diff --git a/packages/contract.cm/src/onchain/mutex/lock.ts b/packages/contract.cm/src/onchain/mutex/lock.ts deleted file mode 100644 index a4d2b64..0000000 --- a/packages/contract.cm/src/onchain/mutex/lock.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { - constructClaim, - constructClaimKey, - constructContinueTx, - constructContractIssuer, - constructContractRef, - constructStore, - Context, - extractContractInfo, - getContractId, -} from '@coinweb/contract-kit'; - -export const mutexLock = (context: Context) => { - const { providedCweb } = extractContractInfo(context.tx); - const issuer = constructContractIssuer(getContractId(context.tx)); - - return [ - constructContinueTx( - context, - [ - constructStore( - constructClaim(constructClaimKey('mutex', context.call.txid.slice(0, 8)), { word: 'CLASH' }, '0x0') - ), - ], - [ - { - callInfo: { - ref: constructContractRef(issuer, []), - methodInfo: { - methodName: '_unlock', - methodArgs: [], - }, - contractInfo: { - providedCweb: providedCweb ? providedCweb - 1000n : 1000n, - authenticated: null, - }, - contractArgs: [], - }, - }, - ] - ), - ]; -}; diff --git a/packages/contract.cm/src/onchain/mutex/unlock.ts b/packages/contract.cm/src/onchain/mutex/unlock.ts deleted file mode 100644 index 2f3ccdb..0000000 --- a/packages/contract.cm/src/onchain/mutex/unlock.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { - constructContinueTx, - constructContractIssuer, - constructContractRef, - constructRangeRead, - constructTake, - Context, - extractContractArgs, - extractContractInfo, - extractRead, - getContractId, -} from '@coinweb/contract-kit'; - -export const mutexUnlock = (context: Context) => { - const { providedCweb } = extractContractInfo(context.tx); - const issuer = constructContractIssuer(getContractId(context.tx)); - return [ - constructContinueTx( - context, - [], - [ - { - callInfo: { - ref: constructContractRef(issuer, []), - methodInfo: { - methodName: '_unlock_exec', - methodArgs: [], - }, - contractInfo: { - providedCweb: (providedCweb ? providedCweb - 1000n : 1000n) / 2n, - authenticated: null, - }, - contractArgs: [constructRangeRead(issuer, 'mutex', {}, 10000)], - }, - }, - { - callInfo: { - ref: constructContractRef(issuer, []), - methodInfo: { - methodName: '_unlock', - methodArgs: [], - }, - contractInfo: { - providedCweb: (providedCweb ? providedCweb - 1000n : 1000n) / 2n, - authenticated: null, - }, - contractArgs: [], - }, - }, - ] - ), - ]; -}; - -export const mutexUnlockExec = (context: Context) => { - const claim = extractContractArgs(context.tx)[0]!; - const take = extractRead(claim)![0]; - return [constructContinueTx(context, [constructTake(take.content.key)])]; -}; diff --git a/packages/cwait/src/onchain/constructCwebMain.ts b/packages/cwait/src/onchain/constructCwebMain.ts new file mode 100644 index 0000000..88e208b --- /dev/null +++ b/packages/cwait/src/onchain/constructCwebMain.ts @@ -0,0 +1,27 @@ +import { ContractHandlers as ContractHandlersOrig, SELF_REGISTER_HANDLER_NAME } from '@coinweb/contract-kit'; +import { selfRegisterHandler } from '@coinweb/self-register'; +import { queue } from 'lib/onchain'; + +import { addMethodHandler, ContractHandlers, executeHandler } from '../contract-kit'; + +import { executor } from './executor'; +import { mutexMethods } from './mutex'; + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export const constructCwebMain = (methods: Record Promise>) => async () => { + const module: ContractHandlers = { handlers: {} }; + + Object.entries(methods).forEach(([name, handler]) => { + addMethodHandler(module, name, executor(handler)); + }); + + Object.entries(mutexMethods).forEach(([name, handler]) => { + addMethodHandler(module, name, handler); + }); + + addMethodHandler(module, SELF_REGISTER_HANDLER_NAME, selfRegisterHandler); + + queue.applyQueue(module as ContractHandlersOrig, []); + + await executeHandler(module); +}; diff --git a/packages/cwait/src/onchain/executor.ts b/packages/cwait/src/onchain/executor.ts index f917eee..9532673 100644 --- a/packages/cwait/src/onchain/executor.ts +++ b/packages/cwait/src/onchain/executor.ts @@ -43,7 +43,7 @@ export const executor = (method: (...args: any[]) => Promise) => { const isFullyExecuted = await execution; return constructTx(isFullyExecuted); } catch (error) { - console.log('<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< executor-error'); + console.log('<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< executor-error'); console.log((error as Error).message); throw error; } diff --git a/packages/cwait/src/onchain/mutex/calls/constructGetAccessCall.ts b/packages/cwait/src/onchain/mutex/calls/constructGetAccessCall.ts new file mode 100644 index 0000000..88eec8b --- /dev/null +++ b/packages/cwait/src/onchain/mutex/calls/constructGetAccessCall.ts @@ -0,0 +1,26 @@ +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 new file mode 100644 index 0000000..4c2a69a --- /dev/null +++ b/packages/cwait/src/onchain/mutex/calls/constructLockCall.ts @@ -0,0 +1,24 @@ +import { ContractIssuer, constructContractRef } from '@coinweb/contract-kit'; + +import { constructMutexBlockAccessClaimTake, constructMutexLockBlock } from '../claims'; +import { lockMethodName } from '../methods'; +import { lockFee } from '../settings'; + +export const constructLockCall = (issuer: ContractIssuer, lockId: string) => { + return { + callInfo: { + ref: constructContractRef(issuer, []), + methodInfo: { + methodName: lockMethodName, + methodArgs: [], + }, + contractInfo: { + providedCweb: lockFee, + authenticated: null, + }, + contractArgs: [], + }, + ops: [constructMutexLockBlock(lockId, issuer), constructMutexBlockAccessClaimTake(lockId)], + fee: lockFee + 200n, + }; +}; diff --git a/packages/cwait/src/onchain/mutex/calls/constructTryOpCall.ts b/packages/cwait/src/onchain/mutex/calls/constructTryOpCall.ts new file mode 100644 index 0000000..04d6078 --- /dev/null +++ b/packages/cwait/src/onchain/mutex/calls/constructTryOpCall.ts @@ -0,0 +1,23 @@ +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 new file mode 100644 index 0000000..4ce421d --- /dev/null +++ b/packages/cwait/src/onchain/mutex/calls/constructUnlockCall.ts @@ -0,0 +1,24 @@ +import { constructContractRef, ContractIssuer } from '@coinweb/contract-kit'; + +import { constructMutexBlockUnlockClaimTake, constructMutexUnlockBlock } from '../claims'; +import { unlockMethodName } from '../methods'; +import { MutexUnlockArgs } from '../types'; + +export const constructUnlockCall = (issuer: ContractIssuer, ...[lockId, timestamp, notify]: MutexUnlockArgs) => { + return { + callInfo: { + ref: constructContractRef(issuer, []), + methodInfo: { + methodName: unlockMethodName, + methodArgs: [lockId, timestamp] satisfies MutexUnlockArgs, + }, + contractInfo: { + providedCweb: 1000n, + authenticated: null, + }, + contractArgs: [], + }, + ops: notify ? [constructMutexUnlockBlock(lockId, issuer), constructMutexBlockUnlockClaimTake(lockId)] : [], + fee: 1200n, + }; +}; diff --git a/packages/cwait/src/onchain/mutex/calls/index.ts b/packages/cwait/src/onchain/mutex/calls/index.ts new file mode 100644 index 0000000..357971b --- /dev/null +++ b/packages/cwait/src/onchain/mutex/calls/index.ts @@ -0,0 +1,4 @@ +export * from './constructGetAccessCall'; +export * from './constructLockCall'; +export * from './constructTryOpCall'; +export * from './constructUnlockCall'; diff --git a/packages/cwait/src/onchain/mutex/claims/access.ts b/packages/cwait/src/onchain/mutex/claims/access.ts deleted file mode 100644 index 3c1c9ae..0000000 --- a/packages/cwait/src/onchain/mutex/claims/access.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { - constructClaim, - constructClaimKey, - constructStore, - constructTake, - CwebStore, - GStore, -} from '@coinweb/contract-kit'; - -import { MutexAccessResult, MutexAccessStatus } from '../types'; - -export const mutexAccessKey = 'mutex_access'; - -export const constructMutexAccessFirstPart = () => [mutexAccessKey]; - -export const constructMutexAccessClaimKey = (uniqueId: string) => - constructClaimKey(constructMutexAccessFirstPart(), [uniqueId]); - -export const constructMutexAccessClaim = (uniqueId: string, status: MutexAccessStatus) => - constructClaim(constructMutexAccessClaimKey(uniqueId), { status } satisfies MutexAccessResult, '0x0'); - -export const constructMutexAccessClaimStore = (uniqueId: string, status: MutexAccessStatus): GStore => - constructStore(constructMutexAccessClaim(uniqueId, status)); - -export const constructMutexAccessClaimTake = (uniqueId: string) => - constructTake(constructMutexAccessClaimKey(uniqueId)); diff --git a/packages/cwait/src/onchain/mutex/claims/index.ts b/packages/cwait/src/onchain/mutex/claims/index.ts index ffb10d3..c1f0350 100644 --- a/packages/cwait/src/onchain/mutex/claims/index.ts +++ b/packages/cwait/src/onchain/mutex/claims/index.ts @@ -1,2 +1,4 @@ -export * from './access'; -export * from './lock'; +export * from './mutexBlockAccess'; +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 new file mode 100644 index 0000000..8c02e2e --- /dev/null +++ b/packages/cwait/src/onchain/mutex/claims/mutexBlockAccess.ts @@ -0,0 +1,41 @@ +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/mutexBlockLock.ts b/packages/cwait/src/onchain/mutex/claims/mutexBlockLock.ts new file mode 100644 index 0000000..5985ead --- /dev/null +++ b/packages/cwait/src/onchain/mutex/claims/mutexBlockLock.ts @@ -0,0 +1,36 @@ +import { + BlockFilter, + constructBlock, + constructClaim, + constructClaimKey, + constructStore, + constructTake, + ContractIssuer, +} from '@coinweb/contract-kit'; + +export const mutexBlockLockKey = 'mutex_block_lock'; + +export const constructMutexBlockLockClaimKey = (lockId: string) => constructClaimKey([mutexBlockLockKey], [lockId]); + +export const constructMutexBlockLockClaim = ({ lockId }: { lockId: string }) => + constructClaim(constructMutexBlockLockClaimKey(lockId), {}, '0x0'); + +export const constructMutexBlockLockClaimStore = ({ lockId }: { lockId: string }) => + constructStore(constructMutexBlockLockClaim({ lockId })); + +export const constructMutexBlockLockClaimTake = (lockId: string) => + constructTake(constructMutexBlockLockClaimKey(lockId)); + +export const constructMutexLockBlockFilter = (lockId: string, issuer: ContractIssuer): BlockFilter => { + const { first_part: first, second_part: second } = constructMutexBlockLockClaimKey(lockId); + + return { + issuer, + first, + second, + }; +}; + +export const constructMutexLockBlock = (lockId: string, issuer: ContractIssuer) => { + return constructBlock([constructMutexLockBlockFilter(lockId, issuer)]); +}; diff --git a/packages/cwait/src/onchain/mutex/claims/mutexBlockUnlock.ts b/packages/cwait/src/onchain/mutex/claims/mutexBlockUnlock.ts new file mode 100644 index 0000000..6f7f7fd --- /dev/null +++ b/packages/cwait/src/onchain/mutex/claims/mutexBlockUnlock.ts @@ -0,0 +1,39 @@ +import { + BlockFilter, + constructBlock, + constructClaim, + constructClaimKey, + constructStore, + constructTake, + ContractIssuer, + CwebStore, + GStore, +} from '@coinweb/contract-kit'; + +export const mutexBlockUnlockKey = 'mutex_block_unlock'; + +export const constructMutexBlockUnlockClaimKey = (uniqueId: string) => + constructClaimKey([mutexBlockUnlockKey], [uniqueId]); + +export const constructMutexBlockUnlockClaim = (uniqueId: string) => + constructClaim(constructMutexBlockUnlockClaimKey(uniqueId), {}, '0x0'); + +export const constructMutexBlockUnlockClaimStore = (uniqueId: string): GStore => + constructStore(constructMutexBlockUnlockClaim(uniqueId)); + +export const constructMutexBlockUnlockClaimTake = (uniqueId: string) => + constructTake(constructMutexBlockUnlockClaimKey(uniqueId)); + +export const constructMutexUnlockBlockFilter = (lockId: string, issuer: ContractIssuer): BlockFilter => { + const { first_part: first, second_part: second } = constructMutexBlockUnlockClaimKey(lockId); + + return { + issuer, + first, + second, + }; +}; + +export const constructMutexUnlockBlock = (lockId: string, issuer: ContractIssuer) => { + return constructBlock([constructMutexUnlockBlockFilter(lockId, issuer)]); +}; diff --git a/packages/cwait/src/onchain/mutex/claims/lock.ts b/packages/cwait/src/onchain/mutex/claims/mutexLock.ts similarity index 83% rename from packages/cwait/src/onchain/mutex/claims/lock.ts rename to packages/cwait/src/onchain/mutex/claims/mutexLock.ts index c307665..725d80d 100644 --- a/packages/cwait/src/onchain/mutex/claims/lock.ts +++ b/packages/cwait/src/onchain/mutex/claims/mutexLock.ts @@ -1,11 +1,9 @@ import { constructClaim, constructClaimKey, - constructContractIssuer, constructRangeRead, constructStore, - Context, - getContractId, + ContractIssuer, GRead, } from '@coinweb/contract-kit'; import { CwebRead } from '@coinweb/contract-kit/dist/esm/operations/read'; @@ -58,13 +56,8 @@ export const constructMutexLockClaimStore = ({ processId: string; }) => constructStore(constructMutexLockClaim({ fee, keys, locked, lockId, timestamp, processId })); -export const constructMutexLockClaimRangeRead = (context: Context): GRead => - constructRangeRead( - constructContractIssuer(getContractId(context.tx)), - constructMutexLockFirstPart(), - {}, - rangeReadLimit - ); +export const constructMutexLockClaimRangeRead = (issuer: ContractIssuer): GRead => + constructRangeRead(issuer, constructMutexLockFirstPart(), {}, rangeReadLimit); // export const constructMutexLockClaimTake = (lockId: string, timestamp: number) => // constructTake(constructMutexLockClaimKey(lockId, timestamp)); diff --git a/packages/cwait/src/onchain/mutex/methods/execLock.ts b/packages/cwait/src/onchain/mutex/methods/execLock.ts index 0284cf8..3f79a2f 100644 --- a/packages/cwait/src/onchain/mutex/methods/execLock.ts +++ b/packages/cwait/src/onchain/mutex/methods/execLock.ts @@ -16,7 +16,7 @@ import { lockFee } from '../settings'; import { MutexLockState } from '../types'; import { isMatchLockKeys } from '../utils'; -import { lockMethodName } from './names'; +import { lockMethodName, notifyLockMethodName } from './names'; export const mutexExecLock = (context: Context) => { const { availableCweb } = getCallParameters(context); @@ -69,6 +69,20 @@ export const mutexExecLock = (context: Context) => { contractArgs: [], }, }, + { + callInfo: { + ref: constructContractRef(issuer, []), + methodInfo: { + methodName: notifyLockMethodName, + methodArgs: [(lockCandidate.key.second_part as [number, string])[1]], + }, + contractInfo: { + providedCweb: 200n, + authenticated: null, + }, + contractArgs: [], + }, + }, ] ), ]; diff --git a/packages/cwait/src/onchain/mutex/methods/getAccess.ts b/packages/cwait/src/onchain/mutex/methods/getAccess.ts index 28d2bdd..a3365ad 100644 --- a/packages/cwait/src/onchain/mutex/methods/getAccess.ts +++ b/packages/cwait/src/onchain/mutex/methods/getAccess.ts @@ -2,7 +2,7 @@ import { constructContinueTx, Context, extractContractArgs, extractRead } from ' import { getContractArguments } from 'lib/onchain'; import { TypedClaim } from 'lib/shared'; -import { constructMutexAccessClaimStore } from '../claims'; +import { constructMutexBlockAccessClaimStore } from '../claims'; import { MutexAccessStatus, MutexGetAccessArgs, MutexLockState } from '../types'; import { isMatchLockKeys } from '../utils'; @@ -23,7 +23,7 @@ export const mutexGetAccess = (context: Context) => { return [ constructContinueTx(context, [ - constructMutexAccessClaimStore( + 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 287d9f5..78f9c05 100644 --- a/packages/cwait/src/onchain/mutex/methods/index.ts +++ b/packages/cwait/src/onchain/mutex/methods/index.ts @@ -2,4 +2,6 @@ export * from './execLock'; export * from './getAccess'; export * from './lock'; export * from './names'; +export * from './notifyLock'; +export * from './tryOp'; export * from './unlock'; diff --git a/packages/cwait/src/onchain/mutex/methods/lock.ts b/packages/cwait/src/onchain/mutex/methods/lock.ts index 0a31048..fb02ef1 100644 --- a/packages/cwait/src/onchain/mutex/methods/lock.ts +++ b/packages/cwait/src/onchain/mutex/methods/lock.ts @@ -25,7 +25,7 @@ export const mutexLock = (context: Context) => { providedCweb: availableCweb - 700n, authenticated: null, }, - contractArgs: [constructMutexLockClaimRangeRead(context)], + contractArgs: [constructMutexLockClaimRangeRead(issuer)], }, }, ] diff --git a/packages/cwait/src/onchain/mutex/methods/names.ts b/packages/cwait/src/onchain/mutex/methods/names.ts index 269ca95..b430203 100644 --- a/packages/cwait/src/onchain/mutex/methods/names.ts +++ b/packages/cwait/src/onchain/mutex/methods/names.ts @@ -2,3 +2,5 @@ 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'; diff --git a/packages/cwait/src/onchain/mutex/methods/notifyLock.ts b/packages/cwait/src/onchain/mutex/methods/notifyLock.ts new file mode 100644 index 0000000..3d5e838 --- /dev/null +++ b/packages/cwait/src/onchain/mutex/methods/notifyLock.ts @@ -0,0 +1,11 @@ +import { constructContinueTx, Context } from '@coinweb/contract-kit'; +import { getContractArguments } from 'lib/onchain'; + +import { constructMutexBlockLockClaimStore } from '../claims/mutexBlockLock'; +import { MutexNotifyLockArgs } from '../types'; + +export const notifyLock = (context: Context) => { + const [lockId] = getContractArguments(context); + + return [constructContinueTx(context, [constructMutexBlockLockClaimStore({ lockId })])]; +}; diff --git a/packages/cwait/src/onchain/mutex/methods/tryOp.ts b/packages/cwait/src/onchain/mutex/methods/tryOp.ts new file mode 100644 index 0000000..ce986d2 --- /dev/null +++ b/packages/cwait/src/onchain/mutex/methods/tryOp.ts @@ -0,0 +1,30 @@ +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 2aec5c9..9a54e83 100644 --- a/packages/cwait/src/onchain/mutex/types.ts +++ b/packages/cwait/src/onchain/mutex/types.ts @@ -1,4 +1,4 @@ -import { ClaimKey } from '@coinweb/contract-kit'; +import { Claim, ClaimKey, GStore, GTake } from '@coinweb/contract-kit'; export type LockedKey = Omit & Partial>; @@ -19,4 +19,8 @@ export type MutexAccessResult = { export type MutexGetAccessArgs = [claimKey: ClaimKey, processId: string, uniqueId: string]; -export type MutexUnlockArgs = [lockId: string, timestamp: number]; +export type MutexUnlockArgs = [lockId: string, timestamp: number, notify?: boolean]; + +export type MutexTryOpArgs = [op: GTake | GStore, processId: string]; + +export type MutexNotifyLockArgs = [lockId: string]; diff --git a/packages/cwait/src/onchain/promisifiedOps/ops/block.ts b/packages/cwait/src/onchain/promisifiedOps/ops/block.ts index f85fce9..56d1086 100644 --- a/packages/cwait/src/onchain/promisifiedOps/ops/block.ts +++ b/packages/cwait/src/onchain/promisifiedOps/ops/block.ts @@ -27,7 +27,7 @@ export const blockOp = (filters: BlockFilter[]) => { throw new Error('Block operation not found'); } - const result = op && extractBlock(op); + const result = extractBlock(op); resolve(result); } diff --git a/packages/cwait/src/onchain/promisifiedOps/ops/lock.ts b/packages/cwait/src/onchain/promisifiedOps/ops/lock.ts new file mode 100644 index 0000000..7bb3fd6 --- /dev/null +++ b/packages/cwait/src/onchain/promisifiedOps/ops/lock.ts @@ -0,0 +1,42 @@ +import { ResolvedLockOp } from '../../../types'; +import { opMarker } from '../../global'; +import { LockedKey } from '../../mutex'; +import { isResolvedLockOp, isResolvedSlotOp, uuid } from '../../utils'; +import { pushAwaitedTask } from '../awaited'; +import { shiftResolvedOp } from '../resolved'; + +export const lockOp = (keys: LockedKey[] | LockedKey) => { + console.log('lockOp'); + let opMarkerValue = false; + + const result = new Promise((resolve, reject) => { + try { + const { op, isOp } = shiftResolvedOp(); + + if (!isOp) { + pushAwaitedTask({ LockOp: { lockId: uuid(), keys: Array.isArray(keys) ? keys : [keys] } }); + opMarkerValue = true; + } else { + if (isResolvedSlotOp(op)) { + console.log('storeOp-slotOp'); + return; + } + + if (!isResolvedLockOp(op)) { + console.log('lockOp-error'); + throw new Error('Lock operation not found'); + } + + const result = op.LockOp; + + resolve(result); + } + } catch (error) { + reject(error); + } + }); + + (result as Promise & { [opMarker]: boolean })[opMarker] = opMarkerValue; + + return result; +}; diff --git a/packages/cwait/src/onchain/promisifiedOps/ops/rangeRead.ts b/packages/cwait/src/onchain/promisifiedOps/ops/rangeRead.ts index 7b223f6..57137ab 100644 --- a/packages/cwait/src/onchain/promisifiedOps/ops/rangeRead.ts +++ b/packages/cwait/src/onchain/promisifiedOps/ops/rangeRead.ts @@ -34,7 +34,7 @@ export const rangeReadOp = ( throw new Error('Read operation not found'); } - const claim = op && ((extractRead(op)?.map((result) => result.content) ?? null) as TClaims | null); + const claim = (extractRead(op)?.map((result) => result.content) ?? null) as TClaims | null; resolve(claim); } } catch (error) { diff --git a/packages/cwait/src/onchain/promisifiedOps/ops/read.ts b/packages/cwait/src/onchain/promisifiedOps/ops/read.ts index 0e58301..72f0320 100644 --- a/packages/cwait/src/onchain/promisifiedOps/ops/read.ts +++ b/packages/cwait/src/onchain/promisifiedOps/ops/read.ts @@ -29,7 +29,7 @@ export const readOp = (key: ClaimKey) => { throw new Error('Read operation not found'); } - const claim = op && ((extractRead(op)?.[0]?.content ?? null) as TClaim | null); + const claim = (extractRead(op)?.[0]?.content ?? null) as TClaim | null; resolve(claim); } } catch (error) { diff --git a/packages/cwait/src/onchain/promisifiedOps/ops/store.ts b/packages/cwait/src/onchain/promisifiedOps/ops/store.ts index 15a5254..57b35d7 100644 --- a/packages/cwait/src/onchain/promisifiedOps/ops/store.ts +++ b/packages/cwait/src/onchain/promisifiedOps/ops/store.ts @@ -28,7 +28,7 @@ export const storeOp = (claim: Claim) => { throw new Error('Store operation not found'); } - const result = op && extractStore(op); + const result = extractStore(op); resolve(result); } diff --git a/packages/cwait/src/onchain/promisifiedOps/ops/take.ts b/packages/cwait/src/onchain/promisifiedOps/ops/take.ts index 20041bd..2f4dcdd 100644 --- a/packages/cwait/src/onchain/promisifiedOps/ops/take.ts +++ b/packages/cwait/src/onchain/promisifiedOps/ops/take.ts @@ -28,7 +28,7 @@ export const takeOp = (key: ClaimKey) => { throw new Error('Take operation not found'); } - const claim = op && extractTake(op); + const claim = extractTake(op); resolve(claim as TClaim | null); } } catch (error) { diff --git a/packages/cwait/src/onchain/promisifiedOps/ops/unlock.ts b/packages/cwait/src/onchain/promisifiedOps/ops/unlock.ts new file mode 100644 index 0000000..404792a --- /dev/null +++ b/packages/cwait/src/onchain/promisifiedOps/ops/unlock.ts @@ -0,0 +1,38 @@ +import { opMarker } from '../../global'; +import { isResolvedSlotOp, isResolvedUnlockOp } from '../../utils'; +import { pushAwaitedTask } from '../awaited'; +import { shiftResolvedOp } from '../resolved'; + +export const unlock = (lockId: string) => { + console.log('lockOp'); + let opMarkerValue = false; + + const result = new Promise((resolve, reject) => { + try { + const { op, isOp } = shiftResolvedOp(); + + if (!isOp) { + pushAwaitedTask({ UnlockOp: { lockId } }); + opMarkerValue = true; + } else { + if (isResolvedSlotOp(op)) { + console.log('unlockOp-slotOp'); + return; + } + + if (!isResolvedUnlockOp(op)) { + console.log('unlockOp-error'); + throw new Error('Unlock operation not found'); + } + + resolve(); + } + } catch (error) { + reject(error); + } + }); + + (result as Promise & { [opMarker]: boolean })[opMarker] = opMarkerValue; + + return result; +}; diff --git a/packages/cwait/src/onchain/promisifiedTxs/index.ts b/packages/cwait/src/onchain/promisifiedTxs/index.ts deleted file mode 100644 index 6b03561..0000000 --- a/packages/cwait/src/onchain/promisifiedTxs/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './jumpTx'; -export * from './tx'; diff --git a/packages/cwait/src/onchain/promisifiedTxs/jumpTx.ts b/packages/cwait/src/onchain/promisifiedTxs/jumpTx.ts deleted file mode 100644 index 6ef6f86..0000000 --- a/packages/cwait/src/onchain/promisifiedTxs/jumpTx.ts +++ /dev/null @@ -1 +0,0 @@ -export const jumpTx = () => null; diff --git a/packages/cwait/src/onchain/promisifiedTxs/tx.ts b/packages/cwait/src/onchain/promisifiedTxs/tx.ts deleted file mode 100644 index 62a9327..0000000 --- a/packages/cwait/src/onchain/promisifiedTxs/tx.ts +++ /dev/null @@ -1 +0,0 @@ -export const tx = () => null; diff --git a/packages/cwait/src/onchain/utils/typeGuards.ts b/packages/cwait/src/onchain/utils/typeGuards.ts index 6a5bfa3..ff95a00 100644 --- a/packages/cwait/src/onchain/utils/typeGuards.ts +++ b/packages/cwait/src/onchain/utils/typeGuards.ts @@ -13,7 +13,16 @@ import { CwebCallRefResolved, isResolvedCall } from '@coinweb/contract-kit/dist/ 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 { PreparedExecOp, PreparedOp, ResolvedChildOp, ResolvedExecOp, ResolvedOp, ResolvedSlotOp } from '../../types'; +import { + PreparedExecOp, + PreparedOp, + ResolvedChildOp, + ResolvedExecOp, + ResolvedLockOp, + ResolvedOp, + ResolvedSlotOp, + ResolvedUnlockOp, +} from '../../types'; export const isResolvedSlotOp = (op?: ResolvedOp | null): op is ResolvedSlotOp => { if (op && 'SlotOp' in op) { @@ -39,6 +48,22 @@ export const isResolvedChildOp = (op?: ResolvedOp | null): op is ResolvedChildOp return !!(op && 'ChildOp' in op); }; +export const isResolvedLockOp = (op?: ResolvedOp | null): op is ResolvedLockOp => { + if (op && 'LockOp' in op) { + console.log('isResolvedLockOp >>> ', JSON.stringify(op)); + } + + return !!(op && 'LockOp' in op); +}; + +export const isResolvedUnlockOp = (op?: ResolvedOp | null): op is ResolvedUnlockOp => { + if (op && 'UnlockOp' in op) { + console.log('isResolvedUnlockOp >>> ', JSON.stringify(op)); + } + + return !!(op && 'UnlockOp' in op); +}; + export const isResolvedBlockOp = (op?: ResolvedOp | null): op is GBlock => { if (op && 'BlockOp' in op) { console.log('isResolvedBlockOp >>> ', JSON.stringify(op)); diff --git a/packages/cwait/src/types.ts b/packages/cwait/src/types.ts index a5b7fb3..c0601b9 100644 --- a/packages/cwait/src/types.ts +++ b/packages/cwait/src/types.ts @@ -1,5 +1,7 @@ import { Claim, ClaimKey, OrdJson, PreparedOperation, ResolvedOperation, User } from '@coinweb/contract-kit'; +import { LockedKey } from './onchain/mutex'; + export type HexBigInt = `0x${string}`; export type TypedClaim = Claim & { @@ -22,7 +24,23 @@ export type ResolvedChildOp = { ChildOp: 0; }; -export type ResolvedOp = ResolvedOperation | ResolvedSlotOp | ResolvedExecOp | ResolvedChildOp; +export type ResolvedLockOp = { + LockOp: { + lockId: string; + }; +}; + +export type ResolvedUnlockOp = { + UnlockOp: 0; +}; + +export type ResolvedOp = + | ResolvedOperation + | ResolvedSlotOp + | ResolvedExecOp + | ResolvedChildOp + | ResolvedLockOp + | ResolvedUnlockOp; export type PreparedExecOp = { ExecOp: { @@ -30,7 +48,20 @@ export type PreparedExecOp = { }; }; -export type PreparedOp = PreparedOperation | PreparedExecOp; +export type PreparedLockOp = { + LockOp: { + lockId: string; + keys: LockedKey[]; + }; +}; + +export type PreparedUnlockOp = { + UnlockOp: { + lockId: string; + }; +}; + +export type PreparedOp = PreparedOperation | PreparedExecOp | PreparedLockOp | PreparedUnlockOp; export type Task = { op: PreparedOp;