@callcium/sdk 0.9.0 → 0.10.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.
@@ -1,31 +1,28 @@
1
1
  import { bigintToHex, bytesToHex, hexToBytes } from "./bytes";
2
- import {
3
- classifyTypeCode,
4
- isQuantifier,
5
- isValidOperatorData,
6
- Limits,
7
- lookupContextProperty,
8
- MAX_CONTEXT_PROPERTY_ID,
9
- Op,
10
- Quantifier,
11
- Scope,
12
- TypeCode,
13
- } from "./constants";
2
+ import { PolicyFormat, lookupContextProperty, MAX_CONTEXT_PROPERTY_ID, Op, Scope, TypeCode } from "./constants";
14
3
  import { Descriptor, type TypeInfo } from "./descriptor";
15
4
  import { decodeDescriptor } from "./descriptor-coder";
16
5
  import { CallciumError, type CallciumErrorCode } from "./errors";
17
- import { canonicalize, isLeftAligned, isSigned, isLengthOp, isLengthValidType } from "./operators";
18
- import { parsePathSteps } from "./policy-coder";
6
+ import {
7
+ canonicalize,
8
+ isLeftAligned,
9
+ isSigned,
10
+ isLengthOp,
11
+ isLengthValidType,
12
+ classifyTypeCode,
13
+ isValidOperatorData,
14
+ } from "./operators";
15
+ import { Quantifier, isQuantifier, parsePathSteps } from "./path";
19
16
  import * as ValidationIssue from "./validation-issue";
20
17
 
21
- import type { Constraint, Hex, Issue, PolicyData } from "./types";
18
+ import type { Constraint, Hex, Issue, IssueCode, PolicyData } from "./types";
22
19
 
23
20
  ///////////////////////////////////////////////////////////////////////////
24
21
  // Internal types
25
22
  ///////////////////////////////////////////////////////////////////////////
26
23
 
27
24
  /** Codes a path walk raises when the path does not navigate the descriptor (PV-1). */
28
- const NAVIGABILITY_CODES: ReadonlySet<CallciumErrorCode> = new Set<CallciumErrorCode>([
25
+ const navigabilityCodes: ReadonlySet<CallciumErrorCode> = new Set<CallciumErrorCode>([
29
26
  "EMPTY_PATH",
30
27
  "PARAM_INDEX_OUT_OF_BOUNDS",
31
28
  "TUPLE_FIELD_OUT_OF_BOUNDS",
@@ -159,7 +156,7 @@ function isBitmaskOp(opBase: number): boolean {
159
156
  ///////////////////////////////////////////////////////////////////////////
160
157
 
161
158
  /** Return the incompatibility reason, or null if the operator is allowed. */
162
- function getIncompat(opBase: number, typeInfo: TypeInfo): { code: string; message: string } | null {
159
+ function getIncompat(opBase: number, typeInfo: TypeInfo): { code: IssueCode; message: string } | null {
163
160
  const { typeCode, isDynamic, staticSize } = typeInfo;
164
161
  if (isValueOp(opBase)) {
165
162
  if (isDynamic || staticSize !== 32) {
@@ -1036,13 +1033,13 @@ function validateGroup(data: PolicyData, descBytes: Uint8Array, groupIndex: numb
1036
1033
  let typeInfo: TypeInfo;
1037
1034
  if (constraint.scope === Scope.CALLDATA) {
1038
1035
  // Compatibility warnings against the limits the spec fixes (PWF-17, PV-7).
1039
- if (steps.length > Limits.MAX_PATH_DEPTH) {
1036
+ if (steps.length > PolicyFormat.MAX_PATH_DEPTH) {
1040
1037
  issues.push(
1041
1038
  ValidationIssue.pathDepthExceeded(
1042
1039
  groupIndex,
1043
1040
  constraintIndex,
1044
1041
  bigintToHex(BigInt(steps.length)),
1045
- bigintToHex(BigInt(Limits.MAX_PATH_DEPTH)),
1042
+ bigintToHex(BigInt(PolicyFormat.MAX_PATH_DEPTH)),
1046
1043
  ),
1047
1044
  );
1048
1045
  }
@@ -1051,7 +1048,7 @@ function validateGroup(data: PolicyData, descBytes: Uint8Array, groupIndex: numb
1051
1048
  walk = Descriptor.walkPath(descBytes, steps);
1052
1049
  } catch (error) {
1053
1050
  // Navigability faults become issues; anything else propagates.
1054
- if (error instanceof CallciumError && NAVIGABILITY_CODES.has(error.code)) {
1051
+ if (error instanceof CallciumError && navigabilityCodes.has(error.code)) {
1055
1052
  issues.push(ValidationIssue.unnavigablePath(groupIndex, constraintIndex));
1056
1053
  continue;
1057
1054
  }
@@ -1062,13 +1059,13 @@ function validateGroup(data: PolicyData, descBytes: Uint8Array, groupIndex: numb
1062
1059
  issues.push(ValidationIssue.nestedQuantifier(groupIndex, constraintIndex));
1063
1060
  continue;
1064
1061
  }
1065
- if (walk.quantifiedStaticLength > Limits.MAX_QUANTIFIED_ARRAY_LENGTH) {
1062
+ if (walk.quantifiedStaticLength > PolicyFormat.MAX_QUANTIFIED_ARRAY_LENGTH) {
1066
1063
  issues.push(
1067
1064
  ValidationIssue.quantifierOverStaticLimit(
1068
1065
  groupIndex,
1069
1066
  constraintIndex,
1070
1067
  bigintToHex(BigInt(walk.quantifiedStaticLength)),
1071
- bigintToHex(BigInt(Limits.MAX_QUANTIFIED_ARRAY_LENGTH)),
1068
+ bigintToHex(BigInt(PolicyFormat.MAX_QUANTIFIED_ARRAY_LENGTH)),
1072
1069
  ),
1073
1070
  );
1074
1071
  }
@@ -1103,7 +1100,7 @@ function validateGroup(data: PolicyData, descBytes: Uint8Array, groupIndex: numb
1103
1100
  if (
1104
1101
  constraint.scope === Scope.CALLDATA &&
1105
1102
  constraint.hint !== undefined &&
1106
- ctx.steps.length <= Limits.MAX_PATH_DEPTH
1103
+ ctx.steps.length <= PolicyFormat.MAX_PATH_DEPTH
1107
1104
  ) {
1108
1105
  const compiled = bytesToHex(Descriptor.compileHint(descBytes, ctx.steps));
1109
1106
  if (compiled !== constraint.hint.toLowerCase()) {
package/src/type-desc.ts CHANGED
@@ -48,7 +48,7 @@ export function function_(): Uint8Array {
48
48
  */
49
49
  export function uintN(bits: number): Uint8Array {
50
50
  if (bits < 8 || bits > 256 || bits % 8 !== 0) {
51
- throw new CallciumError("UNKNOWN_TYPE", `Invalid uintN bits: ${bits}. Must be 8–256 in steps of 8.`);
51
+ throw new CallciumError("UNKNOWN_TYPE", `Invalid uintN bits: ${bits}. Must be 8–256 in steps of 8`);
52
52
  }
53
53
  return new Uint8Array([TypeCode.UINT_MIN + (bits / 8 - 1)]);
54
54
  }
@@ -60,7 +60,7 @@ export function uintN(bits: number): Uint8Array {
60
60
  */
61
61
  export function intN(bits: number): Uint8Array {
62
62
  if (bits < 8 || bits > 256 || bits % 8 !== 0) {
63
- throw new CallciumError("UNKNOWN_TYPE", `Invalid intN bits: ${bits}. Must be 8–256 in steps of 8.`);
63
+ throw new CallciumError("UNKNOWN_TYPE", `Invalid intN bits: ${bits}. Must be 8–256 in steps of 8`);
64
64
  }
65
65
  return new Uint8Array([TypeCode.INT_MIN + (bits / 8 - 1)]);
66
66
  }
@@ -82,7 +82,7 @@ export function string_(): Uint8Array {
82
82
  */
83
83
  export function bytesN(n: number): Uint8Array {
84
84
  if (n < 1 || n > 32) {
85
- throw new CallciumError("UNKNOWN_TYPE", `Invalid bytesN size: ${n}. Must be 1–32.`);
85
+ throw new CallciumError("UNKNOWN_TYPE", `Invalid bytesN size: ${n}. Must be 1–32`);
86
86
  }
87
87
  return new Uint8Array([TypeCode.FIXED_BYTES_MIN + (n - 1)]);
88
88
  }
@@ -108,7 +108,7 @@ export function array(elemDesc: Uint8Array, length?: number): Uint8Array {
108
108
  if (nodeLength > DF.MAX_NODE_LENGTH) {
109
109
  throw new CallciumError(
110
110
  "NODE_LENGTH_TOO_LARGE",
111
- `Dynamic array node length ${nodeLength} exceeds maximum ${DF.MAX_NODE_LENGTH}.`,
111
+ `Dynamic array node length ${nodeLength} exceeds maximum ${DF.MAX_NODE_LENGTH}`,
112
112
  );
113
113
  }
114
114
  const meta24 = (0 << DF.META_STATIC_WORDS_SHIFT) | nodeLength;
@@ -123,14 +123,14 @@ export function array(elemDesc: Uint8Array, length?: number): Uint8Array {
123
123
  if (length <= 0 || length > DF.MAX_STATIC_ARRAY_LENGTH) {
124
124
  throw new CallciumError(
125
125
  "INVALID_ARRAY_LENGTH",
126
- `Static array length ${length} out of range. Must be 1–${DF.MAX_STATIC_ARRAY_LENGTH}.`,
126
+ `Static array length ${length} out of range. Must be 1–${DF.MAX_STATIC_ARRAY_LENGTH}`,
127
127
  );
128
128
  }
129
129
  const nodeLength = DF.ARRAY_HEADER_SIZE + elemDesc.length + DF.ARRAY_LENGTH_SIZE;
130
130
  if (nodeLength > DF.MAX_NODE_LENGTH) {
131
131
  throw new CallciumError(
132
132
  "NODE_LENGTH_TOO_LARGE",
133
- `Static array node length ${nodeLength} exceeds maximum ${DF.MAX_NODE_LENGTH}.`,
133
+ `Static array node length ${nodeLength} exceeds maximum ${DF.MAX_NODE_LENGTH}`,
134
134
  );
135
135
  }
136
136
  const elemStaticWords = extractStaticWords(elemDesc);
@@ -138,7 +138,7 @@ export function array(elemDesc: Uint8Array, length?: number): Uint8Array {
138
138
  if (staticWords > DF.MAX_STATIC_WORDS) {
139
139
  throw new CallciumError(
140
140
  "STATIC_WORDS_TOO_LARGE",
141
- `Static array static words ${staticWords} exceeds maximum ${DF.MAX_STATIC_WORDS}.`,
141
+ `Static array static words ${staticWords} exceeds maximum ${DF.MAX_STATIC_WORDS}`,
142
142
  );
143
143
  }
144
144
  const meta24 = (staticWords << DF.META_STATIC_WORDS_SHIFT) | nodeLength;
@@ -162,7 +162,7 @@ export function tuple(fieldDescs: Uint8Array[]): Uint8Array {
162
162
  if (fieldDescs.length === 0 || fieldDescs.length > DF.MAX_TUPLE_FIELDS) {
163
163
  throw new CallciumError(
164
164
  "INVALID_TUPLE_FIELD_COUNT",
165
- `Tuple field count ${fieldDescs.length} out of range. Must be 1–${DF.MAX_TUPLE_FIELDS}.`,
165
+ `Tuple field count ${fieldDescs.length} out of range. Must be 1–${DF.MAX_TUPLE_FIELDS}`,
166
166
  );
167
167
  }
168
168
  const totalFieldBytes = fieldDescs.reduce((sum, f) => sum + f.length, 0);
@@ -170,7 +170,7 @@ export function tuple(fieldDescs: Uint8Array[]): Uint8Array {
170
170
  if (nodeLength > DF.MAX_NODE_LENGTH) {
171
171
  throw new CallciumError(
172
172
  "NODE_LENGTH_TOO_LARGE",
173
- `Tuple node length ${nodeLength} exceeds maximum ${DF.MAX_NODE_LENGTH}.`,
173
+ `Tuple node length ${nodeLength} exceeds maximum ${DF.MAX_NODE_LENGTH}`,
174
174
  );
175
175
  }
176
176
 
@@ -187,7 +187,7 @@ export function tuple(fieldDescs: Uint8Array[]): Uint8Array {
187
187
  if (staticWords > DF.MAX_STATIC_WORDS) {
188
188
  throw new CallciumError(
189
189
  "STATIC_WORDS_TOO_LARGE",
190
- `Tuple static words ${staticWords} exceeds maximum ${DF.MAX_STATIC_WORDS}.`,
190
+ `Tuple static words ${staticWords} exceeds maximum ${DF.MAX_STATIC_WORDS}`,
191
191
  );
192
192
  }
193
193
  const meta24 = (staticWords << DF.META_STATIC_WORDS_SHIFT) | nodeLength;
package/src/types.ts CHANGED
@@ -130,7 +130,7 @@ export type CalldataNavigationViolation =
130
130
  | CalldataNavigationVariant<"CALLDATA_OUT_OF_BOUNDS">
131
131
  | CalldataNavigationVariant<"ARRAY_INDEX_OUT_OF_BOUNDS">;
132
132
 
133
- /** A quantified array exceeded `Limits.MAX_QUANTIFIED_ARRAY_LENGTH`. */
133
+ /** A quantified array exceeded `PolicyFormat.MAX_QUANTIFIED_ARRAY_LENGTH`. */
134
134
  export type QuantifierLimitExceededViolation = {
135
135
  code: "QUANTIFIER_LIMIT_EXCEEDED";
136
136
  group: number;
@@ -142,7 +142,7 @@ export type QuantifierLimitExceededViolation = {
142
142
  };
143
143
 
144
144
  /**
145
- * A resolved word is not the canonical encoding of its declared type (spec §7.4).
145
+ * A resolved word is not the canonical encoding of its declared type.
146
146
  *
147
147
  * `resolvedValue` is the raw 32-byte word as it appeared in calldata, before any masking.
148
148
  */
@@ -274,13 +274,80 @@ export type IssueSeverity = "info" | "warning" | "error";
274
274
  /** Category of a validation issue. */
275
275
  export type IssueCategory = "typeMismatch" | "contradiction" | "redundancy" | "vacuity" | "compatibility";
276
276
 
277
+ /**
278
+ * Machine-readable code for a policy validation issue.
279
+ *
280
+ * Each code names one finding of the semantic validator and matches the constant of the same
281
+ * name in the Solidity `IssueCode` library.
282
+ */
283
+ export type IssueCode =
284
+ // Type mismatch.
285
+ | "VALUE_OP_ON_DYNAMIC"
286
+ | "VALUE_OP_ON_COMPOSITE"
287
+ | "NUMERIC_OP_ON_NON_NUMERIC"
288
+ | "BITMASK_ON_INVALID"
289
+ | "IN_ON_BOOL"
290
+ | "LENGTH_ON_STATIC"
291
+ | "CONTEXT_TYPE_MISMATCH"
292
+ | "UNKNOWN_OPERATOR"
293
+ | "NON_CANONICAL_OPERAND"
294
+ | "UNNAVIGABLE_PATH"
295
+ | "NESTED_QUANTIFIER"
296
+ | "HINT_MISMATCH"
297
+ // Contradiction.
298
+ | "EQ_NEQ_CONTRADICTION"
299
+ | "CONFLICTING_EQUALITY"
300
+ | "OUT_OF_PHYSICAL_BOUNDS"
301
+ | "IMPOSSIBLE_GT"
302
+ | "IMPOSSIBLE_LT"
303
+ | "BOUNDS_EXCLUDE_EQUALITY"
304
+ | "IMPOSSIBLE_RANGE"
305
+ | "SET_EXCLUDES_EQUALITY"
306
+ | "EMPTY_SET_INTERSECTION"
307
+ | "SET_FULLY_EXCLUDED"
308
+ | "LENGTH_EQ_NEQ_CONTRADICTION"
309
+ | "CONFLICTING_LENGTH"
310
+ | "BOUNDS_EXCLUDE_LENGTH"
311
+ | "IMPOSSIBLE_LENGTH_RANGE"
312
+ | "OUT_OF_PHYSICAL_LENGTH_BOUNDS"
313
+ | "IMPOSSIBLE_LENGTH_GT"
314
+ | "IMPOSSIBLE_LENGTH_LT"
315
+ | "BITMASK_CONTRADICTION"
316
+ | "BITMASK_ANY_IMPOSSIBLE"
317
+ | "UNSORTED_IN_SET"
318
+ | "EMPTY_GROUP"
319
+ // Redundancy.
320
+ | "DOMINATED_BOUND"
321
+ | "REDUNDANT_BOUND"
322
+ | "SET_REDUCTION"
323
+ | "SET_REDUNDANCY"
324
+ | "SET_PARTIALLY_EXCLUDED"
325
+ | "DOMINATED_LENGTH_BOUND"
326
+ | "REDUNDANT_LENGTH_BOUND"
327
+ | "REDUNDANT_BITMASK"
328
+ | "DUPLICATE_CONSTRAINT"
329
+ | "FUSIBLE_RANGE"
330
+ | "FUSIBLE_LENGTH_RANGE"
331
+ // Vacuity.
332
+ | "VACUOUS_GTE"
333
+ | "VACUOUS_LTE"
334
+ | "VACUOUS_LENGTH_GTE"
335
+ | "VACUOUS_LENGTH_LTE"
336
+ | "VACUOUS_NEGATED_RANGE"
337
+ | "VACUOUS_NEGATED_LENGTH_RANGE"
338
+ // Compatibility.
339
+ | "PATH_DEPTH_EXCEEDED"
340
+ | "QUANTIFIER_OVER_STATIC_LIMIT"
341
+ | "UNKNOWN_CONTEXT_PROPERTY"
342
+ | "NEGATION_UNDER_ANY";
343
+
277
344
  /** A single validation issue found during policy analysis. */
278
345
  export type Issue = {
279
346
  severity: IssueSeverity;
280
347
  category: IssueCategory;
281
348
  groupIndex: number;
282
349
  constraintIndex: number;
283
- code: string;
350
+ code: IssueCode;
284
351
  value1: Hex;
285
352
  value2: Hex;
286
353
  message: string;
@@ -1,4 +1,4 @@
1
- import type { Hex, Issue } from "./types";
1
+ import type { Hex, Issue, IssueCode } from "./types";
2
2
 
3
3
  ///////////////////////////////////////////////////////////////////////////
4
4
  // Constants
@@ -12,7 +12,7 @@ const ZERO: Hex = `0x${"0".repeat(64)}`;
12
12
 
13
13
  /** Create an issue from operator compatibility results. */
14
14
  export function fromOpRule(
15
- code: string,
15
+ code: IssueCode,
16
16
  message: string,
17
17
  groupIndex: number,
18
18
  constraintIndex: number,
@@ -304,7 +304,7 @@ export function unsortedInSet(groupIndex: number, constraintIndex: number): Issu
304
304
  code: "UNSORTED_IN_SET",
305
305
  value1: ZERO,
306
306
  value2: ZERO,
307
- message: "isIn/notIn set is not strictly sorted and deduplicated.",
307
+ message: "isIn/notIn set is not strictly sorted and deduplicated",
308
308
  };
309
309
  }
310
310
 
@@ -318,7 +318,7 @@ export function emptyGroup(groupIndex: number): Issue {
318
318
  code: "EMPTY_GROUP",
319
319
  value1: ZERO,
320
320
  value2: ZERO,
321
- message: "Group contains zero constraints.",
321
+ message: "Group contains zero constraints",
322
322
  };
323
323
  }
324
324