@crediolabs/policy-synth 0.1.4 → 0.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/dist/codegen/template.js +3 -3
- package/dist/record/decode.d.ts +6 -3
- package/dist/record/decode.js +13 -3
- package/dist/run/index.d.ts +31 -0
- package/dist/run/index.js +164 -0
- package/dist/run/schemas.d.ts +2003 -0
- package/dist/run/schemas.js +215 -0
- package/dist/synth/deny-cases.js +18 -2
- package/dist/synth/evaluate.js +2 -2
- package/dist/synth/synthesize-from-recording.d.ts +5 -2
- package/dist/synth/synthesize-from-recording.js +121 -63
- package/dist/types.d.ts +17 -0
- package/dist/types.js +17 -0
- package/dist/verify/simulate.js +18 -2
- package/dist/verify/verify.js +18 -2
- package/dist-cjs/adapters/interpreter/adapter.d.ts +38 -0
- package/dist-cjs/adapters/interpreter/adapter.js +527 -0
- package/dist-cjs/adapters/interpreter/index.d.ts +1 -0
- package/dist-cjs/adapters/interpreter/index.js +8 -0
- package/dist-cjs/adapters/oz/adapter.d.ts +20 -0
- package/dist-cjs/adapters/oz/adapter.js +289 -0
- package/dist-cjs/adapters/oz/index.d.ts +1 -0
- package/dist-cjs/adapters/oz/index.js +8 -0
- package/dist-cjs/codegen/compile-gate.d.ts +33 -0
- package/dist-cjs/codegen/compile-gate.js +123 -0
- package/dist-cjs/codegen/index.d.ts +2 -0
- package/dist-cjs/codegen/index.js +14 -0
- package/dist-cjs/codegen/template.d.ts +18 -0
- package/dist-cjs/codegen/template.js +134 -0
- package/dist-cjs/errors.d.ts +37 -0
- package/dist-cjs/errors.js +3 -0
- package/dist-cjs/index.d.ts +11 -0
- package/dist-cjs/index.js +27 -0
- package/dist-cjs/ir/index.d.ts +1 -0
- package/dist-cjs/ir/index.js +3 -0
- package/dist-cjs/ir/types.d.ts +108 -0
- package/dist-cjs/ir/types.js +12 -0
- package/dist-cjs/mandate/index.d.ts +2 -0
- package/dist-cjs/mandate/index.js +6 -0
- package/dist-cjs/mandate/to-ir.d.ts +3 -0
- package/dist-cjs/mandate/to-ir.js +63 -0
- package/dist-cjs/mandate/types.d.ts +20 -0
- package/dist-cjs/mandate/types.js +9 -0
- package/dist-cjs/package.json +3 -0
- package/dist-cjs/predicate/encode.d.ts +10 -0
- package/dist-cjs/predicate/encode.js +252 -0
- package/dist-cjs/predicate/index.d.ts +1 -0
- package/dist-cjs/predicate/index.js +6 -0
- package/dist-cjs/record/decode.d.ts +79 -0
- package/dist-cjs/record/decode.js +398 -0
- package/dist-cjs/record/freshness.d.ts +17 -0
- package/dist-cjs/record/freshness.js +55 -0
- package/dist-cjs/record/index.d.ts +21 -0
- package/dist-cjs/record/index.js +166 -0
- package/dist-cjs/record/movements.d.ts +20 -0
- package/dist-cjs/record/movements.js +192 -0
- package/dist-cjs/record/rpc.d.ts +22 -0
- package/dist-cjs/record/rpc.js +74 -0
- package/dist-cjs/record/validate.d.ts +22 -0
- package/dist-cjs/record/validate.js +63 -0
- package/dist-cjs/registry/identify.d.ts +11 -0
- package/dist-cjs/registry/identify.js +87 -0
- package/dist-cjs/registry/index.d.ts +3 -0
- package/dist-cjs/registry/index.js +15 -0
- package/dist-cjs/registry/known-addresses.d.ts +16 -0
- package/dist-cjs/registry/known-addresses.js +53 -0
- package/dist-cjs/registry/protocols.d.ts +38 -0
- package/dist-cjs/registry/protocols.js +153 -0
- package/dist-cjs/review-card/builder.d.ts +14 -0
- package/dist-cjs/review-card/builder.js +264 -0
- package/dist-cjs/review-card/conflict.d.ts +40 -0
- package/dist-cjs/review-card/conflict.js +114 -0
- package/dist-cjs/review-card/cross-check.d.ts +11 -0
- package/dist-cjs/review-card/cross-check.js +151 -0
- package/dist-cjs/review-card/index.d.ts +3 -0
- package/dist-cjs/review-card/index.js +10 -0
- package/dist-cjs/run/index.d.ts +31 -0
- package/dist-cjs/run/index.js +178 -0
- package/dist-cjs/run/schemas.d.ts +2003 -0
- package/dist-cjs/run/schemas.js +218 -0
- package/dist-cjs/seams/index.d.ts +1 -0
- package/dist-cjs/seams/index.js +3 -0
- package/dist-cjs/seams/types.d.ts +66 -0
- package/dist-cjs/seams/types.js +12 -0
- package/dist-cjs/synth/compose-from-recording.d.ts +64 -0
- package/dist-cjs/synth/compose-from-recording.js +356 -0
- package/dist-cjs/synth/deny-cases.d.ts +12 -0
- package/dist-cjs/synth/deny-cases.js +380 -0
- package/dist-cjs/synth/evaluate.d.ts +39 -0
- package/dist-cjs/synth/evaluate.js +428 -0
- package/dist-cjs/synth/harness.d.ts +16 -0
- package/dist-cjs/synth/harness.js +29 -0
- package/dist-cjs/synth/index.d.ts +9 -0
- package/dist-cjs/synth/index.js +23 -0
- package/dist-cjs/synth/lower.d.ts +23 -0
- package/dist-cjs/synth/lower.js +119 -0
- package/dist-cjs/synth/minimize.d.ts +4 -0
- package/dist-cjs/synth/minimize.js +41 -0
- package/dist-cjs/synth/predicate-literals.d.ts +5 -0
- package/dist-cjs/synth/predicate-literals.js +28 -0
- package/dist-cjs/synth/scope.d.ts +26 -0
- package/dist-cjs/synth/scope.js +81 -0
- package/dist-cjs/synth/synthesize-from-mandate.d.ts +5 -0
- package/dist-cjs/synth/synthesize-from-mandate.js +37 -0
- package/dist-cjs/synth/synthesize-from-recording.d.ts +49 -0
- package/dist-cjs/synth/synthesize-from-recording.js +711 -0
- package/dist-cjs/types.d.ts +288 -0
- package/dist-cjs/types.js +55 -0
- package/dist-cjs/verify/envelope.d.ts +15 -0
- package/dist-cjs/verify/envelope.js +23 -0
- package/dist-cjs/verify/index.d.ts +3 -0
- package/dist-cjs/verify/index.js +8 -0
- package/dist-cjs/verify/simulate.d.ts +31 -0
- package/dist-cjs/verify/simulate.js +261 -0
- package/dist-cjs/verify/verify.d.ts +21 -0
- package/dist-cjs/verify/verify.js +192 -0
- package/package.json +43 -3
- package/src/codegen/template.ts +3 -3
- package/src/record/decode.ts +21 -10
- package/src/run/index.ts +213 -0
- package/src/run/schemas.ts +242 -0
- package/src/synth/deny-cases.ts +21 -2
- package/src/synth/evaluate.ts +2 -2
- package/src/synth/synthesize-from-recording.ts +141 -62
- package/src/types.ts +19 -0
- package/src/verify/simulate.ts +21 -2
- package/src/verify/verify.ts +21 -2
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
export type Network = 'mainnet' | 'testnet';
|
|
2
|
+
/** Decoded Soroban value - the normalised subset synthesis needs. */
|
|
3
|
+
export type ScVal = {
|
|
4
|
+
type: 'address';
|
|
5
|
+
value: string;
|
|
6
|
+
} | {
|
|
7
|
+
type: 'i128';
|
|
8
|
+
value: string;
|
|
9
|
+
} | {
|
|
10
|
+
type: 'u64';
|
|
11
|
+
value: string;
|
|
12
|
+
} | {
|
|
13
|
+
type: 'u32';
|
|
14
|
+
value: string;
|
|
15
|
+
} | {
|
|
16
|
+
type: 'symbol';
|
|
17
|
+
value: string;
|
|
18
|
+
} | {
|
|
19
|
+
type: 'vec';
|
|
20
|
+
value: ScVal[];
|
|
21
|
+
} | {
|
|
22
|
+
type: 'bytes';
|
|
23
|
+
value: string;
|
|
24
|
+
} | {
|
|
25
|
+
type: 'other';
|
|
26
|
+
value: string;
|
|
27
|
+
};
|
|
28
|
+
/** OZ Accounts framework hard limits (verified against canonical OZ source). */
|
|
29
|
+
export declare const OZ_LIMITS: {
|
|
30
|
+
readonly maxPoliciesPerRule: 5;
|
|
31
|
+
readonly maxSignersPerRule: 15;
|
|
32
|
+
};
|
|
33
|
+
/** Soroban / Stellar protocol limits used to bound inputs fail-closed. Single
|
|
34
|
+
* source so the schema boundary and the core validators cannot drift. */
|
|
35
|
+
export declare const SOROBAN_LIMITS: {
|
|
36
|
+
/** `valid_until` and other ledger-sequence fields are u32. */
|
|
37
|
+
readonly u32Max: 4294967295;
|
|
38
|
+
/** Upper bound on top-level invocations in a recorded transaction; a real
|
|
39
|
+
* Stellar tx caps operations well below this. Bounds a hand-crafted payload. */
|
|
40
|
+
readonly maxInvocations: 512;
|
|
41
|
+
/** Stellar targets a ~5s ledger close time (DAY_IN_LEDGERS = 17280 = 86400/5),
|
|
42
|
+
* used to convert a spend window in seconds to OZ `period_ledgers`. */
|
|
43
|
+
readonly secondsPerLedger: 5;
|
|
44
|
+
};
|
|
45
|
+
/** Maximum recursion depth for decoding Recursive `ScVal` shapes (vec-in-vec).
|
|
46
|
+
* A real Stellar tx caps nesting well below this; the bound hard-caps a
|
|
47
|
+
* hand-crafted payload so the recorder fails CLOSED (records the over-depth
|
|
48
|
+
* branch as opaque `depth-exceeded`) rather than RangeErroring the JS stack.
|
|
49
|
+
* Mirrors the `MAX_AUTH_TREE_DEPTH = 16` boundary the auth-tree decoder
|
|
50
|
+
* already enforces; lifted to 30 so legitimate nested args (a path vec
|
|
51
|
+
* inside an order vec inside a market-data vec) decode cleanly while a
|
|
52
|
+
* malformed ~10000-deep payload still trips the cap. */
|
|
53
|
+
export declare const MAX_SCVAL_DEPTH: 30;
|
|
54
|
+
/** Maximum recursion depth for cloning nested `ScVal` shapes (vec-in-vec) into
|
|
55
|
+
* the simulator / verify / harness contexts. Mirrors `MAX_SCVAL_DEPTH` above
|
|
56
|
+
* so a hand-crafted nested-vec payload cannot RangeError the JS stack during
|
|
57
|
+
* context building. Over-depth throws a ToolError (caught by the
|
|
58
|
+
* `synthesizeFromRecording` envelope + the simulator/verify boundaries) so
|
|
59
|
+
* the caller gets a structured `{ok:false, error}` instead of a thrown
|
|
60
|
+
* RangeError. */
|
|
61
|
+
export declare const MAX_SCVAL_CLONE_DEPTH: 30;
|
|
62
|
+
/** Predicate-document caps - enforced fail-closed at install by BOTH the synth (TS)
|
|
63
|
+
* AND the interpreter (Rust). Single source: a CI test asserts the two cap sets are
|
|
64
|
+
* byte-identical (TS reads the same JSON manifest the Rust build emits). If they
|
|
65
|
+
* disagree, the interpreter is authoritative. Rationale per cap:
|
|
66
|
+
* - MAX_DEPTH = 5: 3 walkthroughs need depth 2; budget = 2x growth + 1 for `not` chain.
|
|
67
|
+
* - MAX_LEAVES = 200: allowlists (recipients, paths) typically < 100; budget = 2x + 1 nested allowlist.
|
|
68
|
+
* - MAX_PREDICATE_BYTES = 32 KB: well under the per-ledger-entry 64 KB persistent-storage limit.
|
|
69
|
+
* - MAX_ORACLE_READS = 6: two-round confirmation per asset consumes 2 reads; cap accommodates a predicate that references up to 3 unique oracle assets (3 * 2 = 6 reads). Stays a fail-closed install-time cap.
|
|
70
|
+
* - MAX_IN_OPERAND_COUNT = 32: allowlist < 32 is a UX-natural bound. */
|
|
71
|
+
export declare const PREDICATE_CAPS: {
|
|
72
|
+
readonly MAX_DEPTH: 5;
|
|
73
|
+
readonly MAX_LEAVES: 200;
|
|
74
|
+
readonly MAX_PREDICATE_BYTES: number;
|
|
75
|
+
readonly MAX_ORACLE_READS: 6;
|
|
76
|
+
readonly MAX_IN_OPERAND_COUNT: 32;
|
|
77
|
+
};
|
|
78
|
+
/** OZ context-rule draft - the unit the install builder builds add_context_rule from. */
|
|
79
|
+
export type ContextRuleDraft = {
|
|
80
|
+
contextRuleType: {
|
|
81
|
+
kind: 'default';
|
|
82
|
+
} | {
|
|
83
|
+
kind: 'call_contract';
|
|
84
|
+
contract: string;
|
|
85
|
+
} | {
|
|
86
|
+
kind: 'create_contract';
|
|
87
|
+
wasmHash: string;
|
|
88
|
+
};
|
|
89
|
+
name: string;
|
|
90
|
+
/** Ledger sequence (u32). Soroban `valid_until: Option<u32>`. */
|
|
91
|
+
validUntilLedger: number | null;
|
|
92
|
+
/** Mirrors OZ `Signer` enum exactly: `Delegated(Address) | External(Address, Bytes)`.
|
|
93
|
+
* The verifier address + key bytes for an external signer are carried in the
|
|
94
|
+
* `External` variant - there is no `Key` or `SmartAccount` variant, and no
|
|
95
|
+
* master/non-master discriminator lives on the type itself. */
|
|
96
|
+
signers: SignerDraft[];
|
|
97
|
+
policies: PolicyRef[];
|
|
98
|
+
};
|
|
99
|
+
/** Mirrors OZ `Signer` exactly: `Signer::Delegated(Address) | Signer::External(Address, Bytes)`. */
|
|
100
|
+
export type SignerDraft = {
|
|
101
|
+
kind: 'delegated';
|
|
102
|
+
address: string;
|
|
103
|
+
} | {
|
|
104
|
+
kind: 'external';
|
|
105
|
+
verifier: string;
|
|
106
|
+
keyBytes: string;
|
|
107
|
+
};
|
|
108
|
+
/** Reference to one policy attached to a context rule. */
|
|
109
|
+
export type PolicyRef = {
|
|
110
|
+
kind: 'oz_builtin';
|
|
111
|
+
primitive: OZPrimitiveConfig;
|
|
112
|
+
instanceAddress: string;
|
|
113
|
+
} | {
|
|
114
|
+
kind: 'interpreter';
|
|
115
|
+
interpreterAddress: string;
|
|
116
|
+
predicateBlobBase64: string;
|
|
117
|
+
};
|
|
118
|
+
/** A serialised predicate document stored against a (smart_account, rule_id) pair. */
|
|
119
|
+
export interface PolicyDocument {
|
|
120
|
+
/** Grammar version baked into the interpreter wasm. Fail-closed strict match at install
|
|
121
|
+
* AND at evaluate (defence in depth). NOT the per-edit revision. */
|
|
122
|
+
grammarVersion: 1;
|
|
123
|
+
/** Per-rule install nonce. Must equal the interpreter's stored nonce + 1 at install.
|
|
124
|
+
* First install accepts nonce = 1 with stored nonce = 0. Re-install is structurally
|
|
125
|
+
* possible by incrementing the nonce; uninstall removes the nonce with state so a
|
|
126
|
+
* subsequent install returns to nonce = 1. Replay of an old install denied. */
|
|
127
|
+
installNonce: number;
|
|
128
|
+
/** Canonical ScVal encoding (NOT JSON) - the version-match bypass is closed by the
|
|
129
|
+
* on-chain serialisation, not by a parser. */
|
|
130
|
+
encodedPredicate: string;
|
|
131
|
+
/** Hash of the post-canonicalisation bytes of encodedPredicate. Stored alongside the
|
|
132
|
+
* doc and re-verified at evaluate. */
|
|
133
|
+
predicateHash: string;
|
|
134
|
+
/** Per-policy overrides of oracle thresholds; absent = use defaults.
|
|
135
|
+
* Overrides may TIGHTEN only (never widen) - the interpreter rejects any per-policy
|
|
136
|
+
* value looser than the wasm-level defaults at install. */
|
|
137
|
+
oracleParams?: {
|
|
138
|
+
/** Seconds; must be <= MAX_ORACLE_STALENESS_SECONDS (600). */
|
|
139
|
+
maxStalenessSeconds?: number;
|
|
140
|
+
/** Basis points; must be <= MAX_ORACLE_DEVIATION_BPS (200). */
|
|
141
|
+
maxDeviationBps?: number;
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
/** The versioned predicate AST (interpreter side). Tagged union. */
|
|
145
|
+
export type PredicateNode = {
|
|
146
|
+
op: 'and';
|
|
147
|
+
children: PredicateNode[];
|
|
148
|
+
} | {
|
|
149
|
+
op: 'or';
|
|
150
|
+
children: PredicateNode[];
|
|
151
|
+
} | {
|
|
152
|
+
op: 'not';
|
|
153
|
+
child: PredicateNode;
|
|
154
|
+
} | {
|
|
155
|
+
op: 'eq';
|
|
156
|
+
left: PredicateLeaf;
|
|
157
|
+
right: PredicateLeaf;
|
|
158
|
+
} | {
|
|
159
|
+
op: 'lt' | 'lte' | 'gt' | 'gte';
|
|
160
|
+
left: PredicateLeaf;
|
|
161
|
+
right: PredicateLeaf;
|
|
162
|
+
} | {
|
|
163
|
+
op: 'in';
|
|
164
|
+
needle: PredicateLeaf;
|
|
165
|
+
haystack: PredicateLeaf[]; /** set-valued only - the haystack is always sorted by canonical XDR bytes (pure membership). */
|
|
166
|
+
};
|
|
167
|
+
export type PredicateLeaf = {
|
|
168
|
+
kind: 'call_contract';
|
|
169
|
+
} | {
|
|
170
|
+
kind: 'call_fn';
|
|
171
|
+
} | {
|
|
172
|
+
kind: 'call_arg';
|
|
173
|
+
index: number;
|
|
174
|
+
} | {
|
|
175
|
+
kind: 'amount';
|
|
176
|
+
token: string;
|
|
177
|
+
} | {
|
|
178
|
+
kind: 'window_spent';
|
|
179
|
+
token: string;
|
|
180
|
+
windowSeconds: number;
|
|
181
|
+
} | {
|
|
182
|
+
kind: 'now';
|
|
183
|
+
} | {
|
|
184
|
+
kind: 'valid_until';
|
|
185
|
+
} | {
|
|
186
|
+
kind: 'invocation_count_in_window';
|
|
187
|
+
windowSecs: number;
|
|
188
|
+
} | {
|
|
189
|
+
kind: 'oracle_price';
|
|
190
|
+
asset: string;
|
|
191
|
+
} | {
|
|
192
|
+
kind: 'literal_address';
|
|
193
|
+
value: string;
|
|
194
|
+
} | {
|
|
195
|
+
kind: 'literal_i128';
|
|
196
|
+
value: string;
|
|
197
|
+
} | {
|
|
198
|
+
kind: 'literal_symbol';
|
|
199
|
+
value: string;
|
|
200
|
+
} | {
|
|
201
|
+
kind: 'literal_u32';
|
|
202
|
+
value: number;
|
|
203
|
+
} | {
|
|
204
|
+
kind: 'literal_u64';
|
|
205
|
+
value: string;
|
|
206
|
+
} | {
|
|
207
|
+
kind: 'literal_bytes';
|
|
208
|
+
value: string;
|
|
209
|
+
} | {
|
|
210
|
+
kind: 'literal_vec';
|
|
211
|
+
elements: PredicateLeaf[];
|
|
212
|
+
};
|
|
213
|
+
export interface OZPrimitiveConfig {
|
|
214
|
+
primitive: 'spending_limit' | 'simple_threshold' | 'weighted_threshold';
|
|
215
|
+
params: Record<string, unknown>;
|
|
216
|
+
}
|
|
217
|
+
export interface ContractInvocation {
|
|
218
|
+
contract: string;
|
|
219
|
+
fn: string;
|
|
220
|
+
args: ScVal[];
|
|
221
|
+
subInvocations: ContractInvocation[];
|
|
222
|
+
}
|
|
223
|
+
export interface TokenMovement {
|
|
224
|
+
token: string;
|
|
225
|
+
from: string;
|
|
226
|
+
to: string;
|
|
227
|
+
amount: string;
|
|
228
|
+
}
|
|
229
|
+
export interface OnChainEvent {
|
|
230
|
+
contract: string;
|
|
231
|
+
topics: string[];
|
|
232
|
+
data: ScVal;
|
|
233
|
+
}
|
|
234
|
+
export interface RecordedTransaction {
|
|
235
|
+
network: Network;
|
|
236
|
+
/** Authorising signers (the agent / owner set). */
|
|
237
|
+
signers: string[];
|
|
238
|
+
/** Top-level call + nested sub-invocations captured for analysis:
|
|
239
|
+
* v1 grammar matches only the top-level call's own `(contract, fn_name, args)`
|
|
240
|
+
* that `Policy::enforce` receives - sub-invocations are NOT walked in v1. */
|
|
241
|
+
invocations: ContractInvocation[];
|
|
242
|
+
/** All token movements (from events). */
|
|
243
|
+
tokenMovements: TokenMovement[];
|
|
244
|
+
/** Raw on-chain events from getTransaction (used for validation). */
|
|
245
|
+
events: OnChainEvent[];
|
|
246
|
+
/** Soroban auth entries (which signers, which contracts, which fns). */
|
|
247
|
+
authEntries: unknown[];
|
|
248
|
+
ledgerSequence: number;
|
|
249
|
+
fetchedAt: number;
|
|
250
|
+
/** The parsed-decoding freshness, computed by the recorder. The synth refuses on
|
|
251
|
+
* parseConfidence.overall < parseConfidence.thresholdUsed. */
|
|
252
|
+
parseConfidence: ParseConfidence;
|
|
253
|
+
/** Source account (already recorded, needed by install + reviewer). */
|
|
254
|
+
sourceAccount: string;
|
|
255
|
+
}
|
|
256
|
+
export interface ProposedPolicy {
|
|
257
|
+
contextRule: ContextRuleDraft;
|
|
258
|
+
policyDocuments: PolicyDocument[];
|
|
259
|
+
policyRefs: PolicyRef[];
|
|
260
|
+
/** Parsed-decoding freshness mirror; the orchestrator refuses when below threshold. */
|
|
261
|
+
parseConfidence: ParseConfidence;
|
|
262
|
+
warnings: string[];
|
|
263
|
+
ambiguities: AmbiguityPrompt[];
|
|
264
|
+
}
|
|
265
|
+
export interface AmbiguityPrompt {
|
|
266
|
+
code: AmbiguityCode;
|
|
267
|
+
question: string;
|
|
268
|
+
}
|
|
269
|
+
/** Catalogue of named ambiguity codes (the skill's prompt files are one-per-code).
|
|
270
|
+
* Adding a code is a synthesizer decision; the prompt authoring must not
|
|
271
|
+
* invent codes outside this union. */
|
|
272
|
+
export type AmbiguityCode = 'DURATION_UNSPECIFIED' | 'AMOUNT_BOUND_MISSING' | 'RECIPIENT_ALLOWLIST_EMPTY' | 'FREQUENCY_BOUND_MISSING' | 'ORACLE_ASSET_UNKNOWN' | 'MULTIPLE_UNRELATED_TARGETS';
|
|
273
|
+
/** Structured recording freshness. `overall` is the gate threshold (default 1.0); the
|
|
274
|
+
* breakdown is the diagnostic the user / agent sees when the gate fires. The
|
|
275
|
+
* computation rule is pinned: `overall = 1 - (unknownContracts + opaqueScVals) / total`. */
|
|
276
|
+
export interface ParseConfidence {
|
|
277
|
+
overall: number;
|
|
278
|
+
knownContracts: string[];
|
|
279
|
+
unknownContracts: Array<{
|
|
280
|
+
contract: string;
|
|
281
|
+
reason: 'no-abi' | 'version-mismatch' | 'opaque-result';
|
|
282
|
+
}>;
|
|
283
|
+
opaqueScVals: Array<{
|
|
284
|
+
path: string;
|
|
285
|
+
type: string;
|
|
286
|
+
}>;
|
|
287
|
+
thresholdUsed: number;
|
|
288
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// src/types.ts - shared domain types (single source of truth)
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.PREDICATE_CAPS = exports.MAX_SCVAL_CLONE_DEPTH = exports.MAX_SCVAL_DEPTH = exports.SOROBAN_LIMITS = exports.OZ_LIMITS = void 0;
|
|
5
|
+
/** OZ Accounts framework hard limits (verified against canonical OZ source). */
|
|
6
|
+
exports.OZ_LIMITS = {
|
|
7
|
+
maxPoliciesPerRule: 5,
|
|
8
|
+
maxSignersPerRule: 15,
|
|
9
|
+
// NO per-account rule cap - OZ imposes none.
|
|
10
|
+
};
|
|
11
|
+
/** Soroban / Stellar protocol limits used to bound inputs fail-closed. Single
|
|
12
|
+
* source so the schema boundary and the core validators cannot drift. */
|
|
13
|
+
exports.SOROBAN_LIMITS = {
|
|
14
|
+
/** `valid_until` and other ledger-sequence fields are u32. */
|
|
15
|
+
u32Max: 4294967295,
|
|
16
|
+
/** Upper bound on top-level invocations in a recorded transaction; a real
|
|
17
|
+
* Stellar tx caps operations well below this. Bounds a hand-crafted payload. */
|
|
18
|
+
maxInvocations: 512,
|
|
19
|
+
/** Stellar targets a ~5s ledger close time (DAY_IN_LEDGERS = 17280 = 86400/5),
|
|
20
|
+
* used to convert a spend window in seconds to OZ `period_ledgers`. */
|
|
21
|
+
secondsPerLedger: 5,
|
|
22
|
+
};
|
|
23
|
+
/** Maximum recursion depth for decoding Recursive `ScVal` shapes (vec-in-vec).
|
|
24
|
+
* A real Stellar tx caps nesting well below this; the bound hard-caps a
|
|
25
|
+
* hand-crafted payload so the recorder fails CLOSED (records the over-depth
|
|
26
|
+
* branch as opaque `depth-exceeded`) rather than RangeErroring the JS stack.
|
|
27
|
+
* Mirrors the `MAX_AUTH_TREE_DEPTH = 16` boundary the auth-tree decoder
|
|
28
|
+
* already enforces; lifted to 30 so legitimate nested args (a path vec
|
|
29
|
+
* inside an order vec inside a market-data vec) decode cleanly while a
|
|
30
|
+
* malformed ~10000-deep payload still trips the cap. */
|
|
31
|
+
exports.MAX_SCVAL_DEPTH = 30;
|
|
32
|
+
/** Maximum recursion depth for cloning nested `ScVal` shapes (vec-in-vec) into
|
|
33
|
+
* the simulator / verify / harness contexts. Mirrors `MAX_SCVAL_DEPTH` above
|
|
34
|
+
* so a hand-crafted nested-vec payload cannot RangeError the JS stack during
|
|
35
|
+
* context building. Over-depth throws a ToolError (caught by the
|
|
36
|
+
* `synthesizeFromRecording` envelope + the simulator/verify boundaries) so
|
|
37
|
+
* the caller gets a structured `{ok:false, error}` instead of a thrown
|
|
38
|
+
* RangeError. */
|
|
39
|
+
exports.MAX_SCVAL_CLONE_DEPTH = 30;
|
|
40
|
+
/** Predicate-document caps - enforced fail-closed at install by BOTH the synth (TS)
|
|
41
|
+
* AND the interpreter (Rust). Single source: a CI test asserts the two cap sets are
|
|
42
|
+
* byte-identical (TS reads the same JSON manifest the Rust build emits). If they
|
|
43
|
+
* disagree, the interpreter is authoritative. Rationale per cap:
|
|
44
|
+
* - MAX_DEPTH = 5: 3 walkthroughs need depth 2; budget = 2x growth + 1 for `not` chain.
|
|
45
|
+
* - MAX_LEAVES = 200: allowlists (recipients, paths) typically < 100; budget = 2x + 1 nested allowlist.
|
|
46
|
+
* - MAX_PREDICATE_BYTES = 32 KB: well under the per-ledger-entry 64 KB persistent-storage limit.
|
|
47
|
+
* - MAX_ORACLE_READS = 6: two-round confirmation per asset consumes 2 reads; cap accommodates a predicate that references up to 3 unique oracle assets (3 * 2 = 6 reads). Stays a fail-closed install-time cap.
|
|
48
|
+
* - MAX_IN_OPERAND_COUNT = 32: allowlist < 32 is a UX-natural bound. */
|
|
49
|
+
exports.PREDICATE_CAPS = {
|
|
50
|
+
MAX_DEPTH: 5,
|
|
51
|
+
MAX_LEAVES: 200,
|
|
52
|
+
MAX_PREDICATE_BYTES: 32 * 1024,
|
|
53
|
+
MAX_ORACLE_READS: 6,
|
|
54
|
+
MAX_IN_OPERAND_COUNT: 32,
|
|
55
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export type SimulationResult = {
|
|
2
|
+
permit: {
|
|
3
|
+
tx: 'permit';
|
|
4
|
+
} | {
|
|
5
|
+
tx: 'deny';
|
|
6
|
+
reason: string;
|
|
7
|
+
};
|
|
8
|
+
evaluatedCases: Array<{
|
|
9
|
+
dimension: string;
|
|
10
|
+
outcome: 'permit' | 'deny';
|
|
11
|
+
reason: string;
|
|
12
|
+
}>;
|
|
13
|
+
backend: 'interpreter-v1' | 'ts-model';
|
|
14
|
+
simulatorVersion: string;
|
|
15
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// src/verify/envelope.ts - the post-simulation result envelope used by
|
|
3
|
+
// review-card rendering and verification.
|
|
4
|
+
//
|
|
5
|
+
// `SimulationResult` is the structured verdict a `simulate_policy` run
|
|
6
|
+
// produces; the review-card builder reads it as one of its inputs (so the
|
|
7
|
+
// rendered card can quote the backend that evaluated the policy) and the
|
|
8
|
+
// verification pipeline reads `permit` + `evaluatedCases` to confirm every
|
|
9
|
+
// generated deny case really did deny.
|
|
10
|
+
//
|
|
11
|
+
// This envelope is intentionally separate from the CustodyAdapter
|
|
12
|
+
// `SimulationResult` in `src/seams/types.ts` (which is the dry-run stub
|
|
13
|
+
// returned by `adapter.simulate(ir, permitTx)`); the seam result is the
|
|
14
|
+
// adapter contract, this envelope is the post-simulation record consumed by
|
|
15
|
+
// downstream rendering + verification.
|
|
16
|
+
//
|
|
17
|
+
// Fields:
|
|
18
|
+
// - `permit` is the single verdict for the candidate recorded tx.
|
|
19
|
+
// - `evaluatedCases` is the deny-case battery outcome (every dimension
|
|
20
|
+
// must report `deny` when the policy is minimal).
|
|
21
|
+
// - `backend` is the actual evaluator that produced the verdict.
|
|
22
|
+
// - `simulatorVersion` lets the reviewer / audit log distinguish runs.
|
|
23
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// src/verify/index.ts - re-export the simulation / verification surface.
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.verifyPolicy = exports.simulatePolicy = void 0;
|
|
5
|
+
var simulate_ts_1 = require("./simulate.js");
|
|
6
|
+
Object.defineProperty(exports, "simulatePolicy", { enumerable: true, get: function () { return simulate_ts_1.simulatePolicy; } });
|
|
7
|
+
var verify_ts_1 = require("./verify.js");
|
|
8
|
+
Object.defineProperty(exports, "verifyPolicy", { enumerable: true, get: function () { return verify_ts_1.verifyPolicy; } });
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { ToolResponse } from '../errors.ts';
|
|
2
|
+
import type { PredicateNode, RecordedTransaction } from '../types.ts';
|
|
3
|
+
import type { SimulationResult } from './envelope.ts';
|
|
4
|
+
/** Options for `simulatePolicy`. `validUntilLedger` is propagated onto the
|
|
5
|
+
* permit EvalContext so the simulator exercises the expiry gate; absent ->
|
|
6
|
+
* no expiry check (mirrors the orchestrator's "no `validUntilLedger`
|
|
7
|
+
* supplied" path). `oraclePricesByAsset` is the test fixture the simulator
|
|
8
|
+
* uses to satisfy `oracle_price` leaves so the permit call evaluates under
|
|
9
|
+
* the bound; absent -> derive satisfying prices from the predicate itself
|
|
10
|
+
* (the orchestrator's oracle-satisfying-price logic). */
|
|
11
|
+
export interface SimulateOptions {
|
|
12
|
+
validUntilLedger?: number;
|
|
13
|
+
/** Pre-populated oracle-price entries keyed by asset address. The fixture
|
|
14
|
+
* must satisfy every `oracle_price` leaf in the predicate; absent or
|
|
15
|
+
* unsatisfying entries cause `SIMULATION_ERROR`. */
|
|
16
|
+
oraclePricesByAsset?: Record<string, {
|
|
17
|
+
price: string;
|
|
18
|
+
timestampSeconds: number;
|
|
19
|
+
} | {
|
|
20
|
+
error: 'stale' | 'missing' | 'deviation' | 'paused' | 'decimals' | 'fingerprint';
|
|
21
|
+
}>;
|
|
22
|
+
}
|
|
23
|
+
/** Replay a recorded transaction against a proposed predicate and emit the
|
|
24
|
+
* `SimulationResult` envelope. The simulator returns the SAME permit
|
|
25
|
+
* verdict `runHarness` expects: the intended recorded call must permit; every
|
|
26
|
+
* generated deny dimension must deny. A runtime evaluation failure
|
|
27
|
+
* (malformed input, missing oracle fixture, etc.) returns a `SIMULATION_ERROR`
|
|
28
|
+
* `ToolError` - NOT a deny verdict, NOT `VERIFICATION_FAILED`. The boundary
|
|
29
|
+
* is pinned: minimality is a verify-time concern, runtime evaluation is a
|
|
30
|
+
* simulate-time concern. */
|
|
31
|
+
export declare function simulatePolicy(predicate: PredicateNode | null, permitTx: RecordedTransaction, opts?: SimulateOptions): ToolResponse<SimulationResult>;
|