Callcium

Policy Validation

Type mismatches, contradictions, and redundancies.

Callcium validates policies at build time through semantic analysis. When you call PolicyBuilder.build(), the PolicyValidator inspects every constraint for type errors, logical contradictions, redundancies, and vacuous conditions — then reverts if any issue is found.

Using validation

PolicyBuilder.build() validates by default and reverts on any issue:

// Reverts with ValidationError if issues are found.
bytes memory policy = PolicyBuilder
    .create("approve(address,uint256)")
    .add(arg(0).isIn(trustedSpenders))
    .build();

To inspect issues without reverting, use PolicyBuilder.validate():

Issue[] memory issues = PolicyBuilder
    .create("approve(address,uint256)")
    .add(arg(0).isIn(trustedSpenders))
    .validate();

for (uint256 i = 0; i < issues.length; i++) {
    // Handle each issue: severity, category, code, message.
}

PolicyBuilder.buildUnsafe() skips validation entirely. The resulting policy may be invalid.

Issue structure

Each issue contains:

FieldTypeDescription
severityIssueSeverityError, Warning, or Info.
categoryIssueCategoryTypeMismatch, Contradiction, Redundancy, Vacuity, or Compatibility.
codebytes32Machine-readable identifier.
groupIndexuint256Index of the group holding the rule the issue names.
ruleIndexuint256Index of that rule within the group: a test, or the loop of a body test.
bodyRuleIndexuint256Index of the test within its loop's body; NO_INDEX for a root rule.
scopeuint8Scope of the rule.
pathbytesPath of the rule; a body test's path starts at the element.
operatorbytesEncoded operator of a test; empty for a loop.
value1bytes32First value that shows the defect, or zero.
value2bytes32Second value that shows the defect, or zero.
messagestringHuman-readable description.

An issue about the descriptor names no rule: its indices are ValidationIssue.NO_INDEX. The indices follow the policy's normal form, the one build() encodes: groups and rules sorted, constraints on one array and quantifier joined into one loop, a gte and lte pair on one path joined into one between. They do not follow the order of .add() and .or() calls; match an issue to a constraint by its scope and path.

Severity levels

PolicyBuilder.build() reverts on any issue regardless of severity. Use PolicyBuilder.validate() to inspect issues without reverting.

SeverityMeaning
ErrorThe policy is logically invalid (contradictions, type mismatches).
WarningThe policy is valid but contains redundant constraints.
InfoThe constraint is technically correct but vacuous (always true).

Issue catalog

Type mismatch

Operators, operands, and paths that do not fit the descriptor's declared types. All are Error severity.

CodeDescription
INVALID_DESCRIPTORDescriptor whose composite node metadata disagrees with its children.
UNNAVIGABLE_PATHPath that does not navigate the descriptor.
QUANTIFIER_ON_NON_ARRAYLoop path that does not end at an array.
HINT_MISMATCHHint other than the compilation of its path against the descriptor.
NUMERIC_OP_ON_NON_NUMERICOrdering comparison on a type that is neither uint* nor int*.
BITMASK_ON_INVALIDBitmask comparison on a bool or function.
IN_ON_BOOLisIn/notIn on a bool.
CONTEXT_TYPE_MISMATCHContext operand whose property type does not pair with the target type.

Contradiction

Make the constraint set unsatisfiable. All are Error severity.

CodeDescription
OUT_OF_PHYSICAL_BOUNDSConstant outside its type's values.
CONTRADICTORY_RULERule at which the rules on its subject stop being satisfiable together (PV-9).

Redundancy

Constraints with no effect because others imply them. All are Warning severity.

CodeDescription
REDUNDANT_RULERule the other rules on its subject imply (PV-9).
DOMINATED_BOUNDNumeric bound dominated by a stricter bound.
DOMINATED_LENGTH_BOUNDLength bound dominated by a stricter bound.

Vacuity

Constraints that are always true. Info severity.

CodeDescription
VACUOUS_RULERule that holds for every value and every context (PV-9).

Compatibility

Loops that cross a normative limit or hold no test of their element. All are Error severity. Normative limits are fixed for each minor version and every conformant enforcer applies the same values, so buildUnsafe() is the only way to emit a policy that crosses one.

CodeDescription
QUANTIFIER_OVER_STATIC_LIMITLoop over a static array declared longer than MAX_QUANTIFIED_ARRAY_LENGTH (PV-7).
CONTEXT_ONLY_BODYLoop body that holds no test of the element (PV-8).

On this page