midnight-mcp 0.2.9 → 0.2.10
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 +18 -11
- package/dist/bin.js +3 -3
- package/dist/{chunk-5PGBYGCG.js → chunk-7KJSVMFI.js} +957 -473
- package/dist/{chunk-WZVWUFLH.js → chunk-C23TNY65.js} +16 -7
- package/dist/db-BCMNMI6F.js +7 -0
- package/dist/index.js +2 -2
- package/package.json +1 -1
- package/dist/db-2VUY2I5E.js +0 -7
|
@@ -25,7 +25,7 @@ import {
|
|
|
25
25
|
validateNumber,
|
|
26
26
|
validateQuery,
|
|
27
27
|
vectorStore
|
|
28
|
-
} from "./chunk-
|
|
28
|
+
} from "./chunk-C23TNY65.js";
|
|
29
29
|
|
|
30
30
|
// src/tools/search/schemas.ts
|
|
31
31
|
import { z } from "zod";
|
|
@@ -786,11 +786,11 @@ async function analyzeContract(input) {
|
|
|
786
786
|
});
|
|
787
787
|
}
|
|
788
788
|
}
|
|
789
|
-
if (!parsed.imports.includes("
|
|
789
|
+
if (!parsed.imports.includes("CompactStandardLibrary")) {
|
|
790
790
|
findings.push({
|
|
791
791
|
severity: "info",
|
|
792
792
|
message: "Standard library not imported",
|
|
793
|
-
suggestion:
|
|
793
|
+
suggestion: "Consider adding 'import CompactStandardLibrary;' for common utilities"
|
|
794
794
|
});
|
|
795
795
|
}
|
|
796
796
|
}
|
|
@@ -1630,13 +1630,15 @@ Always import the standard library:
|
|
|
1630
1630
|
import CompactStandardLibrary;
|
|
1631
1631
|
\`\`\`
|
|
1632
1632
|
|
|
1633
|
-
For
|
|
1633
|
+
For modular code, use module imports (not a separate \`include\` directive):
|
|
1634
1634
|
\`\`\`compact
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
include "circuits";
|
|
1635
|
+
import "path/to/module";
|
|
1636
|
+
import { SomeType } from "other/module";
|
|
1638
1637
|
\`\`\`
|
|
1639
1638
|
|
|
1639
|
+
> **Note:** The docs describe modules and \`import\` syntax, but do not document
|
|
1640
|
+
> a separate \`include\` directive. Use \`import\` for code organization.
|
|
1641
|
+
|
|
1640
1642
|
---
|
|
1641
1643
|
|
|
1642
1644
|
## 3. Ledger Declarations
|
|
@@ -1681,6 +1683,14 @@ ledger privateData: Field; // Private, not exported
|
|
|
1681
1683
|
| \`Uint<N>\` | Unsigned integer (N = 8, 16, 32, 64, 128, 256) | \`balance: Uint<64>\` |
|
|
1682
1684
|
| \`Uint<MIN..MAX>\` | Bounded unsigned integer | \`score: Uint<0..100>\` |
|
|
1683
1685
|
|
|
1686
|
+
**\u26A0\uFE0F Uint Type Equivalence:** \`Uint<N>\` (sized) and \`Uint<0..MAX>\` (bounded) are the **SAME type family**.
|
|
1687
|
+
\`Uint<N>\` is exactly equivalent to \`Uint<0..(2^N - 1)>\`:
|
|
1688
|
+
- \`Uint<8>\` = \`Uint<0..255>\` (2^8 - 1 = 255)
|
|
1689
|
+
- \`Uint<16>\` = \`Uint<0..65535>\` (2^16 - 1 = 65535)
|
|
1690
|
+
- \`Uint<64>\` = \`Uint<0..18446744073709551615>\`
|
|
1691
|
+
|
|
1692
|
+
These can be used interchangeably - they are **not** separate types.
|
|
1693
|
+
|
|
1684
1694
|
### Collection Types
|
|
1685
1695
|
| Type | Description | Example |
|
|
1686
1696
|
|------|-------------|---------|
|
|
@@ -1913,41 +1923,60 @@ export circuit check_broken(guess: Field): Boolean {
|
|
|
1913
1923
|
|
|
1914
1924
|
### Counter Operations
|
|
1915
1925
|
\`\`\`compact
|
|
1916
|
-
//
|
|
1917
|
-
counter.increment(1);
|
|
1918
|
-
counter.decrement(1);
|
|
1919
|
-
counter.
|
|
1920
|
-
|
|
1921
|
-
//
|
|
1922
|
-
|
|
1923
|
-
//
|
|
1926
|
+
// ALL Counter methods work in circuits:
|
|
1927
|
+
counter.increment(1); // Increase by amount (Uint<16>)
|
|
1928
|
+
counter.decrement(1); // Decrease by amount (Uint<16>)
|
|
1929
|
+
const val = counter.read(); // Get current value (returns Uint<64>)
|
|
1930
|
+
const low = counter.lessThan(100); // Compare with threshold (returns Boolean)
|
|
1931
|
+
counter.resetToDefault(); // Reset to zero
|
|
1932
|
+
|
|
1933
|
+
// \u26A0\uFE0F WRONG: counter.value() does NOT exist - use counter.read()
|
|
1924
1934
|
\`\`\`
|
|
1925
1935
|
|
|
1926
1936
|
### Map Operations
|
|
1927
1937
|
\`\`\`compact
|
|
1928
|
-
//
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
//
|
|
1933
|
-
|
|
1934
|
-
//
|
|
1935
|
-
|
|
1936
|
-
|
|
1938
|
+
// Map<key_type, value_type> - All operations work in circuits unless noted
|
|
1939
|
+
|
|
1940
|
+
// Insert/update operations
|
|
1941
|
+
balances.insert(address, 100); // insert(key, value): []
|
|
1942
|
+
balances.insertDefault(address); // insertDefault(key): [] - inserts default value
|
|
1943
|
+
|
|
1944
|
+
// Query operations (all work in circuits \u2705)
|
|
1945
|
+
const balance = balances.lookup(address); // lookup(key): value_type
|
|
1946
|
+
const exists = balances.member(address); // member(key): Boolean
|
|
1947
|
+
const empty = balances.isEmpty(); // isEmpty(): Boolean
|
|
1948
|
+
const count = balances.size(); // size(): Uint<64>
|
|
1949
|
+
|
|
1950
|
+
// Remove operations
|
|
1951
|
+
balances.remove(address); // remove(key): []
|
|
1952
|
+
balances.resetToDefault(); // resetToDefault(): [] - clears entire map
|
|
1953
|
+
|
|
1954
|
+
// Coin-specific (only when value_type is QualifiedCoinInfo)
|
|
1955
|
+
// coinMap.insertCoin(key, coinInfo, recipient): []
|
|
1937
1956
|
\`\`\`
|
|
1938
1957
|
|
|
1958
|
+
**TypeScript-only:** \`[Symbol.iterator]()\` for iteration - not available in circuits.
|
|
1959
|
+
|
|
1939
1960
|
### Set Operations
|
|
1940
1961
|
\`\`\`compact
|
|
1941
|
-
//
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
//
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1962
|
+
// Set<value_type> - All operations work in circuits unless noted
|
|
1963
|
+
|
|
1964
|
+
// Insert/remove operations
|
|
1965
|
+
members.insert(address); // insert(elem): []
|
|
1966
|
+
members.remove(address); // remove(elem): []
|
|
1967
|
+
members.resetToDefault(); // resetToDefault(): [] - clears entire set
|
|
1968
|
+
|
|
1969
|
+
// Query operations (all work in circuits \u2705)
|
|
1970
|
+
const isMember = members.member(address); // member(elem): Boolean
|
|
1971
|
+
const empty = members.isEmpty(); // isEmpty(): Boolean
|
|
1972
|
+
const count = members.size(); // size(): Uint<64>
|
|
1973
|
+
|
|
1974
|
+
// Coin-specific (only when value_type is QualifiedCoinInfo)
|
|
1975
|
+
// coinSet.insertCoin(coinInfo, recipient): []
|
|
1949
1976
|
\`\`\`
|
|
1950
1977
|
|
|
1978
|
+
**TypeScript-only:** \`[Symbol.iterator]()\` for iteration - not available in circuits.
|
|
1979
|
+
|
|
1951
1980
|
### Maybe Operations
|
|
1952
1981
|
\`\`\`compact
|
|
1953
1982
|
const opt: Maybe<Field> = some<Field>(42);
|
|
@@ -2162,9 +2191,13 @@ npm install @openzeppelin/compact-contracts
|
|
|
2162
2191
|
## Usage Example
|
|
2163
2192
|
|
|
2164
2193
|
\`\`\`compact
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
|
|
2194
|
+
pragma language_version >= 0.18.0;
|
|
2195
|
+
|
|
2196
|
+
import CompactStandardLibrary;
|
|
2197
|
+
import "@openzeppelin/compact-contracts/src/token/FungibleToken"
|
|
2198
|
+
prefix FungibleToken_;
|
|
2199
|
+
import "@openzeppelin/compact-contracts/src/access/Ownable"
|
|
2200
|
+
prefix Ownable_;
|
|
2168
2201
|
|
|
2169
2202
|
ledger {
|
|
2170
2203
|
// Inherit from OpenZeppelin contracts
|
|
@@ -2199,8 +2232,11 @@ The recommended standard for privacy-preserving tokens on Midnight.
|
|
|
2199
2232
|
## Basic Usage
|
|
2200
2233
|
|
|
2201
2234
|
\`\`\`compact
|
|
2202
|
-
|
|
2203
|
-
|
|
2235
|
+
pragma language_version >= 0.18.0;
|
|
2236
|
+
|
|
2237
|
+
import CompactStandardLibrary;
|
|
2238
|
+
import "@openzeppelin/compact-contracts/src/token/FungibleToken"
|
|
2239
|
+
prefix FungibleToken_;
|
|
2204
2240
|
|
|
2205
2241
|
ledger {
|
|
2206
2242
|
...FungibleToken.ledger;
|
|
@@ -2241,7 +2277,8 @@ export circuit revealBalance(): Field {
|
|
|
2241
2277
|
## Minting and Burning
|
|
2242
2278
|
|
|
2243
2279
|
\`\`\`compact
|
|
2244
|
-
|
|
2280
|
+
import "@openzeppelin/compact-contracts/src/access/Ownable"
|
|
2281
|
+
prefix Ownable_;
|
|
2245
2282
|
|
|
2246
2283
|
ledger {
|
|
2247
2284
|
...FungibleToken.ledger;
|
|
@@ -2283,7 +2320,8 @@ Patterns for controlling who can call contract functions.
|
|
|
2283
2320
|
Simple single-owner access control.
|
|
2284
2321
|
|
|
2285
2322
|
\`\`\`compact
|
|
2286
|
-
|
|
2323
|
+
import "@openzeppelin/compact-contracts/src/access/Ownable"
|
|
2324
|
+
prefix Ownable_;
|
|
2287
2325
|
|
|
2288
2326
|
ledger {
|
|
2289
2327
|
...Ownable.ledger;
|
|
@@ -2309,7 +2347,8 @@ export circuit transferOwnership(newOwner: Address): Void {
|
|
|
2309
2347
|
For more complex permission systems.
|
|
2310
2348
|
|
|
2311
2349
|
\`\`\`compact
|
|
2312
|
-
|
|
2350
|
+
import "@openzeppelin/compact-contracts/src/access/AccessControl"
|
|
2351
|
+
prefix AccessControl_;
|
|
2313
2352
|
|
|
2314
2353
|
ledger {
|
|
2315
2354
|
...AccessControl.ledger;
|
|
@@ -2337,8 +2376,10 @@ export circuit grantMinterRole(account: Address): Void {
|
|
|
2337
2376
|
## Combining Patterns
|
|
2338
2377
|
|
|
2339
2378
|
\`\`\`compact
|
|
2340
|
-
|
|
2341
|
-
|
|
2379
|
+
import "@openzeppelin/compact-contracts/src/access/Ownable"
|
|
2380
|
+
prefix Ownable_;
|
|
2381
|
+
import "@openzeppelin/compact-contracts/src/security/Pausable"
|
|
2382
|
+
prefix Pausable_;
|
|
2342
2383
|
|
|
2343
2384
|
ledger {
|
|
2344
2385
|
...Ownable.ledger;
|
|
@@ -2366,8 +2407,10 @@ Security utilities for Compact contracts.
|
|
|
2366
2407
|
Emergency stop mechanism for contracts.
|
|
2367
2408
|
|
|
2368
2409
|
\`\`\`compact
|
|
2369
|
-
|
|
2370
|
-
|
|
2410
|
+
import "@openzeppelin/compact-contracts/src/security/Pausable"
|
|
2411
|
+
prefix Pausable_;
|
|
2412
|
+
import "@openzeppelin/compact-contracts/src/access/Ownable"
|
|
2413
|
+
prefix Ownable_;
|
|
2371
2414
|
|
|
2372
2415
|
ledger {
|
|
2373
2416
|
...Pausable.ledger;
|
|
@@ -2773,134 +2816,131 @@ var EMBEDDED_CODE = {
|
|
|
2773
2816
|
"midnight://code/examples/counter": `// Counter Example Contract
|
|
2774
2817
|
// A simple contract demonstrating basic Compact concepts
|
|
2775
2818
|
|
|
2776
|
-
|
|
2819
|
+
pragma language_version >= 0.16 && <= 0.18;
|
|
2777
2820
|
|
|
2778
|
-
|
|
2779
|
-
|
|
2780
|
-
|
|
2781
|
-
|
|
2782
|
-
|
|
2783
|
-
|
|
2784
|
-
|
|
2821
|
+
import CompactStandardLibrary;
|
|
2822
|
+
|
|
2823
|
+
// Public counter - visible to everyone
|
|
2824
|
+
export ledger counter: Counter;
|
|
2825
|
+
|
|
2826
|
+
// Track last modifier (public)
|
|
2827
|
+
export ledger lastModifier: Opaque<"address">;
|
|
2785
2828
|
|
|
2786
2829
|
// Increment the counter
|
|
2787
|
-
export circuit increment(amount:
|
|
2830
|
+
export circuit increment(amount: Uint<16>): Uint<64> {
|
|
2788
2831
|
// Validate input
|
|
2789
|
-
assert(amount > 0
|
|
2790
|
-
assert(amount <= 100
|
|
2832
|
+
assert(amount > 0 as Uint<16>, "Amount must be positive");
|
|
2833
|
+
assert(amount <= 100 as Uint<16>, "Amount too large");
|
|
2791
2834
|
|
|
2792
2835
|
// Update counter
|
|
2793
|
-
|
|
2836
|
+
counter.increment(amount);
|
|
2794
2837
|
|
|
2795
2838
|
// Return new value
|
|
2796
|
-
return
|
|
2839
|
+
return counter.read();
|
|
2797
2840
|
}
|
|
2798
2841
|
|
|
2799
2842
|
// Decrement the counter
|
|
2800
|
-
export circuit decrement(amount:
|
|
2843
|
+
export circuit decrement(amount: Uint<16>): Uint<64> {
|
|
2801
2844
|
// Validate input
|
|
2802
|
-
assert(amount > 0
|
|
2803
|
-
assert(
|
|
2845
|
+
assert(amount > 0 as Uint<16>, "Amount must be positive");
|
|
2846
|
+
assert(counter.read() >= (amount as Uint<64>), "Counter would go negative");
|
|
2804
2847
|
|
|
2805
2848
|
// Update counter
|
|
2806
|
-
|
|
2849
|
+
counter.decrement(amount);
|
|
2807
2850
|
|
|
2808
2851
|
// Return new value
|
|
2809
|
-
return
|
|
2852
|
+
return counter.read();
|
|
2810
2853
|
}
|
|
2811
2854
|
|
|
2812
2855
|
// Read current value (view function)
|
|
2813
|
-
export circuit getValue():
|
|
2814
|
-
return
|
|
2856
|
+
export circuit getValue(): Uint<64> {
|
|
2857
|
+
return counter.read();
|
|
2858
|
+
}
|
|
2859
|
+
|
|
2860
|
+
// Check if counter is below threshold
|
|
2861
|
+
export circuit isLessThan(threshold: Uint<64>): Boolean {
|
|
2862
|
+
return counter.lessThan(threshold);
|
|
2815
2863
|
}
|
|
2816
2864
|
`,
|
|
2817
2865
|
"midnight://code/examples/bboard": `// Bulletin Board Example Contract
|
|
2818
2866
|
// Demonstrates private messaging with selective disclosure
|
|
2819
2867
|
|
|
2820
|
-
|
|
2868
|
+
pragma language_version >= 0.16 && <= 0.18;
|
|
2821
2869
|
|
|
2822
|
-
|
|
2823
|
-
|
|
2824
|
-
|
|
2825
|
-
|
|
2826
|
-
|
|
2827
|
-
|
|
2828
|
-
|
|
2829
|
-
|
|
2830
|
-
|
|
2831
|
-
|
|
2832
|
-
|
|
2833
|
-
|
|
2834
|
-
|
|
2870
|
+
import CompactStandardLibrary;
|
|
2871
|
+
|
|
2872
|
+
// Public: message count and IDs
|
|
2873
|
+
export ledger messageCount: Counter;
|
|
2874
|
+
export ledger messageIds: Set<Field>;
|
|
2875
|
+
|
|
2876
|
+
// Private: actual message contents (no export = private)
|
|
2877
|
+
ledger messages: Map<Field, Opaque<"string">>;
|
|
2878
|
+
|
|
2879
|
+
// Private: message authors
|
|
2880
|
+
ledger authors: Map<Field, Opaque<"address">>;
|
|
2881
|
+
|
|
2882
|
+
// Witness to fetch message content
|
|
2883
|
+
witness getMessageContent(id: Field): Opaque<"string">;
|
|
2835
2884
|
|
|
2836
2885
|
// Post a new message (content is private)
|
|
2837
|
-
export circuit postMessage(content: Opaque<"string">, author: Opaque<"address">):
|
|
2838
|
-
// Generate unique message ID
|
|
2839
|
-
const messageId =
|
|
2886
|
+
export circuit postMessage(content: Opaque<"string">, author: Opaque<"address">): Uint<64> {
|
|
2887
|
+
// Generate unique message ID using counter read
|
|
2888
|
+
const messageId = messageCount.read();
|
|
2840
2889
|
|
|
2841
2890
|
// Store message privately
|
|
2842
|
-
|
|
2843
|
-
|
|
2891
|
+
messages.insert(messageId as Field, content);
|
|
2892
|
+
authors.insert(messageId as Field, author);
|
|
2844
2893
|
|
|
2845
2894
|
// Update public counters
|
|
2846
|
-
|
|
2847
|
-
|
|
2895
|
+
messageCount.increment(1);
|
|
2896
|
+
messageIds.insert(messageId as Field);
|
|
2848
2897
|
|
|
2849
2898
|
return messageId;
|
|
2850
2899
|
}
|
|
2851
2900
|
|
|
2852
|
-
// Witness to fetch message content
|
|
2853
|
-
witness getMessageContent(id: Field): Opaque<"string"> {
|
|
2854
|
-
return ledger.messages.get(id);
|
|
2855
|
-
}
|
|
2856
|
-
|
|
2857
2901
|
// Reveal a message publicly (owner's choice)
|
|
2858
2902
|
export circuit revealMessage(id: Field): Opaque<"string"> {
|
|
2859
|
-
assert(
|
|
2903
|
+
assert(messageIds.member(id), "Message not found");
|
|
2860
2904
|
|
|
2861
2905
|
const content = getMessageContent(id);
|
|
2862
2906
|
return disclose(content);
|
|
2863
2907
|
}
|
|
2864
2908
|
|
|
2865
2909
|
// Get total message count
|
|
2866
|
-
export circuit getMessageCount():
|
|
2867
|
-
return
|
|
2910
|
+
export circuit getMessageCount(): Uint<64> {
|
|
2911
|
+
return messageCount.read();
|
|
2868
2912
|
}
|
|
2869
2913
|
`,
|
|
2870
2914
|
"midnight://code/patterns/state-management": `// State Management Pattern
|
|
2871
2915
|
// Best practices for managing public and private state
|
|
2872
2916
|
|
|
2873
|
-
|
|
2917
|
+
pragma language_version >= 0.16 && <= 0.18;
|
|
2874
2918
|
|
|
2875
|
-
|
|
2876
|
-
// PUBLIC STATE
|
|
2877
|
-
// - Use for data that should be transparent
|
|
2878
|
-
// - Visible in blockchain explorers
|
|
2879
|
-
// - Can be queried by anyone
|
|
2880
|
-
|
|
2881
|
-
totalSupply: Counter;
|
|
2882
|
-
publicConfig: Field;
|
|
2883
|
-
|
|
2884
|
-
// PRIVATE STATE
|
|
2885
|
-
// - Use for sensitive user data
|
|
2886
|
-
// - Only owner can read
|
|
2887
|
-
// - Requires witnesses to access in circuits
|
|
2888
|
-
|
|
2889
|
-
@private
|
|
2890
|
-
userSecrets: Map<Opaque<"address">, Bytes<32>>;
|
|
2891
|
-
|
|
2892
|
-
@private
|
|
2893
|
-
privateBalances: Map<Opaque<"address">, Field>;
|
|
2894
|
-
}
|
|
2919
|
+
import CompactStandardLibrary;
|
|
2895
2920
|
|
|
2896
|
-
//
|
|
2897
|
-
export
|
|
2898
|
-
|
|
2899
|
-
|
|
2921
|
+
// PUBLIC STATE
|
|
2922
|
+
// - Use 'export ledger' for data that should be transparent
|
|
2923
|
+
// - Visible in blockchain explorers
|
|
2924
|
+
// - Can be queried by anyone
|
|
2925
|
+
|
|
2926
|
+
export ledger totalSupply: Uint<64>;
|
|
2927
|
+
export ledger publicConfig: Field;
|
|
2928
|
+
|
|
2929
|
+
// PRIVATE STATE
|
|
2930
|
+
// - Use 'ledger' without export for sensitive user data
|
|
2931
|
+
// - Only owner can read
|
|
2932
|
+
// - Requires witnesses to access in circuits
|
|
2900
2933
|
|
|
2901
|
-
|
|
2902
|
-
|
|
2903
|
-
|
|
2934
|
+
ledger userSecrets: Map<Opaque<"address">, Bytes<32>>;
|
|
2935
|
+
ledger privateBalances: Map<Opaque<"address">, Field>;
|
|
2936
|
+
|
|
2937
|
+
// Witnesses for private data access
|
|
2938
|
+
witness getUserSecret(user: Opaque<"address">): Bytes<32>;
|
|
2939
|
+
witness getPrivateBalance(user: Opaque<"address">): Field;
|
|
2940
|
+
|
|
2941
|
+
// Reading public state is straightforward
|
|
2942
|
+
export circuit getTotalSupply(): Uint<64> {
|
|
2943
|
+
return ledger.totalSupply;
|
|
2904
2944
|
}
|
|
2905
2945
|
|
|
2906
2946
|
// Using private state in a circuit
|
|
@@ -2911,7 +2951,7 @@ export circuit proveSecretKnowledge(
|
|
|
2911
2951
|
const secret = getUserSecret(user);
|
|
2912
2952
|
|
|
2913
2953
|
// Prove knowledge without revealing secret
|
|
2914
|
-
assert(
|
|
2954
|
+
assert(persistentHash(secret) == secretHash, "Invalid secret");
|
|
2915
2955
|
return true;
|
|
2916
2956
|
}
|
|
2917
2957
|
|
|
@@ -2921,53 +2961,45 @@ export circuit revealBalance(user: Opaque<"address">): Field {
|
|
|
2921
2961
|
// Explicitly reveal - user's choice
|
|
2922
2962
|
return disclose(balance);
|
|
2923
2963
|
}
|
|
2924
|
-
|
|
2925
|
-
witness getPrivateBalance(user: Opaque<"address">): Field {
|
|
2926
|
-
return ledger.privateBalances.get(user);
|
|
2927
|
-
}
|
|
2928
2964
|
`,
|
|
2929
2965
|
"midnight://code/patterns/access-control": `// Access Control Pattern
|
|
2930
2966
|
// Implementing permissions and authorization
|
|
2931
2967
|
|
|
2932
|
-
|
|
2968
|
+
pragma language_version >= 0.16 && <= 0.18;
|
|
2969
|
+
|
|
2970
|
+
import CompactStandardLibrary;
|
|
2933
2971
|
|
|
2934
|
-
|
|
2935
|
-
|
|
2936
|
-
|
|
2937
|
-
|
|
2938
|
-
|
|
2939
|
-
|
|
2940
|
-
|
|
2941
|
-
|
|
2942
|
-
|
|
2943
|
-
adminKeys: Map<Opaque<"address">, Bytes<32>>;
|
|
2944
|
-
}
|
|
2972
|
+
// Role definitions
|
|
2973
|
+
export ledger owner: Opaque<"address">;
|
|
2974
|
+
export ledger admins: Set<Opaque<"address">>;
|
|
2975
|
+
|
|
2976
|
+
// Access-controlled state
|
|
2977
|
+
export ledger sensitiveData: Field;
|
|
2978
|
+
|
|
2979
|
+
// Private admin keys
|
|
2980
|
+
ledger adminKeys: Map<Opaque<"address">, Bytes<32>>;
|
|
2945
2981
|
|
|
2946
2982
|
// Witness to get caller identity
|
|
2947
|
-
witness getCaller(): Opaque<"address"
|
|
2948
|
-
return getCurrentCaller();
|
|
2949
|
-
}
|
|
2983
|
+
witness getCaller(): Opaque<"address">;
|
|
2950
2984
|
|
|
2951
2985
|
// Only owner can call
|
|
2952
|
-
export circuit onlyOwnerAction(newValue: Field):
|
|
2986
|
+
export circuit onlyOwnerAction(newValue: Field): [] {
|
|
2953
2987
|
const caller = getCaller();
|
|
2954
|
-
assert(caller ==
|
|
2988
|
+
assert(caller == owner, "Not owner");
|
|
2955
2989
|
|
|
2956
|
-
|
|
2990
|
+
sensitiveData = newValue;
|
|
2957
2991
|
}
|
|
2958
2992
|
|
|
2959
2993
|
// Only admins can call
|
|
2960
|
-
export circuit onlyAdminAction(data: Field):
|
|
2994
|
+
export circuit onlyAdminAction(data: Field): [] {
|
|
2961
2995
|
const caller = getCaller();
|
|
2962
|
-
assert(
|
|
2996
|
+
assert(admins.member(caller), "Not admin");
|
|
2963
2997
|
|
|
2964
2998
|
// Admin action here
|
|
2965
2999
|
}
|
|
2966
3000
|
|
|
2967
3001
|
// Multi-sig pattern (require multiple approvals)
|
|
2968
|
-
witness getApprovalCount(action: Bytes<32>): Field
|
|
2969
|
-
return countApprovals(action);
|
|
2970
|
-
}
|
|
3002
|
+
witness getApprovalCount(action: Bytes<32>): Field;
|
|
2971
3003
|
|
|
2972
3004
|
export circuit requireMultisig(action: Bytes<32>, threshold: Field): Boolean {
|
|
2973
3005
|
const approvals = getApprovalCount(action);
|
|
@@ -2976,11 +3008,9 @@ export circuit requireMultisig(action: Bytes<32>, threshold: Field): Boolean {
|
|
|
2976
3008
|
}
|
|
2977
3009
|
|
|
2978
3010
|
// Time-locked action
|
|
2979
|
-
witness getCurrentTime(): Field
|
|
2980
|
-
return getBlockTimestamp();
|
|
2981
|
-
}
|
|
3011
|
+
witness getCurrentTime(): Field;
|
|
2982
3012
|
|
|
2983
|
-
export circuit timeLockedAction(unlockTime: Field):
|
|
3013
|
+
export circuit timeLockedAction(unlockTime: Field): [] {
|
|
2984
3014
|
const currentTime = getCurrentTime();
|
|
2985
3015
|
assert(currentTime >= unlockTime, "Action is timelocked");
|
|
2986
3016
|
|
|
@@ -2990,21 +3020,23 @@ export circuit timeLockedAction(unlockTime: Field): Void {
|
|
|
2990
3020
|
"midnight://code/patterns/privacy-preserving": `// Privacy-Preserving Patterns
|
|
2991
3021
|
// Techniques for maintaining privacy in smart contracts
|
|
2992
3022
|
|
|
2993
|
-
|
|
3023
|
+
pragma language_version >= 0.16 && <= 0.18;
|
|
3024
|
+
|
|
3025
|
+
import CompactStandardLibrary;
|
|
2994
3026
|
|
|
2995
|
-
|
|
2996
|
-
|
|
2997
|
-
|
|
2998
|
-
|
|
2999
|
-
|
|
3000
|
-
|
|
3001
|
-
|
|
3002
|
-
|
|
3003
|
-
|
|
3004
|
-
|
|
3005
|
-
|
|
3006
|
-
|
|
3007
|
-
|
|
3027
|
+
// Commitment-based private balance
|
|
3028
|
+
export ledger balanceCommitments: Map<Opaque<"address">, Field>;
|
|
3029
|
+
|
|
3030
|
+
// Nullifier set (prevents double-spending)
|
|
3031
|
+
export ledger nullifiers: Set<Field>;
|
|
3032
|
+
|
|
3033
|
+
// Private state
|
|
3034
|
+
ledger secretBalances: Map<Opaque<"address">, Field>;
|
|
3035
|
+
ledger secretNonces: Map<Opaque<"address">, Field>;
|
|
3036
|
+
|
|
3037
|
+
// Witnesses for private state
|
|
3038
|
+
witness getSecretBalance(user: Opaque<"address">): Field;
|
|
3039
|
+
witness getSecretNonce(user: Opaque<"address">): Field;
|
|
3008
3040
|
|
|
3009
3041
|
// PATTERN 1: Commitment Scheme
|
|
3010
3042
|
// Store commitments instead of values
|
|
@@ -3014,11 +3046,14 @@ export circuit deposit(
|
|
|
3014
3046
|
amount: Field,
|
|
3015
3047
|
nonce: Field
|
|
3016
3048
|
): Field {
|
|
3017
|
-
// Create commitment: hash(amount
|
|
3018
|
-
|
|
3049
|
+
// Create commitment: hash(amount || nonce)
|
|
3050
|
+
// Use a struct to combine values into a single hashable input
|
|
3051
|
+
struct CommitmentInput { amount: Field, nonce: Field }
|
|
3052
|
+
const input = CommitmentInput { amount: amount, nonce: nonce };
|
|
3053
|
+
const commitment = persistentHash<Field>(input);
|
|
3019
3054
|
|
|
3020
3055
|
// Store commitment (hides amount)
|
|
3021
|
-
|
|
3056
|
+
balanceCommitments.insert(user, commitment);
|
|
3022
3057
|
|
|
3023
3058
|
return commitment;
|
|
3024
3059
|
}
|
|
@@ -3030,31 +3065,31 @@ export circuit proveBalance(
|
|
|
3030
3065
|
minBalance: Field
|
|
3031
3066
|
): Boolean {
|
|
3032
3067
|
// Verify commitment
|
|
3033
|
-
|
|
3034
|
-
|
|
3068
|
+
struct CommitmentInput { amount: Field, nonce: Field }
|
|
3069
|
+
const input = CommitmentInput { amount: amount, nonce: nonce };
|
|
3070
|
+
const expectedCommitment = persistentHash<Field>(input);
|
|
3071
|
+
assert(balanceCommitments.lookup(user) == expectedCommitment, "Invalid commitment");
|
|
3035
3072
|
|
|
3036
3073
|
// Prove property without revealing value
|
|
3037
|
-
assert(amount >= minBalance);
|
|
3074
|
+
assert(amount >= minBalance, "Insufficient balance");
|
|
3038
3075
|
return true;
|
|
3039
3076
|
}
|
|
3040
3077
|
|
|
3041
3078
|
// PATTERN 2: Nullifiers (Prevent Double-Spending)
|
|
3042
3079
|
|
|
3043
|
-
witness generateNullifier(secret: Bytes<32>, action: Field): Field
|
|
3044
|
-
return hash(secret, action);
|
|
3045
|
-
}
|
|
3080
|
+
witness generateNullifier(secret: Bytes<32>, action: Field): Field;
|
|
3046
3081
|
|
|
3047
3082
|
export circuit spendOnce(
|
|
3048
3083
|
secret: Bytes<32>,
|
|
3049
3084
|
action: Field
|
|
3050
|
-
):
|
|
3085
|
+
): [] {
|
|
3051
3086
|
const nullifier = generateNullifier(secret, action);
|
|
3052
3087
|
|
|
3053
3088
|
// Check nullifier hasn't been used
|
|
3054
|
-
assert(!
|
|
3089
|
+
assert(!nullifiers.member(nullifier), "Already spent");
|
|
3055
3090
|
|
|
3056
3091
|
// Mark as used
|
|
3057
|
-
|
|
3092
|
+
nullifiers.insert(nullifier);
|
|
3058
3093
|
|
|
3059
3094
|
// Perform action
|
|
3060
3095
|
}
|
|
@@ -3062,72 +3097,55 @@ export circuit spendOnce(
|
|
|
3062
3097
|
// PATTERN 3: Range Proofs
|
|
3063
3098
|
|
|
3064
3099
|
export circuit proveInRange(
|
|
3065
|
-
|
|
3100
|
+
value: Field,
|
|
3066
3101
|
min: Field,
|
|
3067
3102
|
max: Field
|
|
3068
3103
|
): Boolean {
|
|
3069
3104
|
// Prove value is in range without revealing it
|
|
3070
|
-
assert(value >= min);
|
|
3071
|
-
assert(value <= max);
|
|
3105
|
+
assert(value >= min, "Below minimum");
|
|
3106
|
+
assert(value <= max, "Above maximum");
|
|
3072
3107
|
return true;
|
|
3073
3108
|
}
|
|
3074
3109
|
|
|
3075
3110
|
// PATTERN 4: Private Set Membership
|
|
3076
3111
|
|
|
3112
|
+
witness computeMerkleRoot(element: Field, proof: Vector<10, Field>): Field;
|
|
3113
|
+
|
|
3077
3114
|
export circuit proveMembership(
|
|
3078
|
-
|
|
3115
|
+
element: Field,
|
|
3079
3116
|
setRoot: Field,
|
|
3080
|
-
|
|
3117
|
+
proof: Vector<10, Field>
|
|
3081
3118
|
): Boolean {
|
|
3082
3119
|
// Prove element is in set without revealing which element
|
|
3083
3120
|
const computedRoot = computeMerkleRoot(element, proof);
|
|
3084
|
-
assert(computedRoot == setRoot);
|
|
3121
|
+
assert(computedRoot == setRoot, "Invalid membership proof");
|
|
3085
3122
|
return true;
|
|
3086
3123
|
}
|
|
3087
|
-
|
|
3088
|
-
witness computeMerkleRoot(element: Field, proof: Array<Field>): Field {
|
|
3089
|
-
// Compute Merkle root from element and proof
|
|
3090
|
-
return merkleCompute(element, proof);
|
|
3091
|
-
}
|
|
3092
3124
|
`,
|
|
3093
3125
|
"midnight://code/templates/token": `// Privacy-Preserving Token Template
|
|
3094
3126
|
// Starter template for token contracts with privacy features
|
|
3095
3127
|
|
|
3096
|
-
|
|
3128
|
+
pragma language_version >= 0.16 && <= 0.18;
|
|
3097
3129
|
|
|
3098
|
-
|
|
3099
|
-
// Public token metadata
|
|
3100
|
-
name: Opaque<"string">;
|
|
3101
|
-
symbol: Opaque<"string">;
|
|
3102
|
-
decimals: Field;
|
|
3103
|
-
totalSupply: Counter;
|
|
3104
|
-
|
|
3105
|
-
// Private balances
|
|
3106
|
-
@private
|
|
3107
|
-
balances: Map<Opaque<"address">, Field>;
|
|
3108
|
-
|
|
3109
|
-
// Private allowances
|
|
3110
|
-
@private
|
|
3111
|
-
allowances: Map<Opaque<"address">, Map<Opaque<"address">, Field>>;
|
|
3112
|
-
}
|
|
3130
|
+
import CompactStandardLibrary;
|
|
3113
3131
|
|
|
3114
|
-
//
|
|
3115
|
-
|
|
3116
|
-
|
|
3117
|
-
|
|
3132
|
+
// Public token metadata
|
|
3133
|
+
export ledger name: Bytes<32>;
|
|
3134
|
+
export ledger symbol: Bytes<8>;
|
|
3135
|
+
export ledger decimals: Uint<8>;
|
|
3136
|
+
export ledger totalSupply: Uint<64>;
|
|
3118
3137
|
|
|
3119
|
-
|
|
3120
|
-
|
|
3121
|
-
}
|
|
3138
|
+
// Private balances
|
|
3139
|
+
ledger balances: Map<Opaque<"address">, Uint<64>>;
|
|
3122
3140
|
|
|
3123
|
-
|
|
3124
|
-
|
|
3125
|
-
|
|
3141
|
+
// Witnesses for private state access
|
|
3142
|
+
witness getBalance(account: Opaque<"address">): Uint<64>;
|
|
3143
|
+
witness getCaller(): Opaque<"address">;
|
|
3126
3144
|
|
|
3127
3145
|
// Transfer tokens privately
|
|
3128
3146
|
export circuit transfer(
|
|
3129
3147
|
to: Opaque<"address">,
|
|
3130
|
-
amount:
|
|
3148
|
+
amount: Uint<64>
|
|
3131
3149
|
): Boolean {
|
|
3132
3150
|
const from = getCaller();
|
|
3133
3151
|
const fromBalance = getBalance(from);
|
|
@@ -3137,224 +3155,153 @@ export circuit transfer(
|
|
|
3137
3155
|
assert(fromBalance >= amount, "Insufficient balance");
|
|
3138
3156
|
|
|
3139
3157
|
// Update balances privately
|
|
3140
|
-
|
|
3141
|
-
|
|
3142
|
-
|
|
3143
|
-
return true;
|
|
3144
|
-
}
|
|
3145
|
-
|
|
3146
|
-
// Approve spender
|
|
3147
|
-
export circuit approve(
|
|
3148
|
-
spender: Opaque<"address">,
|
|
3149
|
-
amount: Field
|
|
3150
|
-
): Boolean {
|
|
3151
|
-
const owner = getCaller();
|
|
3152
|
-
|
|
3153
|
-
// Get or create allowance map for owner
|
|
3154
|
-
// Note: Simplified - actual implementation needs nested map handling
|
|
3155
|
-
ledger.allowances.get(owner).insert(spender, amount);
|
|
3156
|
-
|
|
3157
|
-
return true;
|
|
3158
|
-
}
|
|
3159
|
-
|
|
3160
|
-
// Transfer from approved account
|
|
3161
|
-
export circuit transferFrom(
|
|
3162
|
-
from: Opaque<"address">,
|
|
3163
|
-
to: Opaque<"address">,
|
|
3164
|
-
amount: Field
|
|
3165
|
-
): Boolean {
|
|
3166
|
-
const spender = getCaller();
|
|
3167
|
-
const allowance = getAllowance(from, spender);
|
|
3168
|
-
const fromBalance = getBalance(from);
|
|
3169
|
-
|
|
3170
|
-
// Validate
|
|
3171
|
-
assert(amount > 0, "Invalid amount");
|
|
3172
|
-
assert(allowance >= amount, "Insufficient allowance");
|
|
3173
|
-
assert(fromBalance >= amount, "Insufficient balance");
|
|
3174
|
-
|
|
3175
|
-
// Update state
|
|
3176
|
-
ledger.balances.insert(from, fromBalance - amount);
|
|
3177
|
-
ledger.balances.insert(to, getBalance(to) + amount);
|
|
3178
|
-
ledger.allowances.get(from).insert(spender, allowance - amount);
|
|
3158
|
+
balances.insert(from, fromBalance - amount);
|
|
3159
|
+
balances.insert(to, getBalance(to) + amount);
|
|
3179
3160
|
|
|
3180
3161
|
return true;
|
|
3181
3162
|
}
|
|
3182
3163
|
|
|
3183
3164
|
// Reveal balance (user's choice)
|
|
3184
|
-
export circuit revealMyBalance():
|
|
3165
|
+
export circuit revealMyBalance(): Uint<64> {
|
|
3185
3166
|
const caller = getCaller();
|
|
3186
3167
|
const balance = getBalance(caller);
|
|
3187
3168
|
return disclose(balance);
|
|
3188
3169
|
}
|
|
3170
|
+
|
|
3171
|
+
// Get total supply
|
|
3172
|
+
witness getTotalSupply(): Uint<64> {
|
|
3173
|
+
return ledger.totalSupply;
|
|
3174
|
+
}
|
|
3175
|
+
|
|
3176
|
+
// Mint new tokens (admin only)
|
|
3177
|
+
export circuit mint(to: Opaque<"address">, amount: Uint<64>): Boolean {
|
|
3178
|
+
// Add access control in production
|
|
3179
|
+
balances.insert(to, getBalance(to) + amount);
|
|
3180
|
+
ledger.totalSupply = getTotalSupply() + amount;
|
|
3181
|
+
return true;
|
|
3182
|
+
}
|
|
3189
3183
|
`,
|
|
3190
3184
|
"midnight://code/templates/voting": `// Private Voting Template
|
|
3191
3185
|
// Starter template for privacy-preserving voting contracts
|
|
3192
3186
|
|
|
3193
|
-
|
|
3187
|
+
pragma language_version >= 0.16 && <= 0.18;
|
|
3194
3188
|
|
|
3195
|
-
|
|
3196
|
-
// Public: proposal metadata
|
|
3197
|
-
proposalCount: Counter;
|
|
3198
|
-
proposals: Map<Field, Opaque<"string">>;
|
|
3199
|
-
votingDeadlines: Map<Field, Field>;
|
|
3200
|
-
|
|
3201
|
-
// Public: vote tallies (revealed after voting ends)
|
|
3202
|
-
finalTallies: Map<Field, Map<Field, Field>>; // proposalId -> optionId -> count
|
|
3203
|
-
|
|
3204
|
-
// Private: individual votes
|
|
3205
|
-
@private
|
|
3206
|
-
votes: Map<Field, Map<Opaque<"address">, Field>>; // proposalId -> voter -> option
|
|
3207
|
-
|
|
3208
|
-
// Nullifiers to prevent double voting
|
|
3209
|
-
voteNullifiers: Set<Field>;
|
|
3210
|
-
|
|
3211
|
-
// Eligible voters
|
|
3212
|
-
eligibleVoters: Set<Opaque<"address">>;
|
|
3213
|
-
}
|
|
3189
|
+
import CompactStandardLibrary;
|
|
3214
3190
|
|
|
3215
|
-
//
|
|
3216
|
-
|
|
3217
|
-
|
|
3218
|
-
|
|
3191
|
+
// Public: proposal metadata
|
|
3192
|
+
export ledger proposalCount: Counter;
|
|
3193
|
+
export ledger proposals: Map<Uint<64>, Bytes<256>>;
|
|
3194
|
+
export ledger votingDeadlines: Map<Uint<64>, Uint<64>>;
|
|
3219
3195
|
|
|
3220
|
-
|
|
3221
|
-
|
|
3222
|
-
}
|
|
3196
|
+
// Private: individual votes
|
|
3197
|
+
ledger votes: Map<Uint<64>, Map<Opaque<"address">, Uint<8>>>;
|
|
3223
3198
|
|
|
3224
|
-
|
|
3225
|
-
|
|
3226
|
-
}
|
|
3199
|
+
// Nullifiers to prevent double voting
|
|
3200
|
+
export ledger voteNullifiers: Set<Bytes<32>>;
|
|
3227
3201
|
|
|
3228
|
-
|
|
3229
|
-
|
|
3230
|
-
|
|
3202
|
+
// Eligible voters
|
|
3203
|
+
export ledger eligibleVoters: Set<Opaque<"address">>;
|
|
3204
|
+
|
|
3205
|
+
// Witnesses
|
|
3206
|
+
witness getCaller(): Opaque<"address">;
|
|
3207
|
+
witness getCurrentTime(): Uint<64>;
|
|
3208
|
+
witness getVote(proposalId: Uint<64>, voter: Opaque<"address">): Uint<8>;
|
|
3209
|
+
witness computeNullifier(voter: Opaque<"address">, proposalId: Uint<64>): Bytes<32>;
|
|
3231
3210
|
|
|
3232
3211
|
// Create a new proposal
|
|
3233
3212
|
export circuit createProposal(
|
|
3234
|
-
description:
|
|
3235
|
-
deadline:
|
|
3236
|
-
|
|
3237
|
-
|
|
3238
|
-
const proposalId = ledger.proposalCount.value();
|
|
3239
|
-
|
|
3240
|
-
// Store proposal
|
|
3241
|
-
ledger.proposals.insert(proposalId, description);
|
|
3242
|
-
ledger.votingDeadlines.insert(proposalId, deadline);
|
|
3213
|
+
description: Bytes<256>,
|
|
3214
|
+
deadline: Uint<64>
|
|
3215
|
+
): Uint<64> {
|
|
3216
|
+
const proposalId = proposalCount.read();
|
|
3243
3217
|
|
|
3244
|
-
//
|
|
3245
|
-
|
|
3218
|
+
// Store proposal - proposalId is Uint<64> from Counter.read()
|
|
3219
|
+
proposals.insert(proposalId, description);
|
|
3220
|
+
votingDeadlines.insert(proposalId, deadline);
|
|
3246
3221
|
|
|
3247
|
-
|
|
3222
|
+
proposalCount.increment(1);
|
|
3248
3223
|
return proposalId;
|
|
3249
3224
|
}
|
|
3250
3225
|
|
|
3251
3226
|
// Cast a private vote
|
|
3252
3227
|
export circuit vote(
|
|
3253
|
-
proposalId:
|
|
3254
|
-
option:
|
|
3228
|
+
proposalId: Uint<64>,
|
|
3229
|
+
option: Uint<8>
|
|
3255
3230
|
): Boolean {
|
|
3256
3231
|
const voter = getCaller();
|
|
3257
3232
|
const currentTime = getCurrentTime();
|
|
3258
3233
|
|
|
3259
3234
|
// Check eligibility
|
|
3260
|
-
assert(
|
|
3235
|
+
assert(eligibleVoters.member(voter), "Not eligible to vote");
|
|
3261
3236
|
|
|
3262
3237
|
// Check deadline
|
|
3263
|
-
const deadline =
|
|
3238
|
+
const deadline = votingDeadlines.lookup(proposalId);
|
|
3264
3239
|
assert(currentTime < deadline, "Voting ended");
|
|
3265
3240
|
|
|
3266
3241
|
// Check for double voting using nullifier
|
|
3267
3242
|
const nullifier = computeNullifier(voter, proposalId);
|
|
3268
|
-
assert(!
|
|
3269
|
-
|
|
3270
|
-
// Record vote privately
|
|
3271
|
-
ledger.votes.get(proposalId).insert(voter, option);
|
|
3243
|
+
assert(!voteNullifiers.member(nullifier), "Already voted");
|
|
3272
3244
|
|
|
3273
3245
|
// Add nullifier to prevent double voting
|
|
3274
|
-
|
|
3246
|
+
voteNullifiers.insert(nullifier);
|
|
3275
3247
|
|
|
3276
3248
|
return true;
|
|
3277
3249
|
}
|
|
3278
3250
|
|
|
3279
3251
|
// Reveal individual vote (voter's choice)
|
|
3280
|
-
export circuit revealMyVote(proposalId:
|
|
3252
|
+
export circuit revealMyVote(proposalId: Uint<32>): Uint<8> {
|
|
3281
3253
|
const voter = getCaller();
|
|
3282
3254
|
const myVote = getVote(proposalId, voter);
|
|
3283
3255
|
return disclose(myVote);
|
|
3284
3256
|
}
|
|
3285
3257
|
|
|
3286
|
-
// Tally votes (after deadline)
|
|
3287
|
-
// Note: This is simplified - real implementation would need
|
|
3288
|
-
// a mechanism to privately aggregate votes
|
|
3289
|
-
export circuit tallyVotes(proposalId: Field): Boolean {
|
|
3290
|
-
const currentTime = getCurrentTime();
|
|
3291
|
-
const deadline = ledger.votingDeadlines.get(proposalId);
|
|
3292
|
-
|
|
3293
|
-
assert(currentTime >= deadline, "Voting still active");
|
|
3294
|
-
|
|
3295
|
-
// In a real implementation, votes would be aggregated
|
|
3296
|
-
// using homomorphic encryption or MPC
|
|
3297
|
-
|
|
3298
|
-
return true;
|
|
3299
|
-
}
|
|
3300
|
-
|
|
3301
3258
|
// Add eligible voter (admin only)
|
|
3302
|
-
export circuit addVoter(voter: Opaque<"address">):
|
|
3259
|
+
export circuit addVoter(voter: Opaque<"address">): [] {
|
|
3303
3260
|
// Add access control in real implementation
|
|
3304
|
-
|
|
3261
|
+
eligibleVoters.insert(voter);
|
|
3305
3262
|
}
|
|
3306
3263
|
`,
|
|
3307
3264
|
"midnight://code/examples/nullifier": `// Nullifier Pattern Example
|
|
3308
3265
|
// Demonstrates how to create and use nullifiers to prevent double-spending/actions
|
|
3309
3266
|
|
|
3310
|
-
|
|
3267
|
+
pragma language_version >= 0.16 && <= 0.18;
|
|
3268
|
+
|
|
3269
|
+
import CompactStandardLibrary;
|
|
3311
3270
|
|
|
3312
|
-
|
|
3313
|
-
|
|
3314
|
-
|
|
3315
|
-
|
|
3316
|
-
|
|
3317
|
-
claimedRewards: Counter;
|
|
3318
|
-
}
|
|
3271
|
+
// Set of used nullifiers - prevents replay attacks
|
|
3272
|
+
export ledger usedNullifiers: Set<Bytes<32>>;
|
|
3273
|
+
|
|
3274
|
+
// Track claimed rewards
|
|
3275
|
+
export ledger claimedRewards: Counter;
|
|
3319
3276
|
|
|
3320
3277
|
// Hash function for creating nullifiers
|
|
3321
3278
|
// Combines secret + public data to create unique identifier
|
|
3322
|
-
witness computeNullifier(secret: Field, commitment: Field): Bytes<32
|
|
3323
|
-
// Hash the secret with the commitment
|
|
3324
|
-
// The nullifier reveals nothing about the secret
|
|
3325
|
-
// but is unique per secret+commitment pair
|
|
3326
|
-
return hash(secret, commitment);
|
|
3327
|
-
}
|
|
3279
|
+
witness computeNullifier(secret: Field, commitment: Field): Bytes<32>;
|
|
3328
3280
|
|
|
3329
3281
|
// Alternative: nullifier from address and action ID
|
|
3330
|
-
witness computeActionNullifier(
|
|
3331
|
-
userSecret: Field,
|
|
3332
|
-
actionId: Field
|
|
3333
|
-
): Bytes<32> {
|
|
3334
|
-
// Create nullifier: hash(secret || actionId)
|
|
3335
|
-
return hash(userSecret, actionId);
|
|
3336
|
-
}
|
|
3282
|
+
witness computeActionNullifier(userSecret: Field, actionId: Field): Bytes<32>;
|
|
3337
3283
|
|
|
3338
3284
|
// Claim a reward (can only claim once per user)
|
|
3285
|
+
// Note: rewardAmount is Uint<16> to match Counter.increment signature
|
|
3339
3286
|
export circuit claimReward(
|
|
3340
3287
|
secret: Field,
|
|
3341
3288
|
commitment: Field,
|
|
3342
|
-
rewardAmount:
|
|
3289
|
+
rewardAmount: Uint<16>
|
|
3343
3290
|
): Boolean {
|
|
3344
3291
|
// Compute the nullifier
|
|
3345
3292
|
const nullifier = computeNullifier(secret, commitment);
|
|
3346
3293
|
|
|
3347
3294
|
// Check nullifier hasn't been used (prevents double-claim)
|
|
3348
3295
|
assert(
|
|
3349
|
-
!
|
|
3296
|
+
!usedNullifiers.member(nullifier),
|
|
3350
3297
|
"Reward already claimed"
|
|
3351
3298
|
);
|
|
3352
3299
|
|
|
3353
3300
|
// Mark nullifier as used
|
|
3354
|
-
|
|
3301
|
+
usedNullifiers.insert(nullifier);
|
|
3355
3302
|
|
|
3356
|
-
// Process reward
|
|
3357
|
-
|
|
3303
|
+
// Process reward (Counter.increment takes Uint<16>)
|
|
3304
|
+
claimedRewards.increment(rewardAmount);
|
|
3358
3305
|
|
|
3359
3306
|
return true;
|
|
3360
3307
|
}
|
|
@@ -3370,12 +3317,12 @@ export circuit voteWithNullifier(
|
|
|
3370
3317
|
|
|
3371
3318
|
// Ensure hasn't voted on this proposal
|
|
3372
3319
|
assert(
|
|
3373
|
-
!
|
|
3320
|
+
!usedNullifiers.member(nullifier),
|
|
3374
3321
|
"Already voted on this proposal"
|
|
3375
3322
|
);
|
|
3376
3323
|
|
|
3377
3324
|
// Record nullifier
|
|
3378
|
-
|
|
3325
|
+
usedNullifiers.insert(nullifier);
|
|
3379
3326
|
|
|
3380
3327
|
// Process vote...
|
|
3381
3328
|
return true;
|
|
@@ -3384,42 +3331,18 @@ export circuit voteWithNullifier(
|
|
|
3384
3331
|
"midnight://code/examples/hash": `// Hash Functions in Compact
|
|
3385
3332
|
// Examples of using hash functions for various purposes
|
|
3386
3333
|
|
|
3387
|
-
|
|
3334
|
+
pragma language_version >= 0.16 && <= 0.18;
|
|
3388
3335
|
|
|
3389
|
-
|
|
3390
|
-
commitments: Set<Bytes<32>>;
|
|
3391
|
-
hashedData: Map<Field, Bytes<32>>;
|
|
3392
|
-
}
|
|
3336
|
+
import CompactStandardLibrary;
|
|
3393
3337
|
|
|
3394
|
-
|
|
3395
|
-
|
|
3396
|
-
// Hash a single field element
|
|
3397
|
-
return hash(data);
|
|
3398
|
-
}
|
|
3338
|
+
export ledger commitments: Set<Bytes<32>>;
|
|
3339
|
+
export ledger hashedData: Map<Field, Bytes<32>>;
|
|
3399
3340
|
|
|
3400
|
-
//
|
|
3401
|
-
witness
|
|
3402
|
-
// Concatenate and hash
|
|
3403
|
-
return hash(a, b, c);
|
|
3404
|
-
}
|
|
3341
|
+
// Basic hash function usage
|
|
3342
|
+
witness simpleHash(data: Field): Bytes<32>;
|
|
3405
3343
|
|
|
3406
3344
|
// Create a commitment (hash of value + randomness)
|
|
3407
|
-
witness createCommitment(value: Field, randomness: Field): Bytes<32
|
|
3408
|
-
// Pedersen-style commitment: H(value || randomness)
|
|
3409
|
-
return hash(value, randomness);
|
|
3410
|
-
}
|
|
3411
|
-
|
|
3412
|
-
// Hash bytes data
|
|
3413
|
-
witness hashBytes(data: Bytes<64>): Bytes<32> {
|
|
3414
|
-
return hash(data);
|
|
3415
|
-
}
|
|
3416
|
-
|
|
3417
|
-
// Create nullifier from secret
|
|
3418
|
-
witness createNullifier(secret: Field, publicInput: Field): Bytes<32> {
|
|
3419
|
-
// Nullifier = H(secret || publicInput)
|
|
3420
|
-
// Reveals nothing about secret, but is deterministic
|
|
3421
|
-
return hash(secret, publicInput);
|
|
3422
|
-
}
|
|
3345
|
+
witness createCommitment(value: Field, randomness: Field): Bytes<32>;
|
|
3423
3346
|
|
|
3424
3347
|
// Verify a commitment matches
|
|
3425
3348
|
export circuit verifyCommitment(
|
|
@@ -3435,108 +3358,105 @@ export circuit verifyCommitment(
|
|
|
3435
3358
|
// Store a hashed value
|
|
3436
3359
|
export circuit storeHashed(id: Field, data: Field): Bytes<32> {
|
|
3437
3360
|
const hashed = simpleHash(data);
|
|
3438
|
-
|
|
3361
|
+
hashedData.insert(id, hashed);
|
|
3439
3362
|
return hashed;
|
|
3440
3363
|
}
|
|
3441
3364
|
|
|
3442
3365
|
// Commit-reveal pattern
|
|
3443
3366
|
export circuit commit(commitment: Bytes<32>): Boolean {
|
|
3444
|
-
assert(!
|
|
3445
|
-
|
|
3367
|
+
assert(!commitments.member(commitment), "Already committed");
|
|
3368
|
+
commitments.insert(commitment);
|
|
3446
3369
|
return true;
|
|
3447
3370
|
}
|
|
3448
3371
|
|
|
3449
3372
|
export circuit reveal(value: Field, randomness: Field): Field {
|
|
3450
3373
|
const commitment = createCommitment(value, randomness);
|
|
3451
|
-
assert(
|
|
3374
|
+
assert(commitments.member(commitment), "No matching commitment");
|
|
3452
3375
|
return disclose(value);
|
|
3453
3376
|
}
|
|
3454
3377
|
`,
|
|
3455
3378
|
"midnight://code/examples/simple-counter": `// Simple Counter Contract
|
|
3456
3379
|
// Minimal example for learning Compact basics
|
|
3457
3380
|
|
|
3458
|
-
|
|
3381
|
+
pragma language_version >= 0.16 && <= 0.18;
|
|
3382
|
+
|
|
3383
|
+
import CompactStandardLibrary;
|
|
3459
3384
|
|
|
3460
3385
|
// Ledger state - stored on chain
|
|
3461
|
-
ledger
|
|
3462
|
-
counter: Counter;
|
|
3463
|
-
}
|
|
3386
|
+
export ledger counter: Counter;
|
|
3464
3387
|
|
|
3465
3388
|
// Increment the counter by 1
|
|
3466
|
-
export circuit increment():
|
|
3467
|
-
|
|
3468
|
-
return
|
|
3389
|
+
export circuit increment(): Uint<64> {
|
|
3390
|
+
counter.increment(1);
|
|
3391
|
+
return counter.read();
|
|
3469
3392
|
}
|
|
3470
3393
|
|
|
3471
3394
|
// Decrement the counter by 1
|
|
3472
|
-
export circuit decrement():
|
|
3473
|
-
assert(
|
|
3474
|
-
|
|
3475
|
-
return
|
|
3395
|
+
export circuit decrement(): Uint<64> {
|
|
3396
|
+
assert(counter.read() > 0, "Cannot go below zero");
|
|
3397
|
+
counter.decrement(1);
|
|
3398
|
+
return counter.read();
|
|
3476
3399
|
}
|
|
3477
3400
|
|
|
3478
3401
|
// Get current value
|
|
3479
|
-
export circuit get():
|
|
3480
|
-
return
|
|
3402
|
+
export circuit get(): Uint<64> {
|
|
3403
|
+
return counter.read();
|
|
3404
|
+
}
|
|
3405
|
+
|
|
3406
|
+
// Check if below threshold
|
|
3407
|
+
export circuit isBelowLimit(limit: Uint<64>): Boolean {
|
|
3408
|
+
return counter.lessThan(limit);
|
|
3481
3409
|
}
|
|
3482
3410
|
|
|
3483
|
-
// Reset to zero
|
|
3484
|
-
export circuit reset():
|
|
3485
|
-
|
|
3486
|
-
ledger.counter.decrement(current);
|
|
3411
|
+
// Reset to zero
|
|
3412
|
+
export circuit reset(): [] {
|
|
3413
|
+
counter.resetToDefault();
|
|
3487
3414
|
}
|
|
3488
3415
|
`,
|
|
3489
3416
|
"midnight://code/templates/basic": `// Basic Compact Contract Template
|
|
3490
3417
|
// Starting point for new contracts
|
|
3491
3418
|
|
|
3492
|
-
|
|
3419
|
+
pragma language_version >= 0.16 && <= 0.18;
|
|
3420
|
+
|
|
3421
|
+
import CompactStandardLibrary;
|
|
3493
3422
|
|
|
3494
3423
|
// ============================================
|
|
3495
3424
|
// LEDGER STATE
|
|
3496
3425
|
// ============================================
|
|
3497
3426
|
|
|
3498
|
-
|
|
3499
|
-
|
|
3500
|
-
|
|
3501
|
-
|
|
3502
|
-
|
|
3503
|
-
|
|
3504
|
-
|
|
3505
|
-
|
|
3506
|
-
|
|
3427
|
+
// Public state (visible on-chain)
|
|
3428
|
+
export ledger initialized: Boolean;
|
|
3429
|
+
export ledger owner: Opaque<"address">;
|
|
3430
|
+
|
|
3431
|
+
// Private state (only owner can see)
|
|
3432
|
+
ledger secretData: Field;
|
|
3433
|
+
|
|
3434
|
+
// ============================================
|
|
3435
|
+
// WITNESSES
|
|
3436
|
+
// ============================================
|
|
3437
|
+
|
|
3438
|
+
witness getCaller(): Opaque<"address">;
|
|
3439
|
+
witness getSecret(): Field;
|
|
3507
3440
|
|
|
3508
3441
|
// ============================================
|
|
3509
3442
|
// INITIALIZATION
|
|
3510
3443
|
// ============================================
|
|
3511
3444
|
|
|
3512
3445
|
export circuit initialize(ownerAddress: Opaque<"address">): Boolean {
|
|
3513
|
-
assert(!
|
|
3446
|
+
assert(!initialized, "Already initialized");
|
|
3514
3447
|
|
|
3515
|
-
|
|
3516
|
-
|
|
3448
|
+
owner = ownerAddress;
|
|
3449
|
+
initialized = true;
|
|
3517
3450
|
|
|
3518
3451
|
return true;
|
|
3519
3452
|
}
|
|
3520
3453
|
|
|
3521
|
-
// ============================================
|
|
3522
|
-
// ACCESS CONTROL
|
|
3523
|
-
// ============================================
|
|
3524
|
-
|
|
3525
|
-
witness getCaller(): Opaque<"address"> {
|
|
3526
|
-
// Returns the transaction sender
|
|
3527
|
-
return context.caller;
|
|
3528
|
-
}
|
|
3529
|
-
|
|
3530
|
-
witness isOwner(): Boolean {
|
|
3531
|
-
return getCaller() == ledger.owner;
|
|
3532
|
-
}
|
|
3533
|
-
|
|
3534
3454
|
// ============================================
|
|
3535
3455
|
// PUBLIC FUNCTIONS
|
|
3536
3456
|
// ============================================
|
|
3537
3457
|
|
|
3538
3458
|
export circuit publicFunction(input: Field): Field {
|
|
3539
|
-
assert(
|
|
3459
|
+
assert(initialized, "Not initialized");
|
|
3540
3460
|
|
|
3541
3461
|
// Your logic here
|
|
3542
3462
|
return input * 2;
|
|
@@ -3546,22 +3466,19 @@ export circuit publicFunction(input: Field): Field {
|
|
|
3546
3466
|
// OWNER-ONLY FUNCTIONS
|
|
3547
3467
|
// ============================================
|
|
3548
3468
|
|
|
3549
|
-
export circuit setSecret(newSecret: Field):
|
|
3550
|
-
|
|
3551
|
-
|
|
3469
|
+
export circuit setSecret(newSecret: Field): [] {
|
|
3470
|
+
const caller = getCaller();
|
|
3471
|
+
assert(caller == owner, "Only owner can set secret");
|
|
3472
|
+
secretData = newSecret;
|
|
3552
3473
|
}
|
|
3553
3474
|
|
|
3554
3475
|
// ============================================
|
|
3555
3476
|
// PRIVATE DATA ACCESS
|
|
3556
3477
|
// ============================================
|
|
3557
3478
|
|
|
3558
|
-
witness getSecret(): Field {
|
|
3559
|
-
assert(isOwner(), "Only owner can view");
|
|
3560
|
-
return ledger.secretData;
|
|
3561
|
-
}
|
|
3562
|
-
|
|
3563
3479
|
export circuit revealSecret(): Field {
|
|
3564
|
-
|
|
3480
|
+
const caller = getCaller();
|
|
3481
|
+
assert(caller == owner, "Only owner can reveal");
|
|
3565
3482
|
return disclose(getSecret());
|
|
3566
3483
|
}
|
|
3567
3484
|
`
|
|
@@ -4671,10 +4588,11 @@ Key Compact concepts:
|
|
|
4671
4588
|
- \`Field\`, \`Boolean\`, \`Uint<N>\`, \`Bytes<N>\` - Primitive types
|
|
4672
4589
|
|
|
4673
4590
|
Always include:
|
|
4674
|
-
1. Proper imports (
|
|
4675
|
-
2.
|
|
4676
|
-
3.
|
|
4677
|
-
4.
|
|
4591
|
+
1. Proper imports (import CompactStandardLibrary;)
|
|
4592
|
+
2. pragma language_version >= 0.18.0;
|
|
4593
|
+
3. Clear ledger state definitions
|
|
4594
|
+
4. Access control where appropriate
|
|
4595
|
+
5. Comprehensive inline comments
|
|
4678
4596
|
|
|
4679
4597
|
Return ONLY the Compact code, no explanations.`;
|
|
4680
4598
|
const userPrompt = options.baseExample ? `Based on this example contract:
|
|
@@ -4859,6 +4777,406 @@ ${code}
|
|
|
4859
4777
|
}
|
|
4860
4778
|
}
|
|
4861
4779
|
|
|
4780
|
+
// src/services/syntax-validator.ts
|
|
4781
|
+
var ADT_SEARCH_LIMIT = 10;
|
|
4782
|
+
var SYNTAX_RESULTS_LIMIT = 10;
|
|
4783
|
+
var SYNTAX_SUBQUERY_LIMIT = 5;
|
|
4784
|
+
var MAX_NOTE_SOURCE_LENGTH = 500;
|
|
4785
|
+
var NOTE_MIN_LENGTH = 20;
|
|
4786
|
+
var NOTE_MAX_LENGTH = 300;
|
|
4787
|
+
var MAX_NOTES_PER_ADT = 3;
|
|
4788
|
+
async function searchADTInfo(adtName) {
|
|
4789
|
+
try {
|
|
4790
|
+
const docsResult = await searchDocsHosted(
|
|
4791
|
+
`${adtName} ADT operations methods ledger`,
|
|
4792
|
+
ADT_SEARCH_LIMIT,
|
|
4793
|
+
"reference"
|
|
4794
|
+
);
|
|
4795
|
+
if (!docsResult.results || docsResult.results.length === 0) {
|
|
4796
|
+
logger.debug(`No indexed docs found for ADT: ${adtName}`);
|
|
4797
|
+
return null;
|
|
4798
|
+
}
|
|
4799
|
+
const operations = [];
|
|
4800
|
+
const notes = [];
|
|
4801
|
+
let sourceDocPath;
|
|
4802
|
+
for (const result of docsResult.results) {
|
|
4803
|
+
const content = result.content || result.code || "";
|
|
4804
|
+
const isLedgerADTDoc = result.source.filePath.includes("ledger-adt");
|
|
4805
|
+
if (!sourceDocPath && isLedgerADTDoc) {
|
|
4806
|
+
sourceDocPath = result.source.filePath;
|
|
4807
|
+
}
|
|
4808
|
+
if (isLedgerADTDoc) {
|
|
4809
|
+
const methodPattern = /\|\s*`?(\w+)`?\s*\|\s*`?\(([^)]*)\)\s*(?::\s*([^|`]+))?`?\s*\|\s*([^|]+)\|/g;
|
|
4810
|
+
let match;
|
|
4811
|
+
while ((match = methodPattern.exec(content)) !== null) {
|
|
4812
|
+
const [, methodName, params, returnType, description] = match;
|
|
4813
|
+
if (methodName && !operations.some((op) => op.method === methodName)) {
|
|
4814
|
+
const descText = description?.trim() || "";
|
|
4815
|
+
const worksInCircuits = !/not available|not supported|not in circuits|typescript|sdk|off[-\s]?chain/i.test(
|
|
4816
|
+
descText
|
|
4817
|
+
);
|
|
4818
|
+
operations.push({
|
|
4819
|
+
method: methodName,
|
|
4820
|
+
signature: `(${params})${returnType ? `: ${returnType.trim()}` : ""}`,
|
|
4821
|
+
description: descText,
|
|
4822
|
+
worksInCircuits,
|
|
4823
|
+
source: "indexed-docs"
|
|
4824
|
+
});
|
|
4825
|
+
}
|
|
4826
|
+
}
|
|
4827
|
+
const inlinePattern = /(?:method|operation|function)\s+`?(\w+)`?\s*\(([^)]*)\)/gi;
|
|
4828
|
+
while ((match = inlinePattern.exec(content)) !== null) {
|
|
4829
|
+
const [, methodName, params] = match;
|
|
4830
|
+
if (methodName && !operations.some((op) => op.method === methodName)) {
|
|
4831
|
+
const worksInCircuits = !/not available|not supported|not in circuits|typescript|sdk|off[-\s]?chain/i.test(
|
|
4832
|
+
content
|
|
4833
|
+
);
|
|
4834
|
+
operations.push({
|
|
4835
|
+
method: methodName,
|
|
4836
|
+
signature: `(${params})`,
|
|
4837
|
+
description: "Extracted from documentation",
|
|
4838
|
+
worksInCircuits,
|
|
4839
|
+
source: "indexed-docs"
|
|
4840
|
+
});
|
|
4841
|
+
}
|
|
4842
|
+
}
|
|
4843
|
+
}
|
|
4844
|
+
if (content.toLowerCase().includes(adtName.toLowerCase()) && content.length < MAX_NOTE_SOURCE_LENGTH) {
|
|
4845
|
+
const cleanNote = content.replace(/\s+/g, " ").trim();
|
|
4846
|
+
if (cleanNote.length > NOTE_MIN_LENGTH && cleanNote.length < NOTE_MAX_LENGTH) {
|
|
4847
|
+
notes.push(cleanNote);
|
|
4848
|
+
}
|
|
4849
|
+
}
|
|
4850
|
+
}
|
|
4851
|
+
if (operations.length === 0) {
|
|
4852
|
+
logger.debug(`Could not parse operations for ADT: ${adtName}`);
|
|
4853
|
+
return null;
|
|
4854
|
+
}
|
|
4855
|
+
return {
|
|
4856
|
+
name: adtName,
|
|
4857
|
+
operations,
|
|
4858
|
+
notes: notes.slice(0, MAX_NOTES_PER_ADT),
|
|
4859
|
+
sourceDocPath,
|
|
4860
|
+
lastVerified: (/* @__PURE__ */ new Date()).toISOString()
|
|
4861
|
+
};
|
|
4862
|
+
} catch (error) {
|
|
4863
|
+
logger.warn(`Failed to search indexed docs for ADT ${adtName}`, {
|
|
4864
|
+
error: error instanceof Error ? error.message : String(error)
|
|
4865
|
+
});
|
|
4866
|
+
return null;
|
|
4867
|
+
}
|
|
4868
|
+
}
|
|
4869
|
+
async function searchCompactSyntax(topic) {
|
|
4870
|
+
try {
|
|
4871
|
+
const [docsResult, codeResult] = await Promise.all([
|
|
4872
|
+
searchDocsHosted(
|
|
4873
|
+
`Compact ${topic} syntax`,
|
|
4874
|
+
SYNTAX_SUBQUERY_LIMIT,
|
|
4875
|
+
"reference"
|
|
4876
|
+
),
|
|
4877
|
+
searchCompactHosted(`${topic}`, SYNTAX_SUBQUERY_LIMIT)
|
|
4878
|
+
]);
|
|
4879
|
+
const mergedResults = [
|
|
4880
|
+
...docsResult.results || [],
|
|
4881
|
+
...codeResult.results || []
|
|
4882
|
+
];
|
|
4883
|
+
return {
|
|
4884
|
+
results: mergedResults.slice(0, SYNTAX_RESULTS_LIMIT),
|
|
4885
|
+
totalResults: mergedResults.length,
|
|
4886
|
+
query: topic,
|
|
4887
|
+
lastIndexed: docsResult.lastIndexed || codeResult.lastIndexed
|
|
4888
|
+
};
|
|
4889
|
+
} catch (error) {
|
|
4890
|
+
logger.warn(`Failed to search Compact syntax for topic ${topic}`, {
|
|
4891
|
+
error: error instanceof Error ? error.message : String(error)
|
|
4892
|
+
});
|
|
4893
|
+
return null;
|
|
4894
|
+
}
|
|
4895
|
+
}
|
|
4896
|
+
async function validateADTOperations(adtName, staticOperations) {
|
|
4897
|
+
const discrepancies = [];
|
|
4898
|
+
const enrichments = [];
|
|
4899
|
+
const indexedInfo = await searchADTInfo(adtName);
|
|
4900
|
+
if (!indexedInfo) {
|
|
4901
|
+
return {
|
|
4902
|
+
validated: false,
|
|
4903
|
+
operations: staticOperations.map((op) => ({
|
|
4904
|
+
method: op.method,
|
|
4905
|
+
description: op.note,
|
|
4906
|
+
worksInCircuits: op.works,
|
|
4907
|
+
source: "static-fallback"
|
|
4908
|
+
})),
|
|
4909
|
+
discrepancies: [
|
|
4910
|
+
"Could not validate against indexed docs - using static data"
|
|
4911
|
+
],
|
|
4912
|
+
enrichments: []
|
|
4913
|
+
};
|
|
4914
|
+
}
|
|
4915
|
+
const operations = [];
|
|
4916
|
+
const indexedMethodNames = new Set(
|
|
4917
|
+
indexedInfo.operations.map((op) => op.method.toLowerCase())
|
|
4918
|
+
);
|
|
4919
|
+
const staticMethodNames = new Set(
|
|
4920
|
+
staticOperations.map(
|
|
4921
|
+
(op) => op.method.replace(/^\./, "").replace(/\(.*$/, "").toLowerCase()
|
|
4922
|
+
)
|
|
4923
|
+
);
|
|
4924
|
+
for (const indexedOp of indexedInfo.operations) {
|
|
4925
|
+
operations.push(indexedOp);
|
|
4926
|
+
const staticOp = staticOperations.find(
|
|
4927
|
+
(s) => s.method.toLowerCase().includes(indexedOp.method.toLowerCase()) || indexedOp.method.toLowerCase().includes(
|
|
4928
|
+
s.method.replace(/^\./, "").replace(/\(.*$/, "").toLowerCase()
|
|
4929
|
+
)
|
|
4930
|
+
);
|
|
4931
|
+
if (staticOp && !staticOp.works && indexedOp.worksInCircuits) {
|
|
4932
|
+
discrepancies.push(
|
|
4933
|
+
`Static said ${staticOp.method} doesn't work, but indexed docs show it does`
|
|
4934
|
+
);
|
|
4935
|
+
}
|
|
4936
|
+
}
|
|
4937
|
+
for (const staticOp of staticOperations) {
|
|
4938
|
+
const methodName = staticOp.method.replace(/^\./, "").replace(/\(.*$/, "").toLowerCase();
|
|
4939
|
+
if (!indexedMethodNames.has(methodName)) {
|
|
4940
|
+
discrepancies.push(
|
|
4941
|
+
`Static references ${staticOp.method} but not found in indexed docs - may not exist`
|
|
4942
|
+
);
|
|
4943
|
+
}
|
|
4944
|
+
}
|
|
4945
|
+
for (const indexedOp of indexedInfo.operations) {
|
|
4946
|
+
const methodName = indexedOp.method.toLowerCase();
|
|
4947
|
+
if (!staticMethodNames.has(methodName)) {
|
|
4948
|
+
enrichments.push(
|
|
4949
|
+
`Indexed docs show ${indexedOp.method} exists but was missing from static reference`
|
|
4950
|
+
);
|
|
4951
|
+
}
|
|
4952
|
+
}
|
|
4953
|
+
return {
|
|
4954
|
+
validated: true,
|
|
4955
|
+
operations,
|
|
4956
|
+
discrepancies,
|
|
4957
|
+
enrichments
|
|
4958
|
+
};
|
|
4959
|
+
}
|
|
4960
|
+
async function validateBuiltinFunctions(staticBuiltins) {
|
|
4961
|
+
const discrepancies = [];
|
|
4962
|
+
const enrichments = [];
|
|
4963
|
+
const results = await Promise.all(
|
|
4964
|
+
staticBuiltins.map(async (builtin) => {
|
|
4965
|
+
const searchResult = await searchCompactSyntax(
|
|
4966
|
+
`${builtin.name} function builtin`
|
|
4967
|
+
);
|
|
4968
|
+
return { builtin, found: (searchResult?.results?.length || 0) > 0 };
|
|
4969
|
+
})
|
|
4970
|
+
);
|
|
4971
|
+
for (const { builtin, found } of results) {
|
|
4972
|
+
if (!found) {
|
|
4973
|
+
discrepancies.push(
|
|
4974
|
+
`Builtin "${builtin.name}" not found in indexed docs - may not exist or have different name`
|
|
4975
|
+
);
|
|
4976
|
+
}
|
|
4977
|
+
}
|
|
4978
|
+
const allBuiltinsSearch = await searchCompactSyntax(
|
|
4979
|
+
"builtin function stdlib standard library"
|
|
4980
|
+
);
|
|
4981
|
+
if (allBuiltinsSearch?.results) {
|
|
4982
|
+
const knownNames = new Set(staticBuiltins.map((b) => b.name.toLowerCase()));
|
|
4983
|
+
for (const result of allBuiltinsSearch.results) {
|
|
4984
|
+
const content = result.content || result.code || "";
|
|
4985
|
+
const funcPattern = /\b(export\s+)?function\s+(\w+)/gi;
|
|
4986
|
+
let match;
|
|
4987
|
+
while ((match = funcPattern.exec(content)) !== null) {
|
|
4988
|
+
const funcName = match[2].toLowerCase();
|
|
4989
|
+
if (!knownNames.has(funcName) && funcName.length > 2) {
|
|
4990
|
+
enrichments.push(`Indexed docs mention "${match[2]}" function`);
|
|
4991
|
+
}
|
|
4992
|
+
}
|
|
4993
|
+
}
|
|
4994
|
+
}
|
|
4995
|
+
return {
|
|
4996
|
+
dataType: "BUILTIN_FUNCTIONS",
|
|
4997
|
+
validated: discrepancies.length === 0,
|
|
4998
|
+
discrepancies,
|
|
4999
|
+
enrichments: [...new Set(enrichments)].slice(0, 10),
|
|
5000
|
+
deprecatedPatterns: [],
|
|
5001
|
+
lastValidated: (/* @__PURE__ */ new Date()).toISOString()
|
|
5002
|
+
};
|
|
5003
|
+
}
|
|
5004
|
+
async function validateTypeCompatibility(staticRules) {
|
|
5005
|
+
const discrepancies = [];
|
|
5006
|
+
const enrichments = [];
|
|
5007
|
+
const typeSearch = await searchCompactSyntax(
|
|
5008
|
+
"type cast Field Uint Bytes conversion compatible"
|
|
5009
|
+
);
|
|
5010
|
+
if (typeSearch?.results) {
|
|
5011
|
+
for (const result of typeSearch.results) {
|
|
5012
|
+
const content = (result.content || result.code || "").toLowerCase();
|
|
5013
|
+
for (const rule of staticRules) {
|
|
5014
|
+
const typeMatch = rule.types.match(
|
|
5015
|
+
/^\s*(.+?)\s*(?:==|=|<=|>=|<|>|[+*])\s*(.+?)\s*$/
|
|
5016
|
+
);
|
|
5017
|
+
const typeA = typeMatch?.[1]?.trim().toLowerCase();
|
|
5018
|
+
const typeB = typeMatch?.[2]?.trim().toLowerCase();
|
|
5019
|
+
if (typeA && typeB) {
|
|
5020
|
+
if (content.includes(typeA) && content.includes(typeB) && content.includes("compatible")) {
|
|
5021
|
+
if (!rule.works) {
|
|
5022
|
+
discrepancies.push(
|
|
5023
|
+
`Static says "${rule.types}" doesn't work, but docs suggest compatibility`
|
|
5024
|
+
);
|
|
5025
|
+
}
|
|
5026
|
+
}
|
|
5027
|
+
}
|
|
5028
|
+
}
|
|
5029
|
+
}
|
|
5030
|
+
}
|
|
5031
|
+
return {
|
|
5032
|
+
dataType: "TYPE_COMPATIBILITY",
|
|
5033
|
+
validated: discrepancies.length === 0,
|
|
5034
|
+
discrepancies,
|
|
5035
|
+
enrichments,
|
|
5036
|
+
deprecatedPatterns: [],
|
|
5037
|
+
lastValidated: (/* @__PURE__ */ new Date()).toISOString()
|
|
5038
|
+
};
|
|
5039
|
+
}
|
|
5040
|
+
async function validateCommonErrors(staticErrors) {
|
|
5041
|
+
const discrepancies = [];
|
|
5042
|
+
const enrichments = [];
|
|
5043
|
+
const errorSearch = await searchDocsHosted(
|
|
5044
|
+
"error compile compilation failure parse",
|
|
5045
|
+
ADT_SEARCH_LIMIT,
|
|
5046
|
+
"all"
|
|
5047
|
+
);
|
|
5048
|
+
if (errorSearch?.results) {
|
|
5049
|
+
const knownErrors = new Set(
|
|
5050
|
+
staticErrors.map((e) => e.error.toLowerCase().slice(0, 30))
|
|
5051
|
+
);
|
|
5052
|
+
for (const result of errorSearch.results) {
|
|
5053
|
+
const content = result.content || result.code || "";
|
|
5054
|
+
const errorPattern = /(?:error|Error):\s*["']?([^"'\n]+)["']?/g;
|
|
5055
|
+
let match;
|
|
5056
|
+
while ((match = errorPattern.exec(content)) !== null) {
|
|
5057
|
+
const errorMsg = match[1].toLowerCase().slice(0, 30);
|
|
5058
|
+
if (!knownErrors.has(errorMsg) && errorMsg.length > 10) {
|
|
5059
|
+
enrichments.push(`Docs mention error: "${match[1].slice(0, 50)}..."`);
|
|
5060
|
+
}
|
|
5061
|
+
}
|
|
5062
|
+
}
|
|
5063
|
+
}
|
|
5064
|
+
return {
|
|
5065
|
+
dataType: "COMMON_ERRORS",
|
|
5066
|
+
validated: true,
|
|
5067
|
+
// Errors are informational, hard to validate
|
|
5068
|
+
discrepancies,
|
|
5069
|
+
enrichments: [...new Set(enrichments)].slice(0, 10),
|
|
5070
|
+
deprecatedPatterns: [],
|
|
5071
|
+
lastValidated: (/* @__PURE__ */ new Date()).toISOString()
|
|
5072
|
+
};
|
|
5073
|
+
}
|
|
5074
|
+
var DEPRECATED_SYNTAX_PATTERNS = [
|
|
5075
|
+
{
|
|
5076
|
+
pattern: /ledger\s*\{/,
|
|
5077
|
+
name: "ledger-block",
|
|
5078
|
+
message: "Deprecated: Use 'export ledger field: Type;' instead of ledger { } block",
|
|
5079
|
+
since: "0.16"
|
|
5080
|
+
},
|
|
5081
|
+
{
|
|
5082
|
+
pattern: /Cell\s*<\s*\w+\s*>/,
|
|
5083
|
+
name: "cell-wrapper",
|
|
5084
|
+
message: "Deprecated: Cell<T> wrapper removed, use Type directly",
|
|
5085
|
+
since: "0.15"
|
|
5086
|
+
},
|
|
5087
|
+
{
|
|
5088
|
+
pattern: /:\s*Void\b/,
|
|
5089
|
+
name: "void-type",
|
|
5090
|
+
message: "Void type doesn't exist, use [] (empty tuple) for no return",
|
|
5091
|
+
since: "always"
|
|
5092
|
+
},
|
|
5093
|
+
{
|
|
5094
|
+
pattern: /\.\s*value\s*\(\s*\)/,
|
|
5095
|
+
name: "counter-value",
|
|
5096
|
+
message: "Counter.value() doesn't exist, use Counter.read() instead",
|
|
5097
|
+
since: "always"
|
|
5098
|
+
},
|
|
5099
|
+
{
|
|
5100
|
+
pattern: /::\w+/,
|
|
5101
|
+
name: "rust-enum-syntax",
|
|
5102
|
+
message: "Rust-style :: enum access doesn't work, use dot notation (Choice.rock)",
|
|
5103
|
+
since: "always"
|
|
5104
|
+
},
|
|
5105
|
+
{
|
|
5106
|
+
pattern: /pure\s+function\b/,
|
|
5107
|
+
name: "pure-function",
|
|
5108
|
+
message: "Use 'pure circuit' not 'pure function' for helper functions",
|
|
5109
|
+
since: "0.16"
|
|
5110
|
+
},
|
|
5111
|
+
{
|
|
5112
|
+
pattern: /witness\s+\w+\s*\([^)]*\)\s*:\s*\w+\s*\{/,
|
|
5113
|
+
name: "witness-with-body",
|
|
5114
|
+
message: "Witnesses are declarations only - no body allowed. Implementation goes in TypeScript prover.",
|
|
5115
|
+
since: "always"
|
|
5116
|
+
}
|
|
5117
|
+
];
|
|
5118
|
+
function scanForDeprecatedPatterns(code) {
|
|
5119
|
+
const issues = [];
|
|
5120
|
+
const lines = code.split("\n");
|
|
5121
|
+
for (const { pattern, name, message } of DEPRECATED_SYNTAX_PATTERNS) {
|
|
5122
|
+
for (let i = 0; i < lines.length; i++) {
|
|
5123
|
+
if (pattern.test(lines[i])) {
|
|
5124
|
+
issues.push({
|
|
5125
|
+
pattern: name,
|
|
5126
|
+
message,
|
|
5127
|
+
lineNumber: i + 1
|
|
5128
|
+
});
|
|
5129
|
+
}
|
|
5130
|
+
}
|
|
5131
|
+
}
|
|
5132
|
+
return issues;
|
|
5133
|
+
}
|
|
5134
|
+
async function validateAllStaticData(staticData) {
|
|
5135
|
+
const results = {};
|
|
5136
|
+
const [builtinResult, typeResult, errorResult, adtResults] = await Promise.all([
|
|
5137
|
+
staticData.builtinFunctions ? validateBuiltinFunctions(staticData.builtinFunctions) : Promise.resolve(null),
|
|
5138
|
+
staticData.typeCompatibility ? validateTypeCompatibility(staticData.typeCompatibility) : Promise.resolve(null),
|
|
5139
|
+
staticData.commonErrors ? validateCommonErrors(staticData.commonErrors) : Promise.resolve(null),
|
|
5140
|
+
staticData.ledgerTypeLimits ? Promise.all(
|
|
5141
|
+
Object.entries(staticData.ledgerTypeLimits).map(
|
|
5142
|
+
async ([name, data]) => ({
|
|
5143
|
+
name,
|
|
5144
|
+
result: await validateADTOperations(
|
|
5145
|
+
name,
|
|
5146
|
+
data.circuitOperations
|
|
5147
|
+
)
|
|
5148
|
+
})
|
|
5149
|
+
)
|
|
5150
|
+
) : Promise.resolve([])
|
|
5151
|
+
]);
|
|
5152
|
+
if (builtinResult) results.builtinFunctions = builtinResult;
|
|
5153
|
+
if (typeResult) results.typeCompatibility = typeResult;
|
|
5154
|
+
if (errorResult) results.commonErrors = errorResult;
|
|
5155
|
+
for (const { name, result } of adtResults) {
|
|
5156
|
+
results[`adt_${name}`] = {
|
|
5157
|
+
dataType: `LEDGER_TYPE_LIMITS.${name}`,
|
|
5158
|
+
validated: result.validated,
|
|
5159
|
+
discrepancies: result.discrepancies,
|
|
5160
|
+
enrichments: result.enrichments,
|
|
5161
|
+
deprecatedPatterns: [],
|
|
5162
|
+
lastValidated: (/* @__PURE__ */ new Date()).toISOString()
|
|
5163
|
+
};
|
|
5164
|
+
}
|
|
5165
|
+
const allDiscrepancies = Object.values(results).flatMap(
|
|
5166
|
+
(r) => r.discrepancies
|
|
5167
|
+
);
|
|
5168
|
+
const allEnrichments = Object.values(results).flatMap((r) => r.enrichments);
|
|
5169
|
+
return {
|
|
5170
|
+
overall: {
|
|
5171
|
+
validated: allDiscrepancies.length === 0,
|
|
5172
|
+
totalDiscrepancies: allDiscrepancies.length,
|
|
5173
|
+
totalEnrichments: allEnrichments.length
|
|
5174
|
+
},
|
|
5175
|
+
results,
|
|
5176
|
+
lastValidated: (/* @__PURE__ */ new Date()).toISOString()
|
|
5177
|
+
};
|
|
5178
|
+
}
|
|
5179
|
+
|
|
4862
5180
|
// src/server.ts
|
|
4863
5181
|
var SERVER_INFO = {
|
|
4864
5182
|
name: "midnight-mcp",
|
|
@@ -5725,17 +6043,30 @@ var TYPE_COMPATIBILITY = {
|
|
|
5725
6043
|
var LEDGER_TYPE_LIMITS = {
|
|
5726
6044
|
Counter: {
|
|
5727
6045
|
circuitOperations: [
|
|
5728
|
-
{
|
|
6046
|
+
{
|
|
6047
|
+
method: ".increment(n)",
|
|
6048
|
+
works: true,
|
|
6049
|
+
note: "Increase counter by n (Uint<16>)"
|
|
6050
|
+
},
|
|
5729
6051
|
{
|
|
5730
6052
|
method: ".decrement(n)",
|
|
5731
6053
|
works: true,
|
|
5732
|
-
note: "
|
|
6054
|
+
note: "Decrease counter by n (Uint<16>)"
|
|
6055
|
+
},
|
|
6056
|
+
{
|
|
6057
|
+
method: ".read()",
|
|
6058
|
+
works: true,
|
|
6059
|
+
note: "Get current value (returns Uint<64>)"
|
|
5733
6060
|
},
|
|
5734
|
-
{
|
|
5735
|
-
|
|
6061
|
+
{
|
|
6062
|
+
method: ".lessThan(n)",
|
|
6063
|
+
works: true,
|
|
6064
|
+
note: "Compare with threshold (returns Boolean)"
|
|
6065
|
+
},
|
|
6066
|
+
{ method: ".resetToDefault()", works: true, note: "Reset to 0" }
|
|
5736
6067
|
],
|
|
5737
6068
|
typescriptAccess: "Access counter value via `ledgerState.counter` in TypeScript SDK",
|
|
5738
|
-
|
|
6069
|
+
note: "All Counter operations work in circuits. Use .read() to get value, NOT .value()"
|
|
5739
6070
|
},
|
|
5740
6071
|
Map: {
|
|
5741
6072
|
circuitOperations: [
|
|
@@ -5744,20 +6075,40 @@ var LEDGER_TYPE_LIMITS = {
|
|
|
5744
6075
|
works: true,
|
|
5745
6076
|
note: "Adds/updates entry"
|
|
5746
6077
|
},
|
|
6078
|
+
{
|
|
6079
|
+
method: ".insertDefault(key)",
|
|
6080
|
+
works: true,
|
|
6081
|
+
note: "Inserts default value for key"
|
|
6082
|
+
},
|
|
5747
6083
|
{ method: ".remove(key)", works: true, note: "Removes entry" },
|
|
5748
6084
|
{
|
|
5749
6085
|
method: ".lookup(key)",
|
|
5750
6086
|
works: true,
|
|
5751
|
-
note: "Returns
|
|
6087
|
+
note: "Returns value_type - gets value for key"
|
|
5752
6088
|
},
|
|
5753
6089
|
{
|
|
5754
6090
|
method: ".member(key)",
|
|
5755
6091
|
works: true,
|
|
5756
6092
|
note: "Returns Boolean - checks if key exists"
|
|
6093
|
+
},
|
|
6094
|
+
{
|
|
6095
|
+
method: ".isEmpty()",
|
|
6096
|
+
works: true,
|
|
6097
|
+
note: "Returns Boolean - checks if map is empty"
|
|
6098
|
+
},
|
|
6099
|
+
{
|
|
6100
|
+
method: ".size()",
|
|
6101
|
+
works: true,
|
|
6102
|
+
note: "Returns Uint<64> - number of entries"
|
|
6103
|
+
},
|
|
6104
|
+
{
|
|
6105
|
+
method: ".resetToDefault()",
|
|
6106
|
+
works: true,
|
|
6107
|
+
note: "Clears entire map"
|
|
5757
6108
|
}
|
|
5758
6109
|
],
|
|
5759
|
-
typescriptAccess: "Query map via `contractState.data.get(key)` in TypeScript SDK",
|
|
5760
|
-
note: "
|
|
6110
|
+
typescriptAccess: "Query map via `contractState.data.get(key)` or iterate with `[Symbol.iterator]()` in TypeScript SDK",
|
|
6111
|
+
note: "All Map operations work in circuits. insertCoin() available when value_type is QualifiedCoinInfo."
|
|
5761
6112
|
},
|
|
5762
6113
|
Set: {
|
|
5763
6114
|
circuitOperations: [
|
|
@@ -5767,10 +6118,25 @@ var LEDGER_TYPE_LIMITS = {
|
|
|
5767
6118
|
method: ".member(value)",
|
|
5768
6119
|
works: true,
|
|
5769
6120
|
note: "Returns Boolean - checks if value exists in set"
|
|
6121
|
+
},
|
|
6122
|
+
{
|
|
6123
|
+
method: ".isEmpty()",
|
|
6124
|
+
works: true,
|
|
6125
|
+
note: "Returns Boolean - checks if set is empty"
|
|
6126
|
+
},
|
|
6127
|
+
{
|
|
6128
|
+
method: ".size()",
|
|
6129
|
+
works: true,
|
|
6130
|
+
note: "Returns Uint<64> - number of elements"
|
|
6131
|
+
},
|
|
6132
|
+
{
|
|
6133
|
+
method: ".resetToDefault()",
|
|
6134
|
+
works: true,
|
|
6135
|
+
note: "Clears entire set"
|
|
5770
6136
|
}
|
|
5771
6137
|
],
|
|
5772
|
-
typescriptAccess: "Check membership via `contractState.set.has(value)` in TypeScript SDK",
|
|
5773
|
-
note: "Set.
|
|
6138
|
+
typescriptAccess: "Check membership via `contractState.set.has(value)` or iterate with `[Symbol.iterator]()` in TypeScript SDK",
|
|
6139
|
+
note: "All Set operations work in circuits. insertCoin() available when value_type is QualifiedCoinInfo."
|
|
5774
6140
|
},
|
|
5775
6141
|
MerkleTree: {
|
|
5776
6142
|
circuitOperations: [
|
|
@@ -5800,11 +6166,16 @@ Or use bounded Uint<0..N> for parameters that need constraints`
|
|
|
5800
6166
|
},
|
|
5801
6167
|
{
|
|
5802
6168
|
error: 'operation "value" undefined for ledger field type Counter',
|
|
5803
|
-
cause: "
|
|
5804
|
-
fix: `
|
|
5805
|
-
|
|
5806
|
-
|
|
5807
|
-
|
|
6169
|
+
cause: "Using wrong method name - Counter uses .read() not .value()",
|
|
6170
|
+
fix: `Use counter.read() to get the current value:
|
|
6171
|
+
const current = ledger.counter.read();
|
|
6172
|
+
|
|
6173
|
+
Counter ADT methods available in circuits:
|
|
6174
|
+
- increment(amount: Uint<16>): [] - increase counter
|
|
6175
|
+
- decrement(amount: Uint<16>): [] - decrease counter
|
|
6176
|
+
- read(): Uint<64> - get current value
|
|
6177
|
+
- lessThan(threshold: Uint<64>): Boolean - compare
|
|
6178
|
+
- resetToDefault(): [] - reset to zero`
|
|
5808
6179
|
},
|
|
5809
6180
|
{
|
|
5810
6181
|
error: "implicit disclosure of witness value",
|
|
@@ -6336,7 +6707,7 @@ async function extractContractStructure(input) {
|
|
|
6336
6707
|
});
|
|
6337
6708
|
}
|
|
6338
6709
|
}
|
|
6339
|
-
const hasStdlibImport = imports.includes("CompactStandardLibrary") || code.includes(
|
|
6710
|
+
const hasStdlibImport = imports.includes("CompactStandardLibrary") || code.includes("import CompactStandardLibrary");
|
|
6340
6711
|
if (hasStdlibImport) {
|
|
6341
6712
|
for (const circuit of circuits) {
|
|
6342
6713
|
if (stdlibExports.includes(circuit.name)) {
|
|
@@ -6447,7 +6818,7 @@ async function extractContractStructure(input) {
|
|
|
6447
6818
|
});
|
|
6448
6819
|
break;
|
|
6449
6820
|
}
|
|
6450
|
-
const counterValuePattern = /(\w+)\.value\
|
|
6821
|
+
const counterValuePattern = /(\w+)\.value\s*\(/g;
|
|
6451
6822
|
let counterMatch;
|
|
6452
6823
|
while ((counterMatch = counterValuePattern.exec(code)) !== null) {
|
|
6453
6824
|
const varName = counterMatch[1];
|
|
@@ -6459,8 +6830,8 @@ async function extractContractStructure(input) {
|
|
|
6459
6830
|
potentialIssues.push({
|
|
6460
6831
|
type: "invalid_counter_access",
|
|
6461
6832
|
line: lineNum,
|
|
6462
|
-
message: `Counter type '${varName}' does not have
|
|
6463
|
-
suggestion: `Counter
|
|
6833
|
+
message: `Counter type '${varName}' does not have '.value()' - use '.read()' instead`,
|
|
6834
|
+
suggestion: `Counter ADT methods: increment(n), decrement(n), read(), lessThan(n), resetToDefault()`,
|
|
6464
6835
|
severity: "error"
|
|
6465
6836
|
});
|
|
6466
6837
|
}
|
|
@@ -7106,6 +7477,96 @@ async function getLatestSyntax(input) {
|
|
|
7106
7477
|
} catch {
|
|
7107
7478
|
}
|
|
7108
7479
|
if (compactReference) {
|
|
7480
|
+
logger.debug(
|
|
7481
|
+
"Starting comprehensive hybrid validation against indexed docs"
|
|
7482
|
+
);
|
|
7483
|
+
let comprehensiveValidation;
|
|
7484
|
+
try {
|
|
7485
|
+
comprehensiveValidation = await validateAllStaticData({
|
|
7486
|
+
builtinFunctions: BUILTIN_FUNCTIONS.stdlib,
|
|
7487
|
+
typeCompatibility: [
|
|
7488
|
+
...TYPE_COMPATIBILITY.comparisons,
|
|
7489
|
+
...TYPE_COMPATIBILITY.arithmetic
|
|
7490
|
+
],
|
|
7491
|
+
commonErrors: COMMON_ERRORS,
|
|
7492
|
+
ledgerTypeLimits: LEDGER_TYPE_LIMITS
|
|
7493
|
+
});
|
|
7494
|
+
logger.debug("Comprehensive validation complete", {
|
|
7495
|
+
validated: comprehensiveValidation.overall.validated,
|
|
7496
|
+
discrepancies: comprehensiveValidation.overall.totalDiscrepancies,
|
|
7497
|
+
enrichments: comprehensiveValidation.overall.totalEnrichments
|
|
7498
|
+
});
|
|
7499
|
+
} catch (err) {
|
|
7500
|
+
logger.warn(
|
|
7501
|
+
"Comprehensive validation failed, falling back to ADT-only validation",
|
|
7502
|
+
{
|
|
7503
|
+
error: err instanceof Error ? err.message : String(err)
|
|
7504
|
+
}
|
|
7505
|
+
);
|
|
7506
|
+
}
|
|
7507
|
+
const validationWarnings = [];
|
|
7508
|
+
const validationEnrichments = [];
|
|
7509
|
+
if (comprehensiveValidation) {
|
|
7510
|
+
for (const [key, result] of Object.entries(
|
|
7511
|
+
comprehensiveValidation.results
|
|
7512
|
+
)) {
|
|
7513
|
+
if (result.discrepancies.length > 0) {
|
|
7514
|
+
validationWarnings.push(
|
|
7515
|
+
`${key}: ${result.discrepancies.join("; ")}`
|
|
7516
|
+
);
|
|
7517
|
+
}
|
|
7518
|
+
if (result.enrichments.length > 0) {
|
|
7519
|
+
validationEnrichments.push(
|
|
7520
|
+
`${key}: ${result.enrichments.join("; ")}`
|
|
7521
|
+
);
|
|
7522
|
+
}
|
|
7523
|
+
}
|
|
7524
|
+
}
|
|
7525
|
+
let indexedSyntaxPatterns;
|
|
7526
|
+
try {
|
|
7527
|
+
const syntaxSearch = await searchCompactSyntax(
|
|
7528
|
+
"circuit witness ledger pragma"
|
|
7529
|
+
);
|
|
7530
|
+
if (syntaxSearch && syntaxSearch.results.length > 0) {
|
|
7531
|
+
indexedSyntaxPatterns = {
|
|
7532
|
+
resultCount: syntaxSearch.results.length,
|
|
7533
|
+
topSources: syntaxSearch.results.slice(0, 3).map((r) => ({
|
|
7534
|
+
path: r.source.filePath,
|
|
7535
|
+
repo: r.source.repository
|
|
7536
|
+
}))
|
|
7537
|
+
};
|
|
7538
|
+
}
|
|
7539
|
+
} catch {
|
|
7540
|
+
}
|
|
7541
|
+
const validatedLedgerTypeLimits = {};
|
|
7542
|
+
for (const [adtName, staticInfo] of Object.entries(LEDGER_TYPE_LIMITS)) {
|
|
7543
|
+
const typedStaticInfo = staticInfo;
|
|
7544
|
+
const adtValidation = comprehensiveValidation?.results[`adt_${adtName}`];
|
|
7545
|
+
if (adtValidation?.validated) {
|
|
7546
|
+
validatedLedgerTypeLimits[adtName] = {
|
|
7547
|
+
...typedStaticInfo,
|
|
7548
|
+
validatedAgainstDocs: true,
|
|
7549
|
+
validationDiscrepancies: adtValidation.discrepancies,
|
|
7550
|
+
validationEnrichments: adtValidation.enrichments
|
|
7551
|
+
};
|
|
7552
|
+
} else {
|
|
7553
|
+
validatedLedgerTypeLimits[adtName] = {
|
|
7554
|
+
...typedStaticInfo,
|
|
7555
|
+
validatedAgainstDocs: false,
|
|
7556
|
+
warning: "Could not validate against indexed docs - using static reference"
|
|
7557
|
+
};
|
|
7558
|
+
}
|
|
7559
|
+
}
|
|
7560
|
+
const quickStartTemplate = `${RECOMMENDED_PRAGMA}
|
|
7561
|
+
|
|
7562
|
+
import CompactStandardLibrary;
|
|
7563
|
+
|
|
7564
|
+
export ledger counter: Counter;
|
|
7565
|
+
|
|
7566
|
+
export circuit increment(): [] {
|
|
7567
|
+
counter.increment(1);
|
|
7568
|
+
}`;
|
|
7569
|
+
const deprecatedInTemplate = scanForDeprecatedPatterns(quickStartTemplate);
|
|
7109
7570
|
return {
|
|
7110
7571
|
repository: "midnightntwrk/compact",
|
|
7111
7572
|
version: `${COMPACT_VERSION.min}-${COMPACT_VERSION.max} (current)`,
|
|
@@ -7116,6 +7577,29 @@ async function getLatestSyntax(input) {
|
|
|
7116
7577
|
maintenanceGuide: "See docs/SYNTAX_MAINTENANCE.md for update instructions"
|
|
7117
7578
|
},
|
|
7118
7579
|
...versionWarning && { versionWarning },
|
|
7580
|
+
// Comprehensive hybrid validation status
|
|
7581
|
+
hybridValidation: {
|
|
7582
|
+
enabled: true,
|
|
7583
|
+
comprehensive: true,
|
|
7584
|
+
// Overall validation stats
|
|
7585
|
+
overall: comprehensiveValidation?.overall || {
|
|
7586
|
+
validated: false,
|
|
7587
|
+
totalDiscrepancies: 0,
|
|
7588
|
+
totalEnrichments: 0
|
|
7589
|
+
},
|
|
7590
|
+
// Which data types were validated
|
|
7591
|
+
validatedDataTypes: comprehensiveValidation ? Object.keys(comprehensiveValidation.results) : [],
|
|
7592
|
+
// All warnings from ALL static data validation
|
|
7593
|
+
warnings: validationWarnings.length > 0 ? validationWarnings : void 0,
|
|
7594
|
+
// All enrichments discovered from indexed docs
|
|
7595
|
+
enrichments: validationEnrichments.length > 0 ? validationEnrichments : void 0,
|
|
7596
|
+
// Indexed syntax pattern sources
|
|
7597
|
+
indexedSyntaxPatterns,
|
|
7598
|
+
// Deprecated patterns found in quick start template (should be none!)
|
|
7599
|
+
deprecatedPatternsInTemplate: deprecatedInTemplate.length > 0 ? deprecatedInTemplate : void 0,
|
|
7600
|
+
// Last validation timestamp
|
|
7601
|
+
lastValidated: comprehensiveValidation?.lastValidated || (/* @__PURE__ */ new Date()).toISOString()
|
|
7602
|
+
},
|
|
7119
7603
|
// Quick start template - ALWAYS compiles
|
|
7120
7604
|
quickStartTemplate: `${RECOMMENDED_PRAGMA}
|
|
7121
7605
|
|
|
@@ -7133,8 +7617,8 @@ export circuit increment(): [] {
|
|
|
7133
7617
|
builtinFunctions: BUILTIN_FUNCTIONS,
|
|
7134
7618
|
// Type compatibility rules
|
|
7135
7619
|
typeCompatibility: TYPE_COMPATIBILITY,
|
|
7136
|
-
// Ledger type limitations
|
|
7137
|
-
ledgerTypeLimits:
|
|
7620
|
+
// Ledger type limitations - VALIDATED against indexed docs
|
|
7621
|
+
ledgerTypeLimits: validatedLedgerTypeLimits,
|
|
7138
7622
|
// Common compilation errors with fixes
|
|
7139
7623
|
commonErrors: COMMON_ERRORS,
|
|
7140
7624
|
// Common mistakes that cause compilation failures
|
|
@@ -7176,7 +7660,7 @@ export circuit increment(): [] {
|
|
|
7176
7660
|
},
|
|
7177
7661
|
{
|
|
7178
7662
|
wrong: "counter.value()",
|
|
7179
|
-
correct: "//
|
|
7663
|
+
correct: "counter.read() // Returns Uint<64>",
|
|
7180
7664
|
error: 'operation "value" undefined for Counter'
|
|
7181
7665
|
},
|
|
7182
7666
|
{
|
|
@@ -7241,7 +7725,7 @@ Use quickStartTemplate as your base. Check commonMistakes BEFORE submitting code
|
|
|
7241
7725
|
|
|
7242
7726
|
KEY RULES:
|
|
7243
7727
|
1. public_key() is NOT a builtin - use persistentHash pattern
|
|
7244
|
-
2. Counter.value()
|
|
7728
|
+
2. Counter uses .read() not .value() - all ADT methods work in circuits
|
|
7245
7729
|
3. Map.lookup()/Set.member() ARE available in circuits (verified)
|
|
7246
7730
|
4. Arithmetic results need casting: (a + b) as Uint<64>
|
|
7247
7731
|
5. Uint\u2192Bytes needs two casts: (amount as Field) as Bytes<32>
|
|
@@ -9630,4 +10114,4 @@ export {
|
|
|
9630
10114
|
startServer,
|
|
9631
10115
|
startHttpServer
|
|
9632
10116
|
};
|
|
9633
|
-
//# sourceMappingURL=chunk-
|
|
10117
|
+
//# sourceMappingURL=chunk-7KJSVMFI.js.map
|