@callcium/sdk 0.12.0 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/index.d.ts +659 -611
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2285 -2071
- package/dist/index.js.map +1 -1
- package/package.json +13 -10
- package/src/bytes.ts +1 -0
- package/src/calldata-reader.ts +3 -3
- package/src/constants.ts +156 -86
- package/src/constraint.ts +34 -24
- package/src/descriptor-coder.ts +126 -103
- package/src/descriptor.ts +79 -288
- package/src/errors.ts +30 -19
- package/src/hint.ts +237 -0
- package/src/index.ts +29 -10
- package/src/operators.ts +168 -267
- package/src/path.ts +7 -31
- package/src/policy-builder.ts +201 -181
- package/src/policy-coder.ts +1156 -523
- package/src/policy-enforcer.ts +499 -486
- package/src/policy-validator.ts +557 -1046
- package/src/signature.ts +7 -11
- package/src/type-desc.ts +6 -6
- package/src/types.ts +230 -129
- package/src/validation-issue.ts +78 -465
package/src/signature.ts
CHANGED
|
@@ -31,14 +31,14 @@ function isAlphanum(char: number): boolean {
|
|
|
31
31
|
/**
|
|
32
32
|
* Parse an ABI function signature into its selector and types.
|
|
33
33
|
*
|
|
34
|
-
* Strict mode: no space, tab, LF or CR
|
|
35
|
-
* the final `)` as the last character.
|
|
34
|
+
* Strict mode: no space, tab, LF or CR. The name must match `[A-Za-z_][A-Za-z0-9_]*`. The structure must be
|
|
35
|
+
* `name(types)` with the final `)` as the last character.
|
|
36
36
|
*
|
|
37
|
-
* @param signature - A function signature string,
|
|
37
|
+
* @param signature - A function signature string, for example `"transfer(address,uint256)"`.
|
|
38
38
|
* @returns The 4-byte selector and the comma-separated types string.
|
|
39
39
|
* @throws {CallciumError} with code `"MALFORMED_SIGNATURE"`, `"SIGNATURE_CONTAINS_WHITESPACE"`, or
|
|
40
|
-
* `"INVALID_FUNCTION_NAME"` if the signature is malformed. The types between the parentheses are returned unread
|
|
41
|
-
*
|
|
40
|
+
* `"INVALID_FUNCTION_NAME"` if the signature is malformed. The types between the parentheses are returned unread.
|
|
41
|
+
* The type parser determines whether the types string names types.
|
|
42
42
|
*/
|
|
43
43
|
function parse(signature: string): ParsedSignature {
|
|
44
44
|
if (signature.length < 3) {
|
|
@@ -68,12 +68,8 @@ function parse(signature: string): ParsedSignature {
|
|
|
68
68
|
throw new CallciumError("MALFORMED_SIGNATURE", `Invalid signature: must end with ")", got "${signature}"`);
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
// Validate function name: [A-Za-z_][A-Za-z0-9_]*. Every character outside ASCII carries a code unit above the
|
|
76
|
-
// alphanumeric ranges, so the same test rejects it.
|
|
71
|
+
// Validate function name: [A-Za-z_][A-Za-z0-9_]*. An empty name leaves `(` first, which fails the first-character
|
|
72
|
+
// test; every character outside ASCII carries a code unit above the alphanumeric ranges and fails the same tests.
|
|
77
73
|
const firstChar = signature.charCodeAt(0);
|
|
78
74
|
if (!isAlpha(firstChar) && firstChar !== 0x5f) {
|
|
79
75
|
throw new CallciumError("INVALID_FUNCTION_NAME", "Function name must start with a letter or underscore");
|
package/src/type-desc.ts
CHANGED
|
@@ -42,11 +42,11 @@ export function function_(): Uint8Array {
|
|
|
42
42
|
/**
|
|
43
43
|
* Build a descriptor node for `uintN`.
|
|
44
44
|
* @param bits - Bit width, 8–256 in steps of 8.
|
|
45
|
-
* @throws {CallciumError} With code `
|
|
45
|
+
* @throws {CallciumError} With code `INVALID_UINT_BITS` if bits is out of range or not a multiple of 8.
|
|
46
46
|
*/
|
|
47
47
|
export function uintN(bits: number): Uint8Array {
|
|
48
48
|
if (bits < 8 || bits > 256 || bits % 8 !== 0) {
|
|
49
|
-
throw new CallciumError("
|
|
49
|
+
throw new CallciumError("INVALID_UINT_BITS", `Invalid uint width ${bits}`);
|
|
50
50
|
}
|
|
51
51
|
return new Uint8Array([TypeCode.UINT_MIN + (bits / 8 - 1)]);
|
|
52
52
|
}
|
|
@@ -54,11 +54,11 @@ export function uintN(bits: number): Uint8Array {
|
|
|
54
54
|
/**
|
|
55
55
|
* Build a descriptor node for `intN`.
|
|
56
56
|
* @param bits - Bit width, 8–256 in steps of 8.
|
|
57
|
-
* @throws {CallciumError} With code `
|
|
57
|
+
* @throws {CallciumError} With code `INVALID_INT_BITS` if bits is out of range or not a multiple of 8.
|
|
58
58
|
*/
|
|
59
59
|
export function intN(bits: number): Uint8Array {
|
|
60
60
|
if (bits < 8 || bits > 256 || bits % 8 !== 0) {
|
|
61
|
-
throw new CallciumError("
|
|
61
|
+
throw new CallciumError("INVALID_INT_BITS", `Invalid int width ${bits}`);
|
|
62
62
|
}
|
|
63
63
|
return new Uint8Array([TypeCode.INT_MIN + (bits / 8 - 1)]);
|
|
64
64
|
}
|
|
@@ -76,11 +76,11 @@ export function string_(): Uint8Array {
|
|
|
76
76
|
/**
|
|
77
77
|
* Build a descriptor node for `bytesN`.
|
|
78
78
|
* @param length - Byte width, 1–32.
|
|
79
|
-
* @throws {CallciumError} With code `
|
|
79
|
+
* @throws {CallciumError} With code `INVALID_BYTES_LENGTH` if length is out of range.
|
|
80
80
|
*/
|
|
81
81
|
export function bytesN(length: number): Uint8Array {
|
|
82
82
|
if (length < 1 || length > 32) {
|
|
83
|
-
throw new CallciumError("
|
|
83
|
+
throw new CallciumError("INVALID_BYTES_LENGTH", `Invalid bytes length ${length}`);
|
|
84
84
|
}
|
|
85
85
|
return new Uint8Array([TypeCode.FIXED_BYTES_MIN + (length - 1)]);
|
|
86
86
|
}
|
package/src/types.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type { ComparisonCode, QuantifierCode, Scope, ScopeCode, Source } from "./constants";
|
|
2
|
+
|
|
1
3
|
///////////////////////////////////////////////////////////////////////////
|
|
2
4
|
// Primitives
|
|
3
5
|
///////////////////////////////////////////////////////////////////////////
|
|
@@ -11,6 +13,9 @@ export type Address = `0x${string}`;
|
|
|
11
13
|
/** Byte range in a source blob. */
|
|
12
14
|
export type Span = { start: number; end: number };
|
|
13
15
|
|
|
16
|
+
/** A decoded value with its byte position in the source blob. */
|
|
17
|
+
export type Field<T> = { value: T; span: Span };
|
|
18
|
+
|
|
14
19
|
///////////////////////////////////////////////////////////////////////////
|
|
15
20
|
// Enforce
|
|
16
21
|
///////////////////////////////////////////////////////////////////////////
|
|
@@ -51,51 +56,54 @@ export type SelectorMismatchViolation = {
|
|
|
51
56
|
};
|
|
52
57
|
|
|
53
58
|
/**
|
|
54
|
-
* A
|
|
55
|
-
*
|
|
59
|
+
* A test read a context property not supplied in the execution context — a context test's subject, or a context
|
|
60
|
+
* operand on either kind of test.
|
|
61
|
+
*
|
|
62
|
+
* `group` and `rule` locate the root rule. A test in a loop body adds `bodyRule` and `elementIndex`.
|
|
56
63
|
*/
|
|
57
64
|
export type MissingContextViolation = {
|
|
58
65
|
code: "MISSING_CONTEXT";
|
|
59
66
|
group: number;
|
|
60
67
|
rule: number;
|
|
61
|
-
scope:
|
|
68
|
+
scope: ScopeCode;
|
|
69
|
+
/** The test's path: a context test's property ID, or a calldata test's steps (in a loop body, from the element). */
|
|
62
70
|
path: Hex;
|
|
63
71
|
/** Declared type of the missing context property. */
|
|
64
72
|
typeCode: number;
|
|
65
|
-
/**
|
|
66
|
-
|
|
67
|
-
/**
|
|
68
|
-
|
|
73
|
+
/** The test's operator, absent for a context test's subject. */
|
|
74
|
+
operator?: DecodedOperator;
|
|
75
|
+
/** Position of the test in the loop body, absent for a root test. */
|
|
76
|
+
bodyRule?: number;
|
|
77
|
+
/** Position of the element the body test read, absent for a root test. */
|
|
78
|
+
elementIndex?: number;
|
|
69
79
|
};
|
|
70
80
|
|
|
71
81
|
/**
|
|
72
|
-
* A
|
|
73
|
-
*
|
|
74
|
-
* Field combinations:
|
|
75
|
-
* - `resolvedValue` present, `elementIndex` absent — scalar leaf or context value that failed the operator.
|
|
76
|
-
* - `resolvedValue` present, `elementIndex` present — universal-quantifier per-element failure.
|
|
77
|
-
* - `resolvedValue` absent, `elementIndex` absent — existential-aggregate failure (no element satisfied).
|
|
82
|
+
* A test's operator returned false against the loaded value.
|
|
78
83
|
*
|
|
79
|
-
*
|
|
84
|
+
* `resolvedValue` is the value the operator compared: a word, or for length operators the length as a 32-byte word.
|
|
85
|
+
* `group` and `rule` locate the root rule. A test in a loop body adds `bodyRule` and `elementIndex`.
|
|
80
86
|
*/
|
|
81
87
|
export type ValueMismatchViolation = {
|
|
82
88
|
code: "VALUE_MISMATCH";
|
|
83
89
|
group: number;
|
|
84
90
|
rule: number;
|
|
85
|
-
scope:
|
|
91
|
+
scope: ScopeCode;
|
|
92
|
+
/** The test's path: a context test's property ID, or a calldata test's steps (in a loop body, from the element). */
|
|
86
93
|
path: Hex;
|
|
87
|
-
/**
|
|
88
|
-
|
|
89
|
-
/**
|
|
90
|
-
operandData: Hex;
|
|
91
|
-
/** Type code of the failing value's leaf. */
|
|
94
|
+
/** The test's operator. */
|
|
95
|
+
operator: DecodedOperator;
|
|
96
|
+
/** Type code of the failing value. */
|
|
92
97
|
typeCode: number;
|
|
93
|
-
resolvedValue
|
|
98
|
+
resolvedValue: Hex;
|
|
94
99
|
/**
|
|
95
|
-
* Resolved runtime value of a
|
|
96
|
-
*
|
|
100
|
+
* Resolved runtime value of a context operand as a 32-byte word, present when the operator's operand names a context
|
|
101
|
+
* property, not a constant.
|
|
97
102
|
*/
|
|
98
103
|
resolvedOperand?: Hex;
|
|
104
|
+
/** Position of the test in the loop body, absent for a root test. */
|
|
105
|
+
bodyRule?: number;
|
|
106
|
+
/** Position of the element the body test read, absent for a root test. */
|
|
99
107
|
elementIndex?: number;
|
|
100
108
|
};
|
|
101
109
|
|
|
@@ -107,19 +115,21 @@ type CalldataNavigationVariant<C extends NavigationViolationCode> = {
|
|
|
107
115
|
code: C;
|
|
108
116
|
group: number;
|
|
109
117
|
rule: number;
|
|
110
|
-
scope:
|
|
118
|
+
scope: ScopeCode;
|
|
111
119
|
path: Hex;
|
|
112
|
-
|
|
113
|
-
operandData?: Hex;
|
|
120
|
+
operator?: DecodedOperator;
|
|
114
121
|
typeCode?: number;
|
|
122
|
+
bodyRule?: number;
|
|
115
123
|
elementIndex?: number;
|
|
116
124
|
};
|
|
117
125
|
|
|
118
126
|
/**
|
|
119
|
-
* Calldata structure prevented
|
|
127
|
+
* Calldata structure prevented a rule from being evaluated.
|
|
120
128
|
*
|
|
121
|
-
* The operator was never applied
|
|
122
|
-
* describing the failing site, not a constraint claim.
|
|
129
|
+
* The operator was never applied. `operator`, `typeCode`, `bodyRule` and `elementIndex` are diagnostic context
|
|
130
|
+
* describing the failing site, not a constraint claim. A failure resolving a loop's array carries the loop
|
|
131
|
+
* rule's scope and array path and no operator. A failure resolving an element adds `elementIndex`. A failure in a body
|
|
132
|
+
* test adds the test's fields and `bodyRule`.
|
|
123
133
|
*
|
|
124
134
|
* A union of per-code variants, narrowable with `Extract<Violation, { code: "..." }>`.
|
|
125
135
|
*/
|
|
@@ -127,12 +137,13 @@ export type CalldataNavigationViolation =
|
|
|
127
137
|
| CalldataNavigationVariant<"CALLDATA_OUT_OF_BOUNDS">
|
|
128
138
|
| CalldataNavigationVariant<"ARRAY_INDEX_OUT_OF_BOUNDS">;
|
|
129
139
|
|
|
130
|
-
/** A
|
|
140
|
+
/** A loop's array exceeded `MAX_QUANTIFIED_ARRAY_LENGTH`. */
|
|
131
141
|
export type QuantifierLimitExceededViolation = {
|
|
132
142
|
code: "QUANTIFIER_LIMIT_EXCEEDED";
|
|
133
143
|
group: number;
|
|
134
144
|
rule: number;
|
|
135
|
-
scope:
|
|
145
|
+
scope: ScopeCode;
|
|
146
|
+
/** The loop's array path. */
|
|
136
147
|
path: Hex;
|
|
137
148
|
/** Hex-encoded element count of the offending array. */
|
|
138
149
|
resolvedValue: Hex;
|
|
@@ -147,30 +158,34 @@ export type NonCanonicalValueViolation = {
|
|
|
147
158
|
code: "NON_CANONICAL_VALUE";
|
|
148
159
|
group: number;
|
|
149
160
|
rule: number;
|
|
150
|
-
scope:
|
|
161
|
+
scope: ScopeCode;
|
|
151
162
|
path: Hex;
|
|
152
163
|
/** Type code of the target whose encoding was violated. */
|
|
153
164
|
typeCode: number;
|
|
154
165
|
/** Raw 32-byte word as it appeared in calldata, before any masking. */
|
|
155
166
|
resolvedValue: Hex;
|
|
156
|
-
|
|
157
|
-
|
|
167
|
+
/** The test's operator. */
|
|
168
|
+
operator: DecodedOperator;
|
|
169
|
+
/** Position of the test in the loop body, absent for a root test. */
|
|
170
|
+
bodyRule?: number;
|
|
171
|
+
/** Position of the element the body test read, absent for a root test. */
|
|
158
172
|
elementIndex?: number;
|
|
159
173
|
};
|
|
160
174
|
|
|
161
|
-
/**
|
|
162
|
-
export type
|
|
163
|
-
code: "
|
|
175
|
+
/** No element satisfied an `ANY` loop's body, the empty array included. No element and no test is to blame. */
|
|
176
|
+
export type QuantifierNoMatchViolation = {
|
|
177
|
+
code: "QUANTIFIER_NO_MATCH";
|
|
164
178
|
group: number;
|
|
165
179
|
rule: number;
|
|
166
|
-
scope:
|
|
180
|
+
scope: ScopeCode;
|
|
181
|
+
/** The loop's array path. */
|
|
167
182
|
path: Hex;
|
|
168
183
|
};
|
|
169
184
|
|
|
170
185
|
/**
|
|
171
186
|
* Structured details of a single rule failure during enforcement.
|
|
172
187
|
*
|
|
173
|
-
* Carries semantic data only
|
|
188
|
+
* Carries semantic data only, with no message strings. Discriminate on `code` to narrow to the matching variant.
|
|
174
189
|
*/
|
|
175
190
|
export type Violation =
|
|
176
191
|
| MissingSelectorViolation
|
|
@@ -180,66 +195,188 @@ export type Violation =
|
|
|
180
195
|
| CalldataNavigationViolation
|
|
181
196
|
| NonCanonicalValueViolation
|
|
182
197
|
| QuantifierLimitExceededViolation
|
|
183
|
-
|
|
|
198
|
+
| QuantifierNoMatchViolation;
|
|
184
199
|
|
|
185
200
|
/** Machine-readable reason code for an enforcement violation. Derived from `Violation` to prevent drift. */
|
|
186
201
|
export type ViolationCode = Violation["code"];
|
|
187
202
|
|
|
188
203
|
///////////////////////////////////////////////////////////////////////////
|
|
189
|
-
//
|
|
204
|
+
// Policy types
|
|
190
205
|
///////////////////////////////////////////////////////////////////////////
|
|
191
206
|
|
|
192
|
-
/**
|
|
207
|
+
/** A test or a context test of a loop body, or of a group. */
|
|
208
|
+
export type BodyRule = {
|
|
209
|
+
scope: typeof Scope.CONTEXT | typeof Scope.CALLDATA;
|
|
210
|
+
/** BE16-encoded path from the starting point. A context test's path is its property ID. */
|
|
211
|
+
path: Hex;
|
|
212
|
+
/** The hint a blob carries for the path, absent for a context test and in data not read from a blob. */
|
|
213
|
+
hint?: Hex;
|
|
214
|
+
/** Encoded operator: opCode(1) || each operand value as a 32-byte word, a context property as its ID. */
|
|
215
|
+
operator: Hex;
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
/** A loop: its body over each element of the array at `path`. */
|
|
219
|
+
export type LoopRule = {
|
|
220
|
+
scope: typeof Scope.LOOP;
|
|
221
|
+
quantifier: QuantifierCode;
|
|
222
|
+
/** BE16-encoded path from the arguments to the array. */
|
|
223
|
+
path: Hex;
|
|
224
|
+
/** The frame hint a blob carries for the path, absent in data not read from a blob. */
|
|
225
|
+
hint?: Hex;
|
|
226
|
+
body: BodyRule[];
|
|
227
|
+
};
|
|
228
|
+
|
|
229
|
+
/** A rule of a group: a test, a context test or a loop. */
|
|
230
|
+
export type Rule = BodyRule | LoopRule;
|
|
231
|
+
|
|
232
|
+
/** A policy: its selector, its descriptor and its groups of rules. */
|
|
193
233
|
export type PolicyData = {
|
|
194
234
|
isSelectorless: boolean;
|
|
195
235
|
selector: Hex;
|
|
196
236
|
descriptor: Hex;
|
|
197
|
-
groups:
|
|
198
|
-
span?: Span;
|
|
237
|
+
groups: Rule[][];
|
|
199
238
|
};
|
|
200
239
|
|
|
240
|
+
/**
|
|
241
|
+
* An operator as a policy stores it: its comparison, its polarity and its operands. Constants are loaded to 32-byte
|
|
242
|
+
* words of `operandType`. A context operand names its property.
|
|
243
|
+
*/
|
|
244
|
+
export type DecodedOperator = {
|
|
245
|
+
/** Operator code with the `Op.NOT` bit intact. */
|
|
246
|
+
opCode: number;
|
|
247
|
+
/** Comparison bits of `opCode`. */
|
|
248
|
+
comparison: ComparisonCode;
|
|
249
|
+
/** True when `opCode` carries the `Op.NOT` bit. */
|
|
250
|
+
negated: boolean;
|
|
251
|
+
/**
|
|
252
|
+
* Type code of the operands: the target's type, the type a length compares as for a length comparison,
|
|
253
|
+
* or the context property's type.
|
|
254
|
+
*/
|
|
255
|
+
operandType: number;
|
|
256
|
+
} & (
|
|
257
|
+
| {
|
|
258
|
+
source: typeof Source.CONSTANT;
|
|
259
|
+
/** Each constant loaded to its 32-byte word. */
|
|
260
|
+
values: Hex[];
|
|
261
|
+
}
|
|
262
|
+
| {
|
|
263
|
+
source: typeof Source.CONTEXT;
|
|
264
|
+
/** The context property the operand names. */
|
|
265
|
+
contextPropertyId: number;
|
|
266
|
+
}
|
|
267
|
+
);
|
|
268
|
+
|
|
201
269
|
/** A collection of operators targeting a specific value. */
|
|
202
270
|
export type Constraint = {
|
|
203
|
-
scope:
|
|
271
|
+
scope: typeof Scope.CONTEXT | typeof Scope.CALLDATA;
|
|
204
272
|
path: Hex;
|
|
205
273
|
operators: Hex[];
|
|
206
|
-
/** Compiled hint block as carried on the wire; absent when the constraint has no encoding. */
|
|
207
|
-
hint?: Hex;
|
|
208
|
-
span?: Span;
|
|
209
274
|
};
|
|
210
275
|
|
|
211
276
|
///////////////////////////////////////////////////////////////////////////
|
|
212
|
-
//
|
|
277
|
+
// Inspection
|
|
213
278
|
///////////////////////////////////////////////////////////////////////////
|
|
214
279
|
|
|
215
|
-
/** A
|
|
216
|
-
export type
|
|
217
|
-
|
|
218
|
-
/** Structural representation of a decoded parameter within the descriptor. */
|
|
219
|
-
export type DecodedParam = {
|
|
220
|
-
index: number;
|
|
280
|
+
/** A descriptor node: its type, its head size and the bytes it spans, with its fields or its element. */
|
|
281
|
+
export type DecodedNode = {
|
|
221
282
|
typeCode: number;
|
|
222
283
|
isDynamic: boolean;
|
|
284
|
+
/** Head size in bytes, zero for a dynamic type. */
|
|
223
285
|
staticSize: number;
|
|
224
|
-
|
|
286
|
+
span: Span;
|
|
287
|
+
} & (
|
|
288
|
+
| { typeClass: "elementary" }
|
|
289
|
+
| { typeClass: "tuple"; fields: DecodedNode[] }
|
|
290
|
+
| { typeClass: "staticArray"; length: number; element: DecodedNode }
|
|
291
|
+
| { typeClass: "dynamicArray"; element: DecodedNode }
|
|
292
|
+
);
|
|
293
|
+
|
|
294
|
+
/** A descriptor: its header fields and one node per parameter. */
|
|
295
|
+
export type DecodedDescriptor = {
|
|
296
|
+
version: Field<number>;
|
|
297
|
+
paramCount: Field<number>;
|
|
298
|
+
params: DecodedNode[];
|
|
299
|
+
span: Span;
|
|
300
|
+
};
|
|
301
|
+
|
|
302
|
+
/** An array's meta word. */
|
|
303
|
+
export type ArrayMeta = {
|
|
304
|
+
/** The element type is dynamic. An offset word points to each element. */
|
|
305
|
+
isElementDynamic: boolean;
|
|
306
|
+
/** The array is dynamic. A length word is before the elements. */
|
|
307
|
+
isDynamicArray: boolean;
|
|
308
|
+
/** Element head size in words. */
|
|
309
|
+
strideWords: number;
|
|
310
|
+
};
|
|
311
|
+
|
|
312
|
+
/** A hop of a compiled hint: a plain hop enters the node behind an offset word, an element hop selects an element. */
|
|
313
|
+
export type DecodedHop = {
|
|
314
|
+
kind: "plain" | "element";
|
|
315
|
+
delta: Field<number>;
|
|
316
|
+
index: Field<number>;
|
|
317
|
+
meta: Field<ArrayMeta>;
|
|
225
318
|
span: Span;
|
|
226
319
|
};
|
|
227
320
|
|
|
228
|
-
/**
|
|
229
|
-
export type
|
|
321
|
+
/** A test hint: its hops, then the target's position, meta word and type code. */
|
|
322
|
+
export type DecodedHint = {
|
|
323
|
+
hopCount: Field<number>;
|
|
324
|
+
hops: DecodedHop[];
|
|
325
|
+
targetDelta: Field<number>;
|
|
326
|
+
/** The target's meta word. Every field is false or zero unless the target is a dynamic array. */
|
|
327
|
+
targetMeta: Field<ArrayMeta>;
|
|
328
|
+
typeCode: Field<number>;
|
|
329
|
+
span: Span;
|
|
330
|
+
};
|
|
331
|
+
|
|
332
|
+
/** A frame hint: its hops, then the array's position, declared length and meta word. */
|
|
333
|
+
export type DecodedFrameHint = {
|
|
334
|
+
hopCount: Field<number>;
|
|
335
|
+
hops: DecodedHop[];
|
|
336
|
+
arrayDelta: Field<number>;
|
|
337
|
+
/** Declared length of a static array, or zero for a dynamic array. */
|
|
338
|
+
count: Field<number>;
|
|
339
|
+
meta: Field<ArrayMeta>;
|
|
340
|
+
span: Span;
|
|
341
|
+
};
|
|
342
|
+
|
|
343
|
+
/** A test or a context test, each field with the bytes that contain it. */
|
|
344
|
+
export type DecodedTest = {
|
|
230
345
|
ruleSize: Field<number>;
|
|
231
|
-
scope: Field<
|
|
346
|
+
scope: Field<typeof Scope.CONTEXT | typeof Scope.CALLDATA>;
|
|
232
347
|
pathDepth: Field<number>;
|
|
348
|
+
/** The path steps, or a context test's property ID. */
|
|
233
349
|
path: Field<Hex>;
|
|
234
|
-
/**
|
|
235
|
-
hint?:
|
|
350
|
+
/** The test hint, absent for a context test. */
|
|
351
|
+
hint?: DecodedHint;
|
|
352
|
+
/** Type code of the target: the hint's, or the context property's. */
|
|
353
|
+
targetType: number;
|
|
236
354
|
opCode: Field<number>;
|
|
237
355
|
dataLength: Field<number>;
|
|
356
|
+
/** The operands as stored. */
|
|
238
357
|
data: Field<Hex>;
|
|
358
|
+
operator: DecodedOperator;
|
|
359
|
+
span: Span;
|
|
360
|
+
};
|
|
361
|
+
|
|
362
|
+
/** A loop rule, each field with the bytes that contain it. */
|
|
363
|
+
export type DecodedLoop = {
|
|
364
|
+
ruleSize: Field<number>;
|
|
365
|
+
scope: Field<typeof Scope.LOOP>;
|
|
366
|
+
quantifier: Field<QuantifierCode>;
|
|
367
|
+
pathDepth: Field<number>;
|
|
368
|
+
/** The array's path. */
|
|
369
|
+
path: Field<Hex>;
|
|
370
|
+
hint: DecodedFrameHint;
|
|
371
|
+
ruleCount: Field<number>;
|
|
372
|
+
body: DecodedTest[];
|
|
239
373
|
span: Span;
|
|
240
374
|
};
|
|
241
375
|
|
|
242
|
-
/**
|
|
376
|
+
/** A rule of a group: a test, a context test or a loop. */
|
|
377
|
+
export type DecodedRule = DecodedTest | DecodedLoop;
|
|
378
|
+
|
|
379
|
+
/** A group, each field with the bytes that contain it. */
|
|
243
380
|
export type DecodedGroup = {
|
|
244
381
|
ruleCount: Field<number>;
|
|
245
382
|
groupSize: Field<number>;
|
|
@@ -247,101 +384,65 @@ export type DecodedGroup = {
|
|
|
247
384
|
span: Span;
|
|
248
385
|
};
|
|
249
386
|
|
|
250
|
-
/**
|
|
387
|
+
/** A policy blob, each field with the bytes that contain it. */
|
|
251
388
|
export type DecodedPolicy = {
|
|
252
|
-
header: Field<number>;
|
|
389
|
+
header: Field<{ major: number; minor: number; isSelectorless: boolean }>;
|
|
390
|
+
/** The selector slot, zero for a selectorless policy. */
|
|
253
391
|
selector: Field<Hex>;
|
|
254
392
|
descLength: Field<number>;
|
|
255
|
-
descriptor:
|
|
393
|
+
descriptor: DecodedDescriptor;
|
|
256
394
|
groupCount: Field<number>;
|
|
257
395
|
groups: DecodedGroup[];
|
|
258
396
|
span: Span;
|
|
259
|
-
version: number;
|
|
260
|
-
isSelectorless: boolean;
|
|
261
397
|
};
|
|
262
398
|
|
|
263
399
|
///////////////////////////////////////////////////////////////////////////
|
|
264
|
-
// Validation
|
|
400
|
+
// Validation
|
|
265
401
|
///////////////////////////////////////////////////////////////////////////
|
|
266
402
|
|
|
267
403
|
/** Severity of a validation issue. */
|
|
268
404
|
export type IssueSeverity = "info" | "warning" | "error";
|
|
269
405
|
|
|
270
|
-
/**
|
|
406
|
+
/** Kind of defect a validation issue reports. */
|
|
271
407
|
export type IssueCategory = "typeMismatch" | "contradiction" | "redundancy" | "vacuity" | "compatibility";
|
|
272
408
|
|
|
273
|
-
/**
|
|
274
|
-
* Machine-readable code for a policy validation issue.
|
|
275
|
-
*
|
|
276
|
-
* Each code names one finding of the semantic validator, matching the same-named Solidity `IssueCode` constant.
|
|
277
|
-
*/
|
|
409
|
+
/** Machine-readable code of a validation issue. */
|
|
278
410
|
export type IssueCode =
|
|
279
|
-
|
|
280
|
-
| "
|
|
281
|
-
| "
|
|
411
|
+
| "INVALID_DESCRIPTOR"
|
|
412
|
+
| "UNNAVIGABLE_PATH"
|
|
413
|
+
| "QUANTIFIER_ON_NON_ARRAY"
|
|
414
|
+
| "HINT_MISMATCH"
|
|
282
415
|
| "NUMERIC_OP_ON_NON_NUMERIC"
|
|
283
416
|
| "BITMASK_ON_INVALID"
|
|
284
417
|
| "IN_ON_BOOL"
|
|
285
|
-
| "LENGTH_ON_STATIC"
|
|
286
418
|
| "CONTEXT_TYPE_MISMATCH"
|
|
287
|
-
| "UNKNOWN_OPERATOR"
|
|
288
|
-
| "NON_CANONICAL_OPERAND"
|
|
289
|
-
| "UNNAVIGABLE_PATH"
|
|
290
|
-
| "NESTED_QUANTIFIER"
|
|
291
|
-
| "HINT_MISMATCH"
|
|
292
|
-
// Contradiction.
|
|
293
|
-
| "EQ_NEQ_CONTRADICTION"
|
|
294
|
-
| "CONFLICTING_EQUALITY"
|
|
295
419
|
| "OUT_OF_PHYSICAL_BOUNDS"
|
|
296
|
-
| "
|
|
297
|
-
| "
|
|
298
|
-
| "BOUNDS_EXCLUDE_EQUALITY"
|
|
299
|
-
| "IMPOSSIBLE_RANGE"
|
|
300
|
-
| "SET_EXCLUDES_EQUALITY"
|
|
301
|
-
| "EMPTY_SET_INTERSECTION"
|
|
302
|
-
| "SET_FULLY_EXCLUDED"
|
|
303
|
-
| "LENGTH_EQ_NEQ_CONTRADICTION"
|
|
304
|
-
| "CONFLICTING_LENGTH"
|
|
305
|
-
| "BOUNDS_EXCLUDE_LENGTH"
|
|
306
|
-
| "IMPOSSIBLE_LENGTH_RANGE"
|
|
307
|
-
| "OUT_OF_PHYSICAL_LENGTH_BOUNDS"
|
|
308
|
-
| "IMPOSSIBLE_LENGTH_GT"
|
|
309
|
-
| "IMPOSSIBLE_LENGTH_LT"
|
|
310
|
-
| "BITMASK_CONTRADICTION"
|
|
311
|
-
| "BITMASK_ANY_IMPOSSIBLE"
|
|
312
|
-
| "UNSORTED_IN_SET"
|
|
313
|
-
| "EMPTY_GROUP"
|
|
314
|
-
// Redundancy.
|
|
420
|
+
| "CONTRADICTORY_RULE"
|
|
421
|
+
| "REDUNDANT_RULE"
|
|
315
422
|
| "DOMINATED_BOUND"
|
|
316
|
-
| "REDUNDANT_BOUND"
|
|
317
|
-
| "SET_REDUCTION"
|
|
318
|
-
| "SET_REDUNDANCY"
|
|
319
|
-
| "SET_PARTIALLY_EXCLUDED"
|
|
320
423
|
| "DOMINATED_LENGTH_BOUND"
|
|
321
|
-
| "
|
|
322
|
-
| "REDUNDANT_BITMASK"
|
|
323
|
-
| "DUPLICATE_CONSTRAINT"
|
|
324
|
-
| "FUSIBLE_RANGE"
|
|
325
|
-
| "FUSIBLE_LENGTH_RANGE"
|
|
326
|
-
// Vacuity.
|
|
327
|
-
| "VACUOUS_GTE"
|
|
328
|
-
| "VACUOUS_LTE"
|
|
329
|
-
| "VACUOUS_LENGTH_GTE"
|
|
330
|
-
| "VACUOUS_LENGTH_LTE"
|
|
331
|
-
| "VACUOUS_NEGATED_RANGE"
|
|
332
|
-
| "VACUOUS_NEGATED_LENGTH_RANGE"
|
|
333
|
-
// Compatibility.
|
|
334
|
-
| "PATH_DEPTH_EXCEEDED"
|
|
424
|
+
| "VACUOUS_RULE"
|
|
335
425
|
| "QUANTIFIER_OVER_STATIC_LIMIT"
|
|
336
|
-
| "
|
|
337
|
-
|
|
426
|
+
| "CONTEXT_ONLY_BODY";
|
|
427
|
+
|
|
428
|
+
/**
|
|
429
|
+
* The rule an issue names: its group, its position in the group and, for a test in a loop body, its position in the
|
|
430
|
+
* body. The location also carries the rule's scope, path and operator as decoded.
|
|
431
|
+
* A path in a body starts at the element.
|
|
432
|
+
*/
|
|
433
|
+
export type RuleLocation = {
|
|
434
|
+
groupIndex: number;
|
|
435
|
+
ruleIndex: number;
|
|
436
|
+
bodyRuleIndex?: number;
|
|
437
|
+
scope: ScopeCode;
|
|
438
|
+
path: Hex;
|
|
439
|
+
operator?: Hex;
|
|
440
|
+
};
|
|
338
441
|
|
|
339
|
-
/** A
|
|
340
|
-
export type Issue = {
|
|
442
|
+
/** A validation issue: the rule it names, absent for one about the descriptor, and the values that show the defect. */
|
|
443
|
+
export type Issue = Partial<RuleLocation> & {
|
|
341
444
|
severity: IssueSeverity;
|
|
342
445
|
category: IssueCategory;
|
|
343
|
-
groupIndex: number;
|
|
344
|
-
constraintIndex: number;
|
|
345
446
|
code: IssueCode;
|
|
346
447
|
value1: Hex;
|
|
347
448
|
value2: Hex;
|