@blindmarket/sdk 0.1.8 → 0.5.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/dist/executor/WorkerRuntime.d.ts +21 -0
- package/dist/executor/WorkerRuntime.js +56 -27
- package/dist/index.d.ts +75 -9
- package/dist/index.js +54 -5
- package/dist/tools/helpers.js +14 -5
- package/dist/types.d.ts +7 -2
- package/package.json +8 -1
- package/biome.json +0 -19
- package/examples/agent-demo.ts +0 -121
- package/scripts/gen-crypto-fixtures.mjs +0 -72
- package/src/agent/Agent.ts +0 -122
- package/src/agent/index.ts +0 -1
- package/src/api/index.ts +0 -1
- package/src/chain/BlindEscrowClient.ts +0 -196
- package/src/chain/BlindReputationClient.ts +0 -60
- package/src/chain/ChainClient.ts +0 -65
- package/src/chain/IBlindMarketChain.ts +0 -109
- package/src/chain/TaskRegistryClient.ts +0 -91
- package/src/chain/abi/BlindEscrow.json +0 -1113
- package/src/chain/abi/BlindReputation.json +0 -447
- package/src/chain/abi/TaskRegistry.json +0 -586
- package/src/chain/abi/index.ts +0 -5
- package/src/chain/createChainAdapter.ts +0 -40
- package/src/chain/domain-types.ts +0 -92
- package/src/chain/errors.ts +0 -43
- package/src/chain/evm/EVMChainAdapter.ts +0 -212
- package/src/chain/evm/index.ts +0 -1
- package/src/chain/index.ts +0 -12
- package/src/compute/index.ts +0 -1
- package/src/crypto/aes.ts +0 -104
- package/src/crypto/constants.ts +0 -7
- package/src/crypto/ecies.ts +0 -60
- package/src/crypto/envelope.ts +0 -131
- package/src/crypto/hkdf.ts +0 -45
- package/src/crypto/index.ts +0 -6
- package/src/crypto/keys.ts +0 -56
- package/src/errors/BlindMarketError.ts +0 -38
- package/src/errors/codes.ts +0 -67
- package/src/errors/index.ts +0 -3
- package/src/errors/subclasses.ts +0 -59
- package/src/events/EventBus.ts +0 -45
- package/src/events/index.ts +0 -2
- package/src/events/types.ts +0 -13
- package/src/executor/WorkerRuntime.ts +0 -338
- package/src/executor/index.ts +0 -8
- package/src/index.ts +0 -636
- package/src/keystore/InMemoryKeyStore.ts +0 -146
- package/src/keystore/KeyStore.ts +0 -19
- package/src/keystore/index.ts +0 -2
- package/src/network/index.ts +0 -3
- package/src/network/presets.ts +0 -66
- package/src/network/resolve.ts +0 -31
- package/src/network/types.ts +0 -20
- package/src/signer/EthersSigner.ts +0 -82
- package/src/signer/PrivateKeySigner.ts +0 -19
- package/src/signer/Signer.ts +0 -40
- package/src/signer/index.ts +0 -3
- package/src/storage/MemoryStorage.ts +0 -49
- package/src/storage/Storage.ts +0 -15
- package/src/storage/ZgStorage.ts +0 -124
- package/src/storage/index.ts +0 -3
- package/src/testing/index.ts +0 -1
- package/src/tools/adapters.ts +0 -177
- package/src/tools/helpers.ts +0 -199
- package/src/tools/index.ts +0 -59
- package/src/tools/types.ts +0 -47
- package/src/types.ts +0 -297
- package/src/verifier/index.ts +0 -1
- package/src/worker/Worker.ts +0 -98
- package/src/worker/index.ts +0 -1
- package/test/agent.test.ts +0 -193
- package/test/chain.test.ts +0 -145
- package/test/crypto.aes.test.ts +0 -81
- package/test/crypto.compat.test.ts +0 -33
- package/test/crypto.ecies.test.ts +0 -64
- package/test/envelope.test.ts +0 -82
- package/test/errors.test.ts +0 -64
- package/test/events.test.ts +0 -59
- package/test/fixtures/crypto.json +0 -16
- package/test/keystore.test.ts +0 -103
- package/test/network.test.ts +0 -53
- package/test/signer.test.ts +0 -66
- package/test/smoke.test.ts +0 -7
- package/test/storage.test.ts +0 -55
- package/test/worker.test.ts +0 -181
- package/tsconfig.build.json +0 -1
- package/tsconfig.json +0 -14
- package/tsup.config.ts +0 -32
- package/vitest.config.ts +0 -17
package/src/types.ts
DELETED
|
@@ -1,297 +0,0 @@
|
|
|
1
|
-
export type Address = `0x${string}`;
|
|
2
|
-
export type Hex = `0x${string}`;
|
|
3
|
-
export type TaskId = bigint;
|
|
4
|
-
export type RootHash = Hex;
|
|
5
|
-
|
|
6
|
-
export type TokenSymbol = 'USDC' | 'A0GI' | string;
|
|
7
|
-
|
|
8
|
-
export interface TokenRef {
|
|
9
|
-
address: Address;
|
|
10
|
-
symbol?: TokenSymbol;
|
|
11
|
-
decimals?: number;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export interface Reward {
|
|
15
|
-
token: Address | TokenSymbol;
|
|
16
|
-
amount: bigint;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export type TaskStatus =
|
|
20
|
-
| 'funded'
|
|
21
|
-
| 'assigned'
|
|
22
|
-
| 'submitted'
|
|
23
|
-
| 'verified'
|
|
24
|
-
| 'completed'
|
|
25
|
-
| 'cancelled'
|
|
26
|
-
| 'disputed';
|
|
27
|
-
|
|
28
|
-
export interface TaskMetadata {
|
|
29
|
-
category: string;
|
|
30
|
-
locationZone: string;
|
|
31
|
-
reward: Reward;
|
|
32
|
-
deadline?: Date;
|
|
33
|
-
extra?: Record<string, string>;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
export interface TaskRecord extends TaskMetadata {
|
|
37
|
-
taskId: TaskId;
|
|
38
|
-
agent: Address;
|
|
39
|
-
worker?: Address;
|
|
40
|
-
taskHash: RootHash;
|
|
41
|
-
evidenceHash?: RootHash;
|
|
42
|
-
status: TaskStatus;
|
|
43
|
-
createdAt: Date;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
export interface TaskKey {
|
|
47
|
-
/** AES-256 symmetric key (32 bytes) used to encrypt task content. */
|
|
48
|
-
aesKey: Uint8Array;
|
|
49
|
-
createdAt: Date;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
export interface TaskKeyRef {
|
|
53
|
-
taskId: TaskId;
|
|
54
|
-
createdAt: Date;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
export interface UploadResult {
|
|
58
|
-
rootHash: RootHash;
|
|
59
|
-
txHash?: Hex;
|
|
60
|
-
size: number;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
export interface VerificationResult {
|
|
64
|
-
passed: boolean;
|
|
65
|
-
confidence: number;
|
|
66
|
-
model: string;
|
|
67
|
-
attestation?: Hex;
|
|
68
|
-
completedAt: Date;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
export interface TxReceiptLike {
|
|
72
|
-
hash: Hex;
|
|
73
|
-
blockNumber: number;
|
|
74
|
-
gasUsed: bigint;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
export type Awaitable<T> = T | Promise<T>;
|
|
78
|
-
|
|
79
|
-
// ── REST API types ──────────────────────────────────────────────────────────
|
|
80
|
-
|
|
81
|
-
export interface HealthStatus {
|
|
82
|
-
status: string;
|
|
83
|
-
timestamp: string;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
export interface PlatformStats {
|
|
87
|
-
openTasks: number;
|
|
88
|
-
activeAgents: number;
|
|
89
|
-
activeValidators: number;
|
|
90
|
-
totalAgents: number;
|
|
91
|
-
registeredUsers: number;
|
|
92
|
-
completedTasks: number;
|
|
93
|
-
activeWorkers: number;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
export interface OpenTask {
|
|
97
|
-
id: number;
|
|
98
|
-
agent: Address;
|
|
99
|
-
worker?: Address;
|
|
100
|
-
amount: string;
|
|
101
|
-
token: Address;
|
|
102
|
-
category: string;
|
|
103
|
-
locationZone: string;
|
|
104
|
-
deadline: number;
|
|
105
|
-
status: number;
|
|
106
|
-
taskHash?: Hex;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
export interface CreateTaskTx {
|
|
110
|
-
unsignedTx: {
|
|
111
|
-
to: Address;
|
|
112
|
-
data: Hex;
|
|
113
|
-
value?: string;
|
|
114
|
-
};
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
export interface TaskDetail extends OpenTask {
|
|
118
|
-
metadata?: Record<string, unknown>;
|
|
119
|
-
a2aState?: A2ATaskState;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
export interface A2ATaskState {
|
|
123
|
-
taskId: string;
|
|
124
|
-
status: string;
|
|
125
|
-
executorAddress?: string;
|
|
126
|
-
acceptedAt?: string;
|
|
127
|
-
submittedAt?: string;
|
|
128
|
-
resultData?: Record<string, unknown> | null;
|
|
129
|
-
verificationResult?: {
|
|
130
|
-
passed: boolean;
|
|
131
|
-
reasons: string[];
|
|
132
|
-
score?: number;
|
|
133
|
-
breakdown?: Array<{ name: string; score: number; weight: number; reason: string; error?: string }>;
|
|
134
|
-
};
|
|
135
|
-
assignTxHash?: Hex;
|
|
136
|
-
verifyTxHash?: Hex;
|
|
137
|
-
wrappedKeys?: Record<string, string>;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
export interface ExecutorProfile {
|
|
141
|
-
address: Address;
|
|
142
|
-
displayName: string;
|
|
143
|
-
capabilities: AgentCapability[];
|
|
144
|
-
publicKey: string;
|
|
145
|
-
reputation: number;
|
|
146
|
-
tasksCompleted: number;
|
|
147
|
-
totalEarnedRaw: string;
|
|
148
|
-
minReward?: string;
|
|
149
|
-
preferredCapabilities?: AgentCapability[];
|
|
150
|
-
registeredAt: string;
|
|
151
|
-
decayedScore?: number;
|
|
152
|
-
disputeRatio?: number;
|
|
153
|
-
avgRating?: number;
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
export interface RegisterExecutorInput {
|
|
157
|
-
address: Address;
|
|
158
|
-
displayName: string;
|
|
159
|
-
capabilities: AgentCapability[];
|
|
160
|
-
publicKey: string;
|
|
161
|
-
agentCardUrl?: string;
|
|
162
|
-
mcpEndpointUrl?: string;
|
|
163
|
-
minReward?: string;
|
|
164
|
-
preferredCapabilities?: AgentCapability[];
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
/** Params for BlindMarket.createAgent() — generates wallet + registers executor in one call. */
|
|
168
|
-
export interface CreateAgentParams {
|
|
169
|
-
/** Display name for the agent in the marketplace. */
|
|
170
|
-
displayName: string;
|
|
171
|
-
/** Capabilities this agent offers. Use AgentCap.DATA_PROCESSING etc. */
|
|
172
|
-
capabilities: AgentCapability[];
|
|
173
|
-
/** Which of the above the agent prefers (subset of capabilities). */
|
|
174
|
-
preferredCapabilities?: AgentCapability[];
|
|
175
|
-
/** Minimum reward per task (wei string). */
|
|
176
|
-
minReward?: string;
|
|
177
|
-
/** Agent card URL for marketplace display. */
|
|
178
|
-
agentCardUrl?: string;
|
|
179
|
-
/** MCP endpoint URL for tool-based agents. */
|
|
180
|
-
mcpEndpointUrl?: string;
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
/** Result of BlindMarket.createAgent(). */
|
|
184
|
-
export interface CreateAgentResult {
|
|
185
|
-
executor: ExecutorProfile;
|
|
186
|
-
wallet: {
|
|
187
|
-
address: Address;
|
|
188
|
-
publicKey: string;
|
|
189
|
-
privateKey: string; // hex with 0x prefix — ⚠️ store securely, shown once
|
|
190
|
-
};
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
export interface DeployedAgentInfo {
|
|
194
|
-
id: string;
|
|
195
|
-
name: string;
|
|
196
|
-
ownerAddress: Address;
|
|
197
|
-
walletAddress: Address;
|
|
198
|
-
publicKey: string;
|
|
199
|
-
status: string;
|
|
200
|
-
provider: string;
|
|
201
|
-
model: string;
|
|
202
|
-
capabilities: AgentCapability[];
|
|
203
|
-
inftTokenId?: number;
|
|
204
|
-
deployedAt: string;
|
|
205
|
-
lastActiveAt?: string;
|
|
206
|
-
minReward?: string;
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
export interface AgentWalletInfo {
|
|
210
|
-
walletAddress: Address;
|
|
211
|
-
publicKey: string;
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
export interface ReputationInfo {
|
|
215
|
-
address: Address;
|
|
216
|
-
onChainScore: number;
|
|
217
|
-
decayedScore: number;
|
|
218
|
-
totalTasks: number;
|
|
219
|
-
disputesLost: number;
|
|
220
|
-
disputeRatio: number;
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
export interface LeaderboardEntry extends ReputationInfo {
|
|
224
|
-
rank: number;
|
|
225
|
-
displayName?: string;
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
export interface StorageUploadResult {
|
|
229
|
-
rootHash: RootHash;
|
|
230
|
-
size: number;
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
export interface Message {
|
|
234
|
-
id: number;
|
|
235
|
-
taskId: string;
|
|
236
|
-
fromAddress: Address;
|
|
237
|
-
toAddress: Address;
|
|
238
|
-
content: string;
|
|
239
|
-
read: boolean;
|
|
240
|
-
createdAt: string;
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
export interface AgentSearchResult {
|
|
244
|
-
address: Address;
|
|
245
|
-
displayName: string;
|
|
246
|
-
capabilities: string[];
|
|
247
|
-
avgRating: number;
|
|
248
|
-
reviewCount: number;
|
|
249
|
-
badgeCount: number;
|
|
250
|
-
reputation: number;
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
export interface TaskTemplate {
|
|
254
|
-
id: number;
|
|
255
|
-
title: string;
|
|
256
|
-
description: string;
|
|
257
|
-
category: string;
|
|
258
|
-
instructions: string;
|
|
259
|
-
verificationCriteria: Record<string, unknown>;
|
|
260
|
-
requiredCapabilities?: string[];
|
|
261
|
-
estimatedReward?: string;
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
export interface VerifyTaskInput {
|
|
265
|
-
taskId: number;
|
|
266
|
-
taskCategory: string;
|
|
267
|
-
taskRequirements: string;
|
|
268
|
-
evidenceSummary: string;
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
// ── Agent capabilities ───────────────────────────────────────────────────────
|
|
272
|
-
|
|
273
|
-
/** Dot-notation access to valid capability strings. */
|
|
274
|
-
export const AgentCap = {
|
|
275
|
-
DATA_PROCESSING: 'data_processing',
|
|
276
|
-
WEB_RESEARCH: 'web_research',
|
|
277
|
-
CODE_EXECUTION: 'code_execution',
|
|
278
|
-
CONTENT_GENERATION: 'content_generation',
|
|
279
|
-
API_INTEGRATION: 'api_integration',
|
|
280
|
-
TEXT_ANALYSIS: 'text_analysis',
|
|
281
|
-
TRANSLATION: 'translation',
|
|
282
|
-
SUMMARIZATION: 'summarization',
|
|
283
|
-
IMAGE_ANALYSIS: 'image_analysis',
|
|
284
|
-
DOCUMENT_PROCESSING: 'document_processing',
|
|
285
|
-
MATH_COMPUTATION: 'math_computation',
|
|
286
|
-
DATA_EXTRACTION: 'data_extraction',
|
|
287
|
-
REPORT_GENERATION: 'report_generation',
|
|
288
|
-
CODE_REVIEW: 'code_review',
|
|
289
|
-
TESTING: 'testing',
|
|
290
|
-
SCHEDULING: 'scheduling',
|
|
291
|
-
EMAIL_DRAFTING: 'email_drafting',
|
|
292
|
-
SOCIAL_MEDIA: 'social_media',
|
|
293
|
-
MARKET_RESEARCH: 'market_research',
|
|
294
|
-
COMPETITIVE_ANALYSIS: 'competitive_analysis',
|
|
295
|
-
} as const;
|
|
296
|
-
|
|
297
|
-
export type AgentCapability = typeof AgentCap[keyof typeof AgentCap];
|
package/src/verifier/index.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/src/worker/Worker.ts
DELETED
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
import type { BlindEscrowClient, OnChainTask, TaskRegistryClient } from '../chain/index.js';
|
|
2
|
-
import { aesDecrypt, eciesDecrypt, sealEvidence } from '../crypto/index.js';
|
|
3
|
-
import type { EventBus, SdkEvents } from '../events/index.js';
|
|
4
|
-
import type { Signer } from '../signer/index.js';
|
|
5
|
-
import type { Storage } from '../storage/index.js';
|
|
6
|
-
import type { Hex, RootHash, TaskId, TxReceiptLike } from '../types.js';
|
|
7
|
-
|
|
8
|
-
export interface WorkerDeps {
|
|
9
|
-
escrow: BlindEscrowClient;
|
|
10
|
-
registry: TaskRegistryClient;
|
|
11
|
-
storage: Storage;
|
|
12
|
-
signer: Signer;
|
|
13
|
-
events?: EventBus<SdkEvents>;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export interface DecryptInstructionsInput {
|
|
17
|
-
/** ECIES-wrapped AES key delivered out-of-band by the agent (e.g. via the backend API). */
|
|
18
|
-
wrappedKey: Uint8Array;
|
|
19
|
-
/** Worker's private key, hex with or without 0x prefix. */
|
|
20
|
-
workerPrivKey: string;
|
|
21
|
-
/** Storage root hash from the on-chain task metadata. */
|
|
22
|
-
taskHash: RootHash;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export interface SubmitEvidenceInput {
|
|
26
|
-
taskId: TaskId;
|
|
27
|
-
evidence: string | Uint8Array;
|
|
28
|
-
/** Agent's uncompressed secp256k1 pubkey (retrieved via the backend or chain). */
|
|
29
|
-
agentPubKey: string;
|
|
30
|
-
/** TEE enclave pubkey for Sealed Inference (from 0G Compute broker). */
|
|
31
|
-
enclavePubKey: string;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
export interface SubmitEvidenceResult {
|
|
35
|
-
rootHash: RootHash;
|
|
36
|
-
txHash: Hex;
|
|
37
|
-
receipt: TxReceiptLike;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Worker-side flows: browse open tasks, decrypt assigned instructions, seal
|
|
42
|
-
* evidence for agent + TEE, and claim payment after the deadline if
|
|
43
|
-
* verification stalls. Peer pubkeys and wrapped keys are passed explicitly;
|
|
44
|
-
* the SDK does not assume a specific message-passing substrate.
|
|
45
|
-
*/
|
|
46
|
-
export class Worker {
|
|
47
|
-
constructor(private readonly deps: WorkerDeps) {}
|
|
48
|
-
|
|
49
|
-
async browse(offset: bigint, limit: bigint): Promise<TaskId[]> {
|
|
50
|
-
return this.deps.registry.getOpenTasks(offset, limit);
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
async getTaskDetail(taskId: TaskId): Promise<OnChainTask> {
|
|
54
|
-
return this.deps.escrow.getTask(taskId);
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
async decryptInstructions(input: DecryptInstructionsInput): Promise<Uint8Array> {
|
|
58
|
-
const aesKey = await eciesDecrypt(input.wrappedKey, input.workerPrivKey);
|
|
59
|
-
const ciphertext = await this.deps.storage.download(input.taskHash);
|
|
60
|
-
return aesDecrypt(ciphertext, aesKey);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
async submitEvidence(input: SubmitEvidenceInput): Promise<SubmitEvidenceResult> {
|
|
64
|
-
const evidenceBytes = typeof input.evidence === 'string'
|
|
65
|
-
? new TextEncoder().encode(input.evidence)
|
|
66
|
-
: input.evidence;
|
|
67
|
-
|
|
68
|
-
const { envelope } = await sealEvidence({
|
|
69
|
-
evidence: evidenceBytes,
|
|
70
|
-
agentPubKey: input.agentPubKey,
|
|
71
|
-
enclavePubKey: input.enclavePubKey,
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
const uploaded = await this.deps.storage.upload(envelope);
|
|
75
|
-
const rootHash = uploaded.rootHash;
|
|
76
|
-
|
|
77
|
-
const receipt = await this.deps.escrow.submitEvidence(input.taskId, rootHash);
|
|
78
|
-
|
|
79
|
-
this.deps.events?.emit('evidence.submitted', {
|
|
80
|
-
taskId: input.taskId,
|
|
81
|
-
rootHash,
|
|
82
|
-
txHash: receipt.hash,
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
return { rootHash, txHash: receipt.hash, receipt };
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
/**
|
|
89
|
-
* Worker escape hatch: reclaim escrow after the deadline if completeVerification
|
|
90
|
-
* was never called. Payment released per contract logic (85% worker, 15% treasury
|
|
91
|
-
* or full refund depending on submission state — see BlindEscrow.claimTimeout).
|
|
92
|
-
*/
|
|
93
|
-
async claimTimeout(taskId: TaskId): Promise<TxReceiptLike> {
|
|
94
|
-
const receipt = await this.deps.escrow.claimTimeout(taskId);
|
|
95
|
-
this.deps.events?.emit('payment.claimed', { taskId, txHash: receipt.hash, amount: 0n });
|
|
96
|
-
return receipt;
|
|
97
|
-
}
|
|
98
|
-
}
|
package/src/worker/index.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from './Worker.js';
|
package/test/agent.test.ts
DELETED
|
@@ -1,193 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest';
|
|
2
|
-
import { Agent } from '../src/agent/index.js';
|
|
3
|
-
import type { BlindEscrowClient, CreateTaskArgs } from '../src/chain/index.js';
|
|
4
|
-
import { aesDecrypt, eciesDecrypt, generateKeyPair, sealEvidence } from '../src/crypto/index.js';
|
|
5
|
-
import { EventBus, type SdkEvents } from '../src/events/index.js';
|
|
6
|
-
import { InMemoryKeyStore } from '../src/keystore/index.js';
|
|
7
|
-
import { PrivateKeySigner } from '../src/signer/index.js';
|
|
8
|
-
import { MemoryStorage } from '../src/storage/index.js';
|
|
9
|
-
import type { Address, Hex, TaskId, TxReceiptLike } from '../src/types.js';
|
|
10
|
-
|
|
11
|
-
const AGENT_PK = '0x0123456789012345678901234567890123456789012345678901234567890123';
|
|
12
|
-
const WORKER_PK = '0x1000000000000000000000000000000000000000000000000000000000000001';
|
|
13
|
-
const FAKE_USDC = '0x317227efcA18D004E12CA8046AEf7E1597458F25' as Address;
|
|
14
|
-
const FAKE_WORKER = '0x2000000000000000000000000000000000000002' as Address;
|
|
15
|
-
|
|
16
|
-
interface EscrowCall<M extends string> {
|
|
17
|
-
method: M;
|
|
18
|
-
args: unknown;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* In-memory BlindEscrow mock. Records every call for assertions and hands back
|
|
23
|
-
* deterministic task ids / receipts so the Agent flow can be exercised without
|
|
24
|
-
* a real chain.
|
|
25
|
-
*/
|
|
26
|
-
class MockEscrow {
|
|
27
|
-
readonly calls: EscrowCall<'createTask' | 'assignWorker' | 'cancelTask'>[] = [];
|
|
28
|
-
private nextId = 1n;
|
|
29
|
-
|
|
30
|
-
async createTask(args: CreateTaskArgs) {
|
|
31
|
-
this.calls.push({ method: 'createTask', args });
|
|
32
|
-
const taskId = this.nextId++;
|
|
33
|
-
const receipt: TxReceiptLike = { hash: `0x${'a'.repeat(64)}` as Hex, blockNumber: 1, gasUsed: 21000n };
|
|
34
|
-
return { taskId, receipt };
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
async assignWorker(taskId: TaskId, worker: Address): Promise<TxReceiptLike> {
|
|
38
|
-
this.calls.push({ method: 'assignWorker', args: { taskId, worker } });
|
|
39
|
-
return { hash: `0x${'b'.repeat(64)}` as Hex, blockNumber: 2, gasUsed: 21000n };
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
async cancelTask(taskId: TaskId): Promise<TxReceiptLike> {
|
|
43
|
-
this.calls.push({ method: 'cancelTask', args: { taskId } });
|
|
44
|
-
return { hash: `0x${'c'.repeat(64)}` as Hex, blockNumber: 3, gasUsed: 21000n };
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
function mkDeps() {
|
|
49
|
-
const storage = new MemoryStorage();
|
|
50
|
-
const keystore = new InMemoryKeyStore();
|
|
51
|
-
const signer = new PrivateKeySigner(AGENT_PK);
|
|
52
|
-
const events = new EventBus<SdkEvents>();
|
|
53
|
-
const escrow = new MockEscrow();
|
|
54
|
-
return {
|
|
55
|
-
storage,
|
|
56
|
-
keystore,
|
|
57
|
-
signer,
|
|
58
|
-
events,
|
|
59
|
-
escrow,
|
|
60
|
-
agent: new Agent({
|
|
61
|
-
// biome-ignore lint/suspicious/noExplicitAny: MockEscrow matches the subset the Agent uses
|
|
62
|
-
escrow: escrow as any as BlindEscrowClient,
|
|
63
|
-
storage,
|
|
64
|
-
keystore,
|
|
65
|
-
signer,
|
|
66
|
-
events,
|
|
67
|
-
}),
|
|
68
|
-
};
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
describe('Agent.createTask', () => {
|
|
72
|
-
it('encrypts + uploads + calls chain + stashes key + emits event', async () => {
|
|
73
|
-
const { agent, escrow, storage, keystore, events } = mkDeps();
|
|
74
|
-
|
|
75
|
-
const emitted: SdkEvents['task.created'][] = [];
|
|
76
|
-
events.on('task.created', (p) => {
|
|
77
|
-
emitted.push(p);
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
const deadline = new Date(Date.now() + 3600_000);
|
|
81
|
-
const res = await agent.createTask({
|
|
82
|
-
instructions: 'Take 3 photos of the storefront',
|
|
83
|
-
category: 'photography',
|
|
84
|
-
locationZone: 'Lagos, NG',
|
|
85
|
-
reward: { token: FAKE_USDC, amount: 5_000000n },
|
|
86
|
-
deadline,
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
expect(res.taskId).toBe(1n);
|
|
90
|
-
expect(res.rootHash).toMatch(/^0x[0-9a-f]{64}$/);
|
|
91
|
-
expect(escrow.calls).toHaveLength(1);
|
|
92
|
-
expect(escrow.calls[0]?.method).toBe('createTask');
|
|
93
|
-
|
|
94
|
-
// Keystore now holds the task key for this id
|
|
95
|
-
const stored = await keystore.getTaskKey(1n);
|
|
96
|
-
expect(stored).not.toBeNull();
|
|
97
|
-
expect(stored?.aesKey).toEqual(res.taskKey.aesKey);
|
|
98
|
-
|
|
99
|
-
// The uploaded blob decrypts back to the original instructions
|
|
100
|
-
const blob = await storage.download(res.rootHash);
|
|
101
|
-
const pt = await aesDecrypt(blob, res.taskKey.aesKey);
|
|
102
|
-
expect(new TextDecoder().decode(pt)).toBe('Take 3 photos of the storefront');
|
|
103
|
-
|
|
104
|
-
// Event fired
|
|
105
|
-
expect(emitted).toHaveLength(1);
|
|
106
|
-
expect(emitted[0]?.taskId).toBe(1n);
|
|
107
|
-
expect(emitted[0]?.rootHash).toBe(res.rootHash);
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
it('accepts Uint8Array instructions as well as strings', async () => {
|
|
111
|
-
const { agent, storage } = mkDeps();
|
|
112
|
-
const res = await agent.createTask({
|
|
113
|
-
instructions: new Uint8Array([0xde, 0xad, 0xbe, 0xef]),
|
|
114
|
-
category: 'x',
|
|
115
|
-
locationZone: 'y',
|
|
116
|
-
reward: { token: FAKE_USDC, amount: 1n },
|
|
117
|
-
deadline: new Date(Date.now() + 3600_000),
|
|
118
|
-
});
|
|
119
|
-
const blob = await storage.download(res.rootHash);
|
|
120
|
-
const pt = await aesDecrypt(blob, res.taskKey.aesKey);
|
|
121
|
-
expect(Array.from(pt)).toEqual([0xde, 0xad, 0xbe, 0xef]);
|
|
122
|
-
});
|
|
123
|
-
});
|
|
124
|
-
|
|
125
|
-
describe('Agent.assignWorker', () => {
|
|
126
|
-
it('wraps the task AES key to the worker pubkey and calls chain', async () => {
|
|
127
|
-
const { agent } = mkDeps();
|
|
128
|
-
const created = await agent.createTask({
|
|
129
|
-
instructions: 'hello',
|
|
130
|
-
category: 'c',
|
|
131
|
-
locationZone: 'z',
|
|
132
|
-
reward: { token: FAKE_USDC, amount: 1n },
|
|
133
|
-
deadline: new Date(Date.now() + 3600_000),
|
|
134
|
-
});
|
|
135
|
-
const worker = generateKeyPair();
|
|
136
|
-
const result = await agent.assignWorker(created.taskId, FAKE_WORKER, worker.publicKey);
|
|
137
|
-
|
|
138
|
-
// Worker can recover the AES key from the wrapped blob
|
|
139
|
-
const recoveredKey = await eciesDecrypt(result.wrappedKey, worker.privateKey);
|
|
140
|
-
expect(recoveredKey).toEqual(created.taskKey.aesKey);
|
|
141
|
-
});
|
|
142
|
-
|
|
143
|
-
it('throws LIFECYCLE/TASK_NOT_FOUND when task key is missing', async () => {
|
|
144
|
-
const { agent } = mkDeps();
|
|
145
|
-
const worker = generateKeyPair();
|
|
146
|
-
await expect(agent.assignWorker(999n, FAKE_WORKER, worker.publicKey)).rejects.toMatchObject({
|
|
147
|
-
code: 'LIFECYCLE/TASK_NOT_FOUND',
|
|
148
|
-
});
|
|
149
|
-
});
|
|
150
|
-
});
|
|
151
|
-
|
|
152
|
-
describe('Agent.cancelTask', () => {
|
|
153
|
-
it('calls chain and removes the task key from keystore', async () => {
|
|
154
|
-
const { agent, keystore, events } = mkDeps();
|
|
155
|
-
const created = await agent.createTask({
|
|
156
|
-
instructions: 'hello',
|
|
157
|
-
category: 'c',
|
|
158
|
-
locationZone: 'z',
|
|
159
|
-
reward: { token: FAKE_USDC, amount: 1n },
|
|
160
|
-
deadline: new Date(Date.now() + 3600_000),
|
|
161
|
-
});
|
|
162
|
-
expect(await keystore.getTaskKey(created.taskId)).not.toBeNull();
|
|
163
|
-
|
|
164
|
-
const cancelled: SdkEvents['task.cancelled'][] = [];
|
|
165
|
-
events.on('task.cancelled', (p) => cancelled.push(p));
|
|
166
|
-
|
|
167
|
-
await agent.cancelTask(created.taskId);
|
|
168
|
-
expect(await keystore.getTaskKey(created.taskId)).toBeNull();
|
|
169
|
-
expect(cancelled).toHaveLength(1);
|
|
170
|
-
});
|
|
171
|
-
});
|
|
172
|
-
|
|
173
|
-
describe('Agent.fetchAndDecryptEvidence', () => {
|
|
174
|
-
it('downloads the envelope from storage and unwraps with agent privkey', async () => {
|
|
175
|
-
const { agent, storage, signer } = mkDeps();
|
|
176
|
-
const agentPub = await signer.getPublicKey();
|
|
177
|
-
const enclave = generateKeyPair();
|
|
178
|
-
|
|
179
|
-
const evidence = new TextEncoder().encode('photos + timestamps');
|
|
180
|
-
const sealed = await sealEvidence({
|
|
181
|
-
evidence,
|
|
182
|
-
agentPubKey: agentPub,
|
|
183
|
-
enclavePubKey: enclave.publicKey,
|
|
184
|
-
});
|
|
185
|
-
const { rootHash } = await storage.upload(sealed.envelope);
|
|
186
|
-
|
|
187
|
-
const recovered = await agent.fetchAndDecryptEvidence(rootHash, AGENT_PK);
|
|
188
|
-
expect(new TextDecoder().decode(recovered)).toBe('photos + timestamps');
|
|
189
|
-
});
|
|
190
|
-
});
|
|
191
|
-
|
|
192
|
-
// silence unused-import warning for the imported pk
|
|
193
|
-
void WORKER_PK;
|