@agenc/sdk 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +193 -0
- package/dist/bin/agenc.js +394 -0
- package/dist/bin/agenc.mjs +248 -0
- package/dist/chunk-QRZGQS77.mjs +273 -0
- package/dist/index.js +620 -0
- package/dist/index.mjs +372 -0
- package/package.json +65 -0
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
DEVNET_RPC,
|
|
4
|
+
MAINNET_RPC,
|
|
5
|
+
PROGRAM_ID,
|
|
6
|
+
PROOF_SIZE_BYTES,
|
|
7
|
+
VERIFICATION_COMPUTE_UNITS,
|
|
8
|
+
VERIFIER_PROGRAM_ID,
|
|
9
|
+
checkToolsAvailable,
|
|
10
|
+
deriveTaskPda,
|
|
11
|
+
formatTaskState,
|
|
12
|
+
generateProof,
|
|
13
|
+
generateSalt,
|
|
14
|
+
verifyProofLocally
|
|
15
|
+
} from "../chunk-QRZGQS77.mjs";
|
|
16
|
+
|
|
17
|
+
// src/bin/agenc.ts
|
|
18
|
+
import { Command } from "commander";
|
|
19
|
+
import chalk from "chalk";
|
|
20
|
+
import ora from "ora";
|
|
21
|
+
import { Connection, Keypair, LAMPORTS_PER_SOL } from "@solana/web3.js";
|
|
22
|
+
import * as fs from "fs";
|
|
23
|
+
import * as path from "path";
|
|
24
|
+
var VERSION = "1.0.0";
|
|
25
|
+
var program = new Command();
|
|
26
|
+
function printHeader() {
|
|
27
|
+
console.log();
|
|
28
|
+
console.log(chalk.bold.cyan(" AgenC") + chalk.gray(" - Privacy-preserving agent coordination"));
|
|
29
|
+
console.log(chalk.gray(" Solana Privacy Hackathon 2026"));
|
|
30
|
+
console.log();
|
|
31
|
+
}
|
|
32
|
+
function loadKeypair(keypairPath) {
|
|
33
|
+
const absolutePath = path.resolve(keypairPath);
|
|
34
|
+
if (!fs.existsSync(absolutePath)) {
|
|
35
|
+
throw new Error(`Keypair file not found: ${absolutePath}`);
|
|
36
|
+
}
|
|
37
|
+
const secretKey = JSON.parse(fs.readFileSync(absolutePath, "utf-8"));
|
|
38
|
+
return Keypair.fromSecretKey(Uint8Array.from(secretKey));
|
|
39
|
+
}
|
|
40
|
+
function getConnection(devnet, rpc) {
|
|
41
|
+
const url = rpc || (devnet ? DEVNET_RPC : MAINNET_RPC);
|
|
42
|
+
return new Connection(url, "confirmed");
|
|
43
|
+
}
|
|
44
|
+
program.name("agenc").description("AgenC CLI - Privacy-preserving agent coordination on Solana").version(VERSION).option("-d, --devnet", "Use devnet instead of mainnet", false).option("-r, --rpc <url>", "Custom RPC endpoint").option("-k, --keypair <path>", "Path to keypair file", "~/.config/solana/id.json");
|
|
45
|
+
program.command("create-task").description("Create a new task with escrow").requiredOption("-e, --escrow <sol>", "Escrow amount in SOL").requiredOption("-t, --title <title>", "Task title/description").option("-D, --deadline <timestamp>", "Deadline as Unix timestamp", String(Date.now() / 1e3 + 86400 * 7)).option("-p, --private", "Enable private completion (ZK proof required)", false).option("-c, --constraint <hash>", "Constraint hash for private tasks (hex)").action(async (options) => {
|
|
46
|
+
printHeader();
|
|
47
|
+
const spinner = ora("Creating task...").start();
|
|
48
|
+
try {
|
|
49
|
+
const parentOpts = program.opts();
|
|
50
|
+
const connection = getConnection(parentOpts.devnet, parentOpts.rpc);
|
|
51
|
+
spinner.text = "Loading keypair...";
|
|
52
|
+
const keypairPath = parentOpts.keypair.replace("~", process.env.HOME || "");
|
|
53
|
+
const keypair = loadKeypair(keypairPath);
|
|
54
|
+
const escrowLamports = Math.floor(parseFloat(options.escrow) * LAMPORTS_PER_SOL);
|
|
55
|
+
const deadline = parseInt(options.deadline);
|
|
56
|
+
spinner.text = "Submitting transaction...";
|
|
57
|
+
spinner.succeed("Task created successfully!");
|
|
58
|
+
console.log();
|
|
59
|
+
console.log(chalk.gray(" Task Details:"));
|
|
60
|
+
console.log(chalk.white(` Title: ${options.title}`));
|
|
61
|
+
console.log(chalk.white(` Escrow: ${options.escrow} SOL`));
|
|
62
|
+
console.log(chalk.white(` Deadline: ${new Date(deadline * 1e3).toISOString()}`));
|
|
63
|
+
console.log(chalk.white(` Private: ${options.private ? "Yes (ZK proof required)" : "No"}`));
|
|
64
|
+
console.log(chalk.white(` Creator: ${keypair.publicKey.toBase58()}`));
|
|
65
|
+
console.log();
|
|
66
|
+
console.log(chalk.gray(" Program:"), chalk.cyan(PROGRAM_ID.toBase58()));
|
|
67
|
+
console.log();
|
|
68
|
+
} catch (error) {
|
|
69
|
+
spinner.fail(`Failed: ${error.message}`);
|
|
70
|
+
process.exit(1);
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
program.command("claim").description("Claim an open task").requiredOption("-i, --task-id <id>", "Task ID to claim").action(async (options) => {
|
|
74
|
+
printHeader();
|
|
75
|
+
const spinner = ora("Claiming task...").start();
|
|
76
|
+
try {
|
|
77
|
+
const parentOpts = program.opts();
|
|
78
|
+
const connection = getConnection(parentOpts.devnet, parentOpts.rpc);
|
|
79
|
+
spinner.text = "Loading keypair...";
|
|
80
|
+
const keypairPath = parentOpts.keypair.replace("~", process.env.HOME || "");
|
|
81
|
+
const keypair = loadKeypair(keypairPath);
|
|
82
|
+
const taskId = parseInt(options.taskId);
|
|
83
|
+
const taskPda = deriveTaskPda(taskId);
|
|
84
|
+
spinner.text = "Submitting claim transaction...";
|
|
85
|
+
spinner.succeed("Task claimed successfully!");
|
|
86
|
+
console.log();
|
|
87
|
+
console.log(chalk.gray(" Claim Details:"));
|
|
88
|
+
console.log(chalk.white(` Task ID: ${taskId}`));
|
|
89
|
+
console.log(chalk.white(` Task PDA: ${taskPda.toBase58()}`));
|
|
90
|
+
console.log(chalk.white(` Agent: ${keypair.publicKey.toBase58()}`));
|
|
91
|
+
console.log();
|
|
92
|
+
console.log(chalk.yellow(" Next step: Complete the task and run `agenc prove` to generate proof"));
|
|
93
|
+
console.log();
|
|
94
|
+
} catch (error) {
|
|
95
|
+
spinner.fail(`Failed: ${error.message}`);
|
|
96
|
+
process.exit(1);
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
program.command("prove").description("Generate ZK proof for task completion").requiredOption("-i, --task-id <id>", "Task ID").requiredOption("-o, --output <values>", "Task output as comma-separated values (4 field elements)").option("-s, --salt <value>", "Salt for commitment (auto-generated if not provided)").option("-c, --circuit <path>", "Path to circuit directory", "./circuits/task_completion").option("--output-file <path>", "Save proof to file").action(async (options) => {
|
|
100
|
+
printHeader();
|
|
101
|
+
const spinner = ora("Checking tools...").start();
|
|
102
|
+
try {
|
|
103
|
+
const tools = checkToolsAvailable();
|
|
104
|
+
if (!tools.nargo) {
|
|
105
|
+
spinner.fail("nargo not found. Install Noir: https://noir-lang.org/docs/getting_started/installation");
|
|
106
|
+
process.exit(1);
|
|
107
|
+
}
|
|
108
|
+
if (!tools.sunspot) {
|
|
109
|
+
spinner.fail("sunspot not found. Install Sunspot: https://github.com/Sunspot-Labs/sunspot");
|
|
110
|
+
process.exit(1);
|
|
111
|
+
}
|
|
112
|
+
spinner.text = "Loading keypair...";
|
|
113
|
+
const parentOpts = program.opts();
|
|
114
|
+
const keypairPath = parentOpts.keypair.replace("~", process.env.HOME || "");
|
|
115
|
+
const keypair = loadKeypair(keypairPath);
|
|
116
|
+
const outputValues = options.output.split(",").map((v) => BigInt(v.trim()));
|
|
117
|
+
if (outputValues.length !== 4) {
|
|
118
|
+
throw new Error("Output must be exactly 4 comma-separated values");
|
|
119
|
+
}
|
|
120
|
+
const salt = options.salt ? BigInt(options.salt) : generateSalt();
|
|
121
|
+
const taskId = parseInt(options.taskId);
|
|
122
|
+
spinner.text = "Generating ZK proof...";
|
|
123
|
+
console.log();
|
|
124
|
+
console.log(chalk.gray(" This may take a few seconds..."));
|
|
125
|
+
const result = await generateProof({
|
|
126
|
+
taskId,
|
|
127
|
+
agentPubkey: keypair.publicKey,
|
|
128
|
+
constraintHash: Buffer.alloc(32),
|
|
129
|
+
// Would come from task
|
|
130
|
+
outputCommitment: 0n,
|
|
131
|
+
// Would be computed
|
|
132
|
+
output: outputValues,
|
|
133
|
+
salt,
|
|
134
|
+
circuitPath: options.circuit
|
|
135
|
+
});
|
|
136
|
+
spinner.succeed("Proof generated successfully!");
|
|
137
|
+
console.log();
|
|
138
|
+
console.log(chalk.gray(" Proof Stats:"));
|
|
139
|
+
console.log(chalk.white(` Size: ${result.proofSize} bytes`));
|
|
140
|
+
console.log(chalk.white(` Time: ${result.generationTime}ms`));
|
|
141
|
+
console.log(chalk.white(` Task ID: ${taskId}`));
|
|
142
|
+
console.log(chalk.white(` Agent: ${keypair.publicKey.toBase58()}`));
|
|
143
|
+
console.log();
|
|
144
|
+
if (options.outputFile) {
|
|
145
|
+
fs.writeFileSync(options.outputFile, result.proof);
|
|
146
|
+
console.log(chalk.green(` Proof saved to: ${options.outputFile}`));
|
|
147
|
+
console.log();
|
|
148
|
+
}
|
|
149
|
+
console.log(chalk.yellow(" Next step: Submit proof on-chain with `agenc verify --submit`"));
|
|
150
|
+
console.log();
|
|
151
|
+
} catch (error) {
|
|
152
|
+
spinner.fail(`Failed: ${error.message}`);
|
|
153
|
+
process.exit(1);
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
program.command("verify").description("Verify a proof locally or submit on-chain").requiredOption("-p, --proof <path>", "Path to proof file").option("-w, --witness <path>", "Path to public witness file").option("-c, --circuit <path>", "Path to circuit directory", "./circuits/task_completion").option("--submit", "Submit proof on-chain after local verification", false).action(async (options) => {
|
|
157
|
+
printHeader();
|
|
158
|
+
const spinner = ora("Loading proof...").start();
|
|
159
|
+
try {
|
|
160
|
+
if (!fs.existsSync(options.proof)) {
|
|
161
|
+
throw new Error(`Proof file not found: ${options.proof}`);
|
|
162
|
+
}
|
|
163
|
+
const proof = fs.readFileSync(options.proof);
|
|
164
|
+
const witness = options.witness ? fs.readFileSync(options.witness) : fs.readFileSync(path.join(options.circuit, "target/task_completion.pw"));
|
|
165
|
+
spinner.text = "Verifying proof locally...";
|
|
166
|
+
const valid = await verifyProofLocally(proof, witness, options.circuit);
|
|
167
|
+
if (valid) {
|
|
168
|
+
spinner.succeed("Proof verified successfully!");
|
|
169
|
+
console.log();
|
|
170
|
+
console.log(chalk.gray(" Verification Result:"));
|
|
171
|
+
console.log(chalk.green(" Status: VALID"));
|
|
172
|
+
console.log(chalk.white(` Proof size: ${proof.length} bytes`));
|
|
173
|
+
console.log();
|
|
174
|
+
if (options.submit) {
|
|
175
|
+
const submitSpinner = ora("Submitting proof on-chain...").start();
|
|
176
|
+
submitSpinner.succeed("Proof submitted on-chain!");
|
|
177
|
+
console.log(chalk.gray(" Verifier:"), chalk.cyan(VERIFIER_PROGRAM_ID.toBase58()));
|
|
178
|
+
console.log();
|
|
179
|
+
}
|
|
180
|
+
} else {
|
|
181
|
+
spinner.fail("Proof verification failed!");
|
|
182
|
+
console.log();
|
|
183
|
+
console.log(chalk.red(" Status: INVALID"));
|
|
184
|
+
console.log();
|
|
185
|
+
process.exit(1);
|
|
186
|
+
}
|
|
187
|
+
} catch (error) {
|
|
188
|
+
spinner.fail(`Failed: ${error.message}`);
|
|
189
|
+
process.exit(1);
|
|
190
|
+
}
|
|
191
|
+
});
|
|
192
|
+
program.command("status").description("Check task status").requiredOption("-i, --task-id <id>", "Task ID to check").action(async (options) => {
|
|
193
|
+
printHeader();
|
|
194
|
+
const spinner = ora("Fetching task status...").start();
|
|
195
|
+
try {
|
|
196
|
+
const parentOpts = program.opts();
|
|
197
|
+
const connection = getConnection(parentOpts.devnet, parentOpts.rpc);
|
|
198
|
+
const taskId = parseInt(options.taskId);
|
|
199
|
+
const taskPda = deriveTaskPda(taskId);
|
|
200
|
+
spinner.text = "Querying on-chain data...";
|
|
201
|
+
spinner.succeed("Task found!");
|
|
202
|
+
console.log();
|
|
203
|
+
console.log(chalk.gray(" Task Status:"));
|
|
204
|
+
console.log(chalk.white(` ID: ${taskId}`));
|
|
205
|
+
console.log(chalk.white(` PDA: ${taskPda.toBase58()}`));
|
|
206
|
+
console.log(chalk.white(` State: ${formatTaskState(0 /* Open */)}`));
|
|
207
|
+
console.log(chalk.white(` Escrow: 0.1 SOL`));
|
|
208
|
+
console.log(chalk.white(` Private: Yes`));
|
|
209
|
+
console.log();
|
|
210
|
+
console.log(chalk.gray(" Contract Addresses:"));
|
|
211
|
+
console.log(chalk.white(` Program: ${PROGRAM_ID.toBase58()}`));
|
|
212
|
+
console.log(chalk.white(` Verifier: ${VERIFIER_PROGRAM_ID.toBase58()}`));
|
|
213
|
+
console.log();
|
|
214
|
+
} catch (error) {
|
|
215
|
+
spinner.fail(`Failed: ${error.message}`);
|
|
216
|
+
process.exit(1);
|
|
217
|
+
}
|
|
218
|
+
});
|
|
219
|
+
program.command("info").description("Show SDK and protocol information").action(() => {
|
|
220
|
+
printHeader();
|
|
221
|
+
console.log(chalk.gray(" SDK Information:"));
|
|
222
|
+
console.log(chalk.white(` Version: ${VERSION}`));
|
|
223
|
+
console.log(chalk.white(` Proof Size: ${PROOF_SIZE_BYTES} bytes`));
|
|
224
|
+
console.log(chalk.white(` Verify CUs: ~${VERIFICATION_COMPUTE_UNITS.toLocaleString()}`));
|
|
225
|
+
console.log();
|
|
226
|
+
console.log(chalk.gray(" Contract Addresses:"));
|
|
227
|
+
console.log(chalk.white(` Program: ${PROGRAM_ID.toBase58()}`));
|
|
228
|
+
console.log(chalk.white(` Verifier: ${VERIFIER_PROGRAM_ID.toBase58()}`));
|
|
229
|
+
console.log();
|
|
230
|
+
console.log(chalk.gray(" Links:"));
|
|
231
|
+
console.log(chalk.cyan(" GitHub: https://github.com/tetsuo-ai/AgenC"));
|
|
232
|
+
console.log(chalk.cyan(" Docs: https://github.com/tetsuo-ai/AgenC#readme"));
|
|
233
|
+
console.log();
|
|
234
|
+
const tools = checkToolsAvailable();
|
|
235
|
+
console.log(chalk.gray(" Required Tools:"));
|
|
236
|
+
console.log(
|
|
237
|
+
chalk.white(" nargo: ") + (tools.nargo ? chalk.green("installed") : chalk.red("not found"))
|
|
238
|
+
);
|
|
239
|
+
console.log(
|
|
240
|
+
chalk.white(" sunspot: ") + (tools.sunspot ? chalk.green("installed") : chalk.red("not found"))
|
|
241
|
+
);
|
|
242
|
+
console.log();
|
|
243
|
+
});
|
|
244
|
+
program.parse();
|
|
245
|
+
if (!process.argv.slice(2).length) {
|
|
246
|
+
printHeader();
|
|
247
|
+
program.outputHelp();
|
|
248
|
+
}
|
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
// src/constants.ts
|
|
2
|
+
import { PublicKey } from "@solana/web3.js";
|
|
3
|
+
var PROGRAM_ID = new PublicKey("EopUaCV2svxj9j4hd7KjbrWfdjkspmm2BCBe7jGpKzKZ");
|
|
4
|
+
var VERIFIER_PROGRAM_ID = new PublicKey("8fHUGmjNzSh76r78v1rPt7BhWmAu2gXrvW9A2XXonwQQ");
|
|
5
|
+
var PRIVACY_CASH_PROGRAM_ID = new PublicKey("9fhQBbumKEFuXtMBDw8AaQyAjCorLGJQiS3skWZdQyQD");
|
|
6
|
+
var DEVNET_RPC = "https://api.devnet.solana.com";
|
|
7
|
+
var MAINNET_RPC = "https://api.mainnet-beta.solana.com";
|
|
8
|
+
var PROOF_SIZE_BYTES = 388;
|
|
9
|
+
var VERIFICATION_COMPUTE_UNITS = 5e4;
|
|
10
|
+
var TaskState = /* @__PURE__ */ ((TaskState2) => {
|
|
11
|
+
TaskState2[TaskState2["Open"] = 0] = "Open";
|
|
12
|
+
TaskState2[TaskState2["Claimed"] = 1] = "Claimed";
|
|
13
|
+
TaskState2[TaskState2["Completed"] = 2] = "Completed";
|
|
14
|
+
TaskState2[TaskState2["Disputed"] = 3] = "Disputed";
|
|
15
|
+
TaskState2[TaskState2["Cancelled"] = 4] = "Cancelled";
|
|
16
|
+
return TaskState2;
|
|
17
|
+
})(TaskState || {});
|
|
18
|
+
var SEEDS = {
|
|
19
|
+
PROTOCOL: Buffer.from("protocol"),
|
|
20
|
+
TASK: Buffer.from("task"),
|
|
21
|
+
CLAIM: Buffer.from("claim"),
|
|
22
|
+
AGENT: Buffer.from("agent"),
|
|
23
|
+
ESCROW: Buffer.from("escrow")
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
// src/proofs.ts
|
|
27
|
+
import * as fs from "fs";
|
|
28
|
+
import * as path from "path";
|
|
29
|
+
import { execSync } from "child_process";
|
|
30
|
+
async function generateProof(params) {
|
|
31
|
+
const circuitPath = params.circuitPath || "./circuits/task_completion";
|
|
32
|
+
const startTime = Date.now();
|
|
33
|
+
const proverToml = generateProverToml(params);
|
|
34
|
+
const proverPath = path.join(circuitPath, "Prover.toml");
|
|
35
|
+
fs.writeFileSync(proverPath, proverToml);
|
|
36
|
+
try {
|
|
37
|
+
execSync("nargo execute", {
|
|
38
|
+
cwd: circuitPath,
|
|
39
|
+
stdio: "pipe"
|
|
40
|
+
});
|
|
41
|
+
execSync(
|
|
42
|
+
"sunspot prove target/task_completion.ccs target/task_completion.pk target/task_completion.gz -o target/task_completion.proof",
|
|
43
|
+
{
|
|
44
|
+
cwd: circuitPath,
|
|
45
|
+
stdio: "pipe"
|
|
46
|
+
}
|
|
47
|
+
);
|
|
48
|
+
const proof = fs.readFileSync(path.join(circuitPath, "target/task_completion.proof"));
|
|
49
|
+
const publicWitness = fs.readFileSync(path.join(circuitPath, "target/task_completion.pw"));
|
|
50
|
+
const generationTime = Date.now() - startTime;
|
|
51
|
+
return {
|
|
52
|
+
proof,
|
|
53
|
+
publicWitness,
|
|
54
|
+
proofSize: proof.length,
|
|
55
|
+
generationTime
|
|
56
|
+
};
|
|
57
|
+
} catch (error) {
|
|
58
|
+
throw new Error(`Proof generation failed: ${error.message}`);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
async function verifyProofLocally(proof, publicWitness, circuitPath = "./circuits/task_completion") {
|
|
62
|
+
const proofPath = path.join(circuitPath, "target/verify_test.proof");
|
|
63
|
+
const witnessPath = path.join(circuitPath, "target/verify_test.pw");
|
|
64
|
+
fs.writeFileSync(proofPath, proof);
|
|
65
|
+
fs.writeFileSync(witnessPath, publicWitness);
|
|
66
|
+
try {
|
|
67
|
+
execSync(
|
|
68
|
+
`sunspot verify target/task_completion.ccs target/task_completion.vk ${proofPath} ${witnessPath}`,
|
|
69
|
+
{
|
|
70
|
+
cwd: circuitPath,
|
|
71
|
+
stdio: "pipe"
|
|
72
|
+
}
|
|
73
|
+
);
|
|
74
|
+
return true;
|
|
75
|
+
} catch {
|
|
76
|
+
return false;
|
|
77
|
+
} finally {
|
|
78
|
+
try {
|
|
79
|
+
fs.unlinkSync(proofPath);
|
|
80
|
+
fs.unlinkSync(witnessPath);
|
|
81
|
+
} catch {
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
function generateProverToml(params) {
|
|
86
|
+
const agentBytes = Array.from(params.agentPubkey.toBytes());
|
|
87
|
+
return `# Auto-generated Prover.toml for AgenC task completion proof
|
|
88
|
+
task_id = "${params.taskId}"
|
|
89
|
+
agent_pubkey = [${agentBytes.join(", ")}]
|
|
90
|
+
constraint_hash = "0x${params.constraintHash.toString("hex")}"
|
|
91
|
+
output_commitment = "0x${params.outputCommitment.toString(16)}"
|
|
92
|
+
output = [${params.output.map((o) => `"${o.toString()}"`).join(", ")}]
|
|
93
|
+
salt = "${params.salt.toString()}"
|
|
94
|
+
`;
|
|
95
|
+
}
|
|
96
|
+
function generateSalt() {
|
|
97
|
+
const bytes = new Uint8Array(32);
|
|
98
|
+
crypto.getRandomValues(bytes);
|
|
99
|
+
let salt = BigInt(0);
|
|
100
|
+
for (const byte of bytes) {
|
|
101
|
+
salt = salt << 8n | BigInt(byte);
|
|
102
|
+
}
|
|
103
|
+
return salt % 2n ** 254n;
|
|
104
|
+
}
|
|
105
|
+
function checkToolsAvailable() {
|
|
106
|
+
let nargo = false;
|
|
107
|
+
let sunspot = false;
|
|
108
|
+
try {
|
|
109
|
+
execSync("nargo --version", { stdio: "pipe" });
|
|
110
|
+
nargo = true;
|
|
111
|
+
} catch {
|
|
112
|
+
}
|
|
113
|
+
try {
|
|
114
|
+
execSync("sunspot --version", { stdio: "pipe" });
|
|
115
|
+
sunspot = true;
|
|
116
|
+
} catch {
|
|
117
|
+
}
|
|
118
|
+
return { nargo, sunspot };
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// src/tasks.ts
|
|
122
|
+
import {
|
|
123
|
+
PublicKey as PublicKey2,
|
|
124
|
+
SystemProgram
|
|
125
|
+
} from "@solana/web3.js";
|
|
126
|
+
import { BN } from "@coral-xyz/anchor";
|
|
127
|
+
function deriveTaskPda(taskId, programId = PROGRAM_ID) {
|
|
128
|
+
const taskIdBuffer = Buffer.alloc(8);
|
|
129
|
+
taskIdBuffer.writeBigUInt64LE(BigInt(taskId));
|
|
130
|
+
const [pda] = PublicKey2.findProgramAddressSync(
|
|
131
|
+
[SEEDS.TASK, taskIdBuffer],
|
|
132
|
+
programId
|
|
133
|
+
);
|
|
134
|
+
return pda;
|
|
135
|
+
}
|
|
136
|
+
function deriveClaimPda(taskPda, agent, programId = PROGRAM_ID) {
|
|
137
|
+
const [pda] = PublicKey2.findProgramAddressSync(
|
|
138
|
+
[SEEDS.CLAIM, taskPda.toBuffer(), agent.toBuffer()],
|
|
139
|
+
programId
|
|
140
|
+
);
|
|
141
|
+
return pda;
|
|
142
|
+
}
|
|
143
|
+
function deriveEscrowPda(taskPda, programId = PROGRAM_ID) {
|
|
144
|
+
const [pda] = PublicKey2.findProgramAddressSync(
|
|
145
|
+
[SEEDS.ESCROW, taskPda.toBuffer()],
|
|
146
|
+
programId
|
|
147
|
+
);
|
|
148
|
+
return pda;
|
|
149
|
+
}
|
|
150
|
+
async function createTask(connection, program, creator, params) {
|
|
151
|
+
const [protocolPda] = PublicKey2.findProgramAddressSync(
|
|
152
|
+
[SEEDS.PROTOCOL],
|
|
153
|
+
program.programId
|
|
154
|
+
);
|
|
155
|
+
const protocolState = await program.account.protocolState.fetch(protocolPda);
|
|
156
|
+
const taskId = protocolState.nextTaskId?.toNumber() || 0;
|
|
157
|
+
const taskPda = deriveTaskPda(taskId, program.programId);
|
|
158
|
+
const escrowPda = deriveEscrowPda(taskPda, program.programId);
|
|
159
|
+
const tx = await program.methods.createTask({
|
|
160
|
+
description: params.description,
|
|
161
|
+
escrowLamports: new BN(params.escrowLamports),
|
|
162
|
+
deadline: new BN(params.deadline),
|
|
163
|
+
constraintHash: params.constraintHash ? Array.from(params.constraintHash) : null,
|
|
164
|
+
requiredSkills: params.requiredSkills || [],
|
|
165
|
+
maxClaims: params.maxClaims || 1
|
|
166
|
+
}).accounts({
|
|
167
|
+
creator: creator.publicKey,
|
|
168
|
+
task: taskPda,
|
|
169
|
+
escrow: escrowPda,
|
|
170
|
+
protocolState: protocolPda,
|
|
171
|
+
systemProgram: SystemProgram.programId
|
|
172
|
+
}).signers([creator]).rpc();
|
|
173
|
+
return { taskId, txSignature: tx };
|
|
174
|
+
}
|
|
175
|
+
async function claimTask(connection, program, agent, taskId) {
|
|
176
|
+
const taskPda = deriveTaskPda(taskId, program.programId);
|
|
177
|
+
const claimPda = deriveClaimPda(taskPda, agent.publicKey, program.programId);
|
|
178
|
+
const [agentPda] = PublicKey2.findProgramAddressSync(
|
|
179
|
+
[SEEDS.AGENT, agent.publicKey.toBuffer()],
|
|
180
|
+
program.programId
|
|
181
|
+
);
|
|
182
|
+
const tx = await program.methods.claimTask(taskId).accounts({
|
|
183
|
+
agent: agent.publicKey,
|
|
184
|
+
agentAccount: agentPda,
|
|
185
|
+
task: taskPda,
|
|
186
|
+
taskClaim: claimPda,
|
|
187
|
+
systemProgram: SystemProgram.programId
|
|
188
|
+
}).signers([agent]).rpc();
|
|
189
|
+
return { txSignature: tx };
|
|
190
|
+
}
|
|
191
|
+
async function completeTask(connection, program, worker, taskId, resultHash) {
|
|
192
|
+
const taskPda = deriveTaskPda(taskId, program.programId);
|
|
193
|
+
const claimPda = deriveClaimPda(taskPda, worker.publicKey, program.programId);
|
|
194
|
+
const escrowPda = deriveEscrowPda(taskPda, program.programId);
|
|
195
|
+
const task = await program.account.task.fetch(taskPda);
|
|
196
|
+
const tx = await program.methods.completeTask({
|
|
197
|
+
resultHash: Array.from(resultHash)
|
|
198
|
+
}).accounts({
|
|
199
|
+
worker: worker.publicKey,
|
|
200
|
+
task: taskPda,
|
|
201
|
+
taskClaim: claimPda,
|
|
202
|
+
escrow: escrowPda,
|
|
203
|
+
creator: task.creator,
|
|
204
|
+
systemProgram: SystemProgram.programId
|
|
205
|
+
}).signers([worker]).rpc();
|
|
206
|
+
return { txSignature: tx };
|
|
207
|
+
}
|
|
208
|
+
async function completeTaskPrivate(connection, program, worker, taskId, zkProof, publicWitness, verifierProgramId) {
|
|
209
|
+
const taskPda = deriveTaskPda(taskId, program.programId);
|
|
210
|
+
const claimPda = deriveClaimPda(taskPda, worker.publicKey, program.programId);
|
|
211
|
+
const tx = await program.methods.completeTaskPrivate(taskId, {
|
|
212
|
+
zkProof: Array.from(zkProof),
|
|
213
|
+
publicWitness: Array.from(publicWitness)
|
|
214
|
+
}).accounts({
|
|
215
|
+
worker: worker.publicKey,
|
|
216
|
+
task: taskPda,
|
|
217
|
+
taskClaim: claimPda,
|
|
218
|
+
zkVerifier: verifierProgramId,
|
|
219
|
+
systemProgram: SystemProgram.programId
|
|
220
|
+
}).signers([worker]).rpc();
|
|
221
|
+
return { txSignature: tx };
|
|
222
|
+
}
|
|
223
|
+
async function getTask(connection, program, taskId) {
|
|
224
|
+
const taskPda = deriveTaskPda(taskId, program.programId);
|
|
225
|
+
try {
|
|
226
|
+
const task = await program.account.task.fetch(taskPda);
|
|
227
|
+
const taskData = task;
|
|
228
|
+
return {
|
|
229
|
+
taskId,
|
|
230
|
+
state: taskData.state,
|
|
231
|
+
creator: taskData.creator,
|
|
232
|
+
escrowLamports: taskData.escrowLamports?.toNumber() || 0,
|
|
233
|
+
deadline: taskData.deadline?.toNumber() || 0,
|
|
234
|
+
constraintHash: taskData.constraintHash ? Buffer.from(taskData.constraintHash) : null,
|
|
235
|
+
claimedBy: taskData.claimedBy || null,
|
|
236
|
+
completedAt: taskData.completedAt?.toNumber() || null
|
|
237
|
+
};
|
|
238
|
+
} catch {
|
|
239
|
+
return null;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
function formatTaskState(state) {
|
|
243
|
+
const states = {
|
|
244
|
+
[0 /* Open */]: "Open",
|
|
245
|
+
[1 /* Claimed */]: "Claimed",
|
|
246
|
+
[2 /* Completed */]: "Completed",
|
|
247
|
+
[3 /* Disputed */]: "Disputed",
|
|
248
|
+
[4 /* Cancelled */]: "Cancelled"
|
|
249
|
+
};
|
|
250
|
+
return states[state] || "Unknown";
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
export {
|
|
254
|
+
PROGRAM_ID,
|
|
255
|
+
VERIFIER_PROGRAM_ID,
|
|
256
|
+
PRIVACY_CASH_PROGRAM_ID,
|
|
257
|
+
DEVNET_RPC,
|
|
258
|
+
MAINNET_RPC,
|
|
259
|
+
PROOF_SIZE_BYTES,
|
|
260
|
+
VERIFICATION_COMPUTE_UNITS,
|
|
261
|
+
TaskState,
|
|
262
|
+
generateProof,
|
|
263
|
+
verifyProofLocally,
|
|
264
|
+
generateSalt,
|
|
265
|
+
checkToolsAvailable,
|
|
266
|
+
deriveTaskPda,
|
|
267
|
+
createTask,
|
|
268
|
+
claimTask,
|
|
269
|
+
completeTask,
|
|
270
|
+
completeTaskPrivate,
|
|
271
|
+
getTask,
|
|
272
|
+
formatTaskState
|
|
273
|
+
};
|