Rule

operation-intent-policy

A protected Cell mutation does not satisfy its declared complete operation-intent statement.

Release v0.1.1View Markdown

Specimen 38 of 96 · Policy-dependent rules

Severity
info
Confidence
high
Maturity
experimental
Selection
Opt-in + policy
Policy intent
required
Release
v0.1.1
Engine facts used
declared-operation-intent exact-signed-statement registered-key original-write-history canonical-self nonce-consumption

Selection and scope#

Not selected by default. Select it with --detectors or the detectors list in nightshade.json, and declare the matching application intent in policies. Selecting it alone does not supply that intent.

Shell
./.nightshade/node_modules/.bin/nightshade ./contracts --detectors operation-intent-policy
./.nightshade/node_modules/.bin/nightshade detector show operation-intent-policy

The first command runs only this rule; --detectors takes a comma-separated list. The second prints the rule's metadata from your installed version.

Severity is review priority. It does not express exploitability or impact. Confidence is detector metadata, not a probability. All built-in rules are experimental.

Compiler scope: the engine supports the pinned profiles 0.28.0, 0.29.0, 0.30.0, 0.31.1, 0.33.0-rc.2, 0.34.0, 0.35.0-rc.1 and 0.35.0, with ZKIR2 by default. Support for this rule can be narrower; the rule documentation below states any narrower scope.

Corpus checks#

Synthetic corpus checks for this rule in the 0.1.1 proof run. A check counts only with compiler acceptance and complete analysis.

Positive detectedCorrected quietLookalike quietOther negative quietIncomplete
2/22/22/20/00

No deviations were recorded for this rule.

These are behavior checks on synthetic fixtures, not a precision or recall estimate. See evaluation.

Rule documentation#

From docs/detectors/operation-intent-policy.md at v0.1.1.

Availability: opt-in, explicit policy required. Severity: info. Confidence: high for a represented application-policy mismatch; deployment impact and globally feasible execution are unproved.

Configuration version 1 accepts this additive optional policy. Report version 1.6.0 retains its existing findings/metadata and rule-coverage shape; this rule adds no serialized top-level report fields or custom-facts capability.

Select operation-intent-policy and declare policies.operationIntent. Each policy selects an exported circuit, ordinary scalar protected Cells, a Bytes<32> registered-controller commitment Cell, an unsigned nonce Cell, natural numeric operation/version strings, exact signed statement field names, and exact input bindings. Canonical kernel.self() is mandatory. Operation tags must be distinct for entries sharing controller and nonce. Selectors resolve to unique canonical symbols; a parameter selector resolves inside its entry.

JSON
{
  "operationIntent": [{
    "circuit": "updateDocument",
    "ledgers": ["document"],
    "controllerCommitment": "controllerCommitment",
    "nonce": "authorizationNonce",
    "operation": "1",
    "version": "1",
    "statement": {
      "self": "selfAddress", "operation": "operation",
      "version": "version", "nonce": "nonce"
    },
    "inputs": [{"field": "input", "parameter": "next", "writeValue": "document"}]
  }, {
    "circuit": "revokeDocument",
    "ledgers": ["document"],
    "controllerCommitment": "controllerCommitment",
    "nonce": "authorizationNonce",
    "operation": "2",
    "version": "1",
    "statement": {
      "self": "selfAddress", "operation": "operation",
      "version": "version", "nonce": "nonce"
    },
    "inputs": [{"field": "input", "ledger": "document"}]
  }]
}

parameter inputs require writeValue or explicit context: true. writeValue selects the protected Cell whose actual written scalar must equal that signed field and the declared entry parameter. context: true requires exact signing of the parameter without claiming that it is the protected written value. Required scalar equalities before the write can connect aliases. ledger inputs select immutable initial-entry snapshots and express signed state context; they do not stand in for a dynamic write operand. An unsigned dynamic protected value without a declared write binding is a mismatch. Constants and exact Field self-subtraction have no independent dynamic write value. Ledger/local snapshots, shared source provenance and wildcard regions never donate must-equality.

For each complete original successful effect history, one enforced canonical secp256k1EcdsaVerify result must cover the whole declared tuple before the write. Its actual keccak256 input must have the declared fields. The exact verifier point must supply both canonical coordinates of the persistentHash<Vector<2, Secp256k1Base>> enforced equal to the preexisting registered commitment. Distinct witness calls do not imply equal keys. Signed tuple requirements cannot combine across separate signatures or exclusive histories. Mandatory scalar Boolean relays preserve equality/inequality parity and negation, including independently supplied outcomes: v != relay with !relay enforces the exact v as true, while the opposite relay truth enforces false. A separate bounded parity pass tracks these truth requirements without treating complement as scalar equality. Unanchored relays do not enforce true. Aggregate Boolean comparison projection, lost Boolean structure and conflicting parity evidence are scoped incomplete. Later checks remain outside the earlier effect's authorization prefix. Configured domain storage alone does not replace canonical self; version and current nonce are independent requirements. Native verifier semantics remain the compiler-profile model, not cryptographic protocol verification.

Each successful selected operation with protected effects must have exactly one nonce write after all protected effects, with retained exact operands proving currentNonce + 1 and a value-preserving unsigned result. Earlier nonce/controller writes, resets, repeated consumption, and controller enrollment in a selected mutation are mismatches. All exported callable closures are checked for other successful protected/controller/nonce writers; undeclared writers yield a scoped incomplete diagnostic. Constructors are the explicit enrollment/initialization boundary; trust in enrollment remains external. This rule does not establish nonce exhaustion behavior, trusted configuration, serialization injectivity or signature protocol security. Checked compiler arithmetic determines the modeled successful increment semantics.

Preparation, interpretation, scalar closure, proof and retained evidence share 40,000 steps per entry, 256 paths and 50,000 cumulative abstract-value allocation units for the rule pass. These are the ordinary production limits, not extra proof budgets. Exact digest input and numeric operand evidence are opt-in and shared by both interpreter engines and cached/uncached transfers. The ordinary scan retains no operation-intent carrier, canonical-self marker or extra numeric operand metadata. Opaque statements/keys, lost identities, unsupported aliases, unresolved closures, baseline-incomplete entries, checkpoints requiring atomic linkage and exhausted budgets yield rule-scoped incomplete diagnostics.

A false positive can reflect a stale, overbroad, or incorrect policy rather than a contract defect. Confirm that the declaration, signed fields, input bindings, and nonce expectations describe the application's intended protocol before acting on the finding. The rule checks conformance to configured policy; it does not infer that policy's correctness or prove signature security.

Suppress a match#

If a match is intentional, suppress it on the line before with the exact rule ID and a reason of at least eight characters:

Compact
// nightshade-disable-next-line operation-intent-policy -- Reason this match is intentional

Diagnostics cannot be suppressed, and an unmatched directive for a selected rule makes analysis incomplete. See configuration for fingerprint suppressions and baselines.

References#