@weave_protocol/domere 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/PLANNING.md +231 -0
- package/README.md +50 -0
- package/dist/anchoring/ethereum.d.ts +135 -0
- package/dist/anchoring/ethereum.d.ts.map +1 -0
- package/dist/anchoring/ethereum.js +474 -0
- package/dist/anchoring/ethereum.js.map +1 -0
- package/dist/anchoring/index.d.ts +93 -0
- package/dist/anchoring/index.d.ts.map +1 -0
- package/dist/anchoring/index.js +184 -0
- package/dist/anchoring/index.js.map +1 -0
- package/dist/anchoring/merkle.d.ts +91 -0
- package/dist/anchoring/merkle.d.ts.map +1 -0
- package/dist/anchoring/merkle.js +203 -0
- package/dist/anchoring/merkle.js.map +1 -0
- package/dist/anchoring/solana.d.ts +85 -0
- package/dist/anchoring/solana.d.ts.map +1 -0
- package/dist/anchoring/solana.js +301 -0
- package/dist/anchoring/solana.js.map +1 -0
- package/dist/constants.d.ts +130 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +536 -0
- package/dist/constants.js.map +1 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +37 -0
- package/dist/index.js.map +1 -0
- package/dist/language/code-analyzer.d.ts +80 -0
- package/dist/language/code-analyzer.d.ts.map +1 -0
- package/dist/language/code-analyzer.js +489 -0
- package/dist/language/code-analyzer.js.map +1 -0
- package/dist/language/detector.d.ts +53 -0
- package/dist/language/detector.d.ts.map +1 -0
- package/dist/language/detector.js +248 -0
- package/dist/language/detector.js.map +1 -0
- package/dist/language/index.d.ts +61 -0
- package/dist/language/index.d.ts.map +1 -0
- package/dist/language/index.js +109 -0
- package/dist/language/index.js.map +1 -0
- package/dist/language/nl-analyzer.d.ts +59 -0
- package/dist/language/nl-analyzer.d.ts.map +1 -0
- package/dist/language/nl-analyzer.js +350 -0
- package/dist/language/nl-analyzer.js.map +1 -0
- package/dist/language/semantic.d.ts +48 -0
- package/dist/language/semantic.d.ts.map +1 -0
- package/dist/language/semantic.js +329 -0
- package/dist/language/semantic.js.map +1 -0
- package/dist/storage/index.d.ts +6 -0
- package/dist/storage/index.d.ts.map +1 -0
- package/dist/storage/index.js +6 -0
- package/dist/storage/index.js.map +1 -0
- package/dist/storage/memory.d.ts +48 -0
- package/dist/storage/memory.d.ts.map +1 -0
- package/dist/storage/memory.js +211 -0
- package/dist/storage/memory.js.map +1 -0
- package/dist/thread/drift.d.ts +43 -0
- package/dist/thread/drift.d.ts.map +1 -0
- package/dist/thread/drift.js +248 -0
- package/dist/thread/drift.js.map +1 -0
- package/dist/thread/index.d.ts +9 -0
- package/dist/thread/index.d.ts.map +1 -0
- package/dist/thread/index.js +9 -0
- package/dist/thread/index.js.map +1 -0
- package/dist/thread/intent.d.ts +68 -0
- package/dist/thread/intent.d.ts.map +1 -0
- package/dist/thread/intent.js +333 -0
- package/dist/thread/intent.js.map +1 -0
- package/dist/thread/manager.d.ts +85 -0
- package/dist/thread/manager.d.ts.map +1 -0
- package/dist/thread/manager.js +305 -0
- package/dist/thread/manager.js.map +1 -0
- package/dist/thread/weave.d.ts +61 -0
- package/dist/thread/weave.d.ts.map +1 -0
- package/dist/thread/weave.js +158 -0
- package/dist/thread/weave.js.map +1 -0
- package/dist/tools/index.d.ts +18 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +102 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/types.d.ts +466 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +48 -0
- package/dist/types.js.map +1 -0
- package/package.json +24 -0
- package/src/anchoring/ethereum.ts +568 -0
- package/src/anchoring/index.ts +236 -0
- package/src/anchoring/merkle.ts +256 -0
- package/src/anchoring/solana.ts +370 -0
- package/src/constants.ts +566 -0
- package/src/index.ts +43 -0
- package/src/language/code-analyzer.ts +564 -0
- package/src/language/detector.ts +297 -0
- package/src/language/index.ts +129 -0
- package/src/language/nl-analyzer.ts +411 -0
- package/src/language/semantic.ts +385 -0
- package/src/storage/index.ts +6 -0
- package/src/storage/memory.ts +271 -0
- package/src/thread/drift.ts +319 -0
- package/src/thread/index.ts +9 -0
- package/src/thread/intent.ts +409 -0
- package/src/thread/manager.ts +414 -0
- package/src/thread/weave.ts +205 -0
- package/src/tools/index.ts +107 -0
- package/src/types.ts +736 -0
- package/tsconfig.json +19 -0
package/package.json
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@weave_protocol/domere",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "The Judge Protocol - Thread identity, intent verification, and blockchain anchoring",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"bin": { "domere": "./dist/index.js" },
|
|
8
|
+
"scripts": {
|
|
9
|
+
"build": "tsc",
|
|
10
|
+
"start": "node dist/index.js",
|
|
11
|
+
"dev": "tsx src/index.ts"
|
|
12
|
+
},
|
|
13
|
+
"keywords": ["ai", "security", "mcp", "blockchain", "solana", "ethereum", "intent-verification"],
|
|
14
|
+
"license": "MIT",
|
|
15
|
+
"dependencies": {
|
|
16
|
+
"@modelcontextprotocol/sdk": "^0.5.0"
|
|
17
|
+
},
|
|
18
|
+
"devDependencies": {
|
|
19
|
+
"@types/node": "^20.10.0",
|
|
20
|
+
"typescript": "^5.3.0",
|
|
21
|
+
"tsx": "^4.7.0"
|
|
22
|
+
},
|
|
23
|
+
"engines": { "node": ">=18.0.0" }
|
|
24
|
+
}
|
|
@@ -0,0 +1,568 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dōmere - The Judge Protocol
|
|
3
|
+
* Ethereum Anchoring Client
|
|
4
|
+
*
|
|
5
|
+
* Note: This is the client interface. The actual Ethereum contract
|
|
6
|
+
* should be deployed separately.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type {
|
|
10
|
+
AnchorRequest,
|
|
11
|
+
AnchorResult,
|
|
12
|
+
AnchorVerification,
|
|
13
|
+
BlockchainNetwork
|
|
14
|
+
} from '../types.js';
|
|
15
|
+
import { AnchoringError } from '../types.js';
|
|
16
|
+
import { DEFAULT_CONFIG, PROTOCOL_FEES } from '../constants.js';
|
|
17
|
+
|
|
18
|
+
// ============================================================================
|
|
19
|
+
// Ethereum Client Interface
|
|
20
|
+
// ============================================================================
|
|
21
|
+
|
|
22
|
+
export interface EthereumConfig {
|
|
23
|
+
rpc_url: string;
|
|
24
|
+
contract_address: string;
|
|
25
|
+
chain_id?: number;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface EthereumAnchorData {
|
|
29
|
+
threadId: string; // bytes32
|
|
30
|
+
merkleRoot: string; // bytes32
|
|
31
|
+
hopCount: number; // uint256
|
|
32
|
+
intentHash: string; // bytes32
|
|
33
|
+
compliant: boolean;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// ============================================================================
|
|
37
|
+
// Ethereum Anchoring Client
|
|
38
|
+
// ============================================================================
|
|
39
|
+
|
|
40
|
+
export class EthereumAnchorClient {
|
|
41
|
+
private config: EthereumConfig;
|
|
42
|
+
private isTestnet: boolean;
|
|
43
|
+
|
|
44
|
+
constructor(config?: Partial<EthereumConfig>) {
|
|
45
|
+
this.config = {
|
|
46
|
+
rpc_url: config?.rpc_url ?? DEFAULT_CONFIG.anchoring.ethereum_rpc,
|
|
47
|
+
contract_address: config?.contract_address ?? DEFAULT_CONFIG.anchoring.ethereum_contract,
|
|
48
|
+
chain_id: config?.chain_id ?? 1, // Mainnet default
|
|
49
|
+
};
|
|
50
|
+
this.isTestnet = this.config.rpc_url.includes('sepolia') ||
|
|
51
|
+
this.config.rpc_url.includes('goerli') ||
|
|
52
|
+
this.config.chain_id !== 1;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Prepare anchor data for Ethereum
|
|
57
|
+
*/
|
|
58
|
+
prepareAnchorData(request: AnchorRequest): EthereumAnchorData {
|
|
59
|
+
return {
|
|
60
|
+
threadId: this.stringToBytes32(request.thread_id),
|
|
61
|
+
merkleRoot: this.ensureBytes32(request.merkle_root),
|
|
62
|
+
hopCount: request.hop_count,
|
|
63
|
+
intentHash: this.ensureBytes32(request.intent_hash),
|
|
64
|
+
compliant: request.compliant,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Estimate gas cost
|
|
70
|
+
*/
|
|
71
|
+
async estimateGas(): Promise<{
|
|
72
|
+
gas_limit: number;
|
|
73
|
+
gas_price_gwei: number;
|
|
74
|
+
estimated_eth: string;
|
|
75
|
+
protocol_fee_eth: string;
|
|
76
|
+
total_eth: string;
|
|
77
|
+
usd_estimate?: string;
|
|
78
|
+
}> {
|
|
79
|
+
// Typical gas for storing thread anchor data
|
|
80
|
+
const gasLimit = 80000; // Conservative estimate
|
|
81
|
+
|
|
82
|
+
// This would normally be fetched from the network
|
|
83
|
+
// Using placeholder values
|
|
84
|
+
const gasPriceGwei = 30; // Moderate gas price
|
|
85
|
+
const gasPriceWei = gasPriceGwei * 1e9;
|
|
86
|
+
const gasCostWei = gasLimit * gasPriceWei;
|
|
87
|
+
const gasCostEth = gasCostWei / 1e18;
|
|
88
|
+
|
|
89
|
+
// Protocol fee is 5% of gas
|
|
90
|
+
const protocolFeeEth = gasCostEth * (PROTOCOL_FEES.ethereum.protocol_fee_bps / 10000);
|
|
91
|
+
|
|
92
|
+
const totalEth = gasCostEth + protocolFeeEth;
|
|
93
|
+
|
|
94
|
+
return {
|
|
95
|
+
gas_limit: gasLimit,
|
|
96
|
+
gas_price_gwei: gasPriceGwei,
|
|
97
|
+
estimated_eth: gasCostEth.toFixed(6),
|
|
98
|
+
protocol_fee_eth: protocolFeeEth.toFixed(6),
|
|
99
|
+
total_eth: totalEth.toFixed(6),
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Create anchor transaction
|
|
105
|
+
*
|
|
106
|
+
* Returns unsigned transaction data for client-side signing.
|
|
107
|
+
*/
|
|
108
|
+
async createAnchorTransaction(request: AnchorRequest): Promise<{
|
|
109
|
+
to: string;
|
|
110
|
+
data: string;
|
|
111
|
+
value: string;
|
|
112
|
+
gas_limit: number;
|
|
113
|
+
chain_id: number;
|
|
114
|
+
estimated_cost: any;
|
|
115
|
+
}> {
|
|
116
|
+
const anchorData = this.prepareAnchorData(request);
|
|
117
|
+
const cost = await this.estimateGas();
|
|
118
|
+
|
|
119
|
+
// Encode function call: anchorThread(bytes32,bytes32,uint256,bytes32,bool)
|
|
120
|
+
const functionSelector = '0x' + this.keccak256('anchorThread(bytes32,bytes32,uint256,bytes32,bool)').slice(0, 8);
|
|
121
|
+
|
|
122
|
+
const encodedData = functionSelector +
|
|
123
|
+
this.encodeBytes32(anchorData.threadId) +
|
|
124
|
+
this.encodeBytes32(anchorData.merkleRoot) +
|
|
125
|
+
this.encodeUint256(anchorData.hopCount) +
|
|
126
|
+
this.encodeBytes32(anchorData.intentHash) +
|
|
127
|
+
this.encodeBool(anchorData.compliant);
|
|
128
|
+
|
|
129
|
+
return {
|
|
130
|
+
to: this.config.contract_address,
|
|
131
|
+
data: encodedData,
|
|
132
|
+
value: '0', // Protocol fee handled by contract
|
|
133
|
+
gas_limit: cost.gas_limit,
|
|
134
|
+
chain_id: this.config.chain_id!,
|
|
135
|
+
estimated_cost: cost,
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Submit signed transaction
|
|
141
|
+
*/
|
|
142
|
+
async submitSignedTransaction(signedTransaction: string): Promise<AnchorResult> {
|
|
143
|
+
// This is a placeholder - real implementation would:
|
|
144
|
+
// 1. Submit to Ethereum RPC (eth_sendRawTransaction)
|
|
145
|
+
// 2. Wait for confirmation
|
|
146
|
+
// 3. Return result
|
|
147
|
+
|
|
148
|
+
const network: BlockchainNetwork = this.isTestnet ? 'ethereum-sepolia' : 'ethereum';
|
|
149
|
+
|
|
150
|
+
// Simulate success for testing
|
|
151
|
+
const mockTxHash = '0x' + Array(64).fill(0).map(() =>
|
|
152
|
+
Math.floor(Math.random() * 16).toString(16)
|
|
153
|
+
).join('');
|
|
154
|
+
|
|
155
|
+
const mockBlockNumber = 19000000 + Math.floor(Math.random() * 100000);
|
|
156
|
+
|
|
157
|
+
return {
|
|
158
|
+
success: true,
|
|
159
|
+
network,
|
|
160
|
+
transaction_id: mockTxHash,
|
|
161
|
+
block: mockBlockNumber,
|
|
162
|
+
timestamp: new Date(),
|
|
163
|
+
network_fee: '0.002',
|
|
164
|
+
protocol_fee: '0.0001',
|
|
165
|
+
total_cost: '0.0021',
|
|
166
|
+
verification_url: this.getExplorerUrl(mockTxHash),
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Verify anchor on-chain
|
|
172
|
+
*/
|
|
173
|
+
async verifyAnchor(
|
|
174
|
+
threadId: string,
|
|
175
|
+
expectedMerkleRoot: string
|
|
176
|
+
): Promise<AnchorVerification> {
|
|
177
|
+
// In production, this would call the contract's verifyAnchor function
|
|
178
|
+
|
|
179
|
+
const network: BlockchainNetwork = this.isTestnet ? 'ethereum-sepolia' : 'ethereum';
|
|
180
|
+
|
|
181
|
+
return {
|
|
182
|
+
valid: true,
|
|
183
|
+
thread_id: threadId,
|
|
184
|
+
merkle_root: expectedMerkleRoot,
|
|
185
|
+
anchor: {
|
|
186
|
+
network,
|
|
187
|
+
transaction_id: 'verification_pending',
|
|
188
|
+
timestamp: new Date(),
|
|
189
|
+
verified: false,
|
|
190
|
+
},
|
|
191
|
+
verified_at: new Date(),
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Create batch certification transaction
|
|
197
|
+
*/
|
|
198
|
+
async createCertificationTransaction(config: {
|
|
199
|
+
period_id: string;
|
|
200
|
+
merkle_root: string;
|
|
201
|
+
attestation_count: number;
|
|
202
|
+
violation_count: number;
|
|
203
|
+
period_start: Date;
|
|
204
|
+
period_end: Date;
|
|
205
|
+
}): Promise<{
|
|
206
|
+
to: string;
|
|
207
|
+
data: string;
|
|
208
|
+
estimated_cost: any;
|
|
209
|
+
}> {
|
|
210
|
+
// Encode function call: certifyPeriod(bytes32,bytes32,uint256,uint256,uint256,uint256)
|
|
211
|
+
const functionSelector = '0x' + this.keccak256(
|
|
212
|
+
'certifyPeriod(bytes32,bytes32,uint256,uint256,uint256,uint256)'
|
|
213
|
+
).slice(0, 8);
|
|
214
|
+
|
|
215
|
+
const encodedData = functionSelector +
|
|
216
|
+
this.encodeBytes32(this.stringToBytes32(config.period_id)) +
|
|
217
|
+
this.encodeBytes32(this.ensureBytes32(config.merkle_root)) +
|
|
218
|
+
this.encodeUint256(config.attestation_count) +
|
|
219
|
+
this.encodeUint256(config.violation_count) +
|
|
220
|
+
this.encodeUint256(Math.floor(config.period_start.getTime() / 1000)) +
|
|
221
|
+
this.encodeUint256(Math.floor(config.period_end.getTime() / 1000));
|
|
222
|
+
|
|
223
|
+
return {
|
|
224
|
+
to: this.config.contract_address,
|
|
225
|
+
data: encodedData,
|
|
226
|
+
estimated_cost: await this.estimateGas(),
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Get explorer URL
|
|
232
|
+
*/
|
|
233
|
+
getExplorerUrl(txHash: string): string {
|
|
234
|
+
const base = this.isTestnet
|
|
235
|
+
? 'https://sepolia.etherscan.io'
|
|
236
|
+
: 'https://etherscan.io';
|
|
237
|
+
return `${base}/tx/${txHash}`;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Get contract address
|
|
242
|
+
*/
|
|
243
|
+
getContractAddress(): string {
|
|
244
|
+
return this.config.contract_address;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// ============================================================================
|
|
248
|
+
// Encoding Utilities
|
|
249
|
+
// ============================================================================
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Convert string to bytes32 hex
|
|
253
|
+
*/
|
|
254
|
+
private stringToBytes32(str: string): string {
|
|
255
|
+
const crypto = require('crypto');
|
|
256
|
+
const hash = crypto.createHash('sha256').update(str).digest('hex');
|
|
257
|
+
return '0x' + hash;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* Ensure value is bytes32 format
|
|
262
|
+
*/
|
|
263
|
+
private ensureBytes32(hex: string): string {
|
|
264
|
+
let cleanHex = hex.startsWith('0x') ? hex.slice(2) : hex;
|
|
265
|
+
cleanHex = cleanHex.padStart(64, '0');
|
|
266
|
+
return '0x' + cleanHex;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* Encode bytes32 for ABI
|
|
271
|
+
*/
|
|
272
|
+
private encodeBytes32(value: string): string {
|
|
273
|
+
const clean = value.startsWith('0x') ? value.slice(2) : value;
|
|
274
|
+
return clean.padStart(64, '0');
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* Encode uint256 for ABI
|
|
279
|
+
*/
|
|
280
|
+
private encodeUint256(value: number): string {
|
|
281
|
+
return value.toString(16).padStart(64, '0');
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* Encode bool for ABI
|
|
286
|
+
*/
|
|
287
|
+
private encodeBool(value: boolean): string {
|
|
288
|
+
return value ? '0'.repeat(63) + '1' : '0'.repeat(64);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* Simple keccak256 (placeholder - use ethers.js in production)
|
|
293
|
+
*/
|
|
294
|
+
private keccak256(input: string): string {
|
|
295
|
+
// In production, use ethers.js keccak256
|
|
296
|
+
// This is a placeholder that returns a deterministic hash
|
|
297
|
+
const crypto = require('crypto');
|
|
298
|
+
return crypto.createHash('sha256').update(input).digest('hex');
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// ============================================================================
|
|
303
|
+
// Ethereum Contract ABI (for reference)
|
|
304
|
+
// ============================================================================
|
|
305
|
+
|
|
306
|
+
export const ETHEREUM_CONTRACT_ABI = [
|
|
307
|
+
{
|
|
308
|
+
"inputs": [
|
|
309
|
+
{ "name": "threadId", "type": "bytes32" },
|
|
310
|
+
{ "name": "merkleRoot", "type": "bytes32" },
|
|
311
|
+
{ "name": "hopCount", "type": "uint256" },
|
|
312
|
+
{ "name": "intentHash", "type": "bytes32" },
|
|
313
|
+
{ "name": "compliant", "type": "bool" }
|
|
314
|
+
],
|
|
315
|
+
"name": "anchorThread",
|
|
316
|
+
"outputs": [],
|
|
317
|
+
"stateMutability": "payable",
|
|
318
|
+
"type": "function"
|
|
319
|
+
},
|
|
320
|
+
{
|
|
321
|
+
"inputs": [
|
|
322
|
+
{ "name": "periodId", "type": "bytes32" },
|
|
323
|
+
{ "name": "merkleRoot", "type": "bytes32" },
|
|
324
|
+
{ "name": "attestationCount", "type": "uint256" },
|
|
325
|
+
{ "name": "violationCount", "type": "uint256" },
|
|
326
|
+
{ "name": "periodStart", "type": "uint256" },
|
|
327
|
+
{ "name": "periodEnd", "type": "uint256" }
|
|
328
|
+
],
|
|
329
|
+
"name": "certifyPeriod",
|
|
330
|
+
"outputs": [],
|
|
331
|
+
"stateMutability": "payable",
|
|
332
|
+
"type": "function"
|
|
333
|
+
},
|
|
334
|
+
{
|
|
335
|
+
"inputs": [
|
|
336
|
+
{ "name": "threadId", "type": "bytes32" },
|
|
337
|
+
{ "name": "expectedMerkleRoot", "type": "bytes32" }
|
|
338
|
+
],
|
|
339
|
+
"name": "verifyAnchor",
|
|
340
|
+
"outputs": [
|
|
341
|
+
{ "name": "valid", "type": "bool" },
|
|
342
|
+
{ "name": "timestamp", "type": "uint256" }
|
|
343
|
+
],
|
|
344
|
+
"stateMutability": "view",
|
|
345
|
+
"type": "function"
|
|
346
|
+
},
|
|
347
|
+
{
|
|
348
|
+
"inputs": [
|
|
349
|
+
{ "name": "threadId", "type": "bytes32" },
|
|
350
|
+
{ "name": "attestationHash", "type": "bytes32" },
|
|
351
|
+
{ "name": "merkleProof", "type": "bytes32[]" }
|
|
352
|
+
],
|
|
353
|
+
"name": "verifyAttestation",
|
|
354
|
+
"outputs": [{ "name": "", "type": "bool" }],
|
|
355
|
+
"stateMutability": "view",
|
|
356
|
+
"type": "function"
|
|
357
|
+
},
|
|
358
|
+
{
|
|
359
|
+
"anonymous": false,
|
|
360
|
+
"inputs": [
|
|
361
|
+
{ "indexed": true, "name": "threadId", "type": "bytes32" },
|
|
362
|
+
{ "indexed": false, "name": "merkleRoot", "type": "bytes32" },
|
|
363
|
+
{ "indexed": false, "name": "anchorer", "type": "address" },
|
|
364
|
+
{ "indexed": false, "name": "timestamp", "type": "uint256" }
|
|
365
|
+
],
|
|
366
|
+
"name": "ThreadAnchored",
|
|
367
|
+
"type": "event"
|
|
368
|
+
},
|
|
369
|
+
{
|
|
370
|
+
"anonymous": false,
|
|
371
|
+
"inputs": [
|
|
372
|
+
{ "indexed": true, "name": "periodId", "type": "bytes32" },
|
|
373
|
+
{ "indexed": false, "name": "merkleRoot", "type": "bytes32" },
|
|
374
|
+
{ "indexed": false, "name": "attestationCount", "type": "uint256" },
|
|
375
|
+
{ "indexed": false, "name": "timestamp", "type": "uint256" }
|
|
376
|
+
],
|
|
377
|
+
"name": "PeriodCertified",
|
|
378
|
+
"type": "event"
|
|
379
|
+
}
|
|
380
|
+
];
|
|
381
|
+
|
|
382
|
+
// ============================================================================
|
|
383
|
+
// Solidity Contract (for reference)
|
|
384
|
+
// ============================================================================
|
|
385
|
+
|
|
386
|
+
export const ETHEREUM_CONTRACT_SOURCE = `
|
|
387
|
+
// SPDX-License-Identifier: MIT
|
|
388
|
+
pragma solidity ^0.8.19;
|
|
389
|
+
|
|
390
|
+
/**
|
|
391
|
+
* @title DomereProtocol
|
|
392
|
+
* @dev Thread anchoring and compliance certification for AI agent security
|
|
393
|
+
*/
|
|
394
|
+
contract DomereProtocol {
|
|
395
|
+
|
|
396
|
+
// Protocol fee (5% of gas, calculated off-chain and sent as msg.value)
|
|
397
|
+
uint256 public protocolFeeBps = 500;
|
|
398
|
+
address public treasury;
|
|
399
|
+
address public owner;
|
|
400
|
+
|
|
401
|
+
struct ThreadAnchor {
|
|
402
|
+
bytes32 merkleRoot;
|
|
403
|
+
uint256 hopCount;
|
|
404
|
+
bytes32 intentHash;
|
|
405
|
+
bool compliant;
|
|
406
|
+
uint256 timestamp;
|
|
407
|
+
address anchorer;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
struct CompliancePeriod {
|
|
411
|
+
bytes32 merkleRoot;
|
|
412
|
+
uint256 attestationCount;
|
|
413
|
+
uint256 violationCount;
|
|
414
|
+
uint256 periodStart;
|
|
415
|
+
uint256 periodEnd;
|
|
416
|
+
uint256 timestamp;
|
|
417
|
+
bool certified;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
mapping(bytes32 => ThreadAnchor) public anchors;
|
|
421
|
+
mapping(bytes32 => CompliancePeriod) public periods;
|
|
422
|
+
mapping(address => bool) public authorizedAnchors;
|
|
423
|
+
|
|
424
|
+
event ThreadAnchored(
|
|
425
|
+
bytes32 indexed threadId,
|
|
426
|
+
bytes32 merkleRoot,
|
|
427
|
+
address anchorer,
|
|
428
|
+
uint256 timestamp
|
|
429
|
+
);
|
|
430
|
+
|
|
431
|
+
event PeriodCertified(
|
|
432
|
+
bytes32 indexed periodId,
|
|
433
|
+
bytes32 merkleRoot,
|
|
434
|
+
uint256 attestationCount,
|
|
435
|
+
uint256 timestamp
|
|
436
|
+
);
|
|
437
|
+
|
|
438
|
+
modifier onlyOwner() {
|
|
439
|
+
require(msg.sender == owner, "Not owner");
|
|
440
|
+
_;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
constructor(address _treasury) {
|
|
444
|
+
owner = msg.sender;
|
|
445
|
+
treasury = _treasury;
|
|
446
|
+
authorizedAnchors[msg.sender] = true;
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
/**
|
|
450
|
+
* @dev Anchor a thread to the blockchain
|
|
451
|
+
*/
|
|
452
|
+
function anchorThread(
|
|
453
|
+
bytes32 threadId,
|
|
454
|
+
bytes32 merkleRoot,
|
|
455
|
+
uint256 hopCount,
|
|
456
|
+
bytes32 intentHash,
|
|
457
|
+
bool compliant
|
|
458
|
+
) external payable {
|
|
459
|
+
require(anchors[threadId].timestamp == 0, "Thread already anchored");
|
|
460
|
+
|
|
461
|
+
// Store anchor
|
|
462
|
+
anchors[threadId] = ThreadAnchor({
|
|
463
|
+
merkleRoot: merkleRoot,
|
|
464
|
+
hopCount: hopCount,
|
|
465
|
+
intentHash: intentHash,
|
|
466
|
+
compliant: compliant,
|
|
467
|
+
timestamp: block.timestamp,
|
|
468
|
+
anchorer: msg.sender
|
|
469
|
+
});
|
|
470
|
+
|
|
471
|
+
// Transfer protocol fee to treasury
|
|
472
|
+
if (msg.value > 0) {
|
|
473
|
+
payable(treasury).transfer(msg.value);
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
emit ThreadAnchored(threadId, merkleRoot, msg.sender, block.timestamp);
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
/**
|
|
480
|
+
* @dev Certify a compliance period
|
|
481
|
+
*/
|
|
482
|
+
function certifyPeriod(
|
|
483
|
+
bytes32 periodId,
|
|
484
|
+
bytes32 merkleRoot,
|
|
485
|
+
uint256 attestationCount,
|
|
486
|
+
uint256 violationCount,
|
|
487
|
+
uint256 periodStart,
|
|
488
|
+
uint256 periodEnd
|
|
489
|
+
) external payable {
|
|
490
|
+
require(authorizedAnchors[msg.sender], "Not authorized");
|
|
491
|
+
|
|
492
|
+
periods[periodId] = CompliancePeriod({
|
|
493
|
+
merkleRoot: merkleRoot,
|
|
494
|
+
attestationCount: attestationCount,
|
|
495
|
+
violationCount: violationCount,
|
|
496
|
+
periodStart: periodStart,
|
|
497
|
+
periodEnd: periodEnd,
|
|
498
|
+
timestamp: block.timestamp,
|
|
499
|
+
certified: true
|
|
500
|
+
});
|
|
501
|
+
|
|
502
|
+
if (msg.value > 0) {
|
|
503
|
+
payable(treasury).transfer(msg.value);
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
emit PeriodCertified(periodId, merkleRoot, attestationCount, block.timestamp);
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
/**
|
|
510
|
+
* @dev Verify a thread anchor
|
|
511
|
+
*/
|
|
512
|
+
function verifyAnchor(
|
|
513
|
+
bytes32 threadId,
|
|
514
|
+
bytes32 expectedMerkleRoot
|
|
515
|
+
) external view returns (bool valid, uint256 timestamp) {
|
|
516
|
+
ThreadAnchor memory anchor = anchors[threadId];
|
|
517
|
+
return (
|
|
518
|
+
anchor.merkleRoot == expectedMerkleRoot && anchor.timestamp > 0,
|
|
519
|
+
anchor.timestamp
|
|
520
|
+
);
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
/**
|
|
524
|
+
* @dev Verify an attestation using Merkle proof
|
|
525
|
+
*/
|
|
526
|
+
function verifyAttestation(
|
|
527
|
+
bytes32 threadId,
|
|
528
|
+
bytes32 attestationHash,
|
|
529
|
+
bytes32[] calldata merkleProof
|
|
530
|
+
) external view returns (bool) {
|
|
531
|
+
ThreadAnchor memory anchor = anchors[threadId];
|
|
532
|
+
require(anchor.timestamp > 0, "Thread not anchored");
|
|
533
|
+
|
|
534
|
+
bytes32 computedHash = attestationHash;
|
|
535
|
+
for (uint256 i = 0; i < merkleProof.length; i++) {
|
|
536
|
+
bytes32 proofElement = merkleProof[i];
|
|
537
|
+
if (computedHash <= proofElement) {
|
|
538
|
+
computedHash = keccak256(abi.encodePacked(computedHash, proofElement));
|
|
539
|
+
} else {
|
|
540
|
+
computedHash = keccak256(abi.encodePacked(proofElement, computedHash));
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
return computedHash == anchor.merkleRoot;
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
/**
|
|
548
|
+
* @dev Authorize an address to certify periods
|
|
549
|
+
*/
|
|
550
|
+
function authorizeAnchor(address addr) external onlyOwner {
|
|
551
|
+
authorizedAnchors[addr] = true;
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
/**
|
|
555
|
+
* @dev Revoke authorization
|
|
556
|
+
*/
|
|
557
|
+
function revokeAuthorization(address addr) external onlyOwner {
|
|
558
|
+
authorizedAnchors[addr] = false;
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
/**
|
|
562
|
+
* @dev Update treasury address
|
|
563
|
+
*/
|
|
564
|
+
function setTreasury(address _treasury) external onlyOwner {
|
|
565
|
+
treasury = _treasury;
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
`;
|