@agirails/sdk 2.5.3 → 2.5.5
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/ACTPClient.d.ts +18 -0
- package/dist/ACTPClient.d.ts.map +1 -1
- package/dist/ACTPClient.js +72 -23
- package/dist/ACTPClient.js.map +1 -1
- package/dist/adapters/BasicAdapter.d.ts +15 -0
- package/dist/adapters/BasicAdapter.d.ts.map +1 -1
- package/dist/adapters/BasicAdapter.js +33 -4
- package/dist/adapters/BasicAdapter.js.map +1 -1
- package/dist/adapters/StandardAdapter.d.ts +20 -3
- package/dist/adapters/StandardAdapter.d.ts.map +1 -1
- package/dist/adapters/StandardAdapter.js +90 -12
- package/dist/adapters/StandardAdapter.js.map +1 -1
- package/dist/cli/commands/publish.js +16 -4
- package/dist/cli/commands/publish.js.map +1 -1
- package/dist/cli/commands/register.js +16 -4
- package/dist/cli/commands/register.js.map +1 -1
- package/dist/cli/commands/tx.js +31 -3
- package/dist/cli/commands/tx.js.map +1 -1
- package/dist/config/networks.d.ts +10 -2
- package/dist/config/networks.d.ts.map +1 -1
- package/dist/config/networks.js +31 -22
- package/dist/config/networks.js.map +1 -1
- package/dist/level0/request.d.ts.map +1 -1
- package/dist/level0/request.js +2 -1
- package/dist/level0/request.js.map +1 -1
- package/dist/runtime/BlockchainRuntime.d.ts.map +1 -1
- package/dist/runtime/BlockchainRuntime.js +11 -5
- package/dist/runtime/BlockchainRuntime.js.map +1 -1
- package/dist/utils/IPFSClient.d.ts +3 -1
- package/dist/utils/IPFSClient.d.ts.map +1 -1
- package/dist/utils/IPFSClient.js +27 -7
- package/dist/utils/IPFSClient.js.map +1 -1
- package/dist/wallet/AutoWalletProvider.d.ts +11 -1
- package/dist/wallet/AutoWalletProvider.d.ts.map +1 -1
- package/dist/wallet/AutoWalletProvider.js +84 -19
- package/dist/wallet/AutoWalletProvider.js.map +1 -1
- package/dist/wallet/IWalletProvider.d.ts +34 -0
- package/dist/wallet/IWalletProvider.d.ts.map +1 -1
- package/dist/wallet/SmartWalletRouter.d.ts +128 -0
- package/dist/wallet/SmartWalletRouter.d.ts.map +1 -0
- package/dist/wallet/SmartWalletRouter.js +248 -0
- package/dist/wallet/SmartWalletRouter.js.map +1 -0
- package/dist/wallet/aa/DualNonceManager.d.ts +26 -1
- package/dist/wallet/aa/DualNonceManager.d.ts.map +1 -1
- package/dist/wallet/aa/DualNonceManager.js +140 -6
- package/dist/wallet/aa/DualNonceManager.js.map +1 -1
- package/package.json +3 -6
- package/src/ACTPClient.ts +0 -1579
- package/src/abi/ACTPKernel.json +0 -1356
- package/src/abi/AgentRegistry.json +0 -915
- package/src/abi/ERC20.json +0 -40
- package/src/abi/EscrowVault.json +0 -134
- package/src/abi/IdentityRegistry.json +0 -316
- package/src/adapters/AdapterRegistry.ts +0 -173
- package/src/adapters/AdapterRouter.ts +0 -416
- package/src/adapters/BaseAdapter.ts +0 -498
- package/src/adapters/BasicAdapter.ts +0 -514
- package/src/adapters/IAdapter.ts +0 -292
- package/src/adapters/StandardAdapter.ts +0 -555
- package/src/adapters/X402Adapter.ts +0 -731
- package/src/adapters/index.ts +0 -60
- package/src/builders/DeliveryProofBuilder.ts +0 -327
- package/src/builders/QuoteBuilder.ts +0 -483
- package/src/builders/index.ts +0 -17
- package/src/cli/commands/balance.ts +0 -110
- package/src/cli/commands/batch.ts +0 -487
- package/src/cli/commands/config.ts +0 -231
- package/src/cli/commands/deploy-check.ts +0 -364
- package/src/cli/commands/deploy-env.ts +0 -120
- package/src/cli/commands/diff.ts +0 -141
- package/src/cli/commands/init.ts +0 -469
- package/src/cli/commands/mint.ts +0 -116
- package/src/cli/commands/pay.ts +0 -113
- package/src/cli/commands/publish.ts +0 -475
- package/src/cli/commands/pull.ts +0 -124
- package/src/cli/commands/register.ts +0 -247
- package/src/cli/commands/simulate.ts +0 -345
- package/src/cli/commands/time.ts +0 -302
- package/src/cli/commands/tx.ts +0 -448
- package/src/cli/commands/watch.ts +0 -211
- package/src/cli/index.ts +0 -134
- package/src/cli/utils/client.ts +0 -252
- package/src/cli/utils/config.ts +0 -389
- package/src/cli/utils/output.ts +0 -465
- package/src/cli/utils/wallet.ts +0 -109
- package/src/config/agirailsmd.ts +0 -262
- package/src/config/networks.ts +0 -275
- package/src/config/pendingPublish.ts +0 -237
- package/src/config/publishPipeline.ts +0 -359
- package/src/config/syncOperations.ts +0 -279
- package/src/erc8004/ERC8004Bridge.ts +0 -462
- package/src/erc8004/ReputationReporter.ts +0 -468
- package/src/erc8004/index.ts +0 -61
- package/src/errors/index.ts +0 -427
- package/src/index.ts +0 -364
- package/src/level0/Provider.ts +0 -117
- package/src/level0/ServiceDirectory.ts +0 -131
- package/src/level0/index.ts +0 -10
- package/src/level0/provide.ts +0 -132
- package/src/level0/request.ts +0 -432
- package/src/level1/Agent.ts +0 -1426
- package/src/level1/index.ts +0 -10
- package/src/level1/pricing/PriceCalculator.ts +0 -255
- package/src/level1/pricing/PricingStrategy.ts +0 -198
- package/src/level1/types/Job.ts +0 -179
- package/src/level1/types/Options.ts +0 -291
- package/src/level1/types/index.ts +0 -8
- package/src/protocol/ACTPKernel.ts +0 -808
- package/src/protocol/AgentRegistry.ts +0 -559
- package/src/protocol/DIDManager.ts +0 -629
- package/src/protocol/DIDResolver.ts +0 -554
- package/src/protocol/EASHelper.ts +0 -378
- package/src/protocol/EscrowVault.ts +0 -255
- package/src/protocol/EventMonitor.ts +0 -204
- package/src/protocol/MessageSigner.ts +0 -510
- package/src/protocol/ProofGenerator.ts +0 -339
- package/src/protocol/QuoteBuilder.ts +0 -15
- package/src/registry/AgentRegistryClient.ts +0 -202
- package/src/runtime/BlockchainRuntime.ts +0 -1015
- package/src/runtime/IACTPRuntime.ts +0 -306
- package/src/runtime/MockRuntime.ts +0 -1298
- package/src/runtime/MockStateManager.ts +0 -577
- package/src/runtime/index.ts +0 -25
- package/src/runtime/types/MockState.ts +0 -237
- package/src/storage/ArchiveBundleBuilder.ts +0 -561
- package/src/storage/ArweaveClient.ts +0 -946
- package/src/storage/FilebaseClient.ts +0 -790
- package/src/storage/index.ts +0 -96
- package/src/storage/types.ts +0 -348
- package/src/types/adapter.ts +0 -310
- package/src/types/agent.ts +0 -79
- package/src/types/did.ts +0 -223
- package/src/types/eip712.ts +0 -175
- package/src/types/erc8004.ts +0 -293
- package/src/types/escrow.ts +0 -27
- package/src/types/index.ts +0 -17
- package/src/types/message.ts +0 -145
- package/src/types/state.ts +0 -87
- package/src/types/transaction.ts +0 -69
- package/src/types/x402.ts +0 -251
- package/src/utils/ErrorRecoveryGuide.ts +0 -676
- package/src/utils/Helpers.ts +0 -688
- package/src/utils/IPFSClient.ts +0 -368
- package/src/utils/Logger.ts +0 -484
- package/src/utils/NonceManager.ts +0 -591
- package/src/utils/RateLimiter.ts +0 -534
- package/src/utils/ReceivedNonceTracker.ts +0 -567
- package/src/utils/SDKLifecycle.ts +0 -416
- package/src/utils/SecureNonce.ts +0 -78
- package/src/utils/Semaphore.ts +0 -276
- package/src/utils/UsedAttestationTracker.ts +0 -385
- package/src/utils/canonicalJson.ts +0 -38
- package/src/utils/circuitBreaker.ts +0 -324
- package/src/utils/computeTypeHash.ts +0 -48
- package/src/utils/fsSafe.ts +0 -80
- package/src/utils/index.ts +0 -80
- package/src/utils/retry.ts +0 -364
- package/src/utils/security.ts +0 -418
- package/src/utils/validation.ts +0 -540
- package/src/wallet/AutoWalletProvider.ts +0 -299
- package/src/wallet/EOAWalletProvider.ts +0 -69
- package/src/wallet/IWalletProvider.ts +0 -135
- package/src/wallet/aa/BundlerClient.ts +0 -274
- package/src/wallet/aa/DualNonceManager.ts +0 -173
- package/src/wallet/aa/PaymasterClient.ts +0 -174
- package/src/wallet/aa/TransactionBatcher.ts +0 -353
- package/src/wallet/aa/UserOpBuilder.ts +0 -246
- package/src/wallet/aa/constants.ts +0 -60
- package/src/wallet/keystore.ts +0 -240
|
@@ -1,554 +0,0 @@
|
|
|
1
|
-
import { verifyMessage, getAddress } from 'ethers';
|
|
2
|
-
// NOTE: These imports require npm packages to be installed:
|
|
3
|
-
// - did-resolver
|
|
4
|
-
// - ethr-did-resolver
|
|
5
|
-
import { Resolver } from 'did-resolver';
|
|
6
|
-
import { getResolver } from 'ethr-did-resolver';
|
|
7
|
-
import {
|
|
8
|
-
DID,
|
|
9
|
-
DIDResolutionResult,
|
|
10
|
-
DIDResolverConfig,
|
|
11
|
-
ParsedDID,
|
|
12
|
-
VerifySignatureOptions,
|
|
13
|
-
SignatureVerificationResult,
|
|
14
|
-
VerificationMethod
|
|
15
|
-
} from '../types/did';
|
|
16
|
-
import { getNetwork } from '../config/networks';
|
|
17
|
-
import { ValidationError } from '../errors';
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* DIDResolver - Resolve DIDs to DID Documents (AIP-7 §2.2)
|
|
21
|
-
*
|
|
22
|
-
* Uses ethr-did-resolver library for resolution against AGIRAILS Identity Registry.
|
|
23
|
-
* Supports did:ethr method with explicit chainId format: did:ethr:<chainId>:<address>
|
|
24
|
-
*
|
|
25
|
-
* @example
|
|
26
|
-
* ```typescript
|
|
27
|
-
* const resolver = await DIDResolver.create({ network: 'base-sepolia' });
|
|
28
|
-
*
|
|
29
|
-
* // Resolve DID to DID Document
|
|
30
|
-
* const result = await resolver.resolve('did:ethr:84532:0x742d35cc6634c0532925a3b844bc9e7595f0beb');
|
|
31
|
-
* console.log(result.didDocument);
|
|
32
|
-
*
|
|
33
|
-
* // Verify signature
|
|
34
|
-
* const isValid = await resolver.verifySignature(
|
|
35
|
-
* 'did:ethr:84532:0x742d35cc...',
|
|
36
|
-
* 'Hello AGIRAILS',
|
|
37
|
-
* '0x1234...',
|
|
38
|
-
* { chainId: 84532 }
|
|
39
|
-
* );
|
|
40
|
-
* ```
|
|
41
|
-
*
|
|
42
|
-
* @security
|
|
43
|
-
* - Always validates DID format before resolution
|
|
44
|
-
* - Checks chainId matches expected network
|
|
45
|
-
* - Verifies signatures using ethers.js verifyMessage
|
|
46
|
-
* - Prevents cross-chain replay attacks via chainId validation
|
|
47
|
-
*/
|
|
48
|
-
export class DIDResolver {
|
|
49
|
-
private resolver!: Resolver;
|
|
50
|
-
private config: Required<DIDResolverConfig>;
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* Private constructor - use DIDResolver.create() factory method
|
|
54
|
-
*/
|
|
55
|
-
private constructor(config: Required<DIDResolverConfig>) {
|
|
56
|
-
this.config = config;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* Factory method to create DIDResolver instance
|
|
61
|
-
*
|
|
62
|
-
* @param config - Configuration options
|
|
63
|
-
* @returns Configured DIDResolver instance
|
|
64
|
-
* @throws ValidationError if configuration is invalid
|
|
65
|
-
*
|
|
66
|
-
* @example
|
|
67
|
-
* ```typescript
|
|
68
|
-
* // Using predefined network
|
|
69
|
-
* const resolver = await DIDResolver.create({ network: 'base-sepolia' });
|
|
70
|
-
*
|
|
71
|
-
* // Using custom RPC and registry
|
|
72
|
-
* const resolver = await DIDResolver.create({
|
|
73
|
-
* chainId: 84532,
|
|
74
|
-
* rpcUrl: 'https://sepolia.base.org',
|
|
75
|
-
* registryAddress: '0x...'
|
|
76
|
-
* });
|
|
77
|
-
* ```
|
|
78
|
-
*/
|
|
79
|
-
static async create(config: DIDResolverConfig = {}): Promise<DIDResolver> {
|
|
80
|
-
// Resolve configuration with defaults
|
|
81
|
-
const resolvedConfig = DIDResolver.resolveConfig(config);
|
|
82
|
-
|
|
83
|
-
// Validate configuration
|
|
84
|
-
DIDResolver.validateConfig(resolvedConfig);
|
|
85
|
-
|
|
86
|
-
// Initialize did-resolver with ethr-did-resolver
|
|
87
|
-
const providerConfig = {
|
|
88
|
-
networks: [
|
|
89
|
-
{
|
|
90
|
-
name: resolvedConfig.network || `chain-${resolvedConfig.chainId}`,
|
|
91
|
-
chainId: `0x${resolvedConfig.chainId.toString(16)}`,
|
|
92
|
-
rpcUrl: resolvedConfig.rpcUrl,
|
|
93
|
-
registry: resolvedConfig.registryAddress
|
|
94
|
-
}
|
|
95
|
-
]
|
|
96
|
-
};
|
|
97
|
-
|
|
98
|
-
const ethrResolver = getResolver(providerConfig);
|
|
99
|
-
const resolver = new Resolver(ethrResolver);
|
|
100
|
-
|
|
101
|
-
const instance = new DIDResolver(resolvedConfig);
|
|
102
|
-
instance.resolver = resolver;
|
|
103
|
-
|
|
104
|
-
return instance;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
/**
|
|
108
|
-
* Resolve configuration with defaults from network config
|
|
109
|
-
*/
|
|
110
|
-
private static resolveConfig(config: DIDResolverConfig): Required<DIDResolverConfig> {
|
|
111
|
-
// If network specified, use network defaults
|
|
112
|
-
if (config.network) {
|
|
113
|
-
const networkConfig = getNetwork(config.network);
|
|
114
|
-
return {
|
|
115
|
-
network: config.network,
|
|
116
|
-
chainId: config.chainId || networkConfig.chainId,
|
|
117
|
-
rpcUrl: config.rpcUrl || networkConfig.rpcUrl,
|
|
118
|
-
registryAddress: config.registryAddress || networkConfig.contracts.identityRegistry || ''
|
|
119
|
-
};
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
// Custom configuration (must provide all required fields)
|
|
123
|
-
if (!config.chainId || !config.rpcUrl) {
|
|
124
|
-
throw new ValidationError(
|
|
125
|
-
'config',
|
|
126
|
-
'Must provide either network name or (chainId + rpcUrl)'
|
|
127
|
-
);
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
return {
|
|
131
|
-
network: config.network || '',
|
|
132
|
-
chainId: config.chainId,
|
|
133
|
-
rpcUrl: config.rpcUrl,
|
|
134
|
-
registryAddress: config.registryAddress || ''
|
|
135
|
-
};
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
/**
|
|
139
|
-
* Validate configuration
|
|
140
|
-
*/
|
|
141
|
-
private static validateConfig(config: Required<DIDResolverConfig>): void {
|
|
142
|
-
if (!config.chainId || config.chainId <= 0) {
|
|
143
|
-
throw new ValidationError('chainId', 'Invalid chainId');
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
if (!config.rpcUrl || !config.rpcUrl.startsWith('http')) {
|
|
147
|
-
throw new ValidationError('rpcUrl', 'Invalid RPC URL');
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
// Registry address is optional (will use default if not provided)
|
|
151
|
-
// But if provided, must be valid address
|
|
152
|
-
if (config.registryAddress) {
|
|
153
|
-
try {
|
|
154
|
-
getAddress(config.registryAddress);
|
|
155
|
-
} catch (error) {
|
|
156
|
-
throw new ValidationError('registryAddress', 'Invalid registry address');
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
/**
|
|
162
|
-
* Get resolver configuration
|
|
163
|
-
*/
|
|
164
|
-
getConfig(): Required<DIDResolverConfig> {
|
|
165
|
-
return { ...this.config };
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
/**
|
|
169
|
-
* Resolve DID to DID Document
|
|
170
|
-
*
|
|
171
|
-
* @param did - DID to resolve (format: did:ethr:<chainId>:<address>)
|
|
172
|
-
* @returns DID resolution result with document and metadata
|
|
173
|
-
* @throws ValidationError if DID format is invalid
|
|
174
|
-
*
|
|
175
|
-
* @example
|
|
176
|
-
* ```typescript
|
|
177
|
-
* const result = await resolver.resolve('did:ethr:84532:0x742d35cc6634c0532925a3b844bc9e7595f0beb');
|
|
178
|
-
*
|
|
179
|
-
* if (result.didDocument) {
|
|
180
|
-
* console.log('Identity owner:', result.didDocument.verificationMethod[0].controller);
|
|
181
|
-
* console.log('Service endpoints:', result.didDocument.service);
|
|
182
|
-
* } else {
|
|
183
|
-
* console.error('Resolution failed:', result.didResolutionMetadata.error);
|
|
184
|
-
* }
|
|
185
|
-
* ```
|
|
186
|
-
*/
|
|
187
|
-
async resolve(did: DID): Promise<DIDResolutionResult> {
|
|
188
|
-
// Validate DID format
|
|
189
|
-
const parsed = DIDResolver.parseDID(did);
|
|
190
|
-
|
|
191
|
-
// Check chainId matches expected network
|
|
192
|
-
if (parsed.chainId !== this.config.chainId) {
|
|
193
|
-
return {
|
|
194
|
-
didDocument: null,
|
|
195
|
-
didResolutionMetadata: {
|
|
196
|
-
error: 'invalidDid',
|
|
197
|
-
message: `DID chainId ${parsed.chainId} does not match resolver chainId ${this.config.chainId}`
|
|
198
|
-
},
|
|
199
|
-
didDocumentMetadata: {}
|
|
200
|
-
};
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
// Perform resolution using did-resolver
|
|
204
|
-
const result = await this.resolver.resolve(did);
|
|
205
|
-
return result as DIDResolutionResult;
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
/**
|
|
209
|
-
* Verify a signature was made by DID controller or authorized delegate
|
|
210
|
-
*
|
|
211
|
-
* @param did - DID of expected signer
|
|
212
|
-
* @param message - Original message that was signed
|
|
213
|
-
* @param signature - Signature to verify (0x-prefixed hex string)
|
|
214
|
-
* @param options - Verification options (chainId validation, domain separation, etc.)
|
|
215
|
-
* @returns Verification result with validity and signer info
|
|
216
|
-
*
|
|
217
|
-
* @security
|
|
218
|
-
* - Validates chainId to prevent cross-chain replay attacks
|
|
219
|
-
* - Uses domain separation to prevent cross-protocol replay attacks (MEDIUM finding fix)
|
|
220
|
-
* - Resolves DID to get current owner and delegates
|
|
221
|
-
* - Checks if signer is owner or valid delegate (MEDIUM finding fix)
|
|
222
|
-
* - Validates delegate expiration timestamps if provided
|
|
223
|
-
*
|
|
224
|
-
* @example
|
|
225
|
-
* ```typescript
|
|
226
|
-
* // With domain separation (recommended - default)
|
|
227
|
-
* const result = await resolver.verifySignature(
|
|
228
|
-
* 'did:ethr:84532:0x742d35cc...',
|
|
229
|
-
* 'Hello AGIRAILS',
|
|
230
|
-
* '0x1234...',
|
|
231
|
-
* { chainId: 84532, useDomainSeparation: true }
|
|
232
|
-
* );
|
|
233
|
-
*
|
|
234
|
-
* // Without domain separation (backwards compatibility only)
|
|
235
|
-
* const legacyResult = await resolver.verifySignature(
|
|
236
|
-
* 'did:ethr:84532:0x742d35cc...',
|
|
237
|
-
* 'Hello AGIRAILS',
|
|
238
|
-
* '0x1234...',
|
|
239
|
-
* { chainId: 84532, useDomainSeparation: false }
|
|
240
|
-
* );
|
|
241
|
-
*
|
|
242
|
-
* if (result.valid) {
|
|
243
|
-
* console.log('Signature is valid!');
|
|
244
|
-
* console.log('Signer:', result.signer);
|
|
245
|
-
* console.log('Is delegate:', result.isDelegate);
|
|
246
|
-
* if (result.isDelegate) {
|
|
247
|
-
* console.log('Delegate type:', result.delegateType);
|
|
248
|
-
* }
|
|
249
|
-
* } else {
|
|
250
|
-
* console.error('Invalid signature:', result.error);
|
|
251
|
-
* }
|
|
252
|
-
* ```
|
|
253
|
-
*/
|
|
254
|
-
async verifySignature(
|
|
255
|
-
did: DID,
|
|
256
|
-
message: string,
|
|
257
|
-
signature: string,
|
|
258
|
-
options: VerifySignatureOptions
|
|
259
|
-
): Promise<SignatureVerificationResult> {
|
|
260
|
-
try {
|
|
261
|
-
// Validate DID format
|
|
262
|
-
const parsed = DIDResolver.parseDID(did);
|
|
263
|
-
|
|
264
|
-
// Validate chainId matches
|
|
265
|
-
if (parsed.chainId !== options.chainId) {
|
|
266
|
-
return {
|
|
267
|
-
valid: false,
|
|
268
|
-
error: `DID chainId ${parsed.chainId} does not match expected chainId ${options.chainId}`
|
|
269
|
-
};
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
// SECURITY FIX (MEDIUM): Domain separation to prevent cross-protocol replay attacks
|
|
273
|
-
// Default to true for security, but allow opt-out for backwards compatibility
|
|
274
|
-
const useDomainSeparation = options.useDomainSeparation !== false;
|
|
275
|
-
const messageToVerify = useDomainSeparation
|
|
276
|
-
? this.createDomainSeparatedMessage(message, parsed.chainId, did)
|
|
277
|
-
: message;
|
|
278
|
-
|
|
279
|
-
// Recover signer from signature
|
|
280
|
-
const recoveredAddress = verifyMessage(messageToVerify, signature);
|
|
281
|
-
const signerLower = recoveredAddress.toLowerCase();
|
|
282
|
-
const didAddressLower = parsed.address.toLowerCase();
|
|
283
|
-
|
|
284
|
-
// Check if signer is the DID address itself (owner)
|
|
285
|
-
if (signerLower === didAddressLower) {
|
|
286
|
-
return {
|
|
287
|
-
valid: true,
|
|
288
|
-
signer: recoveredAddress,
|
|
289
|
-
isDelegate: false
|
|
290
|
-
};
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
// SECURITY FIX (MEDIUM): Implement delegate validation
|
|
294
|
-
// Check if signer is a valid delegate in the DID Document
|
|
295
|
-
const resolution = await this.resolve(did);
|
|
296
|
-
if (resolution.didDocument && resolution.didDocument.authentication) {
|
|
297
|
-
// Iterate through authentication methods to find valid delegates
|
|
298
|
-
for (const auth of resolution.didDocument.authentication) {
|
|
299
|
-
// auth can be a string (reference to verificationMethod) or VerificationMethod object
|
|
300
|
-
const verificationMethod = typeof auth === 'string'
|
|
301
|
-
? resolution.didDocument.verificationMethod?.find(vm => vm.id === auth)
|
|
302
|
-
: auth;
|
|
303
|
-
|
|
304
|
-
if (verificationMethod) {
|
|
305
|
-
// Extract delegate address from verification method
|
|
306
|
-
const delegateAddress = this.extractAddressFromVerificationMethod(verificationMethod);
|
|
307
|
-
|
|
308
|
-
if (delegateAddress && delegateAddress.toLowerCase() === signerLower) {
|
|
309
|
-
// Check delegate validity if timestamp provided
|
|
310
|
-
if (options.timestamp && verificationMethod.validTo) {
|
|
311
|
-
const validToTimestamp = typeof verificationMethod.validTo === 'bigint'
|
|
312
|
-
? Number(verificationMethod.validTo)
|
|
313
|
-
: verificationMethod.validTo;
|
|
314
|
-
|
|
315
|
-
if (options.timestamp > validToTimestamp) {
|
|
316
|
-
return {
|
|
317
|
-
valid: false,
|
|
318
|
-
signer: recoveredAddress,
|
|
319
|
-
error: 'Delegate expired'
|
|
320
|
-
};
|
|
321
|
-
}
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
return {
|
|
325
|
-
valid: true,
|
|
326
|
-
signer: recoveredAddress,
|
|
327
|
-
isDelegate: true,
|
|
328
|
-
delegateType: verificationMethod.type
|
|
329
|
-
};
|
|
330
|
-
}
|
|
331
|
-
}
|
|
332
|
-
}
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
// Signer is neither owner nor valid delegate
|
|
336
|
-
return {
|
|
337
|
-
valid: false,
|
|
338
|
-
signer: recoveredAddress,
|
|
339
|
-
error: 'Signer is not the DID controller or a valid delegate'
|
|
340
|
-
};
|
|
341
|
-
|
|
342
|
-
} catch (error) {
|
|
343
|
-
return {
|
|
344
|
-
valid: false,
|
|
345
|
-
error: error instanceof Error ? error.message : 'Signature verification failed'
|
|
346
|
-
};
|
|
347
|
-
}
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
/**
|
|
351
|
-
* Build DID from address and chainId
|
|
352
|
-
*
|
|
353
|
-
* @param address - Ethereum address (with or without 0x prefix)
|
|
354
|
-
* @param chainId - Chain ID (e.g., 84532 for Base Sepolia)
|
|
355
|
-
* @returns Canonical DID string (lowercase address)
|
|
356
|
-
*
|
|
357
|
-
* @example
|
|
358
|
-
* ```typescript
|
|
359
|
-
* const did = DIDResolver.buildDID('0x742d35cc6634c0532925a3b844bc9e7595f0beb', 84532);
|
|
360
|
-
* // Returns: 'did:ethr:84532:0x742d35cc6634c0532925a3b844bc9e7595f0beb'
|
|
361
|
-
* ```
|
|
362
|
-
*/
|
|
363
|
-
static buildDID(address: string, chainId: number): DID {
|
|
364
|
-
// Validate and checksum address
|
|
365
|
-
const checksummed = getAddress(address);
|
|
366
|
-
|
|
367
|
-
// Convert to lowercase for canonical format (per AIP-7 §2.1)
|
|
368
|
-
const canonical = checksummed.toLowerCase();
|
|
369
|
-
|
|
370
|
-
return `did:ethr:${chainId}:${canonical}`;
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
/**
|
|
374
|
-
* Parse DID to extract components
|
|
375
|
-
*
|
|
376
|
-
* @param did - DID to parse
|
|
377
|
-
* @returns Parsed components
|
|
378
|
-
* @throws ValidationError if DID format is invalid
|
|
379
|
-
*
|
|
380
|
-
* @example
|
|
381
|
-
* ```typescript
|
|
382
|
-
* const parsed = DIDResolver.parseDID('did:ethr:84532:0x742d35cc6634c0532925a3b844bc9e7595f0beb');
|
|
383
|
-
* console.log(parsed.method); // 'ethr'
|
|
384
|
-
* console.log(parsed.chainId); // 84532
|
|
385
|
-
* console.log(parsed.address); // '0x742d35cc6634c0532925a3b844bc9e7595f0beb'
|
|
386
|
-
* ```
|
|
387
|
-
*/
|
|
388
|
-
static parseDID(did: DID): ParsedDID {
|
|
389
|
-
// Validate basic format
|
|
390
|
-
if (!did || typeof did !== 'string') {
|
|
391
|
-
throw new ValidationError('did', 'DID must be a non-empty string');
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
// DID format: did:ethr:<chainId>:<address>
|
|
395
|
-
const parts = did.split(':');
|
|
396
|
-
|
|
397
|
-
if (parts.length !== 4) {
|
|
398
|
-
throw new ValidationError(
|
|
399
|
-
'did',
|
|
400
|
-
'Invalid DID format. Expected: did:ethr:<chainId>:<address>'
|
|
401
|
-
);
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
const [scheme, method, chainIdStr, address] = parts;
|
|
405
|
-
|
|
406
|
-
// Validate scheme
|
|
407
|
-
if (scheme !== 'did') {
|
|
408
|
-
throw new ValidationError('did', `Invalid DID scheme: ${scheme}. Expected: did`);
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
// Validate method
|
|
412
|
-
if (method !== 'ethr') {
|
|
413
|
-
throw new ValidationError('did', `Invalid DID method: ${method}. Expected: ethr`);
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
// Validate chainId
|
|
417
|
-
const chainId = parseInt(chainIdStr, 10);
|
|
418
|
-
if (isNaN(chainId) || chainId <= 0) {
|
|
419
|
-
throw new ValidationError('did', `Invalid chainId: ${chainIdStr}`);
|
|
420
|
-
}
|
|
421
|
-
|
|
422
|
-
// Validate address
|
|
423
|
-
try {
|
|
424
|
-
const checksummed = getAddress(address);
|
|
425
|
-
return {
|
|
426
|
-
method,
|
|
427
|
-
chainId,
|
|
428
|
-
address: checksummed.toLowerCase() // Canonical format per AIP-7
|
|
429
|
-
};
|
|
430
|
-
} catch (error) {
|
|
431
|
-
throw new ValidationError('did', `Invalid Ethereum address: ${address}`);
|
|
432
|
-
}
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
/**
|
|
436
|
-
* Validate DID format
|
|
437
|
-
*
|
|
438
|
-
* @param did - DID to validate
|
|
439
|
-
* @returns True if valid, false otherwise
|
|
440
|
-
*/
|
|
441
|
-
static isValidDID(did: DID): boolean {
|
|
442
|
-
try {
|
|
443
|
-
DIDResolver.parseDID(did);
|
|
444
|
-
return true;
|
|
445
|
-
} catch {
|
|
446
|
-
return false;
|
|
447
|
-
}
|
|
448
|
-
}
|
|
449
|
-
|
|
450
|
-
/**
|
|
451
|
-
* Extract address from DID
|
|
452
|
-
*
|
|
453
|
-
* @param did - DID to extract from
|
|
454
|
-
* @returns Ethereum address (lowercase)
|
|
455
|
-
* @throws ValidationError if DID format is invalid
|
|
456
|
-
*/
|
|
457
|
-
static extractAddress(did: DID): string {
|
|
458
|
-
const parsed = DIDResolver.parseDID(did);
|
|
459
|
-
return parsed.address;
|
|
460
|
-
}
|
|
461
|
-
|
|
462
|
-
/**
|
|
463
|
-
* Extract chainId from DID
|
|
464
|
-
*
|
|
465
|
-
* @param did - DID to extract from
|
|
466
|
-
* @returns Chain ID
|
|
467
|
-
* @throws ValidationError if DID format is invalid
|
|
468
|
-
*/
|
|
469
|
-
static extractChainId(did: DID): number {
|
|
470
|
-
const parsed = DIDResolver.parseDID(did);
|
|
471
|
-
return parsed.chainId;
|
|
472
|
-
}
|
|
473
|
-
|
|
474
|
-
/**
|
|
475
|
-
* Create domain-separated message to prevent cross-protocol replay attacks
|
|
476
|
-
*
|
|
477
|
-
* @param message - Original message
|
|
478
|
-
* @param chainId - Chain ID for domain separation
|
|
479
|
-
* @param did - DID for additional context binding
|
|
480
|
-
* @returns Domain-separated message
|
|
481
|
-
*
|
|
482
|
-
* @security
|
|
483
|
-
* - Binds signature to AGIRAILS protocol
|
|
484
|
-
* - Binds signature to specific chainId (prevents cross-chain replay)
|
|
485
|
-
* - Binds signature to specific DID (prevents cross-identity replay)
|
|
486
|
-
*
|
|
487
|
-
* @example
|
|
488
|
-
* ```typescript
|
|
489
|
-
* const domainMsg = this.createDomainSeparatedMessage('Hello', 84532, 'did:ethr:84532:0x...');
|
|
490
|
-
* // Returns: "AGIRAILS:84532:did:ethr:84532:0x...\nHello"
|
|
491
|
-
* ```
|
|
492
|
-
*/
|
|
493
|
-
private createDomainSeparatedMessage(message: string, chainId: number, did: string): string {
|
|
494
|
-
return `AGIRAILS:${chainId}:${did}\n${message}`;
|
|
495
|
-
}
|
|
496
|
-
|
|
497
|
-
/**
|
|
498
|
-
* Extract Ethereum address from a verification method
|
|
499
|
-
*
|
|
500
|
-
* @param vm - Verification method from DID Document
|
|
501
|
-
* @returns Ethereum address (checksummed) or null if not found
|
|
502
|
-
*
|
|
503
|
-
* @security
|
|
504
|
-
* - Handles multiple address formats (blockchainAccountId, ethereumAddress, controller DID)
|
|
505
|
-
* - Validates extracted address format
|
|
506
|
-
* - Returns null instead of throwing for graceful error handling
|
|
507
|
-
*
|
|
508
|
-
* @example
|
|
509
|
-
* ```typescript
|
|
510
|
-
* const vm = {
|
|
511
|
-
* id: 'did:ethr:84532:0x123...#delegate1',
|
|
512
|
-
* type: 'EcdsaSecp256k1RecoveryMethod2020',
|
|
513
|
-
* controller: 'did:ethr:84532:0x123...',
|
|
514
|
-
* blockchainAccountId: '0x456...@eip155:84532'
|
|
515
|
-
* };
|
|
516
|
-
* const address = this.extractAddressFromVerificationMethod(vm);
|
|
517
|
-
* // Returns: '0x456...' (checksummed)
|
|
518
|
-
* ```
|
|
519
|
-
*/
|
|
520
|
-
private extractAddressFromVerificationMethod(vm: VerificationMethod): string | null {
|
|
521
|
-
// Try blockchainAccountId first (CAIP-10 format: "0x123...@eip155:chainId")
|
|
522
|
-
if (vm.blockchainAccountId) {
|
|
523
|
-
const match = vm.blockchainAccountId.match(/^(0x[a-fA-F0-9]{40})@/);
|
|
524
|
-
if (match) {
|
|
525
|
-
try {
|
|
526
|
-
return getAddress(match[1]); // Checksum the address
|
|
527
|
-
} catch {
|
|
528
|
-
return null;
|
|
529
|
-
}
|
|
530
|
-
}
|
|
531
|
-
}
|
|
532
|
-
|
|
533
|
-
// Try ethereumAddress field
|
|
534
|
-
if (vm.ethereumAddress) {
|
|
535
|
-
try {
|
|
536
|
-
return getAddress(vm.ethereumAddress);
|
|
537
|
-
} catch {
|
|
538
|
-
return null;
|
|
539
|
-
}
|
|
540
|
-
}
|
|
541
|
-
|
|
542
|
-
// Try extracting from controller DID
|
|
543
|
-
if (vm.controller) {
|
|
544
|
-
try {
|
|
545
|
-
const { address } = DIDResolver.parseDID(vm.controller);
|
|
546
|
-
return getAddress(address); // Already lowercase, checksum it
|
|
547
|
-
} catch {
|
|
548
|
-
return null;
|
|
549
|
-
}
|
|
550
|
-
}
|
|
551
|
-
|
|
552
|
-
return null;
|
|
553
|
-
}
|
|
554
|
-
}
|