@agirails/sdk 2.0.0-beta
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 +183 -0
- package/dist/ACTPClient.d.ts +52 -0
- package/dist/ACTPClient.d.ts.map +1 -0
- package/dist/ACTPClient.js +120 -0
- package/dist/ACTPClient.js.map +1 -0
- package/dist/abi/ACTPKernel.json +1340 -0
- package/dist/abi/ERC20.json +38 -0
- package/dist/abi/EscrowVault.json +64 -0
- package/dist/builders/DeliveryProofBuilder.d.ts +37 -0
- package/dist/builders/DeliveryProofBuilder.d.ts.map +1 -0
- package/dist/builders/DeliveryProofBuilder.js +165 -0
- package/dist/builders/DeliveryProofBuilder.js.map +1 -0
- package/dist/builders/QuoteBuilder.d.ts +68 -0
- package/dist/builders/QuoteBuilder.d.ts.map +1 -0
- package/dist/builders/QuoteBuilder.js +255 -0
- package/dist/builders/QuoteBuilder.js.map +1 -0
- package/dist/builders/index.d.ts +3 -0
- package/dist/builders/index.d.ts.map +1 -0
- package/dist/builders/index.js +10 -0
- package/dist/builders/index.js.map +1 -0
- package/dist/config/networks.d.ts +27 -0
- package/dist/config/networks.d.ts.map +1 -0
- package/dist/config/networks.js +103 -0
- package/dist/config/networks.js.map +1 -0
- package/dist/errors/index.d.ts +38 -0
- package/dist/errors/index.d.ts.map +1 -0
- package/dist/errors/index.js +87 -0
- package/dist/errors/index.js.map +1 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +68 -0
- package/dist/index.js.map +1 -0
- package/dist/protocol/ACTPKernel.d.ts +30 -0
- package/dist/protocol/ACTPKernel.d.ts.map +1 -0
- package/dist/protocol/ACTPKernel.js +261 -0
- package/dist/protocol/ACTPKernel.js.map +1 -0
- package/dist/protocol/EASHelper.d.ts +23 -0
- package/dist/protocol/EASHelper.d.ts.map +1 -0
- package/dist/protocol/EASHelper.js +106 -0
- package/dist/protocol/EASHelper.js.map +1 -0
- package/dist/protocol/EscrowVault.d.ts +24 -0
- package/dist/protocol/EscrowVault.d.ts.map +1 -0
- package/dist/protocol/EscrowVault.js +114 -0
- package/dist/protocol/EscrowVault.js.map +1 -0
- package/dist/protocol/EventMonitor.d.ts +18 -0
- package/dist/protocol/EventMonitor.d.ts.map +1 -0
- package/dist/protocol/EventMonitor.js +92 -0
- package/dist/protocol/EventMonitor.js.map +1 -0
- package/dist/protocol/MessageSigner.d.ts +23 -0
- package/dist/protocol/MessageSigner.d.ts.map +1 -0
- package/dist/protocol/MessageSigner.js +178 -0
- package/dist/protocol/MessageSigner.js.map +1 -0
- package/dist/protocol/ProofGenerator.d.ts +22 -0
- package/dist/protocol/ProofGenerator.d.ts.map +1 -0
- package/dist/protocol/ProofGenerator.js +64 -0
- package/dist/protocol/ProofGenerator.js.map +1 -0
- package/dist/protocol/QuoteBuilder.d.ts +2 -0
- package/dist/protocol/QuoteBuilder.d.ts.map +1 -0
- package/dist/protocol/QuoteBuilder.js +7 -0
- package/dist/protocol/QuoteBuilder.js.map +1 -0
- package/dist/types/eip712.d.ts +106 -0
- package/dist/types/eip712.d.ts.map +1 -0
- package/dist/types/eip712.js +84 -0
- package/dist/types/eip712.js.map +1 -0
- package/dist/types/escrow.d.ts +18 -0
- package/dist/types/escrow.d.ts.map +1 -0
- package/dist/types/escrow.js +3 -0
- package/dist/types/escrow.js.map +1 -0
- package/dist/types/index.d.ts +6 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +22 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/message.d.ts +109 -0
- package/dist/types/message.d.ts.map +1 -0
- package/dist/types/message.js +3 -0
- package/dist/types/message.js.map +1 -0
- package/dist/types/state.d.ts +19 -0
- package/dist/types/state.d.ts.map +1 -0
- package/dist/types/state.js +49 -0
- package/dist/types/state.js.map +1 -0
- package/dist/types/transaction.d.ts +36 -0
- package/dist/types/transaction.d.ts.map +1 -0
- package/dist/types/transaction.js +3 -0
- package/dist/types/transaction.js.map +1 -0
- package/dist/utils/IPFSClient.d.ts +37 -0
- package/dist/utils/IPFSClient.d.ts.map +1 -0
- package/dist/utils/IPFSClient.js +128 -0
- package/dist/utils/IPFSClient.js.map +1 -0
- package/dist/utils/NonceManager.d.ts +34 -0
- package/dist/utils/NonceManager.d.ts.map +1 -0
- package/dist/utils/NonceManager.js +114 -0
- package/dist/utils/NonceManager.js.map +1 -0
- package/dist/utils/ReceivedNonceTracker.d.ts +35 -0
- package/dist/utils/ReceivedNonceTracker.d.ts.map +1 -0
- package/dist/utils/ReceivedNonceTracker.js +196 -0
- package/dist/utils/ReceivedNonceTracker.js.map +1 -0
- package/dist/utils/canonicalJson.d.ts +4 -0
- package/dist/utils/canonicalJson.d.ts.map +1 -0
- package/dist/utils/canonicalJson.js +21 -0
- package/dist/utils/canonicalJson.js.map +1 -0
- package/dist/utils/computeTypeHash.d.ts +3 -0
- package/dist/utils/computeTypeHash.d.ts.map +1 -0
- package/dist/utils/computeTypeHash.js +30 -0
- package/dist/utils/computeTypeHash.js.map +1 -0
- package/dist/utils/validation.d.ts +6 -0
- package/dist/utils/validation.d.ts.map +1 -0
- package/dist/utils/validation.js +46 -0
- package/dist/utils/validation.js.map +1 -0
- package/package.json +73 -0
- package/src/ACTPClient.ts +276 -0
- package/src/__tests__/ProofGenerator.test.ts +124 -0
- package/src/__tests__/QuoteBuilder.test.ts +516 -0
- package/src/__tests__/StateMachine.test.ts +82 -0
- package/src/__tests__/builders/DeliveryProofBuilder.test.ts +581 -0
- package/src/__tests__/integration/ACTPClient.test.ts +263 -0
- package/src/__tests__/integration.test.ts +289 -0
- package/src/__tests__/protocol/EASHelper.test.ts +472 -0
- package/src/__tests__/protocol/EventMonitor.test.ts +382 -0
- package/src/__tests__/security/ACTPKernel.security.test.ts +1167 -0
- package/src/__tests__/security/EscrowVault.security.test.ts +570 -0
- package/src/__tests__/security/MessageSigner.security.test.ts +286 -0
- package/src/__tests__/security/NonceReplay.security.test.ts +501 -0
- package/src/__tests__/security/validation.security.test.ts +376 -0
- package/src/__tests__/utils/IPFSClient.test.ts +262 -0
- package/src/__tests__/utils/NonceManager.test.ts +205 -0
- package/src/__tests__/utils/canonicalJson.test.ts +153 -0
- package/src/abi/ACTPKernel.json +1340 -0
- package/src/abi/ERC20.json +40 -0
- package/src/abi/EscrowVault.json +66 -0
- package/src/builders/DeliveryProofBuilder.ts +326 -0
- package/src/builders/QuoteBuilder.ts +483 -0
- package/src/builders/index.ts +17 -0
- package/src/config/networks.ts +165 -0
- package/src/errors/index.ts +130 -0
- package/src/index.ts +108 -0
- package/src/protocol/ACTPKernel.ts +625 -0
- package/src/protocol/EASHelper.ts +197 -0
- package/src/protocol/EscrowVault.ts +237 -0
- package/src/protocol/EventMonitor.ts +161 -0
- package/src/protocol/MessageSigner.ts +336 -0
- package/src/protocol/ProofGenerator.ts +119 -0
- package/src/protocol/QuoteBuilder.ts +15 -0
- package/src/types/eip712.ts +175 -0
- package/src/types/escrow.ts +26 -0
- package/src/types/index.ts +10 -0
- package/src/types/message.ts +145 -0
- package/src/types/state.ts +77 -0
- package/src/types/transaction.ts +54 -0
- package/src/utils/IPFSClient.ts +248 -0
- package/src/utils/NonceManager.ts +293 -0
- package/src/utils/ReceivedNonceTracker.ts +397 -0
- package/src/utils/canonicalJson.ts +38 -0
- package/src/utils/computeTypeHash.ts +50 -0
- package/src/utils/validation.ts +82 -0
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
import { ethers, Wallet, Signer } from 'ethers';
|
|
2
|
+
import type { JsonRpcProvider } from 'ethers';
|
|
3
|
+
import { ACTPKernel } from './protocol/ACTPKernel';
|
|
4
|
+
import { EscrowVault } from './protocol/EscrowVault';
|
|
5
|
+
import { EventMonitor } from './protocol/EventMonitor';
|
|
6
|
+
import { ProofGenerator } from './protocol/ProofGenerator';
|
|
7
|
+
import { MessageSigner } from './protocol/MessageSigner';
|
|
8
|
+
import { QuoteBuilder } from './builders/QuoteBuilder';
|
|
9
|
+
import { NetworkConfig, getNetwork } from './config/networks';
|
|
10
|
+
import { NetworkError, ValidationError } from './errors';
|
|
11
|
+
import { EASHelper, EASConfig } from './protocol/EASHelper';
|
|
12
|
+
import { NonceManager, InMemoryNonceManager } from './utils/NonceManager';
|
|
13
|
+
import { IPFSClient } from './utils/IPFSClient';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* ACTPClient configuration
|
|
17
|
+
*/
|
|
18
|
+
export interface ACTPClientConfig {
|
|
19
|
+
network: 'base-sepolia' | 'base-mainnet';
|
|
20
|
+
privateKey?: string;
|
|
21
|
+
signer?: Signer;
|
|
22
|
+
provider?: JsonRpcProvider;
|
|
23
|
+
rpcUrl?: string;
|
|
24
|
+
contracts?: {
|
|
25
|
+
actpKernel?: string;
|
|
26
|
+
escrowVault?: string;
|
|
27
|
+
usdc?: string;
|
|
28
|
+
};
|
|
29
|
+
gasSettings?: {
|
|
30
|
+
maxFeePerGas?: bigint;
|
|
31
|
+
maxPriorityFeePerGas?: bigint;
|
|
32
|
+
};
|
|
33
|
+
eas?: EASConfig;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* ACTPClient - Main entry point for ACTP SDK
|
|
38
|
+
*
|
|
39
|
+
* Example:
|
|
40
|
+
* ```typescript
|
|
41
|
+
* const client = await ACTPClient.create({
|
|
42
|
+
* network: 'base-sepolia',
|
|
43
|
+
* privateKey: process.env.PRIVATE_KEY
|
|
44
|
+
* });
|
|
45
|
+
*
|
|
46
|
+
* const txId = await client.kernel.createTransaction({...});
|
|
47
|
+
* ```
|
|
48
|
+
*/
|
|
49
|
+
export class ACTPClient {
|
|
50
|
+
public readonly kernel: ACTPKernel;
|
|
51
|
+
public readonly escrow: EscrowVault;
|
|
52
|
+
public readonly events: EventMonitor;
|
|
53
|
+
public readonly proofGenerator: ProofGenerator;
|
|
54
|
+
public readonly messageSigner: MessageSigner;
|
|
55
|
+
public readonly quote: QuoteBuilder;
|
|
56
|
+
public readonly eas?: EASHelper;
|
|
57
|
+
|
|
58
|
+
private readonly provider: JsonRpcProvider;
|
|
59
|
+
private readonly signer: Signer;
|
|
60
|
+
private readonly networkConfig: NetworkConfig;
|
|
61
|
+
private readonly nonceManager: NonceManager;
|
|
62
|
+
private readonly ipfs?: IPFSClient;
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Private constructor - use ACTPClient.create() instead
|
|
66
|
+
*/
|
|
67
|
+
private constructor(config: ACTPClientConfig) {
|
|
68
|
+
// Validate config
|
|
69
|
+
this.validateConfig(config);
|
|
70
|
+
|
|
71
|
+
// Get network configuration (already cloned in getNetwork)
|
|
72
|
+
this.networkConfig = getNetwork(config.network);
|
|
73
|
+
|
|
74
|
+
// Apply overrides immutably (create new objects, don't mutate)
|
|
75
|
+
if (config.contracts) {
|
|
76
|
+
this.networkConfig = {
|
|
77
|
+
...this.networkConfig,
|
|
78
|
+
contracts: {
|
|
79
|
+
...this.networkConfig.contracts,
|
|
80
|
+
...config.contracts
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Apply gas settings overrides
|
|
86
|
+
if (config.gasSettings) {
|
|
87
|
+
this.networkConfig = {
|
|
88
|
+
...this.networkConfig,
|
|
89
|
+
gasSettings: {
|
|
90
|
+
...this.networkConfig.gasSettings,
|
|
91
|
+
...config.gasSettings
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Freeze config to prevent accidental mutation
|
|
97
|
+
Object.freeze(this.networkConfig.contracts);
|
|
98
|
+
Object.freeze(this.networkConfig.gasSettings);
|
|
99
|
+
Object.freeze(this.networkConfig);
|
|
100
|
+
|
|
101
|
+
// Setup provider
|
|
102
|
+
if (config.provider) {
|
|
103
|
+
this.provider = config.provider;
|
|
104
|
+
} else {
|
|
105
|
+
const rpcUrl = config.rpcUrl || this.networkConfig.rpcUrl;
|
|
106
|
+
this.provider = new ethers.JsonRpcProvider(rpcUrl, this.networkConfig.chainId);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Setup signer
|
|
110
|
+
if (config.signer) {
|
|
111
|
+
this.signer = config.signer;
|
|
112
|
+
} else if (config.privateKey) {
|
|
113
|
+
this.signer = new Wallet(config.privateKey, this.provider);
|
|
114
|
+
} else {
|
|
115
|
+
// Attempt to derive signer from provider if possible
|
|
116
|
+
// In ethers v6, getSigner() is async and returns a JsonRpcSigner
|
|
117
|
+
throw new ValidationError('signer', 'Either privateKey or signer must be provided');
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Initialize shared utilities
|
|
121
|
+
this.nonceManager = new InMemoryNonceManager();
|
|
122
|
+
|
|
123
|
+
// Initialize IPFS client if configured
|
|
124
|
+
if (config.rpcUrl) {
|
|
125
|
+
// IPFS configuration could be added to ACTPClientConfig in the future
|
|
126
|
+
// For now, QuoteBuilder can work without IPFS (quotes stored only on-chain)
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Initialize protocol modules
|
|
130
|
+
this.kernel = new ACTPKernel(
|
|
131
|
+
this.networkConfig.contracts.actpKernel,
|
|
132
|
+
this.signer,
|
|
133
|
+
this.networkConfig.gasSettings
|
|
134
|
+
);
|
|
135
|
+
|
|
136
|
+
this.escrow = new EscrowVault(
|
|
137
|
+
this.networkConfig.contracts.escrowVault,
|
|
138
|
+
this.signer,
|
|
139
|
+
this.networkConfig.gasSettings
|
|
140
|
+
);
|
|
141
|
+
|
|
142
|
+
this.events = new EventMonitor(
|
|
143
|
+
this.kernel['contract'], // Access private contract field
|
|
144
|
+
this.escrow['contract']
|
|
145
|
+
);
|
|
146
|
+
|
|
147
|
+
this.proofGenerator = new ProofGenerator();
|
|
148
|
+
|
|
149
|
+
this.messageSigner = new MessageSigner(this.signer);
|
|
150
|
+
|
|
151
|
+
// Initialize QuoteBuilder (AIP-2)
|
|
152
|
+
this.quote = new QuoteBuilder(this.signer, this.nonceManager, this.ipfs);
|
|
153
|
+
|
|
154
|
+
if (config.eas) {
|
|
155
|
+
this.eas = new EASHelper(this.signer, config.eas);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Create and initialize ACTPClient (async factory pattern)
|
|
161
|
+
* Ensures all async components (EIP-712 domain) are ready before returning
|
|
162
|
+
*/
|
|
163
|
+
static async create(config: ACTPClientConfig): Promise<ACTPClient> {
|
|
164
|
+
const client = new ACTPClient(config);
|
|
165
|
+
|
|
166
|
+
// Initialize EIP-712 domain for message signing
|
|
167
|
+
await client.messageSigner.initDomain(client.networkConfig.contracts.actpKernel);
|
|
168
|
+
|
|
169
|
+
return client;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* @deprecated Use ACTPClient.create() instead
|
|
174
|
+
* Initialize async components (must be called after construction)
|
|
175
|
+
*/
|
|
176
|
+
async initialize(): Promise<void> {
|
|
177
|
+
await this.messageSigner.initDomain(this.networkConfig.contracts.actpKernel);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Get signer address
|
|
182
|
+
*/
|
|
183
|
+
async getAddress(): Promise<string> {
|
|
184
|
+
return await this.signer.getAddress();
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Get network configuration
|
|
189
|
+
*/
|
|
190
|
+
getNetworkConfig(): NetworkConfig {
|
|
191
|
+
return this.networkConfig;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Get provider
|
|
196
|
+
*/
|
|
197
|
+
getProvider(): JsonRpcProvider {
|
|
198
|
+
return this.provider;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Get current block number
|
|
203
|
+
*/
|
|
204
|
+
async getBlockNumber(): Promise<number> {
|
|
205
|
+
try {
|
|
206
|
+
return await this.provider.getBlockNumber();
|
|
207
|
+
} catch (error: any) {
|
|
208
|
+
throw new NetworkError(this.networkConfig.name, error.message);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Get gas price (ethers v6: use getFeeData instead)
|
|
214
|
+
*/
|
|
215
|
+
async getGasPrice() {
|
|
216
|
+
try {
|
|
217
|
+
const feeData = await this.provider.getFeeData();
|
|
218
|
+
return feeData.gasPrice || 0n;
|
|
219
|
+
} catch (error: any) {
|
|
220
|
+
throw new NetworkError(this.networkConfig.name, error.message);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Release escrow with automatic attestation verification (recommended for security).
|
|
226
|
+
*
|
|
227
|
+
* SECURITY: This method verifies the attestation belongs to the transaction BEFORE
|
|
228
|
+
* releasing escrow. This protects against malicious providers submitting attestations
|
|
229
|
+
* from different transactions.
|
|
230
|
+
*
|
|
231
|
+
* ACTPKernel V1 contract accepts any attestationUID without on-chain validation.
|
|
232
|
+
* This SDK-side verification is the recommended protection until V2 adds on-chain checks.
|
|
233
|
+
*
|
|
234
|
+
* @param txId - Transaction ID to settle
|
|
235
|
+
* @param attestationUID - EAS attestation UID to verify
|
|
236
|
+
* @throws {Error} If EAS is not configured (client.eas is undefined)
|
|
237
|
+
* @throws {Error} If attestation verification fails (revoked, expired, or txId mismatch)
|
|
238
|
+
* @throws {TransactionRevertedError} If escrow release fails
|
|
239
|
+
*
|
|
240
|
+
* @example
|
|
241
|
+
* ```typescript
|
|
242
|
+
* // Get transaction to find attestation UID
|
|
243
|
+
* const tx = await client.kernel.getTransaction(txId);
|
|
244
|
+
*
|
|
245
|
+
* // Verify and release escrow in one call
|
|
246
|
+
* await client.releaseEscrowWithVerification(txId, tx.attestationUID);
|
|
247
|
+
* ```
|
|
248
|
+
*/
|
|
249
|
+
async releaseEscrowWithVerification(txId: string, attestationUID: string): Promise<void> {
|
|
250
|
+
// Ensure EAS is configured
|
|
251
|
+
if (!this.eas) {
|
|
252
|
+
throw new Error(
|
|
253
|
+
'EAS is not configured. Initialize ACTPClient with eas config or use kernel.releaseEscrow() directly (unsafe)'
|
|
254
|
+
);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// Step 1: Verify attestation belongs to this transaction
|
|
258
|
+
await this.eas.verifyDeliveryAttestation(txId, attestationUID);
|
|
259
|
+
|
|
260
|
+
// Step 2: Release escrow (verification passed)
|
|
261
|
+
await this.kernel.releaseEscrow(txId);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Validate configuration
|
|
266
|
+
*/
|
|
267
|
+
private validateConfig(config: ACTPClientConfig): void {
|
|
268
|
+
if (!config.network) {
|
|
269
|
+
throw new ValidationError('network', 'Network is required');
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
if (!config.privateKey && !config.signer && !config.provider) {
|
|
273
|
+
throw new ValidationError('auth', 'Provide either privateKey, signer, or provider with signer access');
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { ProofGenerator } from '../protocol/ProofGenerator';
|
|
2
|
+
|
|
3
|
+
describe('ProofGenerator', () => {
|
|
4
|
+
let proofGenerator: ProofGenerator;
|
|
5
|
+
|
|
6
|
+
beforeEach(() => {
|
|
7
|
+
proofGenerator = new ProofGenerator();
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
describe('hashContent', () => {
|
|
11
|
+
it('should hash string content', () => {
|
|
12
|
+
const content = 'Hello, ACTP!';
|
|
13
|
+
const hash = proofGenerator.hashContent(content);
|
|
14
|
+
|
|
15
|
+
expect(hash).toMatch(/^0x[a-f0-9]{64}$/);
|
|
16
|
+
expect(hash).toHaveLength(66); // 0x + 64 hex chars
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it('should hash buffer content', () => {
|
|
20
|
+
const content = Buffer.from('Hello, ACTP!');
|
|
21
|
+
const hash = proofGenerator.hashContent(content);
|
|
22
|
+
|
|
23
|
+
expect(hash).toMatch(/^0x[a-f0-9]{64}$/);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it('should produce same hash for same content', () => {
|
|
27
|
+
const content = 'Test content';
|
|
28
|
+
const hash1 = proofGenerator.hashContent(content);
|
|
29
|
+
const hash2 = proofGenerator.hashContent(content);
|
|
30
|
+
|
|
31
|
+
expect(hash1).toBe(hash2);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('should produce different hashes for different content', () => {
|
|
35
|
+
const hash1 = proofGenerator.hashContent('Content A');
|
|
36
|
+
const hash2 = proofGenerator.hashContent('Content B');
|
|
37
|
+
|
|
38
|
+
expect(hash1).not.toBe(hash2);
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
describe('generateDeliveryProof', () => {
|
|
43
|
+
it('should generate valid delivery proof', () => {
|
|
44
|
+
const txId = '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef';
|
|
45
|
+
const deliverable = 'Completed translation work';
|
|
46
|
+
|
|
47
|
+
const proof = proofGenerator.generateDeliveryProof({
|
|
48
|
+
txId,
|
|
49
|
+
deliverable,
|
|
50
|
+
metadata: { language: 'es' }
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
expect(proof.txId).toBe(txId);
|
|
54
|
+
expect(proof.contentHash).toMatch(/^0x[a-f0-9]{64}$/);
|
|
55
|
+
expect(proof.timestamp).toBeGreaterThan(0);
|
|
56
|
+
expect(proof.metadata.size).toBeGreaterThan(0);
|
|
57
|
+
expect(proof.metadata.language).toBe('es');
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it('should include default mimeType', () => {
|
|
61
|
+
const proof = proofGenerator.generateDeliveryProof({
|
|
62
|
+
txId: '0x1234',
|
|
63
|
+
deliverable: 'test'
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
expect(proof.metadata.mimeType).toBe('application/octet-stream');
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it('should use custom mimeType', () => {
|
|
70
|
+
const proof = proofGenerator.generateDeliveryProof({
|
|
71
|
+
txId: '0x1234',
|
|
72
|
+
deliverable: 'test',
|
|
73
|
+
metadata: { mimeType: 'text/plain' }
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
expect(proof.metadata.mimeType).toBe('text/plain');
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
describe('verifyDeliverable', () => {
|
|
81
|
+
it('should verify matching deliverable', () => {
|
|
82
|
+
const deliverable = 'Test deliverable';
|
|
83
|
+
const hash = proofGenerator.hashContent(deliverable);
|
|
84
|
+
|
|
85
|
+
const isValid = proofGenerator.verifyDeliverable(deliverable, hash);
|
|
86
|
+
expect(isValid).toBe(true);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it('should reject non-matching deliverable', () => {
|
|
90
|
+
const deliverable = 'Test deliverable';
|
|
91
|
+
const wrongHash = proofGenerator.hashContent('Wrong content');
|
|
92
|
+
|
|
93
|
+
const isValid = proofGenerator.verifyDeliverable(deliverable, wrongHash);
|
|
94
|
+
expect(isValid).toBe(false);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it('should be case-insensitive for hash comparison', () => {
|
|
98
|
+
const deliverable = 'Test';
|
|
99
|
+
const hash = proofGenerator.hashContent(deliverable);
|
|
100
|
+
const upperHash = hash.toUpperCase();
|
|
101
|
+
|
|
102
|
+
const isValid = proofGenerator.verifyDeliverable(deliverable, upperHash);
|
|
103
|
+
expect(isValid).toBe(true);
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
describe('encodeProof and decodeProof', () => {
|
|
108
|
+
it('should encode and decode proof correctly', () => {
|
|
109
|
+
const originalProof = proofGenerator.generateDeliveryProof({
|
|
110
|
+
txId: '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef',
|
|
111
|
+
deliverable: 'test'
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
const encoded = proofGenerator.encodeProof(originalProof);
|
|
115
|
+
const decoded = proofGenerator.decodeProof(encoded);
|
|
116
|
+
|
|
117
|
+
expect(decoded.txId).toBe(originalProof.txId);
|
|
118
|
+
expect(decoded.contentHash).toBe(originalProof.contentHash);
|
|
119
|
+
expect(decoded.timestamp).toBe(originalProof.timestamp);
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
|