Skip to content

OP_INSPECTINTENTMESSAGE #68

@louisinger

Description

@louisinger

Add OP_INSPECTINTENTMESSAGE opcode to inspect intent message in SubmitIntent context

Depends on: #60 (unified big number arithmetic). This opcode's numeric
encoding assumes #60's BigNum model; it should land after that refactor or
be rebased onto it.

Motivation

An Arkade script executed in SubmitIntent today has access to the intent
proof transaction (standard introspection opcodes) and to transaction
extension packets (OP_INSPECTPACKET / OP_INSPECTINPUTPACKET), but has no
way to read the RegisterMessage JSON
that accompanies the proof.

The introspector validates the message shape (ValidAt / ExpireAt) at the
service layer, but scripts cannot express policies such as:

  • "This VTXO may only be spent via an intent that expires within N seconds."
  • "This VTXO may only be co-registered with a specific set of cosigner keys."
  • "This VTXO may only be registered for an intent whose output 2 is declared
    as an onchain output."
  • "Reject registration for this VTXO if a given cosigner key is present."

All of these policies turn on fields of RegisterMessage. Exposing the
message to scripts keeps policy enforcement in the Arkade script itself
rather than requiring bespoke introspector logic per use case.

Proposal

Add a new opcode:

Word Opcode Hex Input Output
OP_INSPECTINTENTMESSAGE 246 0xf6 path value 1 or <empty> 0

0xf6 is currently OP_UNKNOWN246, sitting next to the other
packet-introspection opcodes (OP_INSPECTPACKET = 0xf4,
OP_INSPECTINPUTPACKET = 0xf5).

Semantics

  1. Pop path as a byte string.
  2. If no intent message is bound to the current execution context (i.e. the
    script is running in SubmitTx, SubmitOnchainTx, or
    SubmitFinalization), push <empty> 0.
  3. Interpret path as a gjson path
    evaluated against the raw JSON bytes of the RegisterMessage.
  4. If the query yields Null or NotExists, push <empty> 0.
  5. Otherwise, encode the result and push value 1.

Value encoding

Numeric encoding follows the unified BigNum model from #60
(minimally-encoded sign-magnitude little-endian, up to 520 bytes), matching
what OP_INSPECTOUTPUTVALUE / OP_INSPECTINPUTVALUE push after that
refactor. Pushed values feed directly into standard arithmetic opcodes with
no conversion.

gjson result type Pushed value
String Raw UTF-8 bytes of the string (no JSON quoting).
Number (integer) Minimally-encoded BigNum (sign-magnitude LE).
Number (non-integer) Miss (<empty> 0).
True / False OP_1 (0x01) / OP_0 (empty push).
JSON (obj/arr) Raw JSON bytes (gjson.Result.Raw).
Null / missing Miss (<empty> 0).

A JSON number is "integer" iff its raw textual form has no fractional part
and its exponent (if any) resolves to an integer. Numbers are parsed from
gjson.Result.Raw directly, not via gjson's float64 accessor, so the
BigNum range — not float64's 2^53 exact-integer ceiling — is the
effective limit.

Why a gjson string selector

  • Agnostic. One opcode covers every field of RegisterMessage, now and
    in the future. New fields require no code change.
  • Shape-flexible. Scalars, arrays, and nested additions are all
    addressable through the same path language (arr.0, arr.# for length,
    arr.#(cond), etc.).
  • Whole-message access. Path "@this" returns the full raw JSON — useful
    for hashing or substring commitments — without a dedicated opcode or
    special sentinel.

Context propagation

The service layer plumbs the raw message bytes into the engine via a new
ExecuteOption, analogous to how SetAssetPacket / SetIntrospectorPacket
work today:

// pkg/arkade engine
func (vm *Engine) SetIntentMessage(raw []byte)

// pkg/arkade script
func WithIntentMessage(raw []byte) ExecuteOption

application.SubmitIntent is the only caller that sets it. Other entry
points (SubmitTx, SubmitOnchainTx, SubmitFinalization) leave it unset,
so the opcode naturally returns a miss in those contexts.

The gRPC handler currently parses message into intent.RegisterMessage and
discards the raw string; it must additionally preserve the raw bytes on
application.Intent for the opcode to consume.

Examples

Expiry cap — require the intent to expire within 60 s of reference time T. Under #60's unified arithmetic the pushed number feeds OP_LESSTHANOREQUAL directly:

<"expire_at"> OP_INSPECTINTENTMESSAGE OP_VERIFY
<T+60> OP_LESSTHANOREQUAL

Array length assertion — require exactly one onchain output:

<"onchain_output_indexes.#"> OP_INSPECTINTENTMESSAGE OP_VERIFY
<1> OP_EQUALVERIFY

Whole-message hash commitment:

<"@this"> OP_INSPECTINTENTMESSAGE OP_VERIFY
OP_SHA256
<expectedHash> OP_EQUALVERIFY

(Clients using this pattern must reproduce the exact JSON byte sequence the
introspector received — whitespace and key ordering included.)

Non-goals

  • Exposing the intent message in SubmitTx / SubmitOnchainTx /
    SubmitFinalization. The opcode evaluates to a miss there.
  • Defining a canonical binary encoding for RegisterMessage. The opcode
    operates on the JSON the client sent.
  • Supporting message types other than RegisterMessage. The opcode is
    message-type-agnostic (it queries whatever JSON was supplied), but
    RegisterMessage is the only type reaching Arkade scripts today.

Open questions

  • gjson dependency. The repo does not currently depend on
    github.com/tidwall/gjson. Small, widely used, no transitive deps.
    Alternative: a minimal dotted-path walker over encoding/json.
    Recommendation: take the dep.
  • Non-integer numbers. Current proposal: miss. Alternative: script-fatal.
    Miss is more robust against malformed messages. Parsing goes through the
    raw JSON number string, so the BigNum range from Unified big number arithmetic #60 is the ceiling — not
    float64 precision.

Reference

Full design doc: docs/superpowers/specs/2026-04-21-op-inspectintentmessage-design.md

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions