@callcium/sdk 0.3.0 → 0.4.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/dist/index.d.ts +47 -10
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +139 -22
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -20,6 +20,8 @@ type Context = {
|
|
|
20
20
|
blockNumber?: bigint;
|
|
21
21
|
chainId?: bigint;
|
|
22
22
|
txOrigin?: Address;
|
|
23
|
+
baseFee?: bigint;
|
|
24
|
+
gasPrice?: bigint;
|
|
23
25
|
};
|
|
24
26
|
/** Result of enforcing a policy: pass with matched group index, or fail with one violation per failed group. */
|
|
25
27
|
type EnforceResult = {
|
|
@@ -203,7 +205,7 @@ type DecodedPolicy = {
|
|
|
203
205
|
/** Severity of a validation issue. */
|
|
204
206
|
type IssueSeverity = "info" | "warning" | "error";
|
|
205
207
|
/** Category of a validation issue. */
|
|
206
|
-
type IssueCategory = "typeMismatch" | "contradiction" | "redundancy" | "vacuity";
|
|
208
|
+
type IssueCategory = "typeMismatch" | "contradiction" | "redundancy" | "vacuity" | "compatibility";
|
|
207
209
|
/** A single validation issue found during policy analysis. */
|
|
208
210
|
type Issue = {
|
|
209
211
|
severity: IssueSeverity;
|
|
@@ -285,10 +287,7 @@ declare class ConstraintBuilder implements Constraint {
|
|
|
285
287
|
* Target a calldata argument by path.
|
|
286
288
|
* Each argument is a big-endian uint16 step; multiple steps navigate into nested types.
|
|
287
289
|
*/
|
|
288
|
-
declare function arg(p0: number): ConstraintBuilder;
|
|
289
|
-
declare function arg(p0: number, p1: number): ConstraintBuilder;
|
|
290
|
-
declare function arg(p0: number, p1: number, p2: number): ConstraintBuilder;
|
|
291
|
-
declare function arg(p0: number, p1: number, p2: number, p3: number): ConstraintBuilder;
|
|
290
|
+
declare function arg(p0: number, ...rest: number[]): ConstraintBuilder;
|
|
292
291
|
/** Target the `msg.sender` context property. */
|
|
293
292
|
declare function msgSender(): ConstraintBuilder;
|
|
294
293
|
/** Target the `msg.value` context property. */
|
|
@@ -301,6 +300,10 @@ declare function blockNumber(): ConstraintBuilder;
|
|
|
301
300
|
declare function chainId(): ConstraintBuilder;
|
|
302
301
|
/** Target the `tx.origin` context property. */
|
|
303
302
|
declare function txOrigin(): ConstraintBuilder;
|
|
303
|
+
/** Target the `block.basefee` context property. */
|
|
304
|
+
declare function baseFee(): ConstraintBuilder;
|
|
305
|
+
/** Target the `tx.gasprice` context property. */
|
|
306
|
+
declare function gasPrice(): ConstraintBuilder;
|
|
304
307
|
//#endregion
|
|
305
308
|
//#region src/policy-builder.d.ts
|
|
306
309
|
/** Fluent builder for constructing Callcium policies. */
|
|
@@ -325,11 +328,21 @@ declare class PolicyBuilder {
|
|
|
325
328
|
/** Start a new constraint group (OR branch). */
|
|
326
329
|
or(): this;
|
|
327
330
|
/**
|
|
328
|
-
* Build the policy into an encoded binary blob.
|
|
331
|
+
* Build the policy into an encoded binary blob with strict validation.
|
|
332
|
+
* Throws on any issue, regardless of severity. Use {@link validate} to
|
|
333
|
+
* inspect issues, or {@link buildUnsafe} to encode without validation.
|
|
329
334
|
* @returns The policy as a 0x-prefixed hex string.
|
|
330
|
-
* @throws {CallciumError} If any group is empty or validation finds
|
|
335
|
+
* @throws {CallciumError} If any group is empty or validation finds any issue.
|
|
331
336
|
*/
|
|
332
337
|
build(): Hex;
|
|
338
|
+
/**
|
|
339
|
+
* Build the policy into an encoded binary blob without validation.
|
|
340
|
+
* The resulting policy may be invalid. Prefer {@link build}; use this
|
|
341
|
+
* only to knowingly bypass a reported issue.
|
|
342
|
+
* @returns The policy as a 0x-prefixed hex string.
|
|
343
|
+
* @throws {CallciumError} If any group is empty.
|
|
344
|
+
*/
|
|
345
|
+
buildUnsafe(): Hex;
|
|
333
346
|
/**
|
|
334
347
|
* Validate the policy without encoding.
|
|
335
348
|
* @returns All validation issues found.
|
|
@@ -417,6 +430,19 @@ declare function inspect(desc: Uint8Array, offset: number): TypeInfo;
|
|
|
417
430
|
* @throws {CallciumError} On empty steps, out-of-bounds param, or descent into elementary type.
|
|
418
431
|
*/
|
|
419
432
|
declare function typeAt(desc: Uint8Array, steps: number[]): TypeInfo;
|
|
433
|
+
/**
|
|
434
|
+
* Resolve the type at the path and the declared length of the static array a
|
|
435
|
+
* quantifier step descends into (zero when the path holds no quantifier step
|
|
436
|
+
* over a static array).
|
|
437
|
+
* @param desc - Raw descriptor bytes.
|
|
438
|
+
* @param steps - Path steps, length >= 1.
|
|
439
|
+
* @returns TypeInfo at the resolved path and the quantified static array length.
|
|
440
|
+
* @throws {CallciumError} On empty steps, out-of-bounds param, or descent into elementary type.
|
|
441
|
+
*/
|
|
442
|
+
declare function walkPath(desc: Uint8Array, steps: number[]): {
|
|
443
|
+
typeInfo: TypeInfo;
|
|
444
|
+
quantifiedStaticLength: number;
|
|
445
|
+
};
|
|
420
446
|
/**
|
|
421
447
|
* Return tuple field count at a tuple node offset.
|
|
422
448
|
* @param desc - Raw descriptor bytes.
|
|
@@ -445,6 +471,7 @@ declare const Descriptor: {
|
|
|
445
471
|
staticArrayLength: typeof staticArrayLength;
|
|
446
472
|
nodeLength: typeof nodeLength;
|
|
447
473
|
arrayElementOffset: typeof arrayElementOffset;
|
|
474
|
+
walkPath: typeof walkPath;
|
|
448
475
|
};
|
|
449
476
|
//#endregion
|
|
450
477
|
//#region src/policy-validator.d.ts
|
|
@@ -470,7 +497,7 @@ declare const PolicyValidator: {
|
|
|
470
497
|
* @param policy - Binary policy blob as 0x-prefixed hex string.
|
|
471
498
|
* @param callData - ABI-encoded call data as 0x-prefixed hex string.
|
|
472
499
|
* @param context - Optional execution context for context-scoped rules.
|
|
473
|
-
* @returns Pass with matched group index, or fail with one violation per
|
|
500
|
+
* @returns Pass with matched group index, or fail with one violation per evaluated failing group.
|
|
474
501
|
* @throws {CallciumError} If the policy blob is structurally malformed.
|
|
475
502
|
*/
|
|
476
503
|
declare function check(policy: Hex, callData: Hex, context?: Context): EnforceResult;
|
|
@@ -584,6 +611,16 @@ declare const ContextProperty: CodeMap<readonly [{
|
|
|
584
611
|
readonly code: 5;
|
|
585
612
|
readonly label: "tx.origin";
|
|
586
613
|
readonly typeCode: 64;
|
|
614
|
+
}, {
|
|
615
|
+
readonly key: "BASE_FEE";
|
|
616
|
+
readonly code: 6;
|
|
617
|
+
readonly label: "block.basefee";
|
|
618
|
+
readonly typeCode: 31;
|
|
619
|
+
}, {
|
|
620
|
+
readonly key: "GAS_PRICE";
|
|
621
|
+
readonly code: 7;
|
|
622
|
+
readonly label: "tx.gasprice";
|
|
623
|
+
readonly typeCode: 31;
|
|
587
624
|
}]>;
|
|
588
625
|
/** Display metadata for a context property code. */
|
|
589
626
|
type ContextPropertyInfo = {
|
|
@@ -772,7 +809,7 @@ declare function isLengthOp(opCode: number): boolean;
|
|
|
772
809
|
//#endregion
|
|
773
810
|
//#region src/errors.d.ts
|
|
774
811
|
/** Machine-readable error code for structural decoding and validation failures. */
|
|
775
|
-
type CallciumErrorCode = "UNSUPPORTED_VERSION" | "MALFORMED_HEADER" | "UNEXPECTED_END" | "NODE_OVERFLOW" | "UNKNOWN_TYPE_CODE" | "INVALID_OPERATOR" | "INVALID_ARRAY_LENGTH" | "INVALID_TUPLE_FIELD_COUNT" | "PARAM_COUNT_MISMATCH" | "EMPTY_POLICY" | "EMPTY_GROUP" | "EMPTY_PATH" | "INVALID_CONTEXT_PATH" | "INVALID_CONTEXT_PROPERTY" | "INVALID_QUANTIFIER" | "INVALID_SCOPE" | "RULE_SIZE_MISMATCH" | "GROUP_SIZE_MISMATCH" | "GROUP_OVERFLOW" | "RULE_OVERFLOW" | "TRAILING_BYTES" | "INTERNAL_ERROR" | "INVALID_SIGNATURE" | "INVALID_TYPE_STRING" | "UNKNOWN_TYPE" | "DESCRIPTOR_TOO_LARGE" | "INVALID_PATH" | "DUPLICATE_PATH" | "INVALID_CONSTRAINT" | "INVALID_OPERATOR_DATA" | "EMPTY_SET" | "SET_TOO_LARGE" | "INVALID_RANGE" | "GROUP_COUNT_OVERFLOW" | "RULE_COUNT_OVERFLOW" | "RULE_SIZE_OVERFLOW" | "DESC_LENGTH_OVERFLOW" | "INVALID_HEX" | "VALIDATION_ERROR";
|
|
812
|
+
type CallciumErrorCode = "UNSUPPORTED_VERSION" | "MALFORMED_HEADER" | "UNEXPECTED_END" | "NODE_OVERFLOW" | "NESTING_TOO_DEEP" | "UNKNOWN_TYPE_CODE" | "INVALID_OPERATOR" | "UNSORTED_IN_SET" | "INVALID_ARRAY_LENGTH" | "INVALID_TUPLE_FIELD_COUNT" | "PARAM_COUNT_MISMATCH" | "EMPTY_POLICY" | "EMPTY_GROUP" | "EMPTY_PATH" | "INVALID_CONTEXT_PATH" | "INVALID_CONTEXT_PROPERTY" | "INVALID_QUANTIFIER" | "INVALID_SCOPE" | "RULE_SIZE_MISMATCH" | "GROUP_SIZE_MISMATCH" | "GROUP_OVERFLOW" | "RULE_OVERFLOW" | "TRAILING_BYTES" | "INTERNAL_ERROR" | "INVALID_SIGNATURE" | "INVALID_TYPE_STRING" | "UNKNOWN_TYPE" | "DESCRIPTOR_TOO_LARGE" | "INVALID_PATH" | "PATH_TOO_DEEP" | "DUPLICATE_PATH" | "INVALID_CONSTRAINT" | "INVALID_OPERATOR_DATA" | "EMPTY_SET" | "SET_TOO_LARGE" | "INVALID_RANGE" | "GROUP_COUNT_OVERFLOW" | "RULE_COUNT_OVERFLOW" | "RULE_SIZE_OVERFLOW" | "DESC_LENGTH_OVERFLOW" | "INVALID_HEX" | "VALIDATION_ERROR";
|
|
776
813
|
/**
|
|
777
814
|
* Thrown when a policy or descriptor blob is structurally malformed.
|
|
778
815
|
* The optional `offset` indicates the byte position in the source blob where the error was detected.
|
|
@@ -798,5 +835,5 @@ declare class PolicyViolationError extends Error {
|
|
|
798
835
|
constructor(violations: Violation[]);
|
|
799
836
|
}
|
|
800
837
|
//#endregion
|
|
801
|
-
export { type Address, CallciumError, type CallciumErrorCode, type CalldataNavigationViolation, type Constraint, type ConstraintBuilder, type Context, ContextProperty, type ContextPropertyInfo, type DecodedGroup, type DecodedParam, type DecodedPolicy, type DecodedRule, Descriptor, DescriptorCoder, type EnforceResult, type Field, type Hex, type Issue, type IssueCategory, type IssueSeverity, Limits, type MissingContextViolation, type MissingSelectorViolation, type NavigationViolationCode, Op, type OpInfo, type Operands, PolicyBuilder, PolicyCoder, type PolicyData, PolicyEnforcer, PolicyValidator, PolicyViolationError, Quantifier, type QuantifierEmptyArrayViolation, type QuantifierInfo, type QuantifierLimitExceededViolation, type ScalarValue, Scope, type ScopeInfo, type SelectorMismatchViolation, type Span, type TypeClass, type TypeClassInfo, TypeCode, type TypeCodeInfo, type TypeInfo, type ValueMismatchViolation, type Violation, type ViolationCode, arg, blockNumber, blockTimestamp, bytesToHex, chainId, hexToBytes, isLengthOp, isOpAllowed, isQuantifier, lookupContextProperty, lookupOp, lookupQuantifier, lookupScope, lookupTypeCode, msgSender, msgValue, parsePathSteps, toAddress, txOrigin };
|
|
838
|
+
export { type Address, CallciumError, type CallciumErrorCode, type CalldataNavigationViolation, type Constraint, type ConstraintBuilder, type Context, ContextProperty, type ContextPropertyInfo, type DecodedGroup, type DecodedParam, type DecodedPolicy, type DecodedRule, Descriptor, DescriptorCoder, type EnforceResult, type Field, type Hex, type Issue, type IssueCategory, type IssueSeverity, Limits, type MissingContextViolation, type MissingSelectorViolation, type NavigationViolationCode, Op, type OpInfo, type Operands, PolicyBuilder, PolicyCoder, type PolicyData, PolicyEnforcer, PolicyValidator, PolicyViolationError, Quantifier, type QuantifierEmptyArrayViolation, type QuantifierInfo, type QuantifierLimitExceededViolation, type ScalarValue, Scope, type ScopeInfo, type SelectorMismatchViolation, type Span, type TypeClass, type TypeClassInfo, TypeCode, type TypeCodeInfo, type TypeInfo, type ValueMismatchViolation, type Violation, type ViolationCode, arg, baseFee, blockNumber, blockTimestamp, bytesToHex, chainId, gasPrice, hexToBytes, isLengthOp, isOpAllowed, isQuantifier, lookupContextProperty, lookupOp, lookupQuantifier, lookupScope, lookupTypeCode, msgSender, msgValue, parsePathSteps, toAddress, txOrigin };
|
|
802
839
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/types.ts","../src/constraint.ts","../src/policy-builder.ts","../src/policy-coder.ts","../src/descriptor.ts","../src/policy-validator.ts","../src/policy-enforcer.ts","../src/bytes.ts","../src/descriptor-coder.ts","../src/constants.ts","../src/operators.ts","../src/errors.ts"],"mappings":";;KAKY,GAAA;;KAGA,OAAA;;KAGA,IAAA;EAAS,KAAA;EAAe,GAAA;AAAA;;
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/types.ts","../src/constraint.ts","../src/policy-builder.ts","../src/policy-coder.ts","../src/descriptor.ts","../src/policy-validator.ts","../src/policy-enforcer.ts","../src/bytes.ts","../src/descriptor-coder.ts","../src/constants.ts","../src/operators.ts","../src/errors.ts"],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -116,7 +116,8 @@ const DescriptorFormat = {
|
|
|
116
116
|
MAX_NODE_LENGTH: 4095,
|
|
117
117
|
MAX_STATIC_ARRAY_LENGTH: 4095,
|
|
118
118
|
MAX_TUPLE_FIELDS: 4089,
|
|
119
|
-
MAX_PARAMS: 255
|
|
119
|
+
MAX_PARAMS: 255,
|
|
120
|
+
MAX_NESTING_DEPTH: 64
|
|
120
121
|
};
|
|
121
122
|
/** Binary layout constants for the Callcium policy format. */
|
|
122
123
|
const PolicyFormat = {
|
|
@@ -207,6 +208,18 @@ const CTX_PROP_TABLE = [
|
|
|
207
208
|
code: 5,
|
|
208
209
|
label: "tx.origin",
|
|
209
210
|
typeCode: 64
|
|
211
|
+
},
|
|
212
|
+
{
|
|
213
|
+
key: "BASE_FEE",
|
|
214
|
+
code: 6,
|
|
215
|
+
label: "block.basefee",
|
|
216
|
+
typeCode: 31
|
|
217
|
+
},
|
|
218
|
+
{
|
|
219
|
+
key: "GAS_PRICE",
|
|
220
|
+
code: 7,
|
|
221
|
+
label: "tx.gasprice",
|
|
222
|
+
typeCode: 31
|
|
210
223
|
}
|
|
211
224
|
];
|
|
212
225
|
/** Well-known context property IDs for context-scope rules. */
|
|
@@ -220,7 +233,9 @@ const CTX_KEY_MAP = {
|
|
|
220
233
|
BLOCK_TIMESTAMP: "blockTimestamp",
|
|
221
234
|
BLOCK_NUMBER: "blockNumber",
|
|
222
235
|
CHAIN_ID: "chainId",
|
|
223
|
-
TX_ORIGIN: "txOrigin"
|
|
236
|
+
TX_ORIGIN: "txOrigin",
|
|
237
|
+
BASE_FEE: "baseFee",
|
|
238
|
+
GAS_PRICE: "gasPrice"
|
|
224
239
|
};
|
|
225
240
|
const ctxPropByCode = new Map(CTX_PROP_TABLE.map((e) => [e.code, {
|
|
226
241
|
label: e.label,
|
|
@@ -562,11 +577,24 @@ function inspect$1(desc, offset) {
|
|
|
562
577
|
* @throws {CallciumError} On empty steps, out-of-bounds param, or descent into elementary type.
|
|
563
578
|
*/
|
|
564
579
|
function typeAt(desc, steps) {
|
|
580
|
+
return walkPath(desc, steps).typeInfo;
|
|
581
|
+
}
|
|
582
|
+
/**
|
|
583
|
+
* Resolve the type at the path and the declared length of the static array a
|
|
584
|
+
* quantifier step descends into (zero when the path holds no quantifier step
|
|
585
|
+
* over a static array).
|
|
586
|
+
* @param desc - Raw descriptor bytes.
|
|
587
|
+
* @param steps - Path steps, length >= 1.
|
|
588
|
+
* @returns TypeInfo at the resolved path and the quantified static array length.
|
|
589
|
+
* @throws {CallciumError} On empty steps, out-of-bounds param, or descent into elementary type.
|
|
590
|
+
*/
|
|
591
|
+
function walkPath(desc, steps) {
|
|
565
592
|
if (steps.length === 0) throw new CallciumError("INVALID_PATH", "Path must have at least one step.");
|
|
566
593
|
const paramIndex = steps[0];
|
|
567
594
|
const count = desc[1];
|
|
568
595
|
if (paramIndex >= count) throw new CallciumError("INVALID_PATH", `Param index ${paramIndex} out of range (paramCount=${count}).`);
|
|
569
596
|
let cursor = paramOffset(desc, paramIndex);
|
|
597
|
+
let quantifiedStaticLength = 0;
|
|
570
598
|
for (let stepIndex = 1; stepIndex < steps.length; stepIndex++) {
|
|
571
599
|
const typeCode = desc[cursor];
|
|
572
600
|
const step = steps[stepIndex];
|
|
@@ -574,10 +602,15 @@ function typeAt(desc, steps) {
|
|
|
574
602
|
const fields = tupleFieldCount(desc, cursor);
|
|
575
603
|
if (step >= fields) throw new CallciumError("INVALID_PATH", `Tuple field index ${step} out of range (tuple has ${fields} fields).`);
|
|
576
604
|
cursor = tupleFieldOffset(desc, cursor, step);
|
|
577
|
-
} else if (typeCode === TypeCode.STATIC_ARRAY || typeCode === TypeCode.DYNAMIC_ARRAY)
|
|
578
|
-
|
|
605
|
+
} else if (typeCode === TypeCode.STATIC_ARRAY || typeCode === TypeCode.DYNAMIC_ARRAY) {
|
|
606
|
+
if (typeCode === TypeCode.STATIC_ARRAY && isQuantifier(step)) quantifiedStaticLength = staticArrayLength(desc, cursor);
|
|
607
|
+
cursor = arrayElementOffset(cursor);
|
|
608
|
+
} else throw new CallciumError("INVALID_PATH", `Cannot descend into elementary type at offset ${cursor}.`);
|
|
579
609
|
}
|
|
580
|
-
return
|
|
610
|
+
return {
|
|
611
|
+
typeInfo: inspect$1(desc, cursor),
|
|
612
|
+
quantifiedStaticLength
|
|
613
|
+
};
|
|
581
614
|
}
|
|
582
615
|
/**
|
|
583
616
|
* Return tuple field count at a tuple node offset.
|
|
@@ -613,7 +646,8 @@ const Descriptor = {
|
|
|
613
646
|
tupleFieldCount,
|
|
614
647
|
staticArrayLength,
|
|
615
648
|
nodeLength,
|
|
616
|
-
arrayElementOffset
|
|
649
|
+
arrayElementOffset,
|
|
650
|
+
walkPath
|
|
617
651
|
};
|
|
618
652
|
//#endregion
|
|
619
653
|
//#region src/type-desc.ts
|
|
@@ -939,7 +973,7 @@ function toTypes(desc) {
|
|
|
939
973
|
return types.join(",");
|
|
940
974
|
}
|
|
941
975
|
/** Recursively parse a single descriptor node starting at offset. */
|
|
942
|
-
function parseNode(data, offset) {
|
|
976
|
+
function parseNode(data, offset, depth) {
|
|
943
977
|
if (offset >= data.length) throw new CallciumError("UNEXPECTED_END", "Unexpected end of descriptor", offset);
|
|
944
978
|
const code = data[offset];
|
|
945
979
|
const info = classifyTypeCode(code);
|
|
@@ -957,6 +991,7 @@ function parseNode(data, offset) {
|
|
|
957
991
|
},
|
|
958
992
|
next: metaOffset
|
|
959
993
|
};
|
|
994
|
+
if (depth > DescriptorFormat.MAX_NESTING_DEPTH) throw new CallciumError("NESTING_TOO_DEEP", `Composite nesting exceeds maximum depth ${DescriptorFormat.MAX_NESTING_DEPTH}`, offset);
|
|
960
995
|
const metaEnd = metaOffset + DescriptorFormat.COMPOSITE_META_SIZE;
|
|
961
996
|
if (metaEnd > data.length) throw new CallciumError("UNEXPECTED_END", "Incomplete composite metadata", offset);
|
|
962
997
|
const meta = readU24(data, metaOffset);
|
|
@@ -977,7 +1012,7 @@ function parseNode(data, offset) {
|
|
|
977
1012
|
const fields = [];
|
|
978
1013
|
let cursor = offset + DescriptorFormat.TUPLE_HEADER_SIZE;
|
|
979
1014
|
for (let i = 0; i < fieldCount; i++) {
|
|
980
|
-
const result = parseNode(data, cursor);
|
|
1015
|
+
const result = parseNode(data, cursor, depth + 1);
|
|
981
1016
|
fields.push(result.node);
|
|
982
1017
|
cursor = result.next;
|
|
983
1018
|
}
|
|
@@ -997,7 +1032,7 @@ function parseNode(data, offset) {
|
|
|
997
1032
|
};
|
|
998
1033
|
}
|
|
999
1034
|
if (info.typeClass === "staticArray") {
|
|
1000
|
-
const elemResult = parseNode(data, Descriptor.arrayElementOffset(offset));
|
|
1035
|
+
const elemResult = parseNode(data, Descriptor.arrayElementOffset(offset), depth + 1);
|
|
1001
1036
|
const lengthOffset = elemResult.next;
|
|
1002
1037
|
if (lengthOffset + DescriptorFormat.ARRAY_LENGTH_SIZE > data.length) throw new CallciumError("UNEXPECTED_END", "Missing static array length suffix", offset);
|
|
1003
1038
|
const length = readU16(data, lengthOffset);
|
|
@@ -1025,7 +1060,7 @@ function parseNode(data, offset) {
|
|
|
1025
1060
|
typeCode: code,
|
|
1026
1061
|
isDynamic: true,
|
|
1027
1062
|
staticSize: 0,
|
|
1028
|
-
element: parseNode(data, Descriptor.arrayElementOffset(offset)).node,
|
|
1063
|
+
element: parseNode(data, Descriptor.arrayElementOffset(offset), depth + 1).node,
|
|
1029
1064
|
span: {
|
|
1030
1065
|
start: offset,
|
|
1031
1066
|
end: nodeEnd
|
|
@@ -1050,7 +1085,7 @@ function decodeDescriptor(data) {
|
|
|
1050
1085
|
let cursor = DescriptorFormat.HEADER_SIZE;
|
|
1051
1086
|
while (cursor < data.length) {
|
|
1052
1087
|
if (params.length >= DescriptorFormat.MAX_PARAMS) throw new CallciumError("PARAM_COUNT_MISMATCH", `Descriptor exceeds the maximum of ${DescriptorFormat.MAX_PARAMS} top-level params`);
|
|
1053
|
-
const { node, next } = parseNode(data, cursor);
|
|
1088
|
+
const { node, next } = parseNode(data, cursor, 1);
|
|
1054
1089
|
tree.push(node);
|
|
1055
1090
|
params.push({
|
|
1056
1091
|
index: params.length,
|
|
@@ -1161,10 +1196,15 @@ function decodePolicy(blob) {
|
|
|
1161
1196
|
const depthOffset = ruleOffset + PolicyFormat.RULE_DEPTH_OFFSET;
|
|
1162
1197
|
const depthValue = data[depthOffset];
|
|
1163
1198
|
if (depthValue === 0) throw new CallciumError("EMPTY_PATH", "Rule path must have at least one step", ruleOffset);
|
|
1199
|
+
if (depthValue > Limits.MAX_PATH_DEPTH) throw new CallciumError("PATH_TOO_DEEP", `Path depth ${depthValue} exceeds maximum ${Limits.MAX_PATH_DEPTH}`, ruleOffset);
|
|
1164
1200
|
if (scopeValue === Scope.CONTEXT && depthValue !== 1) throw new CallciumError("INVALID_CONTEXT_PATH", "Context-scope rules must have exactly one path step", ruleOffset);
|
|
1165
1201
|
const pathStart = ruleOffset + PolicyFormat.RULE_PATH_OFFSET;
|
|
1166
1202
|
const pathLength = depthValue * PolicyFormat.PATH_STEP_SIZE;
|
|
1167
1203
|
const pathHex = toHex(data, pathStart, pathStart + pathLength);
|
|
1204
|
+
if (scopeValue === Scope.CONTEXT) {
|
|
1205
|
+
const contextPropertyId = readU16(data, pathStart);
|
|
1206
|
+
if (contextPropertyId > MAX_CONTEXT_PROPERTY_ID) throw new CallciumError("INVALID_CONTEXT_PROPERTY", `Context rule references undefined context property 0x${contextPropertyId.toString(16).padStart(4, "0")}`, ruleOffset);
|
|
1207
|
+
}
|
|
1168
1208
|
const opCodeOffset = pathStart + pathLength;
|
|
1169
1209
|
const opCodeValue = data[opCodeOffset];
|
|
1170
1210
|
const dataLengthOffset = opCodeOffset + PolicyFormat.RULE_OPCODE_SIZE;
|
|
@@ -1174,6 +1214,10 @@ function decodePolicy(blob) {
|
|
|
1174
1214
|
if (ruleSizeValue !== expectedRuleSize) throw new CallciumError("RULE_SIZE_MISMATCH", `Declared rule size ${ruleSizeValue} does not match computed size ${expectedRuleSize}`, ruleOffset);
|
|
1175
1215
|
const opBase = opCodeValue & ~Op.NOT;
|
|
1176
1216
|
if (opBase === 0 || !isValidOperatorData(opBase, dataLengthValue)) throw new CallciumError("INVALID_OPERATOR", "Unrecognized or malformed operator", ruleOffset);
|
|
1217
|
+
if (opBase === Op.IN) {
|
|
1218
|
+
const wordCount = dataLengthValue / 32;
|
|
1219
|
+
for (let word = 1; word < wordCount; word++) if (compareBytes(data.subarray(dataStart + (word - 1) * 32, dataStart + word * 32), data.subarray(dataStart + word * 32, dataStart + (word + 1) * 32)) >= 0) throw new CallciumError("UNSORTED_IN_SET", "IN operands must be strictly ascending", ruleOffset);
|
|
1220
|
+
}
|
|
1177
1221
|
rules.push({
|
|
1178
1222
|
ruleSize: field(ruleSizeValue, ruleOffset, ruleOffset + PolicyFormat.RULE_SIZE_SIZE),
|
|
1179
1223
|
scope: field(scopeValue, scopeOffset, scopeOffset + 1),
|
|
@@ -1264,7 +1308,7 @@ function encodeRule(rule) {
|
|
|
1264
1308
|
if ((rule.path.length & 1) !== 0) throw new CallciumError("INVALID_PATH", "Path byte length must be even.");
|
|
1265
1309
|
if (rule.operator.length < 1) throw new CallciumError("INVALID_OPERATOR", "Operator must have at least one byte (opcode).");
|
|
1266
1310
|
const depth = rule.path.length / 2;
|
|
1267
|
-
if (depth > Limits.MAX_PATH_DEPTH) throw new CallciumError("
|
|
1311
|
+
if (depth > Limits.MAX_PATH_DEPTH) throw new CallciumError("PATH_TOO_DEEP", `Path depth ${depth} exceeds maximum ${Limits.MAX_PATH_DEPTH}.`);
|
|
1268
1312
|
if (rule.scope === Scope.CONTEXT && depth !== 1) throw new CallciumError("INVALID_CONTEXT_PATH", "Context-scope rules must have exactly one path step.");
|
|
1269
1313
|
const opCode = rule.operator[0];
|
|
1270
1314
|
const data = rule.operator.subarray(1);
|
|
@@ -1705,6 +1749,22 @@ function vacuousLengthGte(groupIndex, constraintIndex, value) {
|
|
|
1705
1749
|
function vacuousLengthLte(groupIndex, constraintIndex, value) {
|
|
1706
1750
|
return makeIssue("info", "vacuity", groupIndex, constraintIndex, "VACUOUS_LENGTH_LTE", value, ZERO, "lengthLte() bound equals maximum (always true)");
|
|
1707
1751
|
}
|
|
1752
|
+
/** Path deeper than the reference enforcer's cap. */
|
|
1753
|
+
function pathDepthExceeded(groupIndex, constraintIndex, depth, maxDepth) {
|
|
1754
|
+
return makeIssue("warning", "compatibility", groupIndex, constraintIndex, "PATH_DEPTH_EXCEEDED", depth, maxDepth, "Path depth exceeds the reference enforcer cap");
|
|
1755
|
+
}
|
|
1756
|
+
/** Quantifier over a static array beyond the reference enforcer's iteration cap. */
|
|
1757
|
+
function quantifierOverStaticLimit(groupIndex, constraintIndex, arrayLength, maxLength) {
|
|
1758
|
+
return makeIssue("warning", "compatibility", groupIndex, constraintIndex, "QUANTIFIER_OVER_STATIC_LIMIT", arrayLength, maxLength, "Quantifier over static array exceeds the reference enforcer cap");
|
|
1759
|
+
}
|
|
1760
|
+
/** Context property ID outside the assigned set. */
|
|
1761
|
+
function unknownContextProperty(groupIndex, constraintIndex, contextId, maxContextId) {
|
|
1762
|
+
return makeIssue("warning", "compatibility", groupIndex, constraintIndex, "UNKNOWN_CONTEXT_PROPERTY", contextId, maxContextId, "Unknown context property ID");
|
|
1763
|
+
}
|
|
1764
|
+
/** Negated operator under the existential quantifier. */
|
|
1765
|
+
function negationUnderAny(groupIndex, constraintIndex, opCode) {
|
|
1766
|
+
return makeIssue("warning", "compatibility", groupIndex, constraintIndex, "NEGATION_UNDER_ANY", opCode, ZERO, "Negated operator under any() passes when a decoy element differs");
|
|
1767
|
+
}
|
|
1708
1768
|
/** Pre-select value or length issue factories for a bound domain. */
|
|
1709
1769
|
function boundIssues(isLength) {
|
|
1710
1770
|
return {
|
|
@@ -1824,6 +1884,10 @@ function getIncompat(opBase, typeInfo) {
|
|
|
1824
1884
|
code: "VALUE_OP_ON_DYNAMIC",
|
|
1825
1885
|
message: "Value operator used on dynamic type"
|
|
1826
1886
|
};
|
|
1887
|
+
if (classifyTypeCode(typeCode).typeClass !== "elementary") return {
|
|
1888
|
+
code: "VALUE_OP_ON_COMPOSITE",
|
|
1889
|
+
message: "Value operator used on composite type"
|
|
1890
|
+
};
|
|
1827
1891
|
if (isComparisonOp(opBase) && !isNumericType(typeCode)) return {
|
|
1828
1892
|
code: "NUMERIC_OP_ON_NON_NUMERIC",
|
|
1829
1893
|
message: "Comparison operator used on non-numeric type"
|
|
@@ -1832,6 +1896,10 @@ function getIncompat(opBase, typeInfo) {
|
|
|
1832
1896
|
code: "BITMASK_ON_INVALID",
|
|
1833
1897
|
message: "Bitmask operator used on incompatible type"
|
|
1834
1898
|
};
|
|
1899
|
+
if (opBase === Op.IN && typeCode === TypeCode.BOOL) return {
|
|
1900
|
+
code: "IN_ON_BOOL",
|
|
1901
|
+
message: "IN operator used on boolean type"
|
|
1902
|
+
};
|
|
1835
1903
|
return null;
|
|
1836
1904
|
}
|
|
1837
1905
|
if (isLengthOp(opBase)) {
|
|
@@ -2145,10 +2213,12 @@ function checkDuplicates(issues, operators, groupIndex, constraintIndex) {
|
|
|
2145
2213
|
/** Validate all operators on a single constraint against its domain context. */
|
|
2146
2214
|
function validateConstraint(ctx, constraint, groupIndex, constraintIndex, issues) {
|
|
2147
2215
|
const operators = constraint.operators;
|
|
2216
|
+
const underAny = constraint.scope === Scope.CALLDATA && parsePathSteps(constraint.path).includes(Quantifier.ANY);
|
|
2148
2217
|
for (const opHex of operators) {
|
|
2149
2218
|
const opCode = parseInt(opHex.slice(2, 4), 16);
|
|
2150
2219
|
const base = opCode & ~Op.NOT;
|
|
2151
2220
|
const isNegated = (opCode & Op.NOT) !== 0;
|
|
2221
|
+
if (underAny && isNegated) issues.push(negationUnderAny(groupIndex, constraintIndex, bigintToHex(BigInt(opCode))));
|
|
2152
2222
|
const compat = checkCompatibility(base, ctx.typeInfo.typeCode, ctx.typeInfo.isDynamic, ctx.typeInfo.staticSize);
|
|
2153
2223
|
if (!compat.compatible) {
|
|
2154
2224
|
issues.push(fromOpRule(compat.code, compat.message, groupIndex, constraintIndex, bigintToHex(BigInt(opCode))));
|
|
@@ -2198,10 +2268,20 @@ function validateGroup(data, descBytes, groupIndex, issues) {
|
|
|
2198
2268
|
let typeInfo;
|
|
2199
2269
|
if (constraint.scope === Scope.CALLDATA) {
|
|
2200
2270
|
const steps = parsePathSteps(constraint.path);
|
|
2201
|
-
|
|
2271
|
+
if (steps.length > Limits.MAX_PATH_DEPTH) issues.push(pathDepthExceeded(groupIndex, constraintIndex, bigintToHex(BigInt(steps.length)), bigintToHex(BigInt(Limits.MAX_PATH_DEPTH))));
|
|
2272
|
+
const walk = Descriptor.walkPath(descBytes, steps);
|
|
2273
|
+
if (walk.quantifiedStaticLength > Limits.MAX_QUANTIFIED_ARRAY_LENGTH) issues.push(quantifierOverStaticLimit(groupIndex, constraintIndex, bigintToHex(BigInt(walk.quantifiedStaticLength)), bigintToHex(BigInt(Limits.MAX_QUANTIFIED_ARRAY_LENGTH))));
|
|
2274
|
+
typeInfo = walk.typeInfo;
|
|
2202
2275
|
} else {
|
|
2203
2276
|
const ctxId = parsePathSteps(constraint.path)[0];
|
|
2204
|
-
|
|
2277
|
+
if (ctxId > MAX_CONTEXT_PROPERTY_ID) {
|
|
2278
|
+
issues.push(unknownContextProperty(groupIndex, constraintIndex, bigintToHex(BigInt(ctxId)), bigintToHex(BigInt(MAX_CONTEXT_PROPERTY_ID))));
|
|
2279
|
+
typeInfo = {
|
|
2280
|
+
typeCode: TypeCode.UINT_MAX,
|
|
2281
|
+
isDynamic: false,
|
|
2282
|
+
staticSize: 32
|
|
2283
|
+
};
|
|
2284
|
+
} else typeInfo = {
|
|
2205
2285
|
typeCode: lookupContextProperty(ctxId).typeCode,
|
|
2206
2286
|
isDynamic: false,
|
|
2207
2287
|
staticSize: 32
|
|
@@ -2379,18 +2459,31 @@ var PolicyBuilder = class PolicyBuilder {
|
|
|
2379
2459
|
return this;
|
|
2380
2460
|
}
|
|
2381
2461
|
/**
|
|
2382
|
-
* Build the policy into an encoded binary blob.
|
|
2462
|
+
* Build the policy into an encoded binary blob with strict validation.
|
|
2463
|
+
* Throws on any issue, regardless of severity. Use {@link validate} to
|
|
2464
|
+
* inspect issues, or {@link buildUnsafe} to encode without validation.
|
|
2383
2465
|
* @returns The policy as a 0x-prefixed hex string.
|
|
2384
|
-
* @throws {CallciumError} If any group is empty or validation finds
|
|
2466
|
+
* @throws {CallciumError} If any group is empty or validation finds any issue.
|
|
2385
2467
|
*/
|
|
2386
2468
|
build() {
|
|
2387
2469
|
this.checkGroups();
|
|
2388
2470
|
const policyData = this.toPolicyData();
|
|
2389
|
-
const
|
|
2390
|
-
if (
|
|
2471
|
+
const issues = PolicyValidator.validate(policyData);
|
|
2472
|
+
if (issues.length > 0) throw new CallciumError("VALIDATION_ERROR", issues[0].message);
|
|
2391
2473
|
return PolicyCoder.encode(policyData);
|
|
2392
2474
|
}
|
|
2393
2475
|
/**
|
|
2476
|
+
* Build the policy into an encoded binary blob without validation.
|
|
2477
|
+
* The resulting policy may be invalid. Prefer {@link build}; use this
|
|
2478
|
+
* only to knowingly bypass a reported issue.
|
|
2479
|
+
* @returns The policy as a 0x-prefixed hex string.
|
|
2480
|
+
* @throws {CallciumError} If any group is empty.
|
|
2481
|
+
*/
|
|
2482
|
+
buildUnsafe() {
|
|
2483
|
+
this.checkGroups();
|
|
2484
|
+
return PolicyCoder.encode(this.toPolicyData());
|
|
2485
|
+
}
|
|
2486
|
+
/**
|
|
2394
2487
|
* Validate the policy without encoding.
|
|
2395
2488
|
* @returns All validation issues found.
|
|
2396
2489
|
*/
|
|
@@ -2611,6 +2704,14 @@ function chainId() {
|
|
|
2611
2704
|
function txOrigin() {
|
|
2612
2705
|
return new ConstraintBuilder(Scope.CONTEXT, encodePath([ContextProperty.TX_ORIGIN]));
|
|
2613
2706
|
}
|
|
2707
|
+
/** Target the `block.basefee` context property. */
|
|
2708
|
+
function baseFee() {
|
|
2709
|
+
return new ConstraintBuilder(Scope.CONTEXT, encodePath([ContextProperty.BASE_FEE]));
|
|
2710
|
+
}
|
|
2711
|
+
/** Target the `tx.gasprice` context property. */
|
|
2712
|
+
function gasPrice() {
|
|
2713
|
+
return new ConstraintBuilder(Scope.CONTEXT, encodePath([ContextProperty.GAS_PRICE]));
|
|
2714
|
+
}
|
|
2614
2715
|
//#endregion
|
|
2615
2716
|
//#region src/reader.ts
|
|
2616
2717
|
/** Read 32 bytes at offset, bounds-checked. */
|
|
@@ -2914,9 +3015,16 @@ function loadSlice(callData, location) {
|
|
|
2914
3015
|
ok: false,
|
|
2915
3016
|
code: lengthResult.code
|
|
2916
3017
|
};
|
|
3018
|
+
const dataOffset = dataStart + 32;
|
|
3019
|
+
const node = location.node;
|
|
3020
|
+
const stride = node.type === "dynamicArray" ? node.element.isDynamic ? 32 : node.element.staticSize : 1;
|
|
3021
|
+
if (dataOffset > callData.length || lengthResult > Math.floor((callData.length - dataOffset) / stride)) return {
|
|
3022
|
+
ok: false,
|
|
3023
|
+
code: "CALLDATA_OUT_OF_BOUNDS"
|
|
3024
|
+
};
|
|
2917
3025
|
return {
|
|
2918
3026
|
ok: true,
|
|
2919
|
-
dataOffset
|
|
3027
|
+
dataOffset,
|
|
2920
3028
|
length: lengthResult
|
|
2921
3029
|
};
|
|
2922
3030
|
}
|
|
@@ -2946,6 +3054,12 @@ function descendPath(callData, location, pathBytes) {
|
|
|
2946
3054
|
}
|
|
2947
3055
|
//#endregion
|
|
2948
3056
|
//#region src/policy-enforcer.ts
|
|
3057
|
+
/** Violations that abort evaluation instead of failing only their group (spec §10.3). */
|
|
3058
|
+
const ABORT_VIOLATION_CODES = new Set([
|
|
3059
|
+
"CALLDATA_OUT_OF_BOUNDS",
|
|
3060
|
+
"ARRAY_INDEX_OUT_OF_BOUNDS",
|
|
3061
|
+
"QUANTIFIER_LIMIT_EXCEEDED"
|
|
3062
|
+
]);
|
|
2949
3063
|
/** Convert a hex address to a 256-bit bigint (zero-padded to 32 bytes). */
|
|
2950
3064
|
function addressToBigInt(hex) {
|
|
2951
3065
|
const bytes = hexToBytes(hex);
|
|
@@ -2980,7 +3094,7 @@ function resolveLeafTypeCode(elementNode, remainingPath) {
|
|
|
2980
3094
|
* @param policy - Binary policy blob as 0x-prefixed hex string.
|
|
2981
3095
|
* @param callData - ABI-encoded call data as 0x-prefixed hex string.
|
|
2982
3096
|
* @param context - Optional execution context for context-scoped rules.
|
|
2983
|
-
* @returns Pass with matched group index, or fail with one violation per
|
|
3097
|
+
* @returns Pass with matched group index, or fail with one violation per evaluated failing group.
|
|
2984
3098
|
* @throws {CallciumError} If the policy blob is structurally malformed.
|
|
2985
3099
|
*/
|
|
2986
3100
|
function check(policy, callData, context) {
|
|
@@ -3012,6 +3126,10 @@ function check(policy, callData, context) {
|
|
|
3012
3126
|
const violation = evaluateRule(rule, tree, callDataBytes, baseOffset, groupIndex, ruleIndex, context);
|
|
3013
3127
|
if (violation !== null) {
|
|
3014
3128
|
allViolations.push(violation);
|
|
3129
|
+
if (ABORT_VIOLATION_CODES.has(violation.code)) return {
|
|
3130
|
+
ok: false,
|
|
3131
|
+
violations: allViolations
|
|
3132
|
+
};
|
|
3015
3133
|
groupFailed = true;
|
|
3016
3134
|
break;
|
|
3017
3135
|
}
|
|
@@ -3044,7 +3162,6 @@ function evaluateRule(rule, tree, callDataBytes, baseOffset, groupIndex, ruleInd
|
|
|
3044
3162
|
const pathBytes = hexToBytes(rule.path.value);
|
|
3045
3163
|
const opCode = rule.opCode.value;
|
|
3046
3164
|
const operandData = hexToBytes(rule.data.value);
|
|
3047
|
-
if (pathBytes.length / 2 > Limits.MAX_PATH_DEPTH) throw new CallciumError("INVALID_PATH", `Path depth ${pathBytes.length / 2} exceeds maximum ${Limits.MAX_PATH_DEPTH}.`);
|
|
3048
3165
|
if (scope === Scope.CONTEXT) return evaluateContextRule(pathBytes, opCode, operandData, groupIndex, ruleIndex, rule.path.value, context);
|
|
3049
3166
|
const locResult = locate(tree, callDataBytes, pathBytes, baseOffset);
|
|
3050
3167
|
if (!locResult.ok) return {
|
|
@@ -3266,6 +3383,6 @@ const PolicyEnforcer = {
|
|
|
3266
3383
|
enforce
|
|
3267
3384
|
};
|
|
3268
3385
|
//#endregion
|
|
3269
|
-
export { CallciumError, ContextProperty, Descriptor, DescriptorCoder, Limits, Op, PolicyBuilder, PolicyCoder, PolicyEnforcer, PolicyValidator, PolicyViolationError, Quantifier, Scope, TypeCode, arg, blockNumber, blockTimestamp, bytesToHex, chainId, hexToBytes, isLengthOp, isOpAllowed, isQuantifier, lookupContextProperty, lookupOp, lookupQuantifier, lookupScope, lookupTypeCode, msgSender, msgValue, parsePathSteps, toAddress, txOrigin };
|
|
3386
|
+
export { CallciumError, ContextProperty, Descriptor, DescriptorCoder, Limits, Op, PolicyBuilder, PolicyCoder, PolicyEnforcer, PolicyValidator, PolicyViolationError, Quantifier, Scope, TypeCode, arg, baseFee, blockNumber, blockTimestamp, bytesToHex, chainId, gasPrice, hexToBytes, isLengthOp, isOpAllowed, isQuantifier, lookupContextProperty, lookupOp, lookupQuantifier, lookupScope, lookupTypeCode, msgSender, msgValue, parsePathSteps, toAddress, txOrigin };
|
|
3270
3387
|
|
|
3271
3388
|
//# sourceMappingURL=index.js.map
|