Compare commits

...

2 Commits

Author SHA1 Message Date
186b719a94 wip 2025-05-15 10:28:57 +03:00
748cb6f8f5 fix: fix parallel child process execution 2025-05-07 10:12:31 +03:00
45 changed files with 233 additions and 158 deletions

View File

@ -4,4 +4,4 @@ NPM_PASSWORD=
# This is needed when working with WASM modules (nodeLinker: node-modules is mandatory) # This is needed when working with WASM modules (nodeLinker: node-modules is mandatory)
NODE_OPTIONS="--experimental-wasm-modules --no-warnings=ExperimentalWarning --max-old-space-size=8192" NODE_OPTIONS="--experimental-wasm-modules --no-warnings=ExperimentalWarning --max-old-space-size=8192"
REGISTRATION_PROFILE=devnet REGISTRATION_PROFILE=test

View File

@ -47,7 +47,7 @@
"@coinweb/self-register": "0.1.3", "@coinweb/self-register": "0.1.3",
"@coinweb/claims-client": "0.1.6-debug", "@coinweb/claims-client": "0.1.6-debug",
"@coinweb/contract-kit": "0.2.6", "@coinweb/contract-kit": "0.2.6",
"@coinweb/testing-sdk": "0.0.9-remote", "@coinweb/testing-sdk": "0.0.10-mutex",
"@coinweb/minimal-sdk": "1.2.21" "@coinweb/minimal-sdk": "1.2.21"
}, },
"devDependencies": { "devDependencies": {

View File

@ -9,5 +9,5 @@ export enum PUBLIC_METHODS {
} }
export const FEE = { export const FEE = {
ADD_WORD: 100000n, ADD_WORD: 10000000n,
}; };

View File

@ -3,7 +3,8 @@
"version": "0.0.1", "version": "0.0.1",
"type": "module", "type": "module",
"scripts": { "scripts": {
"build": "yarn g:tsc -p tsconfig.build.json", "clean": "rm -rf dist",
"build": "yarn clean && yarn g:tsc -p tsconfig.build.json",
"dev": "yarn g:tsc -p tsconfig.build.json --watch", "dev": "yarn g:tsc -p tsconfig.build.json --watch",
"lint": "yarn g:lint .", "lint": "yarn g:lint .",
"publish:lib": "node scripts/publish.js" "publish:lib": "node scripts/publish.js"

View File

@ -11,7 +11,7 @@ import { CwebTake } from '@coinweb/contract-kit/dist/types/operations/take';
import { getCallParameters, getContractIssuer } from 'lib/onchain'; import { getCallParameters, getContractIssuer } from 'lib/onchain';
import { ExecutorMethodArgs, ResolvedOp } from '../../types'; import { ExecutorMethodArgs, ResolvedOp } from '../../types';
import { pushAwaitedTask } from '../promisifiedFeatures'; import { pushAwaitedTask } from '../runtime';
import { extractOps } from './extractOps'; import { extractOps } from './extractOps';

View File

@ -112,7 +112,7 @@ export const extractOps = ({
throw new Error('Wrong mutex unlock result'); throw new Error('Wrong mutex unlock result');
} }
extractedOps.push({ SlotOp: { ok: true } }); extractedOps.push({ UnlockOp: 0 });
i += 2; i += 2;
continue; continue;

View File

@ -1,69 +0,0 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import { Context, NewTx, getMethodArguments, isSelfCall } from '@coinweb/contract-kit';
import { Task } from '../types';
import { context, getRawContext, handleContext } from './context';
import { getAwaitedTasks } from './promisifiedFeatures';
import { pushResolvedOp } from './promisifiedFeatures/runtime/resolvedOps';
import { constructTx } from './utils';
let abortExecution: ((result: { isFullyExecuted: boolean; awaitedTasks: Task[] }) => void) | null = null;
export const executor = (method: (...args: any[]) => Promise<void>) => {
return async (ctx: Context): Promise<NewTx[]> => {
console.log('executor-start >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>');
const shouldRestart = handleContext(ctx);
pushResolvedOp(context.ops);
if (getMethodArguments(getRawContext()).length > 2 && !isSelfCall(ctx)) {
throw new Error('Wrong contract call, check the call arguments');
}
if (shouldRestart) {
const awaitedTasks = getAwaitedTasks();
return constructTx(awaitedTasks, false);
}
const execution = new Promise<{ isFullyExecuted: boolean; awaitedTasks: Task[] }>((resolve, reject) => {
abortExecution = resolve;
method(...context.initialArgs).then(
() => {
console.log('<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< executor-resolved');
const awaitedTasks = getAwaitedTasks();
resolve({ isFullyExecuted: true, awaitedTasks });
},
(error) => {
console.log(' <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<executor-rejected');
console.log(error);
reject(error);
}
);
});
//@ts-expect-error
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
os.setTimeout(() => {
const awaitedTasks = getAwaitedTasks();
abortExecution?.({ isFullyExecuted: false, awaitedTasks });
}, 0);
try {
const { isFullyExecuted, awaitedTasks } = await execution;
return constructTx(awaitedTasks, isFullyExecuted);
} catch (error) {
console.log('<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< executor-error');
console.log((error as Error).message);
throw error;
}
};
};
export const stopExecution = () => {
console.log('stopExecution');
const awaitedTasks = getAwaitedTasks();
abortExecution?.({ isFullyExecuted: false, awaitedTasks });
};

View File

@ -1,8 +1,10 @@
import { Task } from '../../../types'; import { getAwaitedTasks } from '../../runtime';
import { prepareTx } from './prepareTxs'; import { prepareTx } from './prepareTxs';
export const constructTx = (awaitedTasks: Task[], isFullyExecuted: boolean) => { export const constructTx = (isFullyExecuted: boolean) => {
const awaitedTasks = getAwaitedTasks();
const { calls, txFee } = prepareTx({ awaitedTasks, isFullyExecuted, txFee: 0n }); const { calls, txFee } = prepareTx({ awaitedTasks, isFullyExecuted, txFee: 0n });
const { txs } = prepareTx({ awaitedTasks, isFullyExecuted, txFee, callsCount: calls }); const { txs } = prepareTx({ awaitedTasks, isFullyExecuted, txFee, callsCount: calls });

View File

@ -22,8 +22,8 @@ import {
isPreparedStoreOp, isPreparedStoreOp,
isPreparedTakeOp, isPreparedTakeOp,
isPreparedUnlockOp, isPreparedUnlockOp,
} from '../typeGuards'; } from '../../utils/opTypeGuards';
import { uuid } from '../uuid'; import { uuid } from '../../utils/uuid';
export const prepareInThreadTxs = ({ export const prepareInThreadTxs = ({
cwebPerCall, cwebPerCall,
@ -160,11 +160,12 @@ export const prepareInThreadTxs = ({
break; break;
} }
case isPreparedUnlockOp(op): { case isPreparedUnlockOp(op): {
console.log('prepareInThreadTxs >>> unlockOp');
const { callInfo, fee, ops } = constructUnlockCall( const { callInfo, fee, ops } = constructUnlockCall(
context.issuer, context.issuer,
op.UnlockOp.lockId, op.UnlockOp.lockId,
op.UnlockOp.timestamp, op.UnlockOp.timestamp,
false true
); );
childCalls.push({ callInfo }); childCalls.push({ callInfo });
@ -194,6 +195,9 @@ export const prepareInThreadTxs = ({
childCalls.push({ callInfo }); childCalls.push({ callInfo });
txFee += fee; txFee += fee;
console.log(txFee);
console.log(context.funds.availableCweb);
callArgs.push(...ops); callArgs.push(...ops);
} }
@ -206,6 +210,7 @@ export const prepareInThreadTxs = ({
if ('StoreOp' in latestCallArg && (latestCallArg.StoreOp.key.first_part as [string])[0] === resultKey) { if ('StoreOp' in latestCallArg && (latestCallArg.StoreOp.key.first_part as [string])[0] === resultKey) {
//SAVE RESULT CLAIMS //SAVE RESULT CLAIMS
console.log('SAVE RESULT CLAIMS');
if (callArgs.length > 1) { if (callArgs.length > 1) {
throw new Error('Unexpected count of result ops'); throw new Error('Unexpected count of result ops');
} }
@ -221,10 +226,12 @@ export const prepareInThreadTxs = ({
const cwebToStore = availableCweb + storedCweb - BigInt(takeOps.length) * 100n - 500n; const cwebToStore = availableCweb + storedCweb - BigInt(takeOps.length) * 100n - 500n;
console.log('cwebToStore: ', cwebToStore);
resultOps.push( resultOps.push(
constructFundsClaimStore(context.parentId, cwebToStore), passCwebFrom(context.issuer, availableCweb),
...takeOps, ...takeOps,
passCwebFrom(context.issuer, availableCweb) constructFundsClaimStore(context.parentId, cwebToStore)
); );
} }
@ -235,6 +242,8 @@ export const prepareInThreadTxs = ({
txFee += 800n + outThreadFee + BigInt(takeOps.length) * 100n; txFee += 800n + outThreadFee + BigInt(takeOps.length) * 100n;
callsPrepared++; callsPrepared++;
console.log('provided cweb: ', cwebPerCall - txFee + storedCweb);
returnTxs.push( returnTxs.push(
constructContinueTx( constructContinueTx(
getRawContext(), getRawContext(),

View File

@ -18,7 +18,7 @@ import {
isPreparedStoreOp, isPreparedStoreOp,
isPreparedTakeOp, isPreparedTakeOp,
isPreparedUnlockOp, isPreparedUnlockOp,
} from '../typeGuards'; } from '../../utils/opTypeGuards';
export const prepareOutThreadTxs = ({ export const prepareOutThreadTxs = ({
ops, ops,
@ -95,6 +95,7 @@ export const prepareOutThreadTxs = ({
break; break;
} }
case isPreparedUnlockOp(op): { case isPreparedUnlockOp(op): {
console.log('prepareOutThreadTxs >>> unlockOp');
const { callInfo, fee } = constructUnlockCall(context.issuer, op.UnlockOp.lockId, op.UnlockOp.timestamp, false); const { callInfo, fee } = constructUnlockCall(context.issuer, op.UnlockOp.lockId, op.UnlockOp.timestamp, false);
preparedCalls.push({ callInfo }); preparedCalls.push({ callInfo });

View File

@ -0,0 +1,42 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import { Context, NewTx, getMethodArguments, isSelfCall } from '@coinweb/contract-kit';
import { context, getRawContext, handleContext } from '../context';
import { execLoop, setNextExec, stopExecution } from '../runtime';
import { pushResolvedOp } from '../runtime/resolvedOps';
import { constructTx } from './constructTx';
export const executor = (method: (...args: any[]) => Promise<void>) => {
return async (ctx: Context): Promise<NewTx[]> => {
console.log('executor-start >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>');
const shouldRestart = handleContext(ctx);
pushResolvedOp(context.ops);
if (getMethodArguments(getRawContext()).length > 2 && !isSelfCall(ctx)) {
throw new Error('Wrong contract call, check the call arguments');
}
if (shouldRestart) {
console.log('<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< executor-finish-restart');
return constructTx(false);
}
//@ts-expect-error
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
os.setTimeout(() => {
stopExecution();
}, 0);
try {
setNextExec(() => method(...context.initialArgs));
const isFullyExecuted = await execLoop();
console.log('<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< executor-finish-end');
return constructTx(isFullyExecuted);
} catch (error) {
console.log((error as Error).message);
console.log('<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< executor-error');
throw error;
}
};
};

View File

@ -0,0 +1 @@
export * from './executor';

View File

@ -2,10 +2,9 @@ import { ContractHandlers as ContractHandlersOrig, SELF_REGISTER_HANDLER_NAME }
import { selfRegisterHandler } from '@coinweb/self-register'; import { selfRegisterHandler } from '@coinweb/self-register';
import { queue } from 'lib/onchain'; import { queue } from 'lib/onchain';
import { addMethodHandler, ContractHandlers, executeHandler } from '../contract-kit'; import { addMethodHandler, ContractHandlers, executeHandler } from '../../../contract-kit';
import { executor } from '../../executor';
import { executor } from './executor'; import { mutexMethods } from '../../mutex';
import { mutexMethods } from './mutex';
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
export const constructCwebMain = (methods: Record<string, (...args: any[]) => Promise<void>>) => async () => { export const constructCwebMain = (methods: Record<string, (...args: any[]) => Promise<void>>) => async () => {

View File

@ -0,0 +1 @@
export * from './constructCwebMain';

View File

@ -1,12 +1,23 @@
import { constructStore } from '@coinweb/contract-kit/dist/esm/operations/store'; import { constructStore } from '@coinweb/contract-kit/dist/esm/operations/store';
import { constructResultClaim } from '../../../claims/result'; import { constructResultClaim } from '../../claims/result';
import { stopExecution } from '../../../executor'; import { opMarker } from '../../globals/promise';
import { opMarker } from '../../../global'; import { setNextExec, stopExecution } from '../../runtime';
import { isResolvedChildOp, isResolvedExecOp, isResolvedSlotOp } from '../../../utils'; import {
import { uuid } from '../../../utils'; freezeAwaitedTasks,
import { getAwaitedTasksCount, pushAwaitedTask } from '../../runtime/awaitedTasks'; getAwaitedTasksCount,
import { getUsedOps, saveUsedOps, shiftResolvedOp } from '../../runtime/resolvedOps'; pushAwaitedTask,
unfreezeAwaitedTasks,
} from '../../runtime/awaitedTasks';
import {
freezeResolvedOps,
getUsedOps,
saveUsedOps,
shiftResolvedOp,
unfreezeResolvedOps,
} from '../../runtime/resolvedOps';
import { isResolvedChildOp, isResolvedExecOp, isResolvedSlotOp } from '../../utils';
import { uuid } from '../../utils';
let isRootDetected = false; let isRootDetected = false;
@ -44,13 +55,24 @@ export const cwait = <TAsyncCallback extends (...args: any[]) => Promise<unknown
} }
if (isResolvedExecOp(op)) { if (isResolvedExecOp(op)) {
saveUsedOps(); freezeAwaitedTasks();
freezeResolvedOps();
asyncCallback(...args); setNextExec(async () => {
unfreezeAwaitedTasks();
unfreezeResolvedOps();
if (!getAwaitedTasksCount()) { saveUsedOps();
pushAwaitedTask(constructStore(constructResultClaim(op.ExecOp.id, getUsedOps())));
} await asyncCallback(...args);
if (!getAwaitedTasksCount()) {
console.log('push result claim');
pushAwaitedTask(constructStore(constructResultClaim(op.ExecOp.id, getUsedOps())));
stopExecution();
}
});
stopExecution(); stopExecution();
@ -61,7 +83,7 @@ export const cwait = <TAsyncCallback extends (...args: any[]) => Promise<unknown
return asyncCallback(...args); return asyncCallback(...args);
} }
throw new Error('Read operation not found'); throw new Error('Exec or Child operation not found');
} }
}) as TAsyncCallback; }) as TAsyncCallback;
}; };

View File

@ -1,3 +1,4 @@
export * from './cwait'; export * from './cwait';
export * from './mutex'; export * from './mutex';
export * from './ops'; export * from './ops';
export * from './constructCwebMain';

View File

@ -1,12 +1,12 @@
import { getTime } from 'lib/onchain'; import { getTime } from 'lib/onchain';
import { opMarker } from '../../../global'; import { opMarker } from '../../globals/promise';
import { LockedKey } from '../../../mutex'; import { LockedKey } from '../../mutex';
import { isResolvedLockOp, isResolvedSlotOp, isResolvedUnlockOp, uuid } from '../../../utils';
import { pushAwaitedTask, shiftResolvedOp } from '../../runtime'; import { pushAwaitedTask, shiftResolvedOp } from '../../runtime';
import { isResolvedLockOp, isResolvedSlotOp, isResolvedUnlockOp, uuid } from '../../utils';
const unlock = (lockId: string, timestamp: number) => { const unlock = (lockId: string, timestamp: number) => {
console.log('lockOp'); console.log('unlockOp');
let opMarkerValue = false; let opMarkerValue = false;
const result = new Promise<void>((resolve, reject) => { const result = new Promise<void>((resolve, reject) => {

View File

@ -1,7 +1,7 @@
import { BlockFilter, constructBlock, extractBlock } from '@coinweb/contract-kit'; import { BlockFilter, constructBlock, extractBlock } from '@coinweb/contract-kit';
import { opMarker } from '../../../global'; import { opMarker } from '../../globals/promise';
import { isResolvedBlockOp, isResolvedSlotOp } from '../../../utils'; import { isResolvedBlockOp, isResolvedSlotOp } from '../../utils';
import { pushAwaitedTask, shiftResolvedOp } from '../../runtime'; import { pushAwaitedTask, shiftResolvedOp } from '../../runtime';
export const blockOp = (filters: BlockFilter[]) => { export const blockOp = (filters: BlockFilter[]) => {

View File

@ -1,10 +1,10 @@
import { Claim, ClaimKey, constructRangeRead, extractRead } from '@coinweb/contract-kit'; import { Claim, ClaimKey, constructRangeRead, extractRead } from '@coinweb/contract-kit';
import { ClaimRange } from '@coinweb/contract-kit/dist/types/operations/read'; import { ClaimRange } from '@coinweb/contract-kit/dist/types/operations/read';
import { TypedClaim } from '../../../../types'; import { TypedClaim } from '../../../types';
import { context } from '../../../context'; import { context } from '../../context';
import { opMarker } from '../../../global'; import { opMarker } from '../../globals/promise';
import { isResolvedReadOp, isResolvedSlotOp } from '../../../utils'; import { isResolvedReadOp, isResolvedSlotOp } from '../../utils';
import { pushAwaitedTask, shiftResolvedOp } from '../../runtime'; import { pushAwaitedTask, shiftResolvedOp } from '../../runtime';
export const rangeReadOp = <TClaims extends Claim[] = TypedClaim[]>( export const rangeReadOp = <TClaims extends Claim[] = TypedClaim[]>(

View File

@ -1,10 +1,10 @@
import { Claim, ClaimKey, constructRead, extractRead } from '@coinweb/contract-kit'; import { Claim, ClaimKey, constructRead, extractRead } from '@coinweb/contract-kit';
import { TypedClaim } from '../../../../types'; import { TypedClaim } from '../../../types';
import { context } from '../../../context'; import { context } from '../../context';
import { opMarker } from '../../../global'; import { opMarker } from '../../globals/promise';
import { isResolvedReadOp, isResolvedSlotOp } from '../../../utils';
import { pushAwaitedTask, shiftResolvedOp } from '../../runtime'; import { pushAwaitedTask, shiftResolvedOp } from '../../runtime';
import { isResolvedReadOp, isResolvedSlotOp } from '../../utils';
export const readOp = <TClaim extends Claim = TypedClaim>(key: ClaimKey) => { export const readOp = <TClaim extends Claim = TypedClaim>(key: ClaimKey) => {
let opMarkerValue = false; let opMarkerValue = false;
@ -22,10 +22,14 @@ export const readOp = <TClaim extends Claim = TypedClaim>(key: ClaimKey) => {
} }
if (!isResolvedReadOp(op)) { if (!isResolvedReadOp(op)) {
console.log(JSON.stringify(op));
throw new Error('Read operation not found'); throw new Error('Read operation not found');
} }
const claim = (extractRead(op)?.[0]?.content ?? null) as TClaim | null; const claim = (extractRead(op)?.[0]?.content ?? null) as TClaim | null;
console.log('ResolveRead claim: ', claim);
resolve(claim); resolve(claim);
} }
} catch (error) { } catch (error) {

View File

@ -1,8 +1,8 @@
import { Claim, constructStore } from '@coinweb/contract-kit'; import { Claim, constructStore } from '@coinweb/contract-kit';
import { extractStore } from '@coinweb/contract-kit/dist/esm/operations/store'; import { extractStore } from '@coinweb/contract-kit/dist/esm/operations/store';
import { opMarker } from '../../../global'; import { opMarker } from '../../globals/promise';
import { isResolvedSlotOp, isResolvedStoreOp } from '../../../utils'; import { isResolvedSlotOp, isResolvedStoreOp } from '../../utils';
import { pushAwaitedTask, shiftResolvedOp } from '../../runtime'; import { pushAwaitedTask, shiftResolvedOp } from '../../runtime';
export const storeOp = (claim: Claim, storeCweb?: bigint) => { export const storeOp = (claim: Claim, storeCweb?: bigint) => {

View File

@ -1,8 +1,8 @@
import { Claim, ClaimKey, constructTake, extractTake } from '@coinweb/contract-kit'; import { Claim, ClaimKey, constructTake, extractTake } from '@coinweb/contract-kit';
import { TypedClaim } from '../../../../types'; import { TypedClaim } from '../../../types';
import { opMarker } from '../../../global'; import { opMarker } from '../../globals/promise';
import { isResolvedSlotOp, isResolvedTakeOp } from '../../../utils'; import { isResolvedSlotOp, isResolvedTakeOp } from '../../utils';
import { pushAwaitedTask, shiftResolvedOp } from '../../runtime'; import { pushAwaitedTask, shiftResolvedOp } from '../../runtime';
export const takeOp = <TClaim extends Claim = TypedClaim>(key: ClaimKey) => { export const takeOp = <TClaim extends Claim = TypedClaim>(key: ClaimKey) => {

View File

@ -0,0 +1 @@
export * from './promise';

View File

@ -1,5 +1,8 @@
import { markTaskBatch } from './promisifiedFeatures/runtime/awaitedTasks'; import { markTaskBatch } from '../runtime/awaitedTasks';
import { getBatchId } from './utils';
let batchId = 0;
const getBatchId = () => batchId++;
export const opMarker = Symbol('opMarker'); export const opMarker = Symbol('opMarker');

View File

@ -1,3 +1,3 @@
export * from './context'; import './globals';
export * from './promisifiedFeatures/features';
export * from './constructCwebMain'; export * from './features';

View File

@ -2,6 +2,7 @@ import { constructContractRef, ContractIssuer } from '@coinweb/contract-kit';
import { constructMutexBlockUnlockClaimTake, constructMutexUnlockBlock } from '../claims'; import { constructMutexBlockUnlockClaimTake, constructMutexUnlockBlock } from '../claims';
import { unlockMethodName } from '../methods'; import { unlockMethodName } from '../methods';
import { unlockFee } from '../settings';
import { MutexUnlockArgs } from '../types'; import { MutexUnlockArgs } from '../types';
export const constructUnlockCall = (issuer: ContractIssuer, ...[lockId, timestamp, notify]: MutexUnlockArgs) => { export const constructUnlockCall = (issuer: ContractIssuer, ...[lockId, timestamp, notify]: MutexUnlockArgs) => {
@ -13,7 +14,7 @@ export const constructUnlockCall = (issuer: ContractIssuer, ...[lockId, timestam
methodArgs: [lockId, timestamp] satisfies MutexUnlockArgs, methodArgs: [lockId, timestamp] satisfies MutexUnlockArgs,
}, },
contractInfo: { contractInfo: {
providedCweb: 1000n, providedCweb: unlockFee,
authenticated: null, authenticated: null,
}, },
contractArgs: [], contractArgs: [],
@ -21,6 +22,6 @@ export const constructUnlockCall = (issuer: ContractIssuer, ...[lockId, timestam
ops: notify ops: notify
? ([constructMutexUnlockBlock(lockId, issuer), constructMutexBlockUnlockClaimTake(lockId)] as const) ? ([constructMutexUnlockBlock(lockId, issuer), constructMutexBlockUnlockClaimTake(lockId)] as const)
: [], : [],
fee: 1200n, fee: unlockFee + (notify ? 1000n : 800n),
}; };
}; };

View File

@ -1,7 +1,7 @@
import { constructContinueTx, constructContractRef, constructTake, Context, passCwebFrom } from '@coinweb/contract-kit'; import { constructContinueTx, constructContractRef, constructTake, Context, passCwebFrom } from '@coinweb/contract-kit';
import { getCallParameters, getContractIssuer, getMethodArguments } from 'lib/onchain'; import { getCallParameters, getContractIssuer, getMethodArguments } from 'lib/onchain';
import { constructMutexLockClaimKey } from '../claims'; import { constructMutexBlockUnlockClaimStore, constructMutexLockClaimKey } from '../claims';
import { lockFee } from '../settings'; import { lockFee } from '../settings';
import { MutexUnlockArgs } from '../types'; import { MutexUnlockArgs } from '../types';
@ -16,7 +16,11 @@ export const mutexUnlock = (context: Context) => {
return [ return [
constructContinueTx( constructContinueTx(
context, context,
[passCwebFrom(issuer, availableCweb), constructTake(constructMutexLockClaimKey(lockId, timestamp))], [
passCwebFrom(issuer, availableCweb),
constructTake(constructMutexLockClaimKey(lockId, timestamp)),
constructMutexBlockUnlockClaimStore(lockId),
],
[ [
{ {
callInfo: { callInfo: {

View File

@ -1,2 +0,0 @@
export * from './runtime';
export * from './features';

View File

@ -1,8 +1,13 @@
import { PreparedOp, Task } from '../../../types'; import { PreparedOp, Task } from '../../types';
const awaitedTasks: Task[] = []; const awaitedTasks: Task[] = [];
let isFreezed = false;
export const pushAwaitedTask = (op: PreparedOp) => { export const pushAwaitedTask = (op: PreparedOp) => {
if (isFreezed) {
return;
}
awaitedTasks.push({ op, batchId: -1 }); awaitedTasks.push({ op, batchId: -1 });
}; };
@ -15,3 +20,11 @@ export const markTaskBatch = (count: number, batchId: number) => {
}; };
export const getAwaitedTasksCount = () => awaitedTasks.length; export const getAwaitedTasksCount = () => awaitedTasks.length;
export const freezeAwaitedTasks = () => {
isFreezed = true;
};
export const unfreezeAwaitedTasks = () => {
isFreezed = false;
};

View File

@ -0,0 +1,41 @@
let abortExecution: ((isFullyExecuted: boolean) => void) | null = null;
export const stopExecution = (isFullyExecuted = false) => {
console.log('stopExecution');
abortExecution?.(isFullyExecuted);
};
type ExecTask = () => Promise<void>;
let execQueue: ExecTask[] = [];
export const setNextExec = (task: ExecTask) => (execQueue = [task]);
export const execLoop = async (): Promise<boolean> => {
const nextExec = execQueue.pop();
if (nextExec) {
const execution = new Promise<boolean>((resolve, reject) => {
abortExecution = resolve;
nextExec().then(
() => {
resolve(true);
},
(error) => {
console.log(error);
reject(error);
}
);
});
const isFullyExecuted = await execution;
if (isFullyExecuted) {
return true;
}
return execLoop();
}
return false;
};

View File

@ -1,2 +1,3 @@
export * from './awaitedTasks'; export * from './awaitedTasks';
export * from './execLoop';
export * from './resolvedOps'; export * from './resolvedOps';

View File

@ -1,11 +1,16 @@
import { ResolvedOp } from '../../../types'; import { ResolvedOp } from '../../types';
const resolvedOps: ResolvedOp[] = []; const resolvedOps: ResolvedOp[] = [];
let usedOps: ResolvedOp[] = []; let usedOps: ResolvedOp[] = [];
let isSavingUsed = false; let isSavingUsed = false;
let isFreezed = false;
export const pushResolvedOp = (op: ResolvedOp | ResolvedOp[]) => { export const pushResolvedOp = (op: ResolvedOp | ResolvedOp[]) => {
if (isFreezed) {
return;
}
if (Array.isArray(op)) { if (Array.isArray(op)) {
resolvedOps.push(...op); resolvedOps.push(...op);
} else { } else {
@ -15,8 +20,8 @@ export const pushResolvedOp = (op: ResolvedOp | ResolvedOp[]) => {
export const shiftResolvedOp = () => { export const shiftResolvedOp = () => {
const result = { const result = {
isOp: resolvedOps.length > 0, isOp: resolvedOps.length > 0 && !isFreezed,
op: resolvedOps.shift(), op: isFreezed ? undefined : resolvedOps.shift(),
} as } as
| { | {
isOp: true; isOp: true;
@ -31,6 +36,8 @@ export const shiftResolvedOp = () => {
usedOps.push(result.op); usedOps.push(result.op);
} }
console.log('shiftResolvedOp: ', JSON.stringify(result));
return result; return result;
}; };
@ -40,3 +47,11 @@ export const saveUsedOps = () => {
usedOps = []; usedOps = [];
isSavingUsed = true; isSavingUsed = true;
}; };
export const freezeResolvedOps = () => {
isFreezed = true;
};
export const unfreezeResolvedOps = () => {
isFreezed = false;
};

View File

@ -1,3 +0,0 @@
let batchId = 0;
export const getBatchId = () => batchId++;

View File

@ -1,10 +0,0 @@
export const getStack = ({ skip = 0 }: { skip?: number } = {}) =>
new Error().stack
?.split('\n')
.slice(2 + skip)
.map((line) => {
const match = line.match(/at\s+([^\s(]+)/);
return match ? match[1] : '';
})
.filter((name) => name && name !== 'Promise')
.join('@') || '';

View File

@ -1,5 +1,2 @@
export * from './batchId'; export * from './opTypeGuards';
export * from './callstack';
export * from './constructTx';
export * from './typeGuards';
export * from './uuid'; export * from './uuid';

View File

@ -9,5 +9,5 @@
"esModuleInterop": true, "esModuleInterop": true,
"types": ["vitest/globals"] "types": ["vitest/globals"]
}, },
"include": ["**/*.ts", "vitest.*.ts", "__tests__", "scripts/publish.js", "src/onchain/utils"] "include": ["**/*.ts", "vitest.*.ts", "__tests__", "scripts/publish.js", "src/onchain/utils", "src/onchain/executor/constructTx"]
} }

View File

@ -1,4 +1,4 @@
VITE_API_URL='https://api-cloud.coinweb.io/wallet' VITE_API_URL='https://api-cloud.coinweb.io/wallet'
VITE_EXPLORER_URL='https://explorer.coinweb.io' VITE_EXPLORER_URL='https://explorer.coinweb.io'
VITE_CONTRACT_ADDRESS="0xedca91a29553fa2466e89eef02ac06dc49ab68be5e81fa91d05765cab79ec02a" VITE_CONTRACT_ADDRESS="0xc599810e4861b7b1ae25695d58eeef10556fc84f87f72fafa31c0921e86e92be"

View File

@ -379,16 +379,16 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@coinweb/testing-sdk@npm:0.0.9-remote": "@coinweb/testing-sdk@npm:0.0.10-mutex":
version: 0.0.9-remote version: 0.0.10-mutex
resolution: "@coinweb/testing-sdk@npm:0.0.9-remote" resolution: "@coinweb/testing-sdk@npm:0.0.10-mutex"
dependencies: dependencies:
"@coinweb/contract-kit": "npm:0.2.0" "@coinweb/contract-kit": "npm:0.2.0"
"@coinweb/minimal-sdk": "npm:1.2.19" "@coinweb/minimal-sdk": "npm:1.2.18"
json-stable-stringify: "npm:^1.1.1" json-stable-stringify: "npm:^1.1.1"
lodash.isequal: "npm:^4.5.0" lodash.isequal: "npm:^4.5.0"
secp256k1: "npm:^5.0.0" secp256k1: "npm:^5.0.0"
checksum: 10c0/80212780455d4bc2c1082d62ce382c6d07c6b5a107dc372f630c2257de686ec7e072c5d7a0b966e611b73f8b49ef9d6d8b68fbd66de1d968992a6b18444f218b checksum: 10c0/84785ecc631510aa39189792826cff454700cba96d13d6c9f93f4166ee035fb1c8a8c0f2eab0f51d7d5fe127c661c7964792f8dfacffb9bec5eaf3a6da7fc744
languageName: node languageName: node
linkType: hard linkType: hard