@stacks/rendezvous
Advanced tools
+8
-17
| #!/usr/bin/env node | ||
| "use strict"; | ||
| var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { | ||
| function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } | ||
| return new (P || (P = Promise))(function (resolve, reject) { | ||
| function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } | ||
| function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } | ||
| function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } | ||
| step((generator = generator.apply(thisArg, _arguments || [])).next()); | ||
| }); | ||
| }; | ||
| Object.defineProperty(exports, "__esModule", { value: true }); | ||
@@ -53,3 +44,3 @@ exports.main = exports.getManifestFileName = void 0; | ||
| }; | ||
| const main = () => __awaiter(void 0, void 0, void 0, function* () { | ||
| const main = async () => { | ||
| const radio = new node_events_1.EventEmitter(); | ||
@@ -67,8 +58,8 @@ radio.on("logMessage", (log) => logger(log)); | ||
| (0, cli_1.logRunConfig)(radio, runConfig, manifestPath); | ||
| const simnet = yield (0, clarinet_sdk_1.initSimnet)(manifestPath); | ||
| const simnet = await (0, clarinet_sdk_1.initSimnet)(manifestPath); | ||
| const { eligibleAccounts, allAddresses } = (0, config_1.resolveAccounts)(simnet.getAccounts(), runConfig.accounts, runConfig.accountsMode); | ||
| const resetSession = () => __awaiter(void 0, void 0, void 0, function* () { | ||
| yield (0, clarinet_sdk_1.initSimnet)(manifestPath); | ||
| const resetSession = async () => { | ||
| await (0, clarinet_sdk_1.initSimnet)(manifestPath); | ||
| radio.emit("logMessage", "Simnet session reset."); | ||
| }); | ||
| }; | ||
| /** | ||
@@ -89,11 +80,11 @@ * The list of contract IDs for the SUT contract names, as per the simnet. | ||
| case "invariant": { | ||
| yield (0, invariant_1.checkInvariants)(simnet, resetSession, rendezvousList, rendezvousAllFunctions, runConfig.seed, runConfig.runs, runConfig.dial, runConfig.bail, runConfig.regr, radio, eligibleAccounts, allAddresses); | ||
| await (0, invariant_1.checkInvariants)(simnet, resetSession, rendezvousList, rendezvousAllFunctions, runConfig.seed, runConfig.runs, runConfig.dial, runConfig.bail, runConfig.regr, radio, eligibleAccounts, allAddresses); | ||
| break; | ||
| } | ||
| case "test": { | ||
| yield (0, property_1.checkProperties)(simnet, resetSession, rendezvousList, rendezvousAllFunctions, runConfig.seed, runConfig.runs, runConfig.bail, runConfig.regr, radio, eligibleAccounts, allAddresses); | ||
| await (0, property_1.checkProperties)(simnet, resetSession, rendezvousList, rendezvousAllFunctions, runConfig.seed, runConfig.runs, runConfig.bail, runConfig.regr, radio, eligibleAccounts, allAddresses); | ||
| break; | ||
| } | ||
| } | ||
| }); | ||
| }; | ||
| exports.main = main; | ||
@@ -100,0 +91,0 @@ if (require.main === module) { |
+1
-1
@@ -19,3 +19,3 @@ "use strict"; | ||
| } | ||
| catch (_a) { | ||
| catch { | ||
| throw new Error(`Failed to parse config file as JSON: ${configPath}`); | ||
@@ -22,0 +22,0 @@ } |
+36
-51
@@ -35,11 +35,2 @@ "use strict"; | ||
| })(); | ||
| var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { | ||
| function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } | ||
| return new (P || (P = Promise))(function (resolve, reject) { | ||
| function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } | ||
| function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } | ||
| function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } | ||
| step((generator = generator.apply(thisArg, _arguments || [])).next()); | ||
| }); | ||
| }; | ||
| Object.defineProperty(exports, "__esModule", { value: true }); | ||
@@ -65,47 +56,41 @@ exports.PostDialerError = exports.PreDialerError = exports.DialerRegistry = void 0; | ||
| } | ||
| registerDialers() { | ||
| return __awaiter(this, void 0, void 0, function* () { | ||
| const resolvedDialPath = (0, node_path_1.resolve)(this.dialPath); | ||
| if (!(0, node_fs_1.existsSync)(resolvedDialPath)) { | ||
| console.error(`Error: Dialer file not found: ${resolvedDialPath}`); | ||
| process.exit(1); | ||
| } | ||
| try { | ||
| const userModule = yield Promise.resolve(`${resolvedDialPath}`).then(s => __importStar(require(s))); | ||
| Object.entries(userModule).forEach(([key, fn]) => { | ||
| if (typeof fn === "function") { | ||
| if (key.startsWith("pre")) { | ||
| this.registerPreDialer(fn); | ||
| } | ||
| else if (key.startsWith("post")) { | ||
| this.registerPostDialer(fn); | ||
| } | ||
| async registerDialers() { | ||
| const resolvedDialPath = (0, node_path_1.resolve)(this.dialPath); | ||
| if (!(0, node_fs_1.existsSync)(resolvedDialPath)) { | ||
| console.error(`Error: Dialer file not found: ${resolvedDialPath}`); | ||
| process.exit(1); | ||
| } | ||
| try { | ||
| const userModule = await Promise.resolve(`${resolvedDialPath}`).then(s => __importStar(require(s))); | ||
| Object.entries(userModule).forEach(([key, fn]) => { | ||
| if (typeof fn === "function") { | ||
| if (key.startsWith("pre")) { | ||
| this.registerPreDialer(fn); | ||
| } | ||
| }); | ||
| } | ||
| catch (error) { | ||
| console.error(`Failed to load dialers:`, error); | ||
| process.exit(1); | ||
| } | ||
| }); | ||
| else if (key.startsWith("post")) { | ||
| this.registerPostDialer(fn); | ||
| } | ||
| } | ||
| }); | ||
| } | ||
| catch (error) { | ||
| console.error(`Failed to load dialers:`, error); | ||
| process.exit(1); | ||
| } | ||
| } | ||
| executePreDialers(context) { | ||
| return __awaiter(this, void 0, void 0, function* () { | ||
| if (this.preDialers.length === 0) { | ||
| return; | ||
| } | ||
| for (const dial of this.preDialers) { | ||
| yield dial(context); | ||
| } | ||
| }); | ||
| async executePreDialers(context) { | ||
| if (this.preDialers.length === 0) { | ||
| return; | ||
| } | ||
| for (const dial of this.preDialers) { | ||
| await dial(context); | ||
| } | ||
| } | ||
| executePostDialers(context) { | ||
| return __awaiter(this, void 0, void 0, function* () { | ||
| if (this.postDialers.length === 0) { | ||
| return; | ||
| } | ||
| for (const dial of this.postDialers) { | ||
| yield dial(context); | ||
| } | ||
| }); | ||
| async executePostDialers(context) { | ||
| if (this.postDialers.length === 0) { | ||
| return; | ||
| } | ||
| for (const dial of this.postDialers) { | ||
| await dial(context); | ||
| } | ||
| } | ||
@@ -112,0 +97,0 @@ } |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"invariant.d.ts","sourceRoot":"","sources":["../invariant.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAGhD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AACnD,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,2BAA2B,CAAC;AAa3E,OAAO,KAAK,EAGV,YAAY,EACb,MAAM,mBAAmB,CAAC;AAY3B,OAAO,KAAK,EAAE,iCAAiC,EAAE,MAAM,gBAAgB,CAAC;AASxE;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,eAAe,GAC1B,QAAQ,MAAM,EACd,cAAc,MAAM,OAAO,CAAC,IAAI,CAAC,EACjC,gBAAgB,MAAM,EAAE,EACxB,wBAAwB,GAAG,CAAC,MAAM,EAAE,yBAAyB,EAAE,CAAC,EAChE,MAAM,MAAM,GAAG,SAAS,EACxB,MAAM,MAAM,GAAG,SAAS,EACxB,MAAM,MAAM,GAAG,SAAS,EACxB,MAAM,OAAO,EACb,MAAM,OAAO,EACb,OAAO,YAAY,EACnB,kBAAkB,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EACrC,cAAc,MAAM,EAAE,kBA+MvB,CAAC;AAmaF;;;;;;GAMG;AACH,eAAO,MAAM,sBAAsB,GACjC,YAAY,MAAM,EAClB,WAAW,iCAAiC,EAAE,KAC7C,YAED,CAAC;AAEH;;;;;;GAMG;AACH,eAAO,MAAM,wBAAwB,GACnC,QAAQ,MAAM,EACd,YAAY,MAAM,EAClB,WAAW,iCAAiC,EAAE,SAgB/C,CAAC;AAyCF,qBAAa,uBAAwB,SAAQ,KAAK;IAChD,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;gBAClB,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM;CAIlD"} | ||
| {"version":3,"file":"invariant.d.ts","sourceRoot":"","sources":["../invariant.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAGhD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AACnD,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,2BAA2B,CAAC;AAa3E,OAAO,KAAK,EAGV,YAAY,EACb,MAAM,mBAAmB,CAAC;AAY3B,OAAO,KAAK,EAAE,iCAAiC,EAAE,MAAM,gBAAgB,CAAC;AASxE;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,eAAe,GAC1B,QAAQ,MAAM,EACd,cAAc,MAAM,OAAO,CAAC,IAAI,CAAC,EACjC,gBAAgB,MAAM,EAAE,EACxB,wBAAwB,GAAG,CAAC,MAAM,EAAE,yBAAyB,EAAE,CAAC,EAChE,MAAM,MAAM,GAAG,SAAS,EACxB,MAAM,MAAM,GAAG,SAAS,EACxB,MAAM,MAAM,GAAG,SAAS,EACxB,MAAM,OAAO,EACb,MAAM,OAAO,EACb,OAAO,YAAY,EACnB,kBAAkB,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EACrC,cAAc,MAAM,EAAE,kBA+MvB,CAAC;AAmaF;;;;;;GAMG;AACH,eAAO,MAAM,sBAAsB,GACjC,YAAY,MAAM,EAClB,WAAW,iCAAiC,EAAE,KAC7C,YAED,CAAC;AAEH;;;;;;GAMG;AACH,eAAO,MAAM,wBAAwB,GACnC,QAAQ,MAAM,EACd,YAAY,MAAM,EAClB,WAAW,iCAAiC,EAAE,SAgB/C,CAAC;AAoCF,qBAAa,uBAAwB,SAAQ,KAAK;IAChD,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;gBAClB,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM;CAIlD"} |
+20
-31
| "use strict"; | ||
| var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { | ||
| function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } | ||
| return new (P || (P = Promise))(function (resolve, reject) { | ||
| function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } | ||
| function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } | ||
| function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } | ||
| step((generator = generator.apply(thisArg, _arguments || [])).next()); | ||
| }); | ||
| }; | ||
| var __importDefault = (this && this.__importDefault) || function (mod) { | ||
@@ -45,3 +36,3 @@ return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
| */ | ||
| const checkInvariants = (simnet, resetSession, rendezvousList, rendezvousAllFunctions, seed, runs, dial, bail, regr, radio, eligibleAccounts, allAddresses) => __awaiter(void 0, void 0, void 0, function* () { | ||
| const checkInvariants = async (simnet, resetSession, rendezvousList, rendezvousAllFunctions, seed, runs, dial, bail, regr, radio, eligibleAccounts, allAddresses) => { | ||
| // The Rendezvous identifier is the first one in the list. Only one contract | ||
@@ -121,4 +112,4 @@ // can be fuzzed at a time. | ||
| emitInvariantRegressionTestHeader(radio, targetContractName, regression.seed, regression.numRuns, regression.dial, regression.timestamp); | ||
| yield resetSession(); | ||
| yield invariantTest({ | ||
| await resetSession(); | ||
| await invariantTest({ | ||
| simnet, | ||
@@ -142,3 +133,3 @@ targetContractName, | ||
| radio.emit("logMessage", `Starting fresh round of invariant testing for the ${targetContractName} contract using user-provided configuration...\n`); | ||
| yield invariantTest({ | ||
| await invariantTest({ | ||
| simnet, | ||
@@ -158,3 +149,3 @@ targetContractName, | ||
| } | ||
| }); | ||
| }; | ||
| exports.checkInvariants = checkInvariants; | ||
@@ -167,3 +158,3 @@ /** | ||
| */ | ||
| const invariantTest = (config) => __awaiter(void 0, void 0, void 0, function* () { | ||
| const invariantTest = async (config) => { | ||
| const { simnet, targetContractName, rendezvousContractId, runs, seed, bail, dial, radio, eligibleAccounts, allAddresses, functions, invariants, } = config; | ||
@@ -205,3 +196,3 @@ // Pre-build one fast-check arbitrary per SUT and invariant function via | ||
| } | ||
| const radioReporter = (runDetails) => __awaiter(void 0, void 0, void 0, function* () { | ||
| const radioReporter = async (runDetails) => { | ||
| (0, heatstroke_1.reporter)(runDetails, radio, "invariant", statistics); | ||
@@ -212,3 +203,3 @@ // Persist failures for regression testing. | ||
| } | ||
| }); | ||
| }; | ||
| // Set up local context to track SUT function call counts. | ||
@@ -218,3 +209,3 @@ const localContext = (0, exports.initializeLocalContext)(rendezvousContractId, functions); | ||
| (0, exports.initializeClarityContext)(simnet, rendezvousContractId, functions); | ||
| yield fast_check_1.default.assert(fast_check_1.default.asyncProperty(fast_check_1.default | ||
| await fast_check_1.default.assert(fast_check_1.default.asyncProperty(fast_check_1.default | ||
| .record({ | ||
@@ -235,3 +226,3 @@ // The target contract identifier. It is a constant value equal | ||
| }) | ||
| .map((selectedFunctions) => (Object.assign(Object.assign({}, r), selectedFunctions)))) | ||
| .map((selectedFunctions) => ({ ...r, ...selectedFunctions }))) | ||
| .chain((r) => fast_check_1.default | ||
@@ -246,3 +237,3 @@ .record({ | ||
| }) | ||
| .map((args) => (Object.assign(Object.assign({}, r), args)))) | ||
| .map((args) => ({ ...r, ...args }))) | ||
| .chain((r) => fast_check_1.default | ||
@@ -263,3 +254,3 @@ .record({ | ||
| }) | ||
| .map((burnBlocks) => (Object.assign(Object.assign({}, r), burnBlocks)))), (r) => __awaiter(void 0, void 0, void 0, function* () { | ||
| .map((burnBlocks) => ({ ...r, ...burnBlocks }))), async (r) => { | ||
| for (const [index, selectedFunction] of r.selectedFunctions.entries()) { | ||
@@ -272,3 +263,3 @@ const [sutCallerWallet, sutCallerAddress] = r.sutCallers[index]; | ||
| if (dialerRegistry !== undefined) { | ||
| yield dialerRegistry.executePreDialers({ | ||
| await dialerRegistry.executePreDialers({ | ||
| selectedFunction: selectedFunction, | ||
@@ -295,3 +286,3 @@ functionCall: undefined, | ||
| localContext[r.rendezvousContractId][selectedFunction.name]++; | ||
| simnet.callPublicFn(r.rendezvousContractId, "update-context", [ | ||
| simnet.callPrivateFn(r.rendezvousContractId, "update-context", [ | ||
| transactions_1.Cl.stringAscii(selectedFunction.name), | ||
@@ -310,3 +301,3 @@ transactions_1.Cl.uint(localContext[r.rendezvousContractId][selectedFunction.name]), | ||
| if (dialerRegistry !== undefined) { | ||
| yield dialerRegistry.executePostDialers({ | ||
| await dialerRegistry.executePostDialers({ | ||
| selectedFunction: selectedFunction, | ||
@@ -412,3 +403,3 @@ functionCall: functionCall, | ||
| } | ||
| })), { | ||
| }), { | ||
| endOnFailure: bail, | ||
@@ -420,3 +411,3 @@ numRuns: runs, | ||
| }); | ||
| }); | ||
| }; | ||
| /** | ||
@@ -460,3 +451,3 @@ * Emits warnings for functions that reference traits without eligible | ||
| functions.forEach((fn) => { | ||
| const { result: initialize } = simnet.callPublicFn(contractId, "update-context", [transactions_1.Cl.stringAscii(fn.name), transactions_1.Cl.uint(0)], simnet.deployer); | ||
| const { result: initialize } = simnet.callPrivateFn(contractId, "update-context", [transactions_1.Cl.stringAscii(fn.name), transactions_1.Cl.uint(0)], simnet.deployer); | ||
| const jsonResult = (0, transactions_1.cvToJSON)(initialize); | ||
@@ -474,3 +465,3 @@ if (!jsonResult.value || !jsonResult.success) { | ||
| * The SUT functions are the ones that have `public` access since they are | ||
| * capable of changing the contract state, and they are not test functions. | ||
| * capable of changing the contract state. | ||
| * @param allFunctionsMap The map containing all the functions for each | ||
@@ -483,5 +474,3 @@ * Rendezvous contract. | ||
| contractId, | ||
| functions.filter((f) => f.access === "public" && | ||
| f.name !== "update-context" && | ||
| !f.name.startsWith("test-")), | ||
| functions.filter((f) => f.access === "public"), | ||
| ])); | ||
@@ -488,0 +477,0 @@ const filterInvariantFunctions = (allFunctionsMap) => new Map(Array.from(allFunctionsMap, ([contractId, functions]) => [ |
| { | ||
| "name": "@stacks/rendezvous", | ||
| "version": "1.0.0-rc.1", | ||
| "version": "1.0.0", | ||
| "description": "Meet your contract's vulnerabilities head-on.", | ||
@@ -47,4 +47,4 @@ "main": "dist/lib.js", | ||
| "dependencies": { | ||
| "@stacks/clarinet-sdk": "^3.16.0", | ||
| "@stacks/transactions": "^7.3.1", | ||
| "@stacks/clarinet-sdk": "^3.18.1", | ||
| "@stacks/transactions": "^7.4.0", | ||
| "ansicolor": "^2.0.3", | ||
@@ -55,4 +55,5 @@ "fast-check": "^4.5.3" | ||
| "@ianvs/prettier-plugin-sort-imports": "^4.7.1", | ||
| "@stacks/clarinet-sdk-wasm": "^3.16.0", | ||
| "@stacks/clarinet-sdk-wasm": "^3.18.1", | ||
| "@types/jest": "^30.0.0", | ||
| "@types/node": "^25.6.0", | ||
| "jest": "^30.2.0", | ||
@@ -59,0 +60,0 @@ "oxlint": "^1.58.0", |
@@ -31,3 +31,3 @@ "use strict"; | ||
| } | ||
| catch (_a) { | ||
| catch { | ||
| return { invariant: [], test: [] }; | ||
@@ -58,3 +58,3 @@ } | ||
| const persistFailure = (runDetails, type, contractId, dial, config) => { | ||
| const { baseDir } = Object.assign(Object.assign({}, DEFAULT_CONFIG), config); | ||
| const { baseDir } = { ...DEFAULT_CONFIG, ...config }; | ||
| // Load existing store. | ||
@@ -93,3 +93,3 @@ const store = loadFailureStore(contractId, baseDir); | ||
| const loadFailures = (contractId, type, config) => { | ||
| const { baseDir } = Object.assign(Object.assign({}, DEFAULT_CONFIG), config); | ||
| const { baseDir } = { ...DEFAULT_CONFIG, ...config }; | ||
| const store = loadFailureStore(contractId, baseDir); | ||
@@ -96,0 +96,0 @@ return store[type]; |
+19
-25
| "use strict"; | ||
| var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { | ||
| function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } | ||
| return new (P || (P = Promise))(function (resolve, reject) { | ||
| function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } | ||
| function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } | ||
| function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } | ||
| step((generator = generator.apply(thisArg, _arguments || [])).next()); | ||
| }); | ||
| }; | ||
| var __importDefault = (this && this.__importDefault) || function (mod) { | ||
@@ -43,3 +34,3 @@ return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
| */ | ||
| const checkProperties = (simnet, resetSession, rendezvousList, rendezvousAllFunctions, seed, runs, bail, regr, radio, eligibleAccounts, allAddresses) => __awaiter(void 0, void 0, void 0, function* () { | ||
| const checkProperties = async (simnet, resetSession, rendezvousList, rendezvousAllFunctions, seed, runs, bail, regr, radio, eligibleAccounts, allAddresses) => { | ||
| // A map where the keys are the test contract identifiers and the values are | ||
@@ -116,4 +107,4 @@ // arrays of their test functions. This map will be used to access the test | ||
| emitPropertyRegressionTestHeader(radio, targetContractName, regression.seed, regression.numRuns, regression.timestamp); | ||
| yield resetSession(); | ||
| yield propertyTest({ | ||
| await resetSession(); | ||
| await propertyTest({ | ||
| simnet, | ||
@@ -139,3 +130,3 @@ targetContractName, | ||
| radio.emit("logMessage", `Starting fresh round of property testing for the ${targetContractName} contract using user-provided configuration...\n`); | ||
| yield propertyTest({ | ||
| await propertyTest({ | ||
| simnet, | ||
@@ -154,3 +145,3 @@ targetContractName, | ||
| } | ||
| }); | ||
| }; | ||
| exports.checkProperties = checkProperties; | ||
@@ -163,3 +154,3 @@ /** | ||
| */ | ||
| const propertyTest = (config) => __awaiter(void 0, void 0, void 0, function* () { | ||
| const propertyTest = async (config) => { | ||
| const { simnet, targetContractName, rendezvousContractId, runs, seed, bail, radio, eligibleAccounts, allAddresses, testFunctions, testContractsPairedFunctions, } = config; | ||
@@ -183,3 +174,3 @@ // Pre-build one fast-check arbitrary per test function via the public | ||
| } | ||
| const radioReporter = (runDetails) => __awaiter(void 0, void 0, void 0, function* () { | ||
| const radioReporter = async (runDetails) => { | ||
| (0, heatstroke_1.reporter)(runDetails, radio, "test", statistics); | ||
@@ -192,4 +183,4 @@ // Persist failures for regression testing. | ||
| } | ||
| }); | ||
| yield fast_check_1.default.assert(fast_check_1.default.asyncProperty(fast_check_1.default | ||
| }; | ||
| await fast_check_1.default.assert(fast_check_1.default.asyncProperty(fast_check_1.default | ||
| .record({ | ||
@@ -204,3 +195,6 @@ rendezvousContractId: fast_check_1.default.constant(rendezvousContractId), | ||
| }) | ||
| .map((selectedTestFunction) => (Object.assign(Object.assign({}, r), selectedTestFunction)))) | ||
| .map((selectedTestFunction) => ({ | ||
| ...r, | ||
| ...selectedTestFunction, | ||
| }))) | ||
| .chain((r) => fast_check_1.default | ||
@@ -210,3 +204,3 @@ .record({ | ||
| }) | ||
| .map((args) => (Object.assign(Object.assign({}, r), args)))) | ||
| .map((args) => ({ ...r, ...args }))) | ||
| .chain((r) => fast_check_1.default | ||
@@ -227,3 +221,3 @@ .record({ | ||
| }) | ||
| .map((burnBlocks) => (Object.assign(Object.assign({}, r), burnBlocks)))), (r) => __awaiter(void 0, void 0, void 0, function* () { | ||
| .map((burnBlocks) => ({ ...r, ...burnBlocks }))), async (r) => { | ||
| const printedTestFunctionArgs = r.functionArgs | ||
@@ -251,3 +245,3 @@ .map((cv) => (0, transactions_1.cvToString)(cv)) | ||
| // be caught and logged as a test failure in the catch block. | ||
| const { result: testFunctionCallResult } = simnet.callPublicFn(r.rendezvousContractId, r.selectedTestFunction.name, r.functionArgs, testCallerAddress); | ||
| const { result: testFunctionCallResult } = simnet.callPrivateFn(r.rendezvousContractId, r.selectedTestFunction.name, r.functionArgs, testCallerAddress); | ||
| const testFunctionCallResultJson = (0, transactions_1.cvToJSON)(testFunctionCallResult); | ||
@@ -314,3 +308,3 @@ const discardedInPlace = (0, exports.isTestDiscardedInPlace)(testFunctionCallResultJson); | ||
| } | ||
| })), { | ||
| }), { | ||
| endOnFailure: bail, | ||
@@ -322,3 +316,3 @@ numRuns: runs, | ||
| }); | ||
| }); | ||
| }; | ||
| /** | ||
@@ -350,3 +344,3 @@ * Emits a warning for test functions that reference traits without eligible | ||
| contractId, | ||
| functions.filter((f) => f.access === "public" && f.name.startsWith("test-")), | ||
| functions.filter((f) => f.access === "private" && f.name.startsWith("test-")), | ||
| ])); | ||
@@ -353,0 +347,0 @@ const isTestDiscardedInPlace = (testFunctionCallResultJson) => testFunctionCallResultJson.success === true && |
+1
-1
@@ -156,3 +156,3 @@ "use strict"; | ||
| }; | ||
| return fast_check_1.default.string(Object.assign(Object.assign({}, constraints), { unit: hexa() })); | ||
| return fast_check_1.default.string({ ...constraints, unit: hexa() }); | ||
| }; | ||
@@ -159,0 +159,0 @@ exports.hexaString = hexaString; |
+34
-13
@@ -39,10 +39,15 @@ "use strict"; | ||
| if (arg.type && arg.type.tuple) { | ||
| return Object.assign(Object.assign({}, arg), { type: { | ||
| return { | ||
| ...arg, | ||
| type: { | ||
| tuple: enrichArgs(arg.type.tuple, functionName, listNested | ||
| ? traitRefMapNode.tuple | ||
| : (_e = traitRefMapNode[arg.name]) === null || _e === void 0 ? void 0 : _e.tuple, currentPath), | ||
| } }); | ||
| }, | ||
| }; | ||
| } | ||
| else if (arg.type && arg.type.list) { | ||
| return Object.assign(Object.assign({}, arg), { type: { | ||
| return { | ||
| ...arg, | ||
| type: { | ||
| list: enrichArgs([arg.type.list], functionName, listNested | ||
@@ -57,3 +62,4 @@ ? traitRefMapNode.list | ||
| : [...currentPath, "list"])[0], | ||
| } }); | ||
| }, | ||
| }; | ||
| } | ||
@@ -73,3 +79,5 @@ else if (arg.type && arg.type.response) { | ||
| }, errorPath)[0]; | ||
| return Object.assign(Object.assign({}, arg), { type: { | ||
| return { | ||
| ...arg, | ||
| type: { | ||
| response: { | ||
@@ -79,3 +87,4 @@ ok: okTraitReference.type, | ||
| }, | ||
| } }); | ||
| }, | ||
| }; | ||
| } | ||
@@ -89,5 +98,8 @@ else if (arg.type && arg.type.optional) { | ||
| }, optionalPath)[0]; | ||
| return Object.assign(Object.assign({}, arg), { type: { | ||
| return { | ||
| ...arg, | ||
| type: { | ||
| optional: optionalTraitReference.type, | ||
| } }); | ||
| }, | ||
| }; | ||
| } | ||
@@ -97,3 +109,5 @@ else if (traitRefMapNode && traitRefMapNode[arg.name]) { | ||
| if (traitReferenceName && traitReferenceImport) { | ||
| return Object.assign(Object.assign({}, arg), { type: { | ||
| return { | ||
| ...arg, | ||
| type: { | ||
| trait_reference: { | ||
@@ -103,3 +117,4 @@ name: traitReferenceName, | ||
| }, | ||
| } }); | ||
| }, | ||
| }; | ||
| } | ||
@@ -110,3 +125,5 @@ } | ||
| if (traitReferenceName && traitReferenceImport) { | ||
| return Object.assign(Object.assign({}, arg), { type: { | ||
| return { | ||
| ...arg, | ||
| type: { | ||
| trait_reference: { | ||
@@ -116,3 +133,4 @@ name: traitReferenceName, | ||
| }, | ||
| } }); | ||
| }, | ||
| }; | ||
| } | ||
@@ -122,3 +140,6 @@ } | ||
| }); | ||
| const enrichedFunctions = functionInterfaceList.map((f) => (Object.assign(Object.assign({}, f), { args: enrichArgs(f.args, f.name, traitReferenceMap.get(f.name)) }))); | ||
| const enrichedFunctions = functionInterfaceList.map((f) => ({ | ||
| ...f, | ||
| args: enrichArgs(f.args, f.name, traitReferenceMap.get(f.name)), | ||
| })); | ||
| enriched.set(targetContractId, enrichedFunctions); | ||
@@ -125,0 +146,0 @@ return enriched; |
+5
-4
| { | ||
| "name": "@stacks/rendezvous", | ||
| "version": "1.0.0-rc.1", | ||
| "version": "1.0.0", | ||
| "description": "Meet your contract's vulnerabilities head-on.", | ||
@@ -47,4 +47,4 @@ "main": "dist/lib.js", | ||
| "dependencies": { | ||
| "@stacks/clarinet-sdk": "^3.16.0", | ||
| "@stacks/transactions": "^7.3.1", | ||
| "@stacks/clarinet-sdk": "^3.18.1", | ||
| "@stacks/transactions": "^7.4.0", | ||
| "ansicolor": "^2.0.3", | ||
@@ -55,4 +55,5 @@ "fast-check": "^4.5.3" | ||
| "@ianvs/prettier-plugin-sort-imports": "^4.7.1", | ||
| "@stacks/clarinet-sdk-wasm": "^3.16.0", | ||
| "@stacks/clarinet-sdk-wasm": "^3.18.1", | ||
| "@types/jest": "^30.0.0", | ||
| "@types/node": "^25.6.0", | ||
| "jest": "^30.2.0", | ||
@@ -59,0 +60,0 @@ "oxlint": "^1.58.0", |
+14
-2
@@ -71,3 +71,4 @@ <div align="center"> | ||
| ```clarity | ||
| (define-public (test-reverse-list (seq (list 127 uint))) | ||
| ;; #[env(simnet)] | ||
| (define-private (test-reverse-list (seq (list 127 uint))) | ||
| (begin | ||
@@ -95,2 +96,3 @@ (asserts! | ||
| ```clarity | ||
| ;; #[env(simnet)] | ||
| (define-read-only (invariant-counter-gt-zero) | ||
@@ -140,6 +142,16 @@ (let | ||
| - `getContractFunction(simnet, contract, fn, deployer?)` — retrieves a function interface, enriched with trait data. | ||
| - `strategyFor(simnet, fn, allAddresses?, projectTraitImplementations?)` — returns an `fc.Arbitrary<ClarityValue[]>` ready for use with `simnet.callPublicFn` or `simnet.callReadOnlyFn`. `allAddresses` restricts the principal pool (defaults to every account in the simnet); `projectTraitImplementations` reuses a precomputed trait map (defaults to extracting it from the simnet). | ||
| - `strategyFor(simnet, fn, allAddresses?, projectTraitImplementations?)` — returns an `fc.Arbitrary<ClarityValue[]>` ready for use with `simnet.callPublicFn`, `simnet.callReadOnlyFn`, or `simnet.callPrivateFn`. `allAddresses` restricts the principal pool (defaults to every account in the simnet); `projectTraitImplementations` reuses a precomputed trait map (defaults to extracting it from the simnet). | ||
| --- | ||
| ### Trophy Case | ||
| Real bugs and security vulnerabilities uncovered with `rv`. To add a finding, open a pull request appending a row to the table below. | ||
| | Date | Project | Vulnerability | Invariant/Property | Version | Submitter | | ||
| | ----------- | --------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------ | ------------------------------------------ | | ||
| | May 8, 2026 | [Jing v3](https://github.com/Rapha-btc/jing-contracts-v3) | Cancel-cycle overwrote next-cycle totals rolled forward by the small-share deposit filter, silently wiping the accounting | [`invariant-balance-eq-cycle-totals`](https://github.com/Rapha-btc/jing-contracts-v3/blob/4ff5170ac912b6948f34e5ad4ab3603ee28e1f35/tests/rv/markets-sbtc-stx-jing.invariants.clar#L226-L237) | `1.0.0-rc.1` | [@Rapha-btc](https://github.com/Rapha-btc) | | ||
| --- | ||
| ### Documentation | ||
@@ -146,0 +158,0 @@ |
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
1
-50%161
8.05%222389
-0.93%9
12.5%3355
-0.59%Updated
Updated