@crediolabs/policy-synth 0.2.0 → 0.3.1
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 +4 -4
- package/dist/adapters/interpreter/adapter.d.ts +19 -25
- package/dist/adapters/interpreter/adapter.js +54 -487
- package/dist/errors.d.ts +1 -1
- package/dist/index.d.ts +1 -4
- package/dist/index.js +1 -4
- package/dist/install/build-add-context-rule.d.ts +3 -19
- package/dist/install/build-add-context-rule.js +3 -98
- package/dist/install/build-install-policy.d.ts +4 -53
- package/dist/install/build-install-policy.js +44 -123
- package/dist/install/index.d.ts +0 -2
- package/dist/install/index.js +0 -7
- package/dist/predicate/decode.d.ts +1 -1
- package/dist/predicate/decode.js +2 -70
- package/dist/predicate/encode.js +45 -229
- package/dist/predicate/from-json.js +1 -42
- package/dist/record/decode.js +3 -8
- package/dist/record/freshness.d.ts +17 -11
- package/dist/record/freshness.js +28 -18
- package/dist/review-card/builder.d.ts +4 -3
- package/dist/review-card/builder.js +18 -162
- package/dist/review-card/cross-check.js +2 -105
- package/dist/review-card/render-leaf.d.ts +4 -0
- package/dist/review-card/render-leaf.js +47 -0
- package/dist/run/index.d.ts +36 -139
- package/dist/run/index.js +125 -437
- package/dist/run/schemas.d.ts +168 -1929
- package/dist/run/schemas.js +40 -269
- package/dist/{synth → simulate}/deny-cases.d.ts +4 -8
- package/dist/simulate/deny-cases.js +282 -0
- package/dist/simulate/evaluate.d.ts +18 -0
- package/dist/simulate/evaluate.js +271 -0
- package/dist/simulate/index.d.ts +4 -0
- package/dist/simulate/index.js +8 -0
- package/dist/synth/compose-from-recording.d.ts +24 -51
- package/dist/synth/compose-from-recording.js +120 -282
- package/dist/synth/index.d.ts +0 -5
- package/dist/synth/index.js +0 -5
- package/dist/synth/synthesize-from-recording.d.ts +1 -29
- package/dist/synth/synthesize-from-recording.js +41 -356
- package/dist/types.d.ts +8 -60
- package/dist/types.js +5 -2
- package/dist-cjs/adapters/interpreter/adapter.d.ts +19 -25
- package/dist-cjs/adapters/interpreter/adapter.js +56 -489
- package/dist-cjs/errors.d.ts +1 -1
- package/dist-cjs/index.d.ts +1 -4
- package/dist-cjs/index.js +1 -4
- package/dist-cjs/install/build-add-context-rule.d.ts +3 -19
- package/dist-cjs/install/build-add-context-rule.js +1 -97
- package/dist-cjs/install/build-install-policy.d.ts +4 -53
- package/dist-cjs/install/build-install-policy.js +44 -122
- package/dist-cjs/install/index.d.ts +0 -2
- package/dist-cjs/install/index.js +2 -23
- package/dist-cjs/predicate/decode.d.ts +1 -1
- package/dist-cjs/predicate/decode.js +2 -70
- package/dist-cjs/predicate/encode.js +45 -229
- package/dist-cjs/predicate/from-json.js +1 -42
- package/dist-cjs/record/decode.js +3 -8
- package/dist-cjs/record/freshness.d.ts +17 -11
- package/dist-cjs/record/freshness.js +28 -18
- package/dist-cjs/review-card/builder.d.ts +4 -3
- package/dist-cjs/review-card/builder.js +23 -167
- package/dist-cjs/review-card/cross-check.js +7 -110
- package/dist-cjs/review-card/render-leaf.d.ts +4 -0
- package/dist-cjs/review-card/render-leaf.js +52 -0
- package/dist-cjs/run/index.d.ts +36 -139
- package/dist-cjs/run/index.js +125 -444
- package/dist-cjs/run/schemas.d.ts +168 -1929
- package/dist-cjs/run/schemas.js +41 -270
- package/dist-cjs/{synth → simulate}/deny-cases.d.ts +4 -8
- package/dist-cjs/simulate/deny-cases.js +286 -0
- package/dist-cjs/simulate/evaluate.d.ts +18 -0
- package/dist-cjs/simulate/evaluate.js +274 -0
- package/dist-cjs/simulate/index.d.ts +4 -0
- package/dist-cjs/simulate/index.js +13 -0
- package/dist-cjs/synth/compose-from-recording.d.ts +24 -51
- package/dist-cjs/synth/compose-from-recording.js +120 -282
- package/dist-cjs/synth/index.d.ts +0 -5
- package/dist-cjs/synth/index.js +1 -11
- package/dist-cjs/synth/synthesize-from-recording.d.ts +1 -29
- package/dist-cjs/synth/synthesize-from-recording.js +39 -354
- package/dist-cjs/types.d.ts +8 -60
- package/dist-cjs/types.js +6 -3
- package/package.json +5 -5
- package/src/adapters/interpreter/adapter.ts +76 -572
- package/src/errors.ts +0 -19
- package/src/index.ts +1 -4
- package/src/install/build-add-context-rule.ts +5 -139
- package/src/install/build-install-policy.ts +93 -214
- package/src/install/index.ts +0 -34
- package/src/predicate/decode.ts +2 -70
- package/src/predicate/encode.ts +49 -261
- package/src/predicate/from-json.ts +1 -43
- package/src/record/decode.ts +3 -8
- package/src/record/freshness.ts +29 -18
- package/src/review-card/builder.ts +19 -168
- package/src/review-card/cross-check.ts +3 -105
- package/src/review-card/render-leaf.ts +48 -0
- package/src/run/index.ts +144 -572
- package/src/run/schemas.ts +42 -291
- package/src/simulate/deny-cases.ts +334 -0
- package/src/simulate/evaluate.ts +284 -0
- package/src/simulate/index.ts +11 -0
- package/src/synth/compose-from-recording.ts +148 -347
- package/src/synth/index.ts +0 -13
- package/src/synth/synthesize-from-recording.ts +43 -456
- package/src/types.ts +13 -49
- package/dist/adapters/interpreter/index.d.ts +0 -1
- package/dist/adapters/interpreter/index.js +0 -2
- package/dist/adapters/oz/adapter.d.ts +0 -20
- package/dist/adapters/oz/adapter.js +0 -295
- package/dist/adapters/oz/index.d.ts +0 -1
- package/dist/adapters/oz/index.js +0 -2
- package/dist/codegen/compile-gate.d.ts +0 -33
- package/dist/codegen/compile-gate.js +0 -124
- package/dist/codegen/index.d.ts +0 -2
- package/dist/codegen/index.js +0 -8
- package/dist/codegen/template.d.ts +0 -18
- package/dist/codegen/template.js +0 -148
- package/dist/install/authority-overlap.d.ts +0 -134
- package/dist/install/authority-overlap.js +0 -0
- package/dist/install/build-install-predicate.d.ts +0 -96
- package/dist/install/build-install-predicate.js +0 -444
- package/dist/install/build-merge-policy.d.ts +0 -70
- package/dist/install/build-merge-policy.js +0 -130
- package/dist/install/plan-merge-policy.d.ts +0 -49
- package/dist/install/plan-merge-policy.js +0 -86
- package/dist/install/read-account-rules.d.ts +0 -100
- package/dist/install/read-account-rules.js +0 -283
- package/dist/ir/index.d.ts +0 -1
- package/dist/ir/index.js +0 -2
- package/dist/ir/types.d.ts +0 -140
- package/dist/ir/types.js +0 -11
- package/dist/mandate/index.d.ts +0 -2
- package/dist/mandate/index.js +0 -2
- package/dist/mandate/to-ir.d.ts +0 -3
- package/dist/mandate/to-ir.js +0 -60
- package/dist/mandate/types.d.ts +0 -20
- package/dist/mandate/types.js +0 -8
- package/dist/seams/index.d.ts +0 -1
- package/dist/seams/index.js +0 -2
- package/dist/seams/types.d.ts +0 -66
- package/dist/seams/types.js +0 -11
- package/dist/synth/deny-cases.js +0 -562
- package/dist/synth/evaluate.d.ts +0 -39
- package/dist/synth/evaluate.js +0 -551
- package/dist/synth/harness.d.ts +0 -28
- package/dist/synth/harness.js +0 -47
- package/dist/synth/minimize.d.ts +0 -4
- package/dist/synth/minimize.js +0 -38
- package/dist/synth/permit-context.d.ts +0 -15
- package/dist/synth/permit-context.js +0 -116
- package/dist/synth/predicate-literals.d.ts +0 -5
- package/dist/synth/predicate-literals.js +0 -25
- package/dist/synth/synthesize-from-mandate.d.ts +0 -20
- package/dist/synth/synthesize-from-mandate.js +0 -59
- package/dist/verify/envelope.d.ts +0 -15
- package/dist/verify/envelope.js +0 -22
- package/dist/verify/index.d.ts +0 -3
- package/dist/verify/index.js +0 -3
- package/dist/verify/simulate.d.ts +0 -31
- package/dist/verify/simulate.js +0 -258
- package/dist/verify/verify.d.ts +0 -21
- package/dist/verify/verify.js +0 -189
- package/dist-cjs/adapters/interpreter/index.d.ts +0 -1
- package/dist-cjs/adapters/interpreter/index.js +0 -8
- package/dist-cjs/adapters/oz/adapter.d.ts +0 -20
- package/dist-cjs/adapters/oz/adapter.js +0 -300
- package/dist-cjs/adapters/oz/index.d.ts +0 -1
- package/dist-cjs/adapters/oz/index.js +0 -8
- package/dist-cjs/codegen/compile-gate.d.ts +0 -33
- package/dist-cjs/codegen/compile-gate.js +0 -128
- package/dist-cjs/codegen/index.d.ts +0 -2
- package/dist-cjs/codegen/index.js +0 -14
- package/dist-cjs/codegen/template.d.ts +0 -18
- package/dist-cjs/codegen/template.js +0 -151
- package/dist-cjs/install/authority-overlap.d.ts +0 -134
- package/dist-cjs/install/authority-overlap.js +0 -0
- package/dist-cjs/install/build-install-predicate.d.ts +0 -96
- package/dist-cjs/install/build-install-predicate.js +0 -479
- package/dist-cjs/install/build-merge-policy.d.ts +0 -70
- package/dist-cjs/install/build-merge-policy.js +0 -134
- package/dist-cjs/install/plan-merge-policy.d.ts +0 -49
- package/dist-cjs/install/plan-merge-policy.js +0 -90
- package/dist-cjs/install/read-account-rules.d.ts +0 -100
- package/dist-cjs/install/read-account-rules.js +0 -296
- package/dist-cjs/ir/index.d.ts +0 -1
- package/dist-cjs/ir/index.js +0 -3
- package/dist-cjs/ir/types.d.ts +0 -140
- package/dist-cjs/ir/types.js +0 -12
- package/dist-cjs/mandate/index.d.ts +0 -2
- package/dist-cjs/mandate/index.js +0 -6
- package/dist-cjs/mandate/to-ir.d.ts +0 -3
- package/dist-cjs/mandate/to-ir.js +0 -63
- package/dist-cjs/mandate/types.d.ts +0 -20
- package/dist-cjs/mandate/types.js +0 -9
- package/dist-cjs/seams/index.d.ts +0 -1
- package/dist-cjs/seams/index.js +0 -3
- package/dist-cjs/seams/types.d.ts +0 -66
- package/dist-cjs/seams/types.js +0 -12
- package/dist-cjs/synth/deny-cases.js +0 -568
- package/dist-cjs/synth/evaluate.d.ts +0 -39
- package/dist-cjs/synth/evaluate.js +0 -554
- package/dist-cjs/synth/harness.d.ts +0 -28
- package/dist-cjs/synth/harness.js +0 -50
- package/dist-cjs/synth/minimize.d.ts +0 -4
- package/dist-cjs/synth/minimize.js +0 -41
- package/dist-cjs/synth/permit-context.d.ts +0 -15
- package/dist-cjs/synth/permit-context.js +0 -119
- package/dist-cjs/synth/predicate-literals.d.ts +0 -5
- package/dist-cjs/synth/predicate-literals.js +0 -28
- package/dist-cjs/synth/synthesize-from-mandate.d.ts +0 -20
- package/dist-cjs/synth/synthesize-from-mandate.js +0 -62
- package/dist-cjs/verify/envelope.d.ts +0 -15
- package/dist-cjs/verify/envelope.js +0 -23
- package/dist-cjs/verify/index.d.ts +0 -3
- package/dist-cjs/verify/index.js +0 -8
- package/dist-cjs/verify/simulate.d.ts +0 -31
- package/dist-cjs/verify/simulate.js +0 -261
- package/dist-cjs/verify/verify.d.ts +0 -21
- package/dist-cjs/verify/verify.js +0 -192
- package/src/adapters/interpreter/index.ts +0 -8
- package/src/adapters/oz/adapter.ts +0 -376
- package/src/adapters/oz/index.ts +0 -9
- package/src/codegen/compile-gate.ts +0 -167
- package/src/codegen/index.ts +0 -17
- package/src/codegen/template.ts +0 -165
- package/src/install/authority-overlap.ts +0 -0
- package/src/install/build-merge-policy.ts +0 -219
- package/src/install/plan-merge-policy.ts +0 -133
- package/src/install/read-account-rules.ts +0 -376
- package/src/ir/index.ts +0 -13
- package/src/ir/types.ts +0 -132
- package/src/mandate/index.ts +0 -4
- package/src/mandate/to-ir.ts +0 -71
- package/src/mandate/types.ts +0 -21
- package/src/seams/index.ts +0 -11
- package/src/seams/types.ts +0 -81
- package/src/synth/deny-cases.ts +0 -663
- package/src/synth/evaluate.ts +0 -613
- package/src/synth/harness.ts +0 -68
- package/src/synth/minimize.ts +0 -48
- package/src/synth/permit-context.ts +0 -136
- package/src/synth/predicate-literals.ts +0 -27
- package/src/synth/synthesize-from-mandate.ts +0 -82
- package/src/verify/envelope.ts +0 -28
- package/src/verify/index.ts +0 -5
- package/src/verify/simulate.ts +0 -311
- package/src/verify/verify.ts +0 -243
package/dist/predicate/decode.js
CHANGED
|
@@ -55,21 +55,6 @@ function expectSymbol(v, what) {
|
|
|
55
55
|
throw malformed(`${what} is not a symbol`);
|
|
56
56
|
return v.sym().toString();
|
|
57
57
|
}
|
|
58
|
-
function expectAddress(v, what) {
|
|
59
|
-
if (!v || v.switch() !== xdr.ScValType.scvAddress())
|
|
60
|
-
throw malformed(`${what} is not an address`);
|
|
61
|
-
return Address.fromScAddress(v.address()).toString();
|
|
62
|
-
}
|
|
63
|
-
function expectU64(v, what) {
|
|
64
|
-
if (!v || v.switch() !== xdr.ScValType.scvU64())
|
|
65
|
-
throw malformed(`${what} is not a u64`);
|
|
66
|
-
return v.u64().toString();
|
|
67
|
-
}
|
|
68
|
-
function expectI128(v, what) {
|
|
69
|
-
if (!v || v.switch() !== xdr.ScValType.scvI128())
|
|
70
|
-
throw malformed(`${what} is not an i128`);
|
|
71
|
-
return scValToBigInt(v).toString();
|
|
72
|
-
}
|
|
73
58
|
/** Arity check with the same intent as the Rust `check_arity`: a selector with
|
|
74
59
|
* the wrong element count is malformed, not silently truncated. */
|
|
75
60
|
function arity(items, n, selector) {
|
|
@@ -99,48 +84,6 @@ function decodeSelectorLeaf(items, sym) {
|
|
|
99
84
|
element: expectU32(items[2], 'call_arg_field element'),
|
|
100
85
|
field: expectSymbol(items[3], 'call_arg_field field'),
|
|
101
86
|
};
|
|
102
|
-
case 'call_arg_scaled':
|
|
103
|
-
arity(items, 4, sym);
|
|
104
|
-
return {
|
|
105
|
-
kind: 'call_arg_scaled',
|
|
106
|
-
index: expectU32(items[1], 'call_arg_scaled index'),
|
|
107
|
-
num: expectI128(items[2], 'call_arg_scaled num'),
|
|
108
|
-
den: expectI128(items[3], 'call_arg_scaled den'),
|
|
109
|
-
};
|
|
110
|
-
case 'amount':
|
|
111
|
-
arity(items, 2, sym);
|
|
112
|
-
return { kind: 'amount', token: expectAddress(items[1], 'amount token') };
|
|
113
|
-
case 'window_spent':
|
|
114
|
-
arity(items, 3, sym);
|
|
115
|
-
return {
|
|
116
|
-
kind: 'window_spent',
|
|
117
|
-
token: expectAddress(items[1], 'window_spent token'),
|
|
118
|
-
windowSeconds: Number(expectU64(items[2], 'window_spent windowSeconds')),
|
|
119
|
-
};
|
|
120
|
-
case 'now':
|
|
121
|
-
arity(items, 1, sym);
|
|
122
|
-
return { kind: 'now' };
|
|
123
|
-
case 'valid_until':
|
|
124
|
-
// The encoder refuses to emit this and the interpreter refuses it at
|
|
125
|
-
// install, so it cannot appear in a document that installed. Decoding it
|
|
126
|
-
// anyway would present a rule the chain would never have accepted.
|
|
127
|
-
throw malformed('valid_until is not a usable predicate leaf');
|
|
128
|
-
case 'invocation_count':
|
|
129
|
-
arity(items, 2, sym);
|
|
130
|
-
return {
|
|
131
|
-
kind: 'invocation_count_in_window',
|
|
132
|
-
windowSecs: Number(expectU64(items[1], 'invocation_count windowSecs')),
|
|
133
|
-
};
|
|
134
|
-
case 'oracle_price':
|
|
135
|
-
arity(items, 2, sym);
|
|
136
|
-
return { kind: 'oracle_price', asset: expectAddress(items[1], 'oracle_price asset') };
|
|
137
|
-
case 'oracle_threshold':
|
|
138
|
-
arity(items, 3, sym);
|
|
139
|
-
return {
|
|
140
|
-
kind: 'oracle_threshold',
|
|
141
|
-
value: expectI128(items[1], 'oracle_threshold value'),
|
|
142
|
-
decimals: expectU32(items[2], 'oracle_threshold decimals'),
|
|
143
|
-
};
|
|
144
87
|
default:
|
|
145
88
|
// Deliberately NOT a literal_vec fallback - see the header note.
|
|
146
89
|
throw malformed(`unknown selector symbol '${sym}'`);
|
|
@@ -164,39 +107,28 @@ export function decodeLeaf(v) {
|
|
|
164
107
|
return { kind: 'literal_symbol', value: v.sym().toString() };
|
|
165
108
|
case xdr.ScValType.scvU32():
|
|
166
109
|
return { kind: 'literal_u32', value: v.u32() };
|
|
167
|
-
case xdr.ScValType.scvU64():
|
|
168
|
-
return { kind: 'literal_u64', value: v.u64().toString() };
|
|
169
110
|
case xdr.ScValType.scvI128():
|
|
170
111
|
return { kind: 'literal_i128', value: scValToBigInt(v).toString() };
|
|
171
|
-
case xdr.ScValType.scvBytes():
|
|
172
|
-
return { kind: 'literal_bytes', value: Buffer.from(v.bytes()).toString('hex') };
|
|
173
112
|
default:
|
|
174
113
|
throw malformed(`unsupported leaf value type ${v.switch().name}`);
|
|
175
114
|
}
|
|
176
115
|
}
|
|
177
|
-
/** One node.
|
|
116
|
+
/** One node. The comparison ops wrap two LEAVES. */
|
|
178
117
|
export function decodeNode(v) {
|
|
179
118
|
const items = expectVec(v, 'node');
|
|
180
119
|
const op = selectorSymbol(items);
|
|
181
120
|
if (op === null)
|
|
182
121
|
throw malformed('node does not start with an operator symbol');
|
|
183
122
|
switch (op) {
|
|
184
|
-
case 'and':
|
|
185
|
-
case 'or': {
|
|
123
|
+
case 'and': {
|
|
186
124
|
arity(items, 2, op);
|
|
187
125
|
const children = expectVec(items[1], `${op} children`).map(decodeNode);
|
|
188
126
|
if (children.length === 0)
|
|
189
127
|
throw malformed(`${op} has no children`);
|
|
190
128
|
return { op, children };
|
|
191
129
|
}
|
|
192
|
-
case 'not':
|
|
193
|
-
arity(items, 2, op);
|
|
194
|
-
return { op: 'not', child: decodeNode(items[1]) };
|
|
195
130
|
case 'eq':
|
|
196
|
-
case 'lt':
|
|
197
131
|
case 'lte':
|
|
198
|
-
case 'gt':
|
|
199
|
-
case 'gte':
|
|
200
132
|
arity(items, 3, op);
|
|
201
133
|
return {
|
|
202
134
|
op,
|
package/dist/predicate/encode.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
//
|
|
3
3
|
// Pure function. Maps a `PredicateNode` AST to the canonical ScVal wire format:
|
|
4
4
|
// - every node is a `ScVal::Vec` whose head element is the tag `ScVal::Symbol`
|
|
5
|
-
// - children of `and`
|
|
5
|
+
// - children of `and` are sorted ascending by their canonical XDR bytes
|
|
6
6
|
// - `in` haystacks are ALWAYS sorted by canonical XDR bytes (pure set
|
|
7
7
|
// membership); an EXACT ordered sequence is expressed as
|
|
8
8
|
// `eq(selector, literal_vec)` where the `literal_vec` element order is
|
|
@@ -17,12 +17,6 @@
|
|
|
17
17
|
// Caps from `PREDICATE_CAPS` are enforced BEFORE returning; a cap breach throws
|
|
18
18
|
// a `ToolError` with the matching error code and `severity: 'error'`.
|
|
19
19
|
//
|
|
20
|
-
// One gap remains, deliberately: the `amount` / `window_spent` leaf branches
|
|
21
|
-
// below are dead ABI - the contract's grammar no longer has those selector
|
|
22
|
-
// symbols, so a predicate carrying one is MALFORMED at decode. The interpreter
|
|
23
|
-
// adapter reports them as uncovered before lowering, so the product path
|
|
24
|
-
// cannot emit one; removing the branches (and the leaf kinds) is a separate
|
|
25
|
-
// change.
|
|
26
20
|
import { createHash } from 'node:crypto';
|
|
27
21
|
import { Address, xdr } from '@stellar/stellar-sdk';
|
|
28
22
|
import { PREDICATE_CAPS } from "../types.js";
|
|
@@ -45,27 +39,22 @@ export function encodePredicate(node) {
|
|
|
45
39
|
throw capError('IN_OPERAND_LIMIT', `\`in\` operand count ${c.count} exceeds MAX_IN_OPERAND_COUNT ${PREDICATE_CAPS.MAX_IN_OPERAND_COUNT}`);
|
|
46
40
|
}
|
|
47
41
|
}
|
|
48
|
-
//
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
42
|
+
// Minimum constraint. A predicate with no selector leaf compares literals
|
|
43
|
+
// to literals, so it is trivially true or false and would permit everything
|
|
44
|
+
// or nothing forever. The contract refuses it at install with
|
|
45
|
+
// SELECTOR_LEAF_REQUIRED (216); refuse it here too so the failure is caught
|
|
46
|
+
// before a transaction is built.
|
|
47
|
+
if (stats.selectorLeaves === 0) {
|
|
48
|
+
throw capError('MALFORMED_PREDICATE', 'predicate constrains nothing: every comparison has literals on both sides. The contract refuses it at install (SELECTOR_LEAF_REQUIRED). Pin the call itself - contract, method or argument.');
|
|
52
49
|
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
//
|
|
57
|
-
//
|
|
58
|
-
//
|
|
59
|
-
//
|
|
60
|
-
//
|
|
61
|
-
// - u32 fields in range (call_arg index, literal_u32 value, oracle
|
|
62
|
-
// threshold decimals, etc.) - the contract decodes as u32
|
|
63
|
-
// - i128 positivity where required (literal_i128 for amount/window
|
|
64
|
-
// caps; `den`/`num` for scaled ratios) - a negative cap would
|
|
65
|
-
// permit everything
|
|
66
|
-
// - hex even-length (literal_bytes) - `Buffer.from(v, 'hex')` silently
|
|
67
|
-
// drops non-hex chars, so 'zz' becomes empty bytes
|
|
68
|
-
// - scaled-ratio num>0 && den>0 - mirrors dsl.rs:661-704
|
|
50
|
+
// --- pass 1.5: leaf-value validation against the cap-set gate the contract
|
|
51
|
+
// enforces at install. Defense in depth: the TS self-verify pipeline should
|
|
52
|
+
// reject the same shapes Rust install refuses, so a hand-crafted predicate
|
|
53
|
+
// that simulate/verify green-lights cannot later be refused at the on-chain
|
|
54
|
+
// install step. The checks are the u32 ranges on the argument selectors
|
|
55
|
+
// (`call_arg.index`, `call_arg_len.index`, `call_arg_field.index` and
|
|
56
|
+
// `.element`) - the contract decodes each as u32, so an out-of-range value
|
|
57
|
+
// would fail there instead.
|
|
69
58
|
// Throws `MALFORMED_PREDICATE` so the error stays a ToolError shape.
|
|
70
59
|
validateLeafValues(node);
|
|
71
60
|
// --- pass 2: build + canonicalise the ScVal ---
|
|
@@ -80,48 +69,35 @@ export function encodePredicate(node) {
|
|
|
80
69
|
}
|
|
81
70
|
function computeStats(node) {
|
|
82
71
|
const inCounts = [];
|
|
83
|
-
const
|
|
84
|
-
const
|
|
85
|
-
const { depth, leaves } = walk(node, inCounts, oracleAssets, counters, false);
|
|
72
|
+
const counters = { selectorLeaves: 0 };
|
|
73
|
+
const { depth, leaves } = walk(node, inCounts, counters);
|
|
86
74
|
return {
|
|
87
75
|
depth,
|
|
88
76
|
leaves,
|
|
89
77
|
inCounts,
|
|
90
|
-
|
|
91
|
-
nonOracleSelectorLeaves: counters.nonOracleSelectorLeaves,
|
|
78
|
+
selectorLeaves: counters.selectorLeaves,
|
|
92
79
|
};
|
|
93
80
|
}
|
|
94
|
-
function walk(node, inCounts,
|
|
95
|
-
/** True once the walk is under a `not` or an `or`, where an oracle read
|
|
96
|
-
* becomes a condition the caller can satisfy by taking the other branch. */
|
|
97
|
-
negatedOrDisjunctive) {
|
|
81
|
+
function walk(node, inCounts, counters) {
|
|
98
82
|
switch (node.op) {
|
|
99
|
-
case 'and':
|
|
100
|
-
case 'or': {
|
|
83
|
+
case 'and': {
|
|
101
84
|
if (node.children.length === 0) {
|
|
102
85
|
throw capError('MALFORMED_PREDICATE', `\`${node.op}\` with no children: the contract refuses it at decode (MALFORMED_PREDICATE), so it can never be installed`);
|
|
103
86
|
}
|
|
104
87
|
let maxChildDepth = 0;
|
|
105
88
|
let totalLeaves = 0;
|
|
106
89
|
for (const c of node.children) {
|
|
107
|
-
const child = walk(c, inCounts,
|
|
90
|
+
const child = walk(c, inCounts, counters);
|
|
108
91
|
if (child.depth > maxChildDepth)
|
|
109
92
|
maxChildDepth = child.depth;
|
|
110
93
|
totalLeaves += child.leaves;
|
|
111
94
|
}
|
|
112
95
|
return { depth: maxChildDepth + 1, leaves: totalLeaves };
|
|
113
96
|
}
|
|
114
|
-
case 'not': {
|
|
115
|
-
const child = walk(node.child, inCounts, oracleAssets, counters, true);
|
|
116
|
-
return { depth: child.depth + 1, leaves: child.leaves };
|
|
117
|
-
}
|
|
118
97
|
case 'eq':
|
|
119
|
-
case '
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
case 'gte': {
|
|
123
|
-
collectOracle(node.left, oracleAssets, counters, negatedOrDisjunctive);
|
|
124
|
-
collectOracle(node.right, oracleAssets, counters, negatedOrDisjunctive);
|
|
98
|
+
case 'lte': {
|
|
99
|
+
collectSelector(node.left, counters);
|
|
100
|
+
collectSelector(node.right, counters);
|
|
125
101
|
return { depth: 1, leaves: leafCount(node.left) + leafCount(node.right) };
|
|
126
102
|
}
|
|
127
103
|
case 'in': {
|
|
@@ -129,10 +105,10 @@ negatedOrDisjunctive) {
|
|
|
129
105
|
throw capError('MALFORMED_PREDICATE', '`in` with an empty haystack: the contract refuses it at decode (MALFORMED_PREDICATE), so it can never be installed');
|
|
130
106
|
}
|
|
131
107
|
inCounts.push({ count: node.haystack.length });
|
|
132
|
-
|
|
108
|
+
collectSelector(node.needle, counters);
|
|
133
109
|
let haystackLeaves = 0;
|
|
134
110
|
for (const h of node.haystack) {
|
|
135
|
-
|
|
111
|
+
collectSelector(h, counters);
|
|
136
112
|
haystackLeaves += leafCount(h);
|
|
137
113
|
}
|
|
138
114
|
return {
|
|
@@ -154,45 +130,27 @@ function leafCount(leaf) {
|
|
|
154
130
|
}
|
|
155
131
|
return 1;
|
|
156
132
|
}
|
|
157
|
-
function
|
|
158
|
-
// literal_vec is the only nested leaf; recurse so
|
|
159
|
-
// vector literal
|
|
160
|
-
|
|
161
|
-
if (leaf.kind === 'oracle_price') {
|
|
162
|
-
if (negatedOrDisjunctive) {
|
|
163
|
-
throw capError('ORACLE_LEAF_INVALID_POSITION', `oracle_price(${leaf.asset}) sits under a \`not\`/\`or\`: the contract refuses that position at install (dsl.rs validate_oracle_placement). An oracle bound must be a conjunct the call has to satisfy.`);
|
|
164
|
-
}
|
|
165
|
-
oracleAssets.add(leaf.asset);
|
|
166
|
-
}
|
|
167
|
-
else if (leaf.kind === 'literal_vec') {
|
|
133
|
+
function collectSelector(leaf, counters) {
|
|
134
|
+
// literal_vec is the only nested leaf; recurse so a selector buried in a
|
|
135
|
+
// vector literal is still counted. Mirrors `dsl::has_selector_leaf`.
|
|
136
|
+
if (leaf.kind === 'literal_vec') {
|
|
168
137
|
for (const el of leaf.elements)
|
|
169
|
-
|
|
138
|
+
collectSelector(el, counters);
|
|
170
139
|
}
|
|
171
|
-
else if (!leaf.kind.startsWith('literal_')
|
|
172
|
-
|
|
173
|
-
// constraint. Counting it would let an oracle-only predicate satisfy the
|
|
174
|
-
// non-oracle envelope with its own bound (mirrors collect_oracle_leaf in
|
|
175
|
-
// dsl.rs, where it sits with the literals).
|
|
176
|
-
counters.nonOracleSelectorLeaves += 1;
|
|
140
|
+
else if (!leaf.kind.startsWith('literal_')) {
|
|
141
|
+
counters.selectorLeaves += 1;
|
|
177
142
|
}
|
|
178
143
|
}
|
|
179
144
|
function encodeNode(node) {
|
|
180
145
|
switch (node.op) {
|
|
181
|
-
case 'and':
|
|
182
|
-
case 'or': {
|
|
146
|
+
case 'and': {
|
|
183
147
|
const encoded = node.children.map(encodeNode);
|
|
184
148
|
// sort children by their canonical XDR bytes ascending.
|
|
185
149
|
const sorted = sortByCanonicalBytes(encoded);
|
|
186
150
|
return xdr.ScVal.scvVec([symbol(node.op), xdr.ScVal.scvVec(sorted)]);
|
|
187
151
|
}
|
|
188
|
-
case 'not': {
|
|
189
|
-
return xdr.ScVal.scvVec([symbol('not'), encodeNode(node.child)]);
|
|
190
|
-
}
|
|
191
152
|
case 'eq':
|
|
192
|
-
case '
|
|
193
|
-
case 'lte':
|
|
194
|
-
case 'gt':
|
|
195
|
-
case 'gte': {
|
|
153
|
+
case 'lte': {
|
|
196
154
|
return xdr.ScVal.scvVec([symbol(node.op), encodeLeaf(node.left), encodeLeaf(node.right)]);
|
|
197
155
|
}
|
|
198
156
|
case 'in': {
|
|
@@ -223,47 +181,6 @@ function encodeLeaf(leaf) {
|
|
|
223
181
|
xdr.ScVal.scvU32(leaf.element),
|
|
224
182
|
xdr.ScVal.scvSymbol(leaf.field),
|
|
225
183
|
]);
|
|
226
|
-
case 'call_arg_scaled':
|
|
227
|
-
// Wire shape `vec[symbol, u32, i128, i128]` mirrors the Rust decoder's
|
|
228
|
-
// expectation in dsl.rs SEL_CALL_ARG_SCALED. `scvI128FromDecimal` is
|
|
229
|
-
// the same range-checked helper used by `literal_i128`; an out-of-range
|
|
230
|
-
// num/den is refused at encode time (rather than at install on chain).
|
|
231
|
-
return xdr.ScVal.scvVec([
|
|
232
|
-
symbol('call_arg_scaled'),
|
|
233
|
-
xdr.ScVal.scvU32(leaf.index),
|
|
234
|
-
scvI128FromDecimal(leaf.num),
|
|
235
|
-
scvI128FromDecimal(leaf.den),
|
|
236
|
-
]);
|
|
237
|
-
case 'amount':
|
|
238
|
-
return xdr.ScVal.scvVec([symbol('amount'), scvAddressFromStrkey(leaf.token)]);
|
|
239
|
-
case 'window_spent':
|
|
240
|
-
return xdr.ScVal.scvVec([
|
|
241
|
-
symbol('window_spent'),
|
|
242
|
-
scvAddressFromStrkey(leaf.token),
|
|
243
|
-
scvU64FromValue(leaf.windowSeconds),
|
|
244
|
-
]);
|
|
245
|
-
case 'now':
|
|
246
|
-
return xdr.ScVal.scvVec([symbol('now')]);
|
|
247
|
-
case 'valid_until':
|
|
248
|
-
// The interpreter refuses this leaf at install: it never sources
|
|
249
|
-
// valid_until_ledger, so a policy built on it would deny forever.
|
|
250
|
-
// Expiry belongs to the smart account, through the context rule's
|
|
251
|
-
// validUntilLedger. Refuse at synthesis so the author finds out here
|
|
252
|
-
// rather than from an install that always fails.
|
|
253
|
-
throw new Error('valid_until is not a usable predicate leaf: the interpreter never sources it and ' +
|
|
254
|
-
'refuses it at install. Put expiry on the context rule (validUntilLedger) instead.');
|
|
255
|
-
case 'invocation_count_in_window':
|
|
256
|
-
return xdr.ScVal.scvVec([symbol('invocation_count'), scvU64FromValue(leaf.windowSecs)]);
|
|
257
|
-
case 'oracle_price':
|
|
258
|
-
return xdr.ScVal.scvVec([symbol('oracle_price'), scvAddressFromStrkey(leaf.asset)]);
|
|
259
|
-
case 'oracle_threshold':
|
|
260
|
-
// Arity 3, matching SEL_ORACLE_THRESHOLD in dsl.rs. The declared basis
|
|
261
|
-
// travels with the value so the contract never has to assume one.
|
|
262
|
-
return xdr.ScVal.scvVec([
|
|
263
|
-
symbol('oracle_threshold'),
|
|
264
|
-
scvI128FromDecimal(leaf.value),
|
|
265
|
-
xdr.ScVal.scvU32(leaf.decimals),
|
|
266
|
-
]);
|
|
267
184
|
case 'literal_address':
|
|
268
185
|
return scvAddressFromStrkey(leaf.value);
|
|
269
186
|
case 'literal_i128':
|
|
@@ -272,10 +189,6 @@ function encodeLeaf(leaf) {
|
|
|
272
189
|
return xdr.ScVal.scvSymbol(leaf.value);
|
|
273
190
|
case 'literal_u32':
|
|
274
191
|
return xdr.ScVal.scvU32(leaf.value);
|
|
275
|
-
case 'literal_u64':
|
|
276
|
-
return scvU64FromValue(leaf.value);
|
|
277
|
-
case 'literal_bytes':
|
|
278
|
-
return xdr.ScVal.scvBytes(Buffer.from(leaf.value, 'hex'));
|
|
279
192
|
case 'literal_vec':
|
|
280
193
|
// Bare ScVal::Vec of element encodings - order is preserved verbatim
|
|
281
194
|
// because the order IS the semantic (exact ordered sequence equality).
|
|
@@ -293,11 +206,6 @@ function sortByCanonicalBytes(values) {
|
|
|
293
206
|
function scvAddressFromStrkey(strkey) {
|
|
294
207
|
return xdr.ScVal.scvAddress(Address.fromString(strkey).toScAddress());
|
|
295
208
|
}
|
|
296
|
-
function scvU64FromValue(value) {
|
|
297
|
-
// The Uint64 (UnsignedHyper) constructor accepts string | bigint | number;
|
|
298
|
-
// string is safest for values > 2^53.
|
|
299
|
-
return xdr.ScVal.scvU64(new xdr.Uint64(String(value)));
|
|
300
|
-
}
|
|
301
209
|
/** Build `ScVal::I128(Int128Parts{hi, lo})` from a signed decimal string.
|
|
302
210
|
* `Int128Parts` encodes the value as `(hi << 64) + lo` with `hi` a SIGNED
|
|
303
211
|
* 64-bit int and `lo` an UNSIGNED 64-bit int (this is NOT signed-magnitude).
|
|
@@ -313,9 +221,8 @@ function scvI128FromDecimal(decimal) {
|
|
|
313
221
|
if (hi < INT64_MIN || hi > INT64_MAX) {
|
|
314
222
|
throw capError('MALFORMED_PREDICATE', `literal_i128 value ${decimal} is outside the Int128 range`);
|
|
315
223
|
}
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
}
|
|
224
|
+
// `lo` needs no range check: masking with UINT64_MAX puts it in
|
|
225
|
+
// [0, 2^64-1] for every v, negative included.
|
|
319
226
|
return xdr.ScVal.scvI128(new xdr.Int128Parts({
|
|
320
227
|
hi: new xdr.Int64(hi),
|
|
321
228
|
lo: new xdr.Uint64(lo),
|
|
@@ -331,19 +238,13 @@ function capError(code, message) {
|
|
|
331
238
|
// u32 boundary - the same constant the contract decodes with. A value above
|
|
332
239
|
// this either overflows during encode or is refused at install.
|
|
333
240
|
const U32_MAX = 4294967295;
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
*
|
|
340
|
-
*
|
|
341
|
-
* positivity, `literal_bytes` hex even-length, `oracle_threshold` decimals
|
|
342
|
-
* range, `call_arg_scaled` positive-ratio). Defense in depth so the TS
|
|
343
|
-
* self-verify pipeline rejects the same shapes Rust install already
|
|
344
|
-
* refuses - a hand-crafted predicate that simulate/verify green-lights must
|
|
345
|
-
* NOT be installable. Throws `MALFORMED_PREDICATE` so the envelope shapes
|
|
346
|
-
* it into a ToolError. */
|
|
241
|
+
/** Walk a `PredicateNode` and fail-closed on any leaf whose value the contract
|
|
242
|
+
* would refuse at install: the argument selectors carry u32 indices, so a
|
|
243
|
+
* value outside that range is refused here rather than on chain. Defense in
|
|
244
|
+
* depth so the TS self-verify pipeline rejects the same shapes Rust install
|
|
245
|
+
* already refuses - a hand-crafted predicate that simulate/verify
|
|
246
|
+
* green-lights must NOT be installable. Throws `MALFORMED_PREDICATE` so the
|
|
247
|
+
* envelope shapes it into a ToolError. */
|
|
347
248
|
function validateLeafValues(node) {
|
|
348
249
|
function walkLeaf(leaf, path) {
|
|
349
250
|
switch (leaf.kind) {
|
|
@@ -365,107 +266,22 @@ function validateLeafValues(node) {
|
|
|
365
266
|
throw malformed(`call_arg_field.element out of u32 range at ${path}`);
|
|
366
267
|
}
|
|
367
268
|
return;
|
|
368
|
-
case 'call_arg_scaled': {
|
|
369
|
-
if (!Number.isInteger(leaf.index) || leaf.index < 0 || leaf.index > U32_MAX) {
|
|
370
|
-
throw malformed(`call_arg_scaled.index out of u32 range at ${path}`);
|
|
371
|
-
}
|
|
372
|
-
// num / den are i128 on chain, decimal strings on the wire. The
|
|
373
|
-
// contract refuses `den == 0` and `num <= 0` / `den <= 0` at install
|
|
374
|
-
// (dsl.rs:664-672); mirror that here so a future regression in
|
|
375
|
-
// `validate_scaled_ratios` cannot let a divide-by-zero policy reach
|
|
376
|
-
// the wire. BigInt throws on non-numeric strings -> malformed.
|
|
377
|
-
let num;
|
|
378
|
-
let den;
|
|
379
|
-
try {
|
|
380
|
-
num = BigInt(leaf.num);
|
|
381
|
-
den = BigInt(leaf.den);
|
|
382
|
-
}
|
|
383
|
-
catch {
|
|
384
|
-
throw malformed(`call_arg_scaled.num/den not a decimal integer at ${path}`);
|
|
385
|
-
}
|
|
386
|
-
if (num <= 0n)
|
|
387
|
-
throw malformed(`call_arg_scaled.num must be > 0 at ${path}`);
|
|
388
|
-
if (den <= 0n)
|
|
389
|
-
throw malformed(`call_arg_scaled.den must be > 0 at ${path}`);
|
|
390
|
-
return;
|
|
391
|
-
}
|
|
392
|
-
case 'literal_u32':
|
|
393
|
-
if (!Number.isInteger(leaf.value) || leaf.value < 0 || leaf.value > U32_MAX) {
|
|
394
|
-
throw malformed(`literal_u32.value out of u32 range at ${path}`);
|
|
395
|
-
}
|
|
396
|
-
return;
|
|
397
|
-
case 'literal_i128':
|
|
398
|
-
// literal_i128 is signed; the contract allows negatives (i128
|
|
399
|
-
// arithmetic), but caps on a positive quantity (amount / window
|
|
400
|
-
// bound) should never be negative - a negative cap is silently
|
|
401
|
-
// satisfied by every non-negative amount. The contract gate is
|
|
402
|
-
// already on the leaf's ROLE (amount vs equality) not the value;
|
|
403
|
-
// here we mirror the value-only invariant the cap-set gate enforces
|
|
404
|
-
// by refusing the syntactic shape that would clearly be a bug
|
|
405
|
-
// (literal_i128 as a CAP with a leading `-` on a non-equality).
|
|
406
|
-
// We do not gate equality i128 - `literal_i128` as an address-by-
|
|
407
|
-
// equality is fine (it is just a constant).
|
|
408
|
-
// The value itself is always accepted; the structural check
|
|
409
|
-
// (non-negative for an amount / window bound) is left to the
|
|
410
|
-
// caller-built predicate, not the encoder.
|
|
411
|
-
return;
|
|
412
|
-
case 'literal_bytes':
|
|
413
|
-
// Hex even-length: a non-hex char silently drops, and an odd
|
|
414
|
-
// length yields a half-byte Buffer. The contract decodes with a
|
|
415
|
-
// strict hex parser and refuses anything that is not even-length
|
|
416
|
-
// hex; mirror that here.
|
|
417
|
-
if (!/^[0-9a-fA-F]*$/.test(leaf.value) || leaf.value.length % 2 !== 0) {
|
|
418
|
-
throw malformed(`literal_bytes.value must be even-length hex at ${path}`);
|
|
419
|
-
}
|
|
420
|
-
return;
|
|
421
|
-
case 'oracle_threshold':
|
|
422
|
-
if (!Number.isInteger(leaf.decimals) ||
|
|
423
|
-
leaf.decimals < 0 ||
|
|
424
|
-
leaf.decimals > MAX_ORACLE_THRESHOLD_DECIMALS) {
|
|
425
|
-
throw malformed(`oracle_threshold.decimals out of range (0..${MAX_ORACLE_THRESHOLD_DECIMALS}) at ${path}`);
|
|
426
|
-
}
|
|
427
|
-
return;
|
|
428
|
-
case 'literal_vec':
|
|
429
|
-
leaf.elements.forEach((e, i) => {
|
|
430
|
-
walkLeaf(e, `${path}.elements[${i}]`);
|
|
431
|
-
});
|
|
432
|
-
return;
|
|
433
|
-
// Selector and other leaves carry no cap-set values; the call_arg
|
|
434
|
-
// branches above cover indices, the literal branches cover typed
|
|
435
|
-
// constants. amount / window_spent / invocation_count / now /
|
|
436
|
-
// valid_until / call_contract / call_fn / literal_address /
|
|
437
|
-
// literal_symbol / literal_u64 / oracle_price are all value-free
|
|
438
|
-
// at this gate.
|
|
439
|
-
case 'amount':
|
|
440
|
-
case 'window_spent':
|
|
441
|
-
case 'invocation_count_in_window':
|
|
442
|
-
case 'now':
|
|
443
|
-
case 'valid_until':
|
|
444
269
|
case 'call_contract':
|
|
445
270
|
case 'call_fn':
|
|
446
271
|
case 'literal_address':
|
|
447
272
|
case 'literal_symbol':
|
|
448
|
-
case 'literal_u64':
|
|
449
|
-
case 'oracle_price':
|
|
450
273
|
return;
|
|
451
274
|
}
|
|
452
275
|
}
|
|
453
276
|
function walkNode(n, path) {
|
|
454
277
|
switch (n.op) {
|
|
455
278
|
case 'and':
|
|
456
|
-
case 'or':
|
|
457
279
|
n.children.forEach((c, i) => {
|
|
458
280
|
walkNode(c, `${path}.children[${i}]`);
|
|
459
281
|
});
|
|
460
282
|
return;
|
|
461
|
-
case 'not':
|
|
462
|
-
walkNode(n.child, `${path}.child`);
|
|
463
|
-
return;
|
|
464
283
|
case 'eq':
|
|
465
|
-
case 'lt':
|
|
466
284
|
case 'lte':
|
|
467
|
-
case 'gt':
|
|
468
|
-
case 'gte':
|
|
469
285
|
walkLeaf(n.left, `${path}.left`);
|
|
470
286
|
walkLeaf(n.right, `${path}.right`);
|
|
471
287
|
return;
|
|
@@ -17,15 +17,9 @@ export function jsonToAst(value) {
|
|
|
17
17
|
const v = value;
|
|
18
18
|
switch (v.op) {
|
|
19
19
|
case 'and':
|
|
20
|
-
|
|
21
|
-
return { op: v.op, children: arrayOf(v.children, jsonToAst) };
|
|
22
|
-
case 'not':
|
|
23
|
-
return { op: 'not', child: jsonToAst(v.child) };
|
|
20
|
+
return { op: 'and', children: arrayOf(v.children, jsonToAst) };
|
|
24
21
|
case 'eq':
|
|
25
|
-
case 'lt':
|
|
26
22
|
case 'lte':
|
|
27
|
-
case 'gt':
|
|
28
|
-
case 'gte':
|
|
29
23
|
return { op: v.op, left: jsonToLeaf(v.left), right: jsonToLeaf(v.right) };
|
|
30
24
|
case 'in':
|
|
31
25
|
return { op: 'in', needle: jsonToLeaf(v.needle), haystack: arrayOf(v.haystack, jsonToLeaf) };
|
|
@@ -45,42 +39,11 @@ function jsonToLeaf(value) {
|
|
|
45
39
|
switch (v.kind) {
|
|
46
40
|
case 'call_contract':
|
|
47
41
|
case 'call_fn':
|
|
48
|
-
case 'now':
|
|
49
|
-
case 'valid_until':
|
|
50
42
|
return { kind: v.kind };
|
|
51
43
|
case 'call_arg':
|
|
52
44
|
return { kind: 'call_arg', index: numberField(v, 'index') };
|
|
53
45
|
case 'call_arg_len':
|
|
54
46
|
return { kind: 'call_arg_len', index: numberField(v, 'index') };
|
|
55
|
-
// num/den are strings, not numbers: they are i128 on chain, and a JSON
|
|
56
|
-
// number would silently lose precision past 2^53 before the encoder
|
|
57
|
-
// ever saw the value.
|
|
58
|
-
case 'call_arg_scaled':
|
|
59
|
-
return {
|
|
60
|
-
kind: 'call_arg_scaled',
|
|
61
|
-
index: numberField(v, 'index'),
|
|
62
|
-
num: stringField(v, 'num'),
|
|
63
|
-
den: stringField(v, 'den'),
|
|
64
|
-
};
|
|
65
|
-
case 'call_arg_field':
|
|
66
|
-
return {
|
|
67
|
-
kind: 'call_arg_field',
|
|
68
|
-
index: numberField(v, 'index'),
|
|
69
|
-
element: numberField(v, 'element'),
|
|
70
|
-
field: stringField(v, 'field'),
|
|
71
|
-
};
|
|
72
|
-
case 'amount':
|
|
73
|
-
return { kind: 'amount', token: stringField(v, 'token') };
|
|
74
|
-
case 'window_spent':
|
|
75
|
-
return {
|
|
76
|
-
kind: 'window_spent',
|
|
77
|
-
token: stringField(v, 'token'),
|
|
78
|
-
windowSeconds: numberField(v, 'windowSeconds'),
|
|
79
|
-
};
|
|
80
|
-
case 'invocation_count_in_window':
|
|
81
|
-
return { kind: 'invocation_count_in_window', windowSecs: numberField(v, 'windowSecs') };
|
|
82
|
-
case 'oracle_price':
|
|
83
|
-
return { kind: 'oracle_price', asset: stringField(v, 'asset') };
|
|
84
47
|
case 'literal_address':
|
|
85
48
|
return { kind: 'literal_address', value: stringField(v, 'value') };
|
|
86
49
|
case 'literal_i128':
|
|
@@ -89,10 +52,6 @@ function jsonToLeaf(value) {
|
|
|
89
52
|
return { kind: 'literal_symbol', value: stringField(v, 'value') };
|
|
90
53
|
case 'literal_u32':
|
|
91
54
|
return { kind: 'literal_u32', value: numberField(v, 'value') };
|
|
92
|
-
case 'literal_u64':
|
|
93
|
-
return { kind: 'literal_u64', value: stringField(v, 'value') };
|
|
94
|
-
case 'literal_bytes':
|
|
95
|
-
return { kind: 'literal_bytes', value: stringField(v, 'value') };
|
|
96
55
|
case 'literal_vec':
|
|
97
56
|
return { kind: 'literal_vec', elements: arrayOf(v.elements, jsonToLeaf) };
|
|
98
57
|
default:
|
package/dist/record/decode.js
CHANGED
|
@@ -325,14 +325,9 @@ function recordInvocation(contract, fn, args, network, knownSet, knownContractSe
|
|
|
325
325
|
// parseConfidence denominator isn't inflated by repeat calls), but each
|
|
326
326
|
// unrecognised invocation is recorded separately so the numerator
|
|
327
327
|
// reflects every fail-closed hit.
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
knownOut.push(contract);
|
|
332
|
-
}
|
|
333
|
-
return;
|
|
334
|
-
}
|
|
335
|
-
if (identifyProtocol(contract, fn, args, network ?? undefined)) {
|
|
328
|
+
// `||` short-circuits, so `identifyProtocol` is still only consulted when the
|
|
329
|
+
// contract is not already in `knownSet` - the same call order as before.
|
|
330
|
+
if (knownSet.has(contract) || identifyProtocol(contract, fn, args, network ?? undefined)) {
|
|
336
331
|
if (!knownContractSet.has(contract)) {
|
|
337
332
|
knownContractSet.add(contract);
|
|
338
333
|
knownOut.push(contract);
|
|
@@ -16,15 +16,21 @@ export declare function isBelowThreshold(c: ParseConfidence): boolean;
|
|
|
16
16
|
* under-confidence recording.
|
|
17
17
|
*
|
|
18
18
|
* The remediation text branches on which diagnostic bucket is non-empty:
|
|
19
|
-
* - unknownContracts only ->
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
* - neither (the "denom === 0" path) ->
|
|
29
|
-
*
|
|
19
|
+
* - unknownContracts only -> the contract is not in the compiled-in
|
|
20
|
+
* registry. NOT "the contract has no interface": most Soroban
|
|
21
|
+
* contracts publish a typed spec on chain, this package just never
|
|
22
|
+
* reads one. The reason code stays `no-abi` because it is a published
|
|
23
|
+
* enum value, so the TEXT has to carry the correction.
|
|
24
|
+
* - opaqueScVals only -> the recorder met a value shape it should
|
|
25
|
+
* support but did not decode. That is a decoder bug, and the guidance
|
|
26
|
+
* says so rather than implying the caller can fix it.
|
|
27
|
+
* - both -> list both diagnostics and chain the right remediation.
|
|
28
|
+
* - neither (the "denom === 0" path) -> the caller did not actually hit
|
|
29
|
+
* a code-level barrier.
|
|
30
|
+
*
|
|
31
|
+
* Do NOT tell the caller to supply an ABI. There is no input for one:
|
|
32
|
+
* recognition runs off the compiled-in registry plus a known-contract set
|
|
33
|
+
* that no public tool boundary exposes. The old text named an action
|
|
34
|
+
* nobody could take and sent users looking for a file they do not have and
|
|
35
|
+
* would not need. */
|
|
30
36
|
export declare function buildLowConfidenceQuestion(c: ParseConfidence): string;
|