Compare commits
2 Commits
cb4a2fb33d
...
5dd0aef472
| Author | SHA1 | Date | |
|---|---|---|---|
| 5dd0aef472 | |||
| 0d5850ec25 |
@ -1,25 +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';
|
||||
|
||||
const createModule = (): ContractHandlers => {
|
||||
const module: ContractHandlers = { handlers: {} };
|
||||
|
||||
addMethodHandler(module, PUBLIC_METHODS.ADD_WORD, executor(addWord));
|
||||
|
||||
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,
|
||||
});
|
||||
|
||||
27
packages/cwait/src/onchain/constructCwebMain.ts
Normal file
27
packages/cwait/src/onchain/constructCwebMain.ts
Normal file
@ -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<string, (...args: any[]) => Promise<void>>) => 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);
|
||||
};
|
||||
@ -43,7 +43,7 @@ export const executor = (method: (...args: any[]) => Promise<void>) => {
|
||||
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;
|
||||
}
|
||||
|
||||
@ -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,
|
||||
};
|
||||
};
|
||||
24
packages/cwait/src/onchain/mutex/calls/constructLockCall.ts
Normal file
24
packages/cwait/src/onchain/mutex/calls/constructLockCall.ts
Normal file
@ -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,
|
||||
};
|
||||
};
|
||||
23
packages/cwait/src/onchain/mutex/calls/constructTryOpCall.ts
Normal file
23
packages/cwait/src/onchain/mutex/calls/constructTryOpCall.ts
Normal file
@ -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,
|
||||
};
|
||||
};
|
||||
@ -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,
|
||||
};
|
||||
};
|
||||
4
packages/cwait/src/onchain/mutex/calls/index.ts
Normal file
4
packages/cwait/src/onchain/mutex/calls/index.ts
Normal file
@ -0,0 +1,4 @@
|
||||
export * from './constructGetAccessCall';
|
||||
export * from './constructLockCall';
|
||||
export * from './constructTryOpCall';
|
||||
export * from './constructUnlockCall';
|
||||
4
packages/cwait/src/onchain/mutex/claims/index.ts
Normal file
4
packages/cwait/src/onchain/mutex/claims/index.ts
Normal file
@ -0,0 +1,4 @@
|
||||
export * from './mutexBlockAccess';
|
||||
export * from './mutexBlockLock';
|
||||
export * from './mutexBlockUnlock';
|
||||
export * from './mutexLock';
|
||||
41
packages/cwait/src/onchain/mutex/claims/mutexBlockAccess.ts
Normal file
41
packages/cwait/src/onchain/mutex/claims/mutexBlockAccess.ts
Normal file
@ -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<CwebStore> =>
|
||||
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)]);
|
||||
};
|
||||
36
packages/cwait/src/onchain/mutex/claims/mutexBlockLock.ts
Normal file
36
packages/cwait/src/onchain/mutex/claims/mutexBlockLock.ts
Normal file
@ -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)]);
|
||||
};
|
||||
39
packages/cwait/src/onchain/mutex/claims/mutexBlockUnlock.ts
Normal file
39
packages/cwait/src/onchain/mutex/claims/mutexBlockUnlock.ts
Normal file
@ -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<CwebStore> =>
|
||||
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)]);
|
||||
};
|
||||
63
packages/cwait/src/onchain/mutex/claims/mutexLock.ts
Normal file
63
packages/cwait/src/onchain/mutex/claims/mutexLock.ts
Normal file
@ -0,0 +1,63 @@
|
||||
import {
|
||||
constructClaim,
|
||||
constructClaimKey,
|
||||
constructRangeRead,
|
||||
constructStore,
|
||||
ContractIssuer,
|
||||
GRead,
|
||||
} from '@coinweb/contract-kit';
|
||||
import { CwebRead } from '@coinweb/contract-kit/dist/esm/operations/read';
|
||||
import { toHex } from 'lib/shared';
|
||||
|
||||
import { rangeReadLimit } from '../settings';
|
||||
import { LockedKey, MutexLockState } from '../types';
|
||||
|
||||
export const mutexLockKey = 'mutex_lock';
|
||||
|
||||
export const constructMutexLockFirstPart = () => [mutexLockKey];
|
||||
|
||||
export const constructMutexLockClaimKey = (lockId: string, timestamp: number) =>
|
||||
constructClaimKey(constructMutexLockFirstPart(), [timestamp, lockId]);
|
||||
|
||||
export const constructMutexLockClaim = ({
|
||||
fee,
|
||||
keys,
|
||||
locked,
|
||||
lockId,
|
||||
timestamp,
|
||||
processId,
|
||||
}: {
|
||||
lockId: string;
|
||||
timestamp: number;
|
||||
locked: boolean;
|
||||
keys: LockedKey[];
|
||||
fee: bigint;
|
||||
processId: string;
|
||||
}) =>
|
||||
constructClaim(
|
||||
constructMutexLockClaimKey(lockId, timestamp),
|
||||
{ locked, keys, processId } satisfies MutexLockState,
|
||||
toHex(fee)
|
||||
);
|
||||
|
||||
export const constructMutexLockClaimStore = ({
|
||||
fee,
|
||||
keys,
|
||||
locked,
|
||||
lockId,
|
||||
timestamp,
|
||||
processId,
|
||||
}: {
|
||||
fee: bigint;
|
||||
keys: LockedKey[];
|
||||
locked: boolean;
|
||||
lockId: string;
|
||||
timestamp: number;
|
||||
processId: string;
|
||||
}) => constructStore(constructMutexLockClaim({ fee, keys, locked, lockId, timestamp, processId }));
|
||||
|
||||
export const constructMutexLockClaimRangeRead = (issuer: ContractIssuer): GRead<CwebRead> =>
|
||||
constructRangeRead(issuer, constructMutexLockFirstPart(), {}, rangeReadLimit);
|
||||
|
||||
// export const constructMutexLockClaimTake = (lockId: string, timestamp: number) =>
|
||||
// constructTake(constructMutexLockClaimKey(lockId, timestamp));
|
||||
20
packages/cwait/src/onchain/mutex/index.ts
Normal file
20
packages/cwait/src/onchain/mutex/index.ts
Normal file
@ -0,0 +1,20 @@
|
||||
import {
|
||||
execLockMethodName,
|
||||
getAccessMethodName,
|
||||
lockMethodName,
|
||||
mutexExecLock,
|
||||
mutexGetAccess,
|
||||
mutexLock,
|
||||
mutexUnlock,
|
||||
unlockMethodName,
|
||||
} from './methods';
|
||||
|
||||
export * from './claims';
|
||||
export * from './types';
|
||||
|
||||
export const mutexMethods = {
|
||||
[execLockMethodName]: mutexExecLock,
|
||||
[getAccessMethodName]: mutexGetAccess,
|
||||
[lockMethodName]: mutexLock,
|
||||
[unlockMethodName]: mutexUnlock,
|
||||
};
|
||||
89
packages/cwait/src/onchain/mutex/methods/execLock.ts
Normal file
89
packages/cwait/src/onchain/mutex/methods/execLock.ts
Normal file
@ -0,0 +1,89 @@
|
||||
import {
|
||||
constructClaim,
|
||||
constructContinueTx,
|
||||
constructContractRef,
|
||||
constructStore,
|
||||
constructTake,
|
||||
Context,
|
||||
extractContractArgs,
|
||||
extractRead,
|
||||
passCwebFrom,
|
||||
} from '@coinweb/contract-kit';
|
||||
import { getCallParameters, getContractIssuer } from 'lib/onchain';
|
||||
import { toHex, TypedClaim } from 'lib/shared';
|
||||
|
||||
import { lockFee } from '../settings';
|
||||
import { MutexLockState } from '../types';
|
||||
import { isMatchLockKeys } from '../utils';
|
||||
|
||||
import { lockMethodName, notifyLockMethodName } from './names';
|
||||
|
||||
export const mutexExecLock = (context: Context) => {
|
||||
const { availableCweb } = getCallParameters(context);
|
||||
const issuer = getContractIssuer(context);
|
||||
|
||||
const lockQueue = extractRead(extractContractArgs(context.tx)[0])?.map(
|
||||
({ content }) => content as TypedClaim<MutexLockState>
|
||||
);
|
||||
|
||||
if (!lockQueue) {
|
||||
throw new Error('No lock queue found');
|
||||
}
|
||||
|
||||
const alreadyLockedKeys = lockQueue.filter(({ body }) => body.locked).map(({ key }) => key);
|
||||
|
||||
const lockCandidate = lockQueue.find(
|
||||
({ body, key }) => !body.locked && !alreadyLockedKeys.some((lockedKey) => isMatchLockKeys(lockedKey, key))
|
||||
);
|
||||
|
||||
if (!lockCandidate) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return [
|
||||
constructContinueTx(
|
||||
context,
|
||||
[
|
||||
passCwebFrom(issuer, availableCweb),
|
||||
constructTake(lockCandidate.key),
|
||||
constructStore(
|
||||
constructClaim(
|
||||
lockCandidate.key,
|
||||
{ ...lockCandidate.body, locked: true },
|
||||
toHex(BigInt(lockCandidate.fees_stored) - lockFee)
|
||||
)
|
||||
),
|
||||
],
|
||||
[
|
||||
{
|
||||
callInfo: {
|
||||
ref: constructContractRef(issuer, []),
|
||||
methodInfo: {
|
||||
methodName: lockMethodName,
|
||||
methodArgs: [],
|
||||
},
|
||||
contractInfo: {
|
||||
providedCweb: lockFee,
|
||||
authenticated: null,
|
||||
},
|
||||
contractArgs: [],
|
||||
},
|
||||
},
|
||||
{
|
||||
callInfo: {
|
||||
ref: constructContractRef(issuer, []),
|
||||
methodInfo: {
|
||||
methodName: notifyLockMethodName,
|
||||
methodArgs: [(lockCandidate.key.second_part as [number, string])[1]],
|
||||
},
|
||||
contractInfo: {
|
||||
providedCweb: 200n,
|
||||
authenticated: null,
|
||||
},
|
||||
contractArgs: [],
|
||||
},
|
||||
},
|
||||
]
|
||||
),
|
||||
];
|
||||
};
|
||||
32
packages/cwait/src/onchain/mutex/methods/getAccess.ts
Normal file
32
packages/cwait/src/onchain/mutex/methods/getAccess.ts
Normal file
@ -0,0 +1,32 @@
|
||||
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<MutexGetAccessArgs>(context);
|
||||
|
||||
const lockQueue = extractRead(extractContractArgs(context.tx)[0])?.map(
|
||||
({ content }) => content as TypedClaim<MutexLockState>
|
||||
);
|
||||
|
||||
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
|
||||
),
|
||||
]),
|
||||
];
|
||||
};
|
||||
7
packages/cwait/src/onchain/mutex/methods/index.ts
Normal file
7
packages/cwait/src/onchain/mutex/methods/index.ts
Normal file
@ -0,0 +1,7 @@
|
||||
export * from './execLock';
|
||||
export * from './getAccess';
|
||||
export * from './lock';
|
||||
export * from './names';
|
||||
export * from './notifyLock';
|
||||
export * from './tryOp';
|
||||
export * from './unlock';
|
||||
34
packages/cwait/src/onchain/mutex/methods/lock.ts
Normal file
34
packages/cwait/src/onchain/mutex/methods/lock.ts
Normal file
@ -0,0 +1,34 @@
|
||||
import { constructContinueTx, constructContractRef, Context } from '@coinweb/contract-kit';
|
||||
import { getCallParameters, getContractIssuer } from 'lib/onchain';
|
||||
|
||||
import { constructMutexLockClaimRangeRead } from '../claims';
|
||||
|
||||
import { execLockMethodName } from './names';
|
||||
|
||||
export const mutexLock = (context: Context) => {
|
||||
const { availableCweb } = getCallParameters(context);
|
||||
const issuer = getContractIssuer(context);
|
||||
|
||||
return [
|
||||
constructContinueTx(
|
||||
context,
|
||||
[],
|
||||
[
|
||||
{
|
||||
callInfo: {
|
||||
ref: constructContractRef(issuer, []),
|
||||
methodInfo: {
|
||||
methodName: execLockMethodName,
|
||||
methodArgs: [],
|
||||
},
|
||||
contractInfo: {
|
||||
providedCweb: availableCweb - 700n,
|
||||
authenticated: null,
|
||||
},
|
||||
contractArgs: [constructMutexLockClaimRangeRead(issuer)],
|
||||
},
|
||||
},
|
||||
]
|
||||
),
|
||||
];
|
||||
};
|
||||
6
packages/cwait/src/onchain/mutex/methods/names.ts
Normal file
6
packages/cwait/src/onchain/mutex/methods/names.ts
Normal file
@ -0,0 +1,6 @@
|
||||
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';
|
||||
11
packages/cwait/src/onchain/mutex/methods/notifyLock.ts
Normal file
11
packages/cwait/src/onchain/mutex/methods/notifyLock.ts
Normal file
@ -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<MutexNotifyLockArgs>(context);
|
||||
|
||||
return [constructContinueTx(context, [constructMutexBlockLockClaimStore({ lockId })])];
|
||||
};
|
||||
30
packages/cwait/src/onchain/mutex/methods/tryOp.ts
Normal file
30
packages/cwait/src/onchain/mutex/methods/tryOp.ts
Normal file
@ -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<MutexTryOpArgs>(context);
|
||||
|
||||
const claimKey = 'StoreOp' in op ? op.StoreOp.key : op.TakeOp.key;
|
||||
|
||||
const lockQueue = extractRead(extractContractArgs(context.tx)[0])?.map(
|
||||
({ content }) => content as TypedClaim<MutexLockState>
|
||||
);
|
||||
|
||||
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])];
|
||||
};
|
||||
38
packages/cwait/src/onchain/mutex/methods/unlock.ts
Normal file
38
packages/cwait/src/onchain/mutex/methods/unlock.ts
Normal file
@ -0,0 +1,38 @@
|
||||
import { constructContinueTx, constructContractRef, constructTake, Context, passCwebFrom } from '@coinweb/contract-kit';
|
||||
import { getCallParameters, getContractArguments, getContractIssuer } from 'lib/onchain';
|
||||
|
||||
import { constructMutexLockClaimKey } from '../claims';
|
||||
import { lockFee } from '../settings';
|
||||
import { MutexUnlockArgs } from '../types';
|
||||
|
||||
import { lockMethodName } from './names';
|
||||
|
||||
export const mutexUnlock = (context: Context) => {
|
||||
const { availableCweb } = getCallParameters(context);
|
||||
const issuer = getContractIssuer(context);
|
||||
|
||||
const [lockId, timestamp] = getContractArguments<MutexUnlockArgs>(context);
|
||||
|
||||
return [
|
||||
constructContinueTx(
|
||||
context,
|
||||
[passCwebFrom(issuer, availableCweb), constructTake(constructMutexLockClaimKey(lockId, timestamp))],
|
||||
[
|
||||
{
|
||||
callInfo: {
|
||||
ref: constructContractRef(issuer, []),
|
||||
methodInfo: {
|
||||
methodName: lockMethodName,
|
||||
methodArgs: [],
|
||||
},
|
||||
contractInfo: {
|
||||
providedCweb: lockFee,
|
||||
authenticated: null,
|
||||
},
|
||||
contractArgs: [],
|
||||
},
|
||||
},
|
||||
]
|
||||
),
|
||||
];
|
||||
};
|
||||
4
packages/cwait/src/onchain/mutex/settings.ts
Normal file
4
packages/cwait/src/onchain/mutex/settings.ts
Normal file
@ -0,0 +1,4 @@
|
||||
export const lockFee = 5000n;
|
||||
export const unlockFee = 1000n + lockFee;
|
||||
|
||||
export const rangeReadLimit = 10000;
|
||||
26
packages/cwait/src/onchain/mutex/types.ts
Normal file
26
packages/cwait/src/onchain/mutex/types.ts
Normal file
@ -0,0 +1,26 @@
|
||||
import { Claim, ClaimKey, GStore, GTake } from '@coinweb/contract-kit';
|
||||
|
||||
export type LockedKey = Omit<ClaimKey, 'second_part'> & Partial<Pick<ClaimKey, 'second_part'>>;
|
||||
|
||||
export type MutexLockState = {
|
||||
locked: boolean;
|
||||
keys: LockedKey[];
|
||||
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 MutexUnlockArgs = [lockId: string, timestamp: number, notify?: boolean];
|
||||
|
||||
export type MutexTryOpArgs = [op: GTake<Claim> | GStore<Claim>, processId: string];
|
||||
|
||||
export type MutexNotifyLockArgs = [lockId: string];
|
||||
1
packages/cwait/src/onchain/mutex/utils/index.ts
Normal file
1
packages/cwait/src/onchain/mutex/utils/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from './isMatchLockKeys';
|
||||
@ -0,0 +1,9 @@
|
||||
import { LockedKey } from '../types';
|
||||
|
||||
export const isMatchLockKeys = (lockKey1: LockedKey, lockKey2: LockedKey) => {
|
||||
if (!lockKey1.second_part || !lockKey2.second_part) {
|
||||
return lockKey1.first_part === lockKey2.first_part;
|
||||
}
|
||||
|
||||
return lockKey1.first_part === lockKey2.first_part && lockKey1.second_part === lockKey2.second_part;
|
||||
};
|
||||
@ -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);
|
||||
}
|
||||
|
||||
42
packages/cwait/src/onchain/promisifiedOps/ops/lock.ts
Normal file
42
packages/cwait/src/onchain/promisifiedOps/ops/lock.ts
Normal file
@ -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<ResolvedLockOp['LockOp']>((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<ResolvedLockOp['LockOp']> & { [opMarker]: boolean })[opMarker] = opMarkerValue;
|
||||
|
||||
return result;
|
||||
};
|
||||
@ -34,7 +34,7 @@ export const rangeReadOp = <TClaims extends Claim[] = TypedClaim[]>(
|
||||
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) {
|
||||
|
||||
@ -29,7 +29,7 @@ export const readOp = <TClaim extends Claim = TypedClaim>(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) {
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -28,7 +28,7 @@ export const takeOp = <TClaim extends Claim = TypedClaim>(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) {
|
||||
|
||||
38
packages/cwait/src/onchain/promisifiedOps/ops/unlock.ts
Normal file
38
packages/cwait/src/onchain/promisifiedOps/ops/unlock.ts
Normal file
@ -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<void>((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<void> & { [opMarker]: boolean })[opMarker] = opMarkerValue;
|
||||
|
||||
return result;
|
||||
};
|
||||
@ -1,2 +0,0 @@
|
||||
export * from './jumpTx';
|
||||
export * from './tx';
|
||||
@ -1 +0,0 @@
|
||||
export const jumpTx = () => null;
|
||||
@ -1 +0,0 @@
|
||||
export const tx = () => null;
|
||||
@ -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<ResolvedBlock> => {
|
||||
if (op && 'BlockOp' in op) {
|
||||
console.log('isResolvedBlockOp >>> ', JSON.stringify(op));
|
||||
|
||||
@ -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<TBody extends OrdJson = OrdJson, TKey extends ClaimKey = ClaimKey> = 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;
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
VITE_API_URL='https://api-cloud.coinweb.io/wallet'
|
||||
VITE_EXPLORER_URL='https://explorer.coinweb.io'
|
||||
|
||||
VITE_CONTRACT_ADDRESS="0x8c89cb42634cfe892290845d907af8ec2d482cead42afaef3ccc3cea4446fce5"
|
||||
VITE_CONTRACT_ADDRESS="0xbf9ca1687e3441bed1afd495405778a9c06c2f5173829d47d9b87dfb150063d8"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user