Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e3c9659af8 | |||
| 99c541a061 |
@ -14,9 +14,11 @@ includes:
|
|||||||
blake3: 321b1f88930aead7fe47961c8664005b3441f6502824769bf21eb06c3e5aaba4
|
blake3: 321b1f88930aead7fe47961c8664005b3441f6502824769bf21eb06c3e5aaba4
|
||||||
|
|
||||||
use:
|
use:
|
||||||
- dex-app.cm v0.0.67+test
|
- jump-listener.cm v0.1.8
|
||||||
- eth_offer_maker 0.0.67+devnet
|
- jump_listener_devnet v0.1.8
|
||||||
- jump-listener.cm v0.0.5
|
- jump-forwarder.cm v0.1.5
|
||||||
|
- jump_forwarder_devnet v0.1.5
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
interpreters: {}
|
interpreters: {}
|
||||||
@ -31,3 +33,4 @@ contract_instances:
|
|||||||
template: contract.cm v0.0.1
|
template: contract.cm v0.0.1
|
||||||
parameters:
|
parameters:
|
||||||
content: []
|
content: []
|
||||||
|
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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": {
|
||||||
|
|||||||
@ -9,5 +9,5 @@ export enum PUBLIC_METHODS {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const FEE = {
|
export const FEE = {
|
||||||
ADD_WORD: 100000n,
|
ADD_WORD: 10000000n,
|
||||||
};
|
};
|
||||||
|
|||||||
94
packages/contract.cm/src/onchain/__tests__/handlers.test.ts
Normal file
94
packages/contract.cm/src/onchain/__tests__/handlers.test.ts
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
import path from 'node:path';
|
||||||
|
|
||||||
|
import { constructContractIssuer, constructContractRef, type PreparedCallInfo } from '@coinweb/contract-kit';
|
||||||
|
import { UnitTest, type ExecInfo, type RoundInfo, type UnitTestContext, type DbWriteOp } from '@coinweb/testing-sdk';
|
||||||
|
import { describe, it, expect, beforeAll } from 'vitest';
|
||||||
|
|
||||||
|
import { AddWordArgs, FEE, PUBLIC_METHODS } from '../../offchain/shared';
|
||||||
|
|
||||||
|
import { CONTRACT_INSTANCE_ID, TEST_PRIVATE_KEY, TEST_PUBLIC_KEY, waitRoundsCount } from './helpers';
|
||||||
|
|
||||||
|
const addWordRound = (creatorAccount: Buffer): RoundInfo => {
|
||||||
|
const self = constructContractIssuer(CONTRACT_INSTANCE_ID);
|
||||||
|
|
||||||
|
const kvDeleteCallInfo: PreparedCallInfo = {
|
||||||
|
ref: constructContractRef(self, []),
|
||||||
|
methodInfo: { methodName: PUBLIC_METHODS.ADD_WORD, methodArgs: ['TEST_HELLO'] satisfies AddWordArgs },
|
||||||
|
contractArgs: [],
|
||||||
|
contractInfo: { providedCweb: FEE.ADD_WORD, authenticated: null }, // null = authenticated account in tests
|
||||||
|
};
|
||||||
|
|
||||||
|
const withFunds = { type: { privateKey: creatorAccount } };
|
||||||
|
|
||||||
|
return {
|
||||||
|
txsInfo: {
|
||||||
|
// @ts-ignore
|
||||||
|
txs: [{ callInfo: kvDeleteCallInfo, withFunds }],
|
||||||
|
l1_events: [],
|
||||||
|
},
|
||||||
|
claims: [],
|
||||||
|
blocks_on: [],
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const runTest = async (context: UnitTestContext, input: ExecInfo, checkFn: (results: DbWriteOp[]) => void) => {
|
||||||
|
const test = new UnitTest(context);
|
||||||
|
await test.load(path.join(import.meta.dirname, '../../../tests_data/state.json'));
|
||||||
|
await test.run(input, { checkFn });
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('PUBLIC_METHODS.ADD_WORD Tests', () => {
|
||||||
|
let creatorAccount: { publicKey: Buffer; privateKey: Buffer };
|
||||||
|
|
||||||
|
beforeAll(() => {
|
||||||
|
creatorAccount = {
|
||||||
|
privateKey: Buffer.from(TEST_PRIVATE_KEY, 'hex'),
|
||||||
|
publicKey: Buffer.from(TEST_PUBLIC_KEY, 'hex'),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should successfully execute', async () => {
|
||||||
|
const input: ExecInfo = {
|
||||||
|
rounds: [...waitRoundsCount(50), addWordRound(creatorAccount.privateKey), ...waitRoundsCount(2)],
|
||||||
|
};
|
||||||
|
|
||||||
|
const checkFn = (results: DbWriteOp[]) => {
|
||||||
|
expect(results).toBeDefined();
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
// const errors = findAllValuesRecursivelyByKey<{ ContractError: ContractError }>(results, 'Invalid');
|
||||||
|
// expect(errors.length).toBe(0);
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
// const storeOps = findAllValuesRecursivelyByKey<CwebStore>(results, 'StoreOp');
|
||||||
|
// expect(storeOps.length).toBeGreaterThan(0);
|
||||||
|
|
||||||
|
// const existingPayload = storeOps.find((s) => isEqual(s.key.first_part, createFirstPartDefault()));
|
||||||
|
|
||||||
|
// expect(existingPayload).toBeDefined();
|
||||||
|
// expect(existingPayload?.key).toStrictEqual(createDefaultKey(Number.MAX_SAFE_INTEGER));
|
||||||
|
// expect(existingPayload?.key.first_part).toStrictEqual(createFirstPartDefault());
|
||||||
|
|
||||||
|
// const { id, data, owner, tx } = existingPayload?.body as StoreKvPayload;
|
||||||
|
// expect(id).toBeDefined();
|
||||||
|
// expect(data).toBeDefined();
|
||||||
|
// expect(owner).toBeDefined();
|
||||||
|
// expect(tx).toBeDefined();
|
||||||
|
// expect(tx?.timestamp).toBeDefined();
|
||||||
|
// expect(tx?.coinwebTxId).toBeDefined();
|
||||||
|
// expect(Number(id)).toStrictEqual(Number.MAX_SAFE_INTEGER);
|
||||||
|
// expect(owner).toStrictEqual({
|
||||||
|
// auth: 'EcdsaContract',
|
||||||
|
// payload: TEST_PUBLIC_KEY,
|
||||||
|
// });
|
||||||
|
};
|
||||||
|
|
||||||
|
const context: UnitTestContext = {
|
||||||
|
name: 'add_word',
|
||||||
|
testPath: path.join(import.meta.dirname, './tests_data/add_word'),
|
||||||
|
verbose: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
await runTest(context, input, checkFn);
|
||||||
|
});
|
||||||
|
});
|
||||||
35
packages/contract.cm/src/onchain/__tests__/helpers.ts
Normal file
35
packages/contract.cm/src/onchain/__tests__/helpers.ts
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
import path from 'node:path';
|
||||||
|
|
||||||
|
import { getInstanceFromIndex, type RoundInfo } from '@coinweb/testing-sdk';
|
||||||
|
|
||||||
|
const onchainPackage = (await import('../../../dist/out/package.json')) as {
|
||||||
|
name: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const CONTRACT_INSTANCE = await getInstanceFromIndex({
|
||||||
|
path: path.resolve(import.meta.dirname, '../../../tests_data/index.yaml'),
|
||||||
|
instance_alias: 'cwait-contract 0.0.1-test',
|
||||||
|
});
|
||||||
|
|
||||||
|
export const CONTRACT_INSTANCE_ID = '0x'.concat(CONTRACT_INSTANCE.instance_id.replace(/0x/, ''));
|
||||||
|
|
||||||
|
export const CONTRACT_TEMPLATE_ID = `0x${onchainPackage.name.substring(5)}`;
|
||||||
|
|
||||||
|
export const TEST_PRIVATE_KEY = '31c70848e4e3aaffcf91f134853ec966e913aa9a813115bcb81512e7625f46a9';
|
||||||
|
export const TEST_PUBLIC_KEY = '03951f89fe78e13f295d96eb7afa1e0da726df7d58f9c84f7144e5febc30efeec4';
|
||||||
|
|
||||||
|
export const waitRoundsCount = (roundsCount: number): RoundInfo[] => {
|
||||||
|
const emptyRound: RoundInfo = {
|
||||||
|
txsInfo: {
|
||||||
|
txs: [],
|
||||||
|
l1_events: [],
|
||||||
|
},
|
||||||
|
claims: [],
|
||||||
|
blocks_on: [],
|
||||||
|
};
|
||||||
|
const rounds: RoundInfo[] = [];
|
||||||
|
for (let i = 0; i < roundsCount; i++) {
|
||||||
|
rounds.push(emptyRound);
|
||||||
|
}
|
||||||
|
return rounds;
|
||||||
|
};
|
||||||
14
packages/contract.cm/vitest.config.ts
Normal file
14
packages/contract.cm/vitest.config.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import { defineConfig } from 'vitest/config';
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
test: {
|
||||||
|
globals: true,
|
||||||
|
environment: 'node',
|
||||||
|
reporters: 'default',
|
||||||
|
silent: false,
|
||||||
|
testTimeout: 240000,
|
||||||
|
teardownTimeout: 240000,
|
||||||
|
hookTimeout: 240000,
|
||||||
|
pool: 'forks',
|
||||||
|
},
|
||||||
|
});
|
||||||
@ -58,7 +58,7 @@ export const handleContext = (ctx: Context) => {
|
|||||||
caller,
|
caller,
|
||||||
thisId,
|
thisId,
|
||||||
parentId,
|
parentId,
|
||||||
saveResult,
|
shouldSaveResult,
|
||||||
takenFundsIds = [],
|
takenFundsIds = [],
|
||||||
execOpsIndexes = [],
|
execOpsIndexes = [],
|
||||||
] = getMethodArgs();
|
] = getMethodArgs();
|
||||||
@ -70,7 +70,7 @@ export const handleContext = (ctx: Context) => {
|
|||||||
initialContext.parentId = parentId;
|
initialContext.parentId = parentId;
|
||||||
initialContext.methodName = methodName;
|
initialContext.methodName = methodName;
|
||||||
initialContext.initialArgs = initialArgs ?? [];
|
initialContext.initialArgs = initialArgs ?? [];
|
||||||
initialContext.needSaveResult = saveResult ?? false;
|
initialContext.needSaveResult = shouldSaveResult ?? false;
|
||||||
|
|
||||||
const { authInfo } = getCallParameters(getRawContext());
|
const { authInfo } = getCallParameters(getRawContext());
|
||||||
initialContext.user = (authInfo && extractUser(authInfo)) ?? caller ?? null;
|
initialContext.user = (authInfo && extractUser(authInfo)) ?? caller ?? null;
|
||||||
|
|||||||
@ -86,7 +86,11 @@ export const extractOps = ({
|
|||||||
throw new Error('Wrong subcall result');
|
throw new Error('Wrong subcall result');
|
||||||
}
|
}
|
||||||
|
|
||||||
extractedOps.push({ ChildOp: 0 }, ...(nextAfterBlock.TakeOp.result as TypedClaim<ResolvedOp[]>).body);
|
extractedOps.push({
|
||||||
|
ChildOp: {
|
||||||
|
ops: (nextAfterBlock.TakeOp.result as TypedClaim<ResolvedOp[]>).body,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
i += 2;
|
i += 2;
|
||||||
continue;
|
continue;
|
||||||
@ -112,7 +116,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;
|
||||||
|
|||||||
@ -11,7 +11,7 @@ import {
|
|||||||
} from '@coinweb/contract-kit';
|
} from '@coinweb/contract-kit';
|
||||||
import { CwebTake } from '@coinweb/contract-kit/dist/types/operations/take';
|
import { CwebTake } from '@coinweb/contract-kit/dist/types/operations/take';
|
||||||
|
|
||||||
import { ExecutorMethodArgs, PreparedExtendedStoreOp, PreparedOp, ResolvedOp, ResolvedSlotOp } from '../../../types';
|
import { ExecutorMethodArgs, PreparedExtendedStoreOp, PreparedOp, ResolvedSlotOp } from '../../../types';
|
||||||
import { constructFundsClaimRangRead, constructFundsClaimStore } from '../../claims/funds';
|
import { constructFundsClaimRangRead, constructFundsClaimStore } from '../../claims/funds';
|
||||||
import { constructResultBlockFilter, constructResultClaimTake, resultKey } from '../../claims/result';
|
import { constructResultBlockFilter, constructResultClaimTake, resultKey } from '../../claims/result';
|
||||||
import { context, getRawContext } from '../../context';
|
import { context, getRawContext } from '../../context';
|
||||||
@ -68,16 +68,18 @@ export const prepareInThreadTxs = ({
|
|||||||
let callsPrepared = 0;
|
let callsPrepared = 0;
|
||||||
const resolvedSlotOps = new Array(outThreadTasksCount).fill({ SlotOp: { ok: true } }) satisfies ResolvedSlotOp[];
|
const resolvedSlotOps = new Array(outThreadTasksCount).fill({ SlotOp: { ok: true } }) satisfies ResolvedSlotOp[];
|
||||||
|
|
||||||
|
//Mutex exec ops
|
||||||
const preparedExecOps: (PreparedExtendedStoreOp | GTake<CwebTake>)[] = [];
|
const preparedExecOps: (PreparedExtendedStoreOp | GTake<CwebTake>)[] = [];
|
||||||
const excOpsIndexes: number[] = [];
|
const excOpsIndexes: number[] = [];
|
||||||
|
|
||||||
const resolvedChildOps: ResolvedOp[] = [...context.ops, ...resolvedSlotOps];
|
// const resolvedChildOps: ResolvedOp[] = [...context.ops, ...resolvedSlotOps];
|
||||||
|
//Children
|
||||||
|
|
||||||
//Arg for the main call
|
//Arg for the main call
|
||||||
const callArgs: PreparedOperation[] = [];
|
const callArgs: PreparedOperation[] = [];
|
||||||
|
|
||||||
//Info for separate child call
|
//Info for separate parallel calls
|
||||||
const childCalls: FullCallInfo[] = [];
|
const parallelCalls: FullCallInfo[] = [];
|
||||||
|
|
||||||
const outThreadOps: PreparedOperation[] = [];
|
const outThreadOps: PreparedOperation[] = [];
|
||||||
|
|
||||||
@ -89,14 +91,20 @@ export const prepareInThreadTxs = ({
|
|||||||
callArgs.push(constructBlock([constructResultBlockFilter(id)]), constructResultClaimTake(id));
|
callArgs.push(constructBlock([constructResultBlockFilter(id)]), constructResultClaimTake(id));
|
||||||
txFee += 200n;
|
txFee += 200n;
|
||||||
|
|
||||||
childCalls.push({
|
const childOps = [
|
||||||
|
...context.ops,
|
||||||
|
...resolvedSlotOps,
|
||||||
|
...ops.map((_, j) => (i === j ? { ExecOp: { id } } : { SlotOp: { ok: true } })),
|
||||||
|
];
|
||||||
|
|
||||||
|
parallelCalls.push({
|
||||||
callInfo: {
|
callInfo: {
|
||||||
ref: constructContractRef(context.issuer, []),
|
ref: constructContractRef(context.issuer, []),
|
||||||
methodInfo: {
|
methodInfo: {
|
||||||
methodName: context.methodName,
|
methodName: context.methodName,
|
||||||
methodArgs: [
|
methodArgs: [
|
||||||
context.initialArgs,
|
context.initialArgs,
|
||||||
[...resolvedChildOps, { ExecOp: { id } }],
|
childOps,
|
||||||
context.user,
|
context.user,
|
||||||
id,
|
id,
|
||||||
context.thisId,
|
context.thisId,
|
||||||
@ -151,7 +159,7 @@ export const prepareInThreadTxs = ({
|
|||||||
processId: context.thisId,
|
processId: context.thisId,
|
||||||
});
|
});
|
||||||
|
|
||||||
childCalls.push({ callInfo });
|
parallelCalls.push({ callInfo });
|
||||||
callArgs.push(...inThreadOps);
|
callArgs.push(...inThreadOps);
|
||||||
txFee += fee;
|
txFee += fee;
|
||||||
|
|
||||||
@ -160,14 +168,15 @@ 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 });
|
parallelCalls.push({ callInfo });
|
||||||
callArgs.push(...ops);
|
callArgs.push(...ops);
|
||||||
txFee += fee;
|
txFee += fee;
|
||||||
|
|
||||||
@ -177,8 +186,6 @@ export const prepareInThreadTxs = ({
|
|||||||
callArgs.push(op);
|
callArgs.push(op);
|
||||||
txFee += 100n;
|
txFee += 100n;
|
||||||
}
|
}
|
||||||
|
|
||||||
resolvedChildOps.push({ SlotOp: { ok: true } });
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (preparedExecOps.length > 0) {
|
if (preparedExecOps.length > 0) {
|
||||||
@ -191,9 +198,12 @@ export const prepareInThreadTxs = ({
|
|||||||
execId,
|
execId,
|
||||||
});
|
});
|
||||||
|
|
||||||
childCalls.push({ callInfo });
|
parallelCalls.push({ callInfo });
|
||||||
txFee += fee;
|
txFee += fee;
|
||||||
|
|
||||||
|
console.log(txFee);
|
||||||
|
console.log(context.funds.availableCweb);
|
||||||
|
|
||||||
callArgs.push(...ops);
|
callArgs.push(...ops);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -206,6 +216,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 +232,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 +248,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(),
|
||||||
@ -267,8 +282,8 @@ export const prepareInThreadTxs = ({
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
if (childCalls.length || outThreadOps.length) {
|
if (parallelCalls.length || outThreadOps.length) {
|
||||||
returnTxs.push(constructContinueTx(getRawContext(), outThreadOps, childCalls));
|
returnTxs.push(constructContinueTx(getRawContext(), outThreadOps, parallelCalls));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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 });
|
||||||
|
|||||||
@ -18,7 +18,7 @@ export const executor = (method: (...args: any[]) => Promise<void>) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (shouldRestart) {
|
if (shouldRestart) {
|
||||||
console.log('<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< executor-finish');
|
console.log('<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< executor-finish-restart');
|
||||||
return constructTx(false);
|
return constructTx(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -29,9 +29,9 @@ export const executor = (method: (...args: any[]) => Promise<void>) => {
|
|||||||
}, 0);
|
}, 0);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
setNextExec(() => method(...context.initialArgs));
|
setNextExec(() => method(...context.initialArgs), []);
|
||||||
const isFullyExecuted = await execLoop();
|
const isFullyExecuted = await execLoop();
|
||||||
console.log('<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< executor-finish');
|
console.log('<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< executor-finish-end');
|
||||||
return constructTx(isFullyExecuted);
|
return constructTx(isFullyExecuted);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log((error as Error).message);
|
console.log((error as Error).message);
|
||||||
|
|||||||
@ -3,19 +3,8 @@ import { constructStore } from '@coinweb/contract-kit/dist/esm/operations/store'
|
|||||||
import { constructResultClaim } from '../../claims/result';
|
import { constructResultClaim } from '../../claims/result';
|
||||||
import { opMarker } from '../../globals/promise';
|
import { opMarker } from '../../globals/promise';
|
||||||
import { setNextExec, stopExecution } from '../../runtime';
|
import { setNextExec, stopExecution } from '../../runtime';
|
||||||
import {
|
import { getAwaitedTasksCount, pushAwaitedTask } from '../../runtime/awaitedTasks';
|
||||||
freezeAwaitedTasks,
|
import { getUsedOps, startSavingUsedOps, stopSavingUsedOps, shiftResolvedOp } from '../../runtime/resolvedOps';
|
||||||
getAwaitedTasksCount,
|
|
||||||
pushAwaitedTask,
|
|
||||||
unfreezeAwaitedTasks,
|
|
||||||
} from '../../runtime/awaitedTasks';
|
|
||||||
import {
|
|
||||||
freezeResolvedOps,
|
|
||||||
getUsedOps,
|
|
||||||
saveUsedOps,
|
|
||||||
shiftResolvedOp,
|
|
||||||
unfreezeResolvedOps,
|
|
||||||
} from '../../runtime/resolvedOps';
|
|
||||||
import { isResolvedChildOp, isResolvedExecOp, isResolvedSlotOp } from '../../utils';
|
import { isResolvedChildOp, isResolvedExecOp, isResolvedSlotOp } from '../../utils';
|
||||||
import { uuid } from '../../utils';
|
import { uuid } from '../../utils';
|
||||||
|
|
||||||
@ -55,32 +44,28 @@ export const cwait = <TAsyncCallback extends (...args: any[]) => Promise<unknown
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (isResolvedExecOp(op)) {
|
if (isResolvedExecOp(op)) {
|
||||||
freezeAwaitedTasks();
|
|
||||||
freezeResolvedOps();
|
|
||||||
|
|
||||||
setNextExec(async () => {
|
setNextExec(async () => {
|
||||||
unfreezeAwaitedTasks();
|
startSavingUsedOps();
|
||||||
unfreezeResolvedOps();
|
|
||||||
|
|
||||||
saveUsedOps();
|
|
||||||
|
|
||||||
await asyncCallback(...args);
|
await asyncCallback(...args);
|
||||||
|
stopSavingUsedOps();
|
||||||
|
|
||||||
if (!getAwaitedTasksCount()) {
|
if (!getAwaitedTasksCount()) {
|
||||||
|
console.log('push result claim');
|
||||||
|
|
||||||
pushAwaitedTask(constructStore(constructResultClaim(op.ExecOp.id, getUsedOps())));
|
pushAwaitedTask(constructStore(constructResultClaim(op.ExecOp.id, getUsedOps())));
|
||||||
}
|
}
|
||||||
});
|
}, []);
|
||||||
|
|
||||||
stopExecution();
|
stopExecution(); //Check: maybe does no affect
|
||||||
|
|
||||||
return new Promise(() => null);
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isResolvedChildOp(op)) {
|
if (isResolvedChildOp(op)) {
|
||||||
return asyncCallback(...args);
|
return setNextExec(() => asyncCallback(...args), op.ChildOp.ops);
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new Error('Read operation not found');
|
throw new Error('Exec or Child operation not found');
|
||||||
}
|
}
|
||||||
}) as TAsyncCallback;
|
}) as TAsyncCallback;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -2,11 +2,11 @@ import { getTime } from 'lib/onchain';
|
|||||||
|
|
||||||
import { opMarker } from '../../globals/promise';
|
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) => {
|
||||||
|
|||||||
@ -3,8 +3,8 @@ import { Claim, ClaimKey, constructRead, extractRead } from '@coinweb/contract-k
|
|||||||
import { TypedClaim } from '../../../types';
|
import { TypedClaim } from '../../../types';
|
||||||
import { context } from '../../context';
|
import { context } from '../../context';
|
||||||
import { opMarker } from '../../globals/promise';
|
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) {
|
||||||
|
|||||||
@ -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),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@ -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: {
|
||||||
|
|||||||
@ -1,13 +1,8 @@
|
|||||||
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 });
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -20,11 +15,3 @@ 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;
|
|
||||||
};
|
|
||||||
|
|||||||
@ -1,3 +1,7 @@
|
|||||||
|
import { ResolvedOp } from '../../types';
|
||||||
|
|
||||||
|
import { pushResolvedOp } from './resolvedOps';
|
||||||
|
|
||||||
let abortExecution: ((isFullyExecuted: boolean) => void) | null = null;
|
let abortExecution: ((isFullyExecuted: boolean) => void) | null = null;
|
||||||
|
|
||||||
export const stopExecution = (isFullyExecuted = false) => {
|
export const stopExecution = (isFullyExecuted = false) => {
|
||||||
@ -5,10 +9,18 @@ export const stopExecution = (isFullyExecuted = false) => {
|
|||||||
abortExecution?.(isFullyExecuted);
|
abortExecution?.(isFullyExecuted);
|
||||||
};
|
};
|
||||||
|
|
||||||
type ExecTask = () => Promise<void>;
|
type ExecTask = () => Promise<unknown>;
|
||||||
|
type Exec = { task: ExecTask; ops: ResolvedOp[] };
|
||||||
|
|
||||||
let execQueue: ExecTask[] = [];
|
const execQueue: Exec[] = [];
|
||||||
export const setNextExec = (task: ExecTask) => (execQueue = [task]);
|
export const setNextExec = (task: ExecTask, ops: ResolvedOp[]) => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
execQueue.push({
|
||||||
|
task: () => task().then(resolve, reject),
|
||||||
|
ops,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
export const execLoop = async (): Promise<boolean> => {
|
export const execLoop = async (): Promise<boolean> => {
|
||||||
const nextExec = execQueue.pop();
|
const nextExec = execQueue.pop();
|
||||||
@ -17,7 +29,8 @@ export const execLoop = async (): Promise<boolean> => {
|
|||||||
const execution = new Promise<boolean>((resolve, reject) => {
|
const execution = new Promise<boolean>((resolve, reject) => {
|
||||||
abortExecution = resolve;
|
abortExecution = resolve;
|
||||||
|
|
||||||
nextExec().then(
|
pushResolvedOp(nextExec.ops);
|
||||||
|
nextExec.task().then(
|
||||||
() => {
|
() => {
|
||||||
resolve(true);
|
resolve(true);
|
||||||
},
|
},
|
||||||
|
|||||||
@ -36,16 +36,22 @@ export const shiftResolvedOp = () => {
|
|||||||
usedOps.push(result.op);
|
usedOps.push(result.op);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log('shiftResolvedOp: ', JSON.stringify(result));
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getUsedOps = () => usedOps;
|
export const getUsedOps = () => usedOps;
|
||||||
|
|
||||||
export const saveUsedOps = () => {
|
export const startSavingUsedOps = () => {
|
||||||
usedOps = [];
|
usedOps = [];
|
||||||
isSavingUsed = true;
|
isSavingUsed = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const stopSavingUsedOps = () => {
|
||||||
|
isSavingUsed = false;
|
||||||
|
};
|
||||||
|
|
||||||
export const freezeResolvedOps = () => {
|
export const freezeResolvedOps = () => {
|
||||||
isFreezed = true;
|
isFreezed = true;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -37,7 +37,9 @@ export type ResolvedExecOp = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export type ResolvedChildOp = {
|
export type ResolvedChildOp = {
|
||||||
ChildOp: 0;
|
ChildOp: {
|
||||||
|
ops: ResolvedOp[];
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ResolvedLockOp = {
|
export type ResolvedLockOp = {
|
||||||
@ -100,7 +102,7 @@ export type ExecutorMethodArgs = [
|
|||||||
caller?: User,
|
caller?: User,
|
||||||
thisId?: string,
|
thisId?: string,
|
||||||
parentId?: string,
|
parentId?: string,
|
||||||
saveResult?: boolean,
|
shouldSaveResult?: boolean,
|
||||||
takenFundsIds?: string[],
|
takenFundsIds?: string[],
|
||||||
execOpsIndexes?: number[],
|
execOpsIndexes?: number[],
|
||||||
];
|
];
|
||||||
|
|||||||
@ -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"
|
||||||
|
|||||||
10
yarn.lock
10
yarn.lock
@ -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
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user