Skip to main content

Smart Contracts

The POSITRONIC upgrade turns Neurai from a transaction-and-asset chain into a programmable covenant environment. It does this by adding a large set of new script opcodes, a transaction-introspection layer, reference inputs (transaction v3), and AuthScript — a new execution model that decouples authentication (who may spend) from authorization (under what conditions), and that natively supports post-quantum ML-DSA-44 signatures.

This page is the technical reference for the scripting layer. For the upgrade overview (blocks, DePIN messaging, MCP, wallets), see the POSITRONIC upgrade page.

Status

Everything documented here as Active is implemented and gated behind consensus verification flags on testnet / regtest in the DePIN-Test branch, pending audit and a coordinated mainnet activation. Items marked Proposed are specified in NIPs but not yet present in the node code. The line is drawn explicitly in each section.

How It Fits Together

POSITRONIC scripting rests on four pillars:

  1. Introspection — scripts can read fields of the spending transaction (its outputs, inputs, locktime, values, asset payloads) and of the chain itself (height, median-time-past, chain id). Before POSITRONIC, a script could only validate the data handed to it; now it can constrain the transaction that spends it.
  2. CovenantsOP_CHECKTEMPLATEVERIFY and OP_CHECKSIGFROMSTACK, combined with OP_CAT / OP_SPLIT and 64-bit arithmetic, let an output dictate the shape of the transaction that spends it. This is the basis for vaults, congestion control, DEX sell-orders and atomic swaps.
  3. Reference inputs (tx v3) — a new vrefin section lets a script read an existing UTXO without spending it, providing oracles and external state to covenants.
  4. AuthScript & Post-Quantum — a witness-v1 execution model whose spending condition is a 32-byte commitment, with pluggable authentication: none, post-quantum (ML-DSA-44), or legacy secp256k1.

Each new opcode is gated by its own SCRIPT_VERIFY_* flag. A flag that occupies a previously unassigned opcode slot fails closed (BAD_OPCODE) on old nodes; a flag that re-purposes a NOP/reserved slot degrades to a no-op unless DISCOURAGE_UPGRADABLE_NOPS is set. This is what makes the rollout a clean soft-fork-style activation.

Opcode Catalogue

All opcodes below are Active (implemented with consensus handlers) unless noted. Stack effect notation is (before -- after), top of stack on the right.

Transaction Introspection

OpcodeHexStack effectDescription
OP_TXHASH0xb5(selector -- hash)Double-SHA256 over a bitmask-selected set of transaction fields (version, locktime, prevouts, sequences, outputs, current input).
OP_TXFIELD0xb6(selector -- bytes)Raw bytes of a single field of the UTXO being spent (value, AuthScript commitment, scriptPubKey).
OP_TXLOCKTIME0xc5(-- nLockTime)The transaction nLockTime as 4 raw bytes.
OP_OUTPUTVALUE0xcc(index -- value)XNA value of output index.
OP_OUTPUTSCRIPT0xcd(index -- scriptPubKey)Full scriptPubKey of output index (including any asset payload).
OP_INPUTVALUE0xd6(index -- value)Value of the prevout consumed by input index (NIP-024).
OP_INPUTCOUNT0xd0(-- n)Number of inputs (NIP-014).
OP_OUTPUTCOUNT0xd1(-- n)Number of outputs (NIP-014).
OP_CHAINCONTEXT0xd7(selector -- value)Chain-level context: height, median-time-past, or chain id (NIP-026).

Covenants & Signatures

OpcodeHexStack effectDescription
OP_CHECKTEMPLATEVERIFY0xb3(hash -- hash)Verifies the spending transaction matches a committed template (BIP-119 style). Leaves the hash on the stack.
OP_CHECKSIGFROMSTACK0xb4(sig msg pubkey -- bool)Verifies a signature over an arbitrary message popped from the stack (oracles, delegation). secp256k1 or ML-DSA-44.
OP_CHECKSIGADD0xde(sig n pubkey -- n')Accumulating multi-signature primitive; n'=n+1 on a valid signature. Accepts legacy and PQ keys (NIP-039).
OP_CHECKSIG_ED255190xdd(sig msg pubkey -- bool)Strict RFC-8032 PureEd25519 verifier (NIP-035).

Byte Manipulation & Arithmetic

OpcodeHexStack effectDescription
OP_CAT0x7e(a b -- a‖b)Concatenate two byte strings (BIP-347).
OP_SPLIT0xb7(data pos -- left right)Split a byte string at pos.
OP_REVERSEBYTES0xbc(data -- reversed)Reverse byte order (endianness conversion).
OP_MUL / OP_DIV / OP_MOD0x95 / 0x96 / 0x97(a b -- result)Re-enabled under 64-bit signed integers.

Asset Introspection

OpcodeHexStack effectDescription
OP_OUTPUTASSETFIELD0xce(index selector -- field)A field of the asset payload attached to output index.
OP_INPUTASSETFIELD0xcf(index selector -- field)A field of the asset payload of input index's prevout.

Reference Inputs (tx v3)

OpcodeHexStack effectDescription
OP_REFINPUTCOUNT0xd4(-- n)Number of reference inputs in vrefin.
OP_REFINPUTFIELD0xd2(index selector -- field)A base field of a reference input (read-only, not spent).
OP_REFINPUTASSETFIELD0xd3(index selector -- field)An asset field of a reference input.

Output Authentication

OpcodeHexStack effectDescription
OP_OUTPUTAUTHCOMMITMENT0xd5(index -- commitment)The 32-byte AuthScript commitment of output index (NIP-023).

Modern & SNARK-Friendly Hashes

OpcodeHexOutputNIP
OP_KECCAK2560xba32 bytesNIP-030
OP_BLAKE2B0xbb32 bytesNIP-030
OP_BLAKE30xc832 bytesNIP-034a
OP_SHA3_2560xca32 bytesNIP-034a
OP_SHA5120xcb64 bytesNIP-034a
OP_POSEIDON0xc932 bytesNIP-036
OP_CHECKMERKLEINCLUSION0xc1boolNIP-031

Covenants

A covenant is a spending condition that constrains the transaction allowed to spend an output. POSITRONIC offers two complementary covenant primitives.

OP_CHECKTEMPLATEVERIFY (CTV)

What it does. OP_CHECKTEMPLATEVERIFY (0xb3) commits an output to an exact template of the transaction that may spend it: its version, locktime, input count, sequences, output set, and the index of the spending input. If the spending transaction does not hash to the committed 32-byte template, the script fails.

Characteristics.

  • No signature is needed — CTV is self-verifying by consensus rule.
  • The hash is not consumed on success (NOP-upgrade semantics), so scripts usually follow it with OP_DROP.
  • A non-32-byte argument behaves as a NOP unless DISCOURAGE_UPGRADABLE_NOPS rejects it as non-standard.
  • Maximally rigid: it fixes every output down to the satoshi. This is its strength (anti-double-spend, deterministic outcome) and its limit (no dynamic amounts on its own).

Template fields committed by the default CTV hash:

SHA256(
nVersion (4 bytes LE)
nLockTime (4 bytes LE)
inputCount (4 bytes LE)
SHA256(all sequences)
outputCount (4 bytes LE)
SHA256(all serialized outputs)
inputIndex (4 bytes LE)
)

Example — fixed three-way distribution. An output that can only ever be spent into a transaction paying 30 / 50 / 20 XNA to three predetermined recipients:

witnessScript:
<32-byte-CTV-hash>
OP_CHECKTEMPLATEVERIFY
OP_DROP
OP_TRUE

Spending it requires no key; only a transaction whose outputs hash to the committed template is valid.

OP_CHECKSIGFROMSTACK (CSFS)

What it does. OP_CHECKSIGFROMSTACK (0xb4) verifies a signature over a message that is popped from the stack rather than computed from the transaction. This breaks the assumption that a signature must commit to a specific transaction, enabling oracles and delegation.

Characteristics.

  • The message can be any bytes: a price, a contract term, a commitment.
  • Accepts both secp256k1 (33-byte pubkey) and ML-DSA-44 (0x05-prefixed, 1313-byte pubkey).
  • The per-element size cap widens from 520 to 3072 bytes whenever CSFS is enabled, so it can carry a 2421-byte PQ signature and a 1313-byte PQ public key.

Example — oracle-gated spend. A script that only proceeds if a trusted oracle signed a particular price:

witness: [ price_bytes, oracle_sig, witnessScript ]

witnessScript:
<oracle_pubkey>
OP_CHECKSIGFROMSTACK # verifies oracle_sig over price_bytes
OP_VERIFY
... # downstream logic can now trust price_bytes

Building Templates Dynamically — CAT, SPLIT, 64-bit Math

CTV alone commits to a static template. To express partially fixed transactions (the seller's payout is fixed, the buyer's change is free), a script assembles the template at validation time:

  • OP_CAT (0x7e) concatenates fixed prefixes with witness-supplied output bytes.
  • OP_SPLIT (0xb7) and OP_REVERSEBYTES (0xbc) parse and re-order serialized fields.
  • OP_MUL / OP_DIV / OP_MOD operate on 64-bit signed integers (the old limit was 4 bytes / 32-bit), which is what makes price × quantity checks possible on full XNA amounts.
64-bit gating

When SCRIPT_VERIFY_64BIT_INTEGERS is active, numeric operands widen from 4 to 8 bytes and OP_MUL/DIV/MOD are re-enabled. Introspection opcodes that return an 8-byte value (OP_OUTPUTVALUE, OP_INPUTVALUE, asset amount fields) are converted to a CScriptNum so they can feed directly into arithmetic. OP_CHAINCONTEXT requires this flag to be co-active.


Transaction v3 and Reference Inputs

POSITRONIC introduces transaction version 3 with a new vrefin section: a vector of outpoints the transaction references but does not spend.

tx v3 {
vin [ ... ] // spent inputs (consume the UTXO, require auth)
vrefin [ ... ] // reference inputs (read-only, NEW)
vout [ ... ] // outputs
}

Serialization & consensus. vrefin is serialized between vout and nLockTime and only when nVersion == 3. It is committed in the txid/wtxid and the signature hash, so tampering with a reference invalidates signatures and CTV hashes. Each referenced outpoint must exist and be unspent in the UTXO set, must not also appear in vin, and remains in the UTXO set after the transaction confirms. Multiple transactions may reference the same UTXO without conflict.

Why it matters. Reference inputs turn any existing UTXO into a portable, read-only source of state. A covenant can read an oracle's published UTXO, a price tag, or a configuration object without that UTXO being consumed — the foundation for oracles and richer DePIN state machines.

The reference-input opcode family. Three opcodes read vrefin without spending it:

  • OP_REFINPUTCOUNT (0xd4) — (-- n).
  • OP_REFINPUTFIELD (0xd2) — (index selector -- field) with selectors:
    • 0x01 value (8-byte LE / CScriptNum under 64-bit)
    • 0x02 AuthScript commitment (32 bytes)
    • 0x03 full scriptPubKey (size-capped)
  • OP_REFINPUTASSETFIELD (0xd3) — (index selector -- field), with selectors 0x010x07 (name, amount, units, reissuable, has-IPFS, IPFS hash, asset type).

All three are gated by SCRIPT_VERIFY_REFINPUTS and degrade to NOPs on nodes that do not recognize the flag.


Asset & Chain Introspection

Asset fields. OP_OUTPUTASSETFIELD (0xce) and OP_INPUTASSETFIELD (0xcf) parse the Neurai asset payload embedded in a scriptPubKey and push a selected field, so a covenant does not have to hand-parse the serialized asset blob:

SelectorField
0x01Asset name
0x02Amount (8-byte LE / CScriptNum)
0x03Units (decimals)
0x04Reissuable flag
0x05Has-IPFS flag
0x06IPFS hash
0x07Asset type

Chain context. OP_CHAINCONTEXT (0xd7, NIP-026) exposes blockchain position to a script via a 1-byte selector: 0x01 height, 0x02 median-time-past, 0x03 chain id (0 mainnet / 1 testnet / 2 regtest). Because height and MTP change with the chain tip, the mempool re-validates any transaction whose script observed chain context on each new block.


AuthScript & Post-Quantum

AuthScript is the headline new script type. It is a witness version 1 program whose witness program is a 32-byte commitment (TX_WITNESS_V1_AUTHSCRIPT), encoded as a Bech32m address. Its purpose is to separate two planes that classic Script conflates:

  • Authentication planewho may spend, verified before the script runs, with the signature and public key kept out of the script stack.
  • Script (authorization) planeunder what conditions, evaluated with only the functional arguments on the stack.

This separation is what lets enormous post-quantum keys and signatures coexist with covenant logic: a 2420-byte ML-DSA-44 signature and a 1312-byte public key would blow past the 80-byte standard stack-item limit if they were ordinary stack pushes, so AuthScript validates them outside the script entirely.

Witness Layout

witness.stack = [
auth_type, // 1 byte: 0x00 NoAuth | 0x01 PQ | 0x02 Legacy
<auth payload>, // signature + pubkey (absent when auth_type = 0x00)
arg1 ... argN, // functional arguments for the script
witnessScript // the script to execute (always last)
]

Auth Types

TypeByteAuthenticationSignaturePublic key
NoAuth0x00none — the script must be self-restricting
Post-Quantum0x01ML-DSA-44 (FIPS 204)2420 + 1 hashtype = 2421 B0x05-prefix + 1312 = 1313 B
Legacy0x02secp256k1 ECDSA~71–73 Bcompressed 33 B

The verifier enforces type consistency: a 0x01 payload must carry a PQ key and a 0x02 payload must carry a non-PQ key. Under SCRIPT_VERIFY_WITNESS_PUBKEYTYPE, legacy keys must be compressed.

The Commitment

The 32-byte program in the scriptPubKey (OP_1 0x20 <32 bytes>) is a tagged hash binding the version, the authentication descriptor, and the script:

commitment = TaggedHash("NeuraiAuthScript",
version_byte ‖ auth_descriptor ‖ SHA256(witnessScript))

where auth_descriptor is 0x00 for NoAuth, or auth_type ‖ Hash160(pubkey) otherwise. The verifier recomputes this from the revealed witnessScript and auth payload and checks it against the committed program before running anything. OP_OUTPUTAUTHCOMMITMENT (0xd5) lets a covenant read this commitment from an output, so one AuthScript can require its spends to flow into specific other AuthScripts.

Signature Hash

AuthScript introduces a third signature version, SIGVERSION_AUTHSCRIPT (= 2), alongside base and witness-v0. Its sighash preimage uses the literal witnessScript as the scriptCode and additionally commits the auth_type byte, so a signature made under one authentication method cannot be replayed under another.

Post-Quantum Addresses & Derivation

  • Algorithm: ML-DSA-44 (FIPS 204) — private key 2560 B, public key 1312 B, signature 2420 B.
  • Address format: Bech32m, witness v1, 32-byte program. HRPs are nq (mainnet), tnq (testnet), rnq (regtest) — e.g. tnq1....
  • HD derivation: PQ keys derive under purpose 100', account path m/100'/<coin>'/0', with receiving keys at m/100'/<coin>'/0'/0/i and change at .../1/i. Coin type is 1900' on mainnet and 1' on testnet/regtest. The 32-byte private bytes of each derived child seed the ML-DSA-44 keygen.
  • Not WIF-compatible: a 2560-byte ML-DSA-44 secret has no WIF representation and no pubkey-recovery, so PQ keys cannot be swept via WIF import.

Policy Notes

The auth payload (PQ signature + key) is exempt from the 80-byte standard stack-item limit because it never enters the script stack; functional arguments remain subject to the normal limits. Mempool relay, witness relay and RBF rules were widened to accommodate the larger PQ script elements, and the wallet persists a commitment → spend-data map (auth type, witnessScript, key reference) since the 32-byte commitment alone is not enough to reconstruct how to spend an AuthScript output.


Covenant Patterns

These are reference patterns built from the primitives above. They are illustrative compositions, not canonical scripts.

Partial-Fill Sell Order (DEX)

A stateful UTXO that lets buyers purchase fractions of an asset listing without the seller re-signing each fill. The covenant enforces correct payment, correct asset delivery, and that the remainder re-locks into the same covenant.

  • Opcodes: OP_OUTPUTVALUE, OP_OUTPUTSCRIPT, OP_OUTPUTASSETFIELD, OP_MUL, plus OP_CHECKSIG on the seller-cancellation path.
  • Mechanism: read output 0's value and recipient (payment to seller), read output 1's asset name/amount (delivery to buyer), read output 2's script and remaining amount (the re-locked remainder), and require payment ≥ unit_price × quantity.
  • Characteristics: self-enforcing (no seller interaction per fill); first-confirmed transaction wins on contention; seller keeps a privileged cancellation branch.
OP_IF
# --- public partial-fill branch ---
0 OP_OUTPUTVALUE <unit_price> <qty> OP_MUL OP_GREATERTHANOREQUAL OP_VERIFY
0 OP_OUTPUTSCRIPT <seller_spk> OP_EQUALVERIFY
1 0x01 OP_OUTPUTASSETFIELD <asset_name> OP_EQUALVERIFY
1 0x02 OP_OUTPUTASSETFIELD <qty> OP_EQUALVERIFY
2 OP_OUTPUTSCRIPT <same_covenant_spk> OP_EQUALVERIFY
2 0x02 OP_OUTPUTASSETFIELD <total> <qty> OP_SUB OP_EQUALVERIFY
OP_TRUE
OP_ELSE
# --- seller cancellation branch ---
<seller_pubkey> OP_CHECKSIG
OP_ENDIF

Atomic Swap with CTV + CAT

The seller fixes the output that pays them; the buyer supplies the output that delivers the asset to their chosen address. OP_CAT assembles the full output set at validation time, OP_SHA256 hashes it, and OP_CHECKTEMPLATEVERIFY confirms the assembled template equals the actual transaction. Net effect: a swap where the buyer cannot redirect the seller's payment, the seller cannot redirect the buyer's asset, and the whole thing is atomic.

Mixed Legacy / Post-Quantum Spends

Because authentication is per-input, a single transaction can mix a legacy ECDSA input and an AuthScript PQ input. Each input verifies under its own auth type; the transaction is atomic. This is the migration path: holders can move value from classical to post-quantum protection one input at a time.


Script Limits & Verification Flags

LimitValue
Default max stack element520 bytes
Widened element cap (when CSFS / Merkle-inclusion / CHECKSIGADD enabled)3072 bytes
Numeric operand width (under SCRIPT_VERIFY_64BIT_INTEGERS)8 bytes (int64)
ML-DSA-44 signature / public key2420 B / 1312 B (+1 each serialized)

Each feature is gated by a dedicated flag (bit position in the script verify bitfield):

FlagBitEnables
SCRIPT_VERIFY_AUTHSCRIPT16AuthScript witness-v1 execution
SCRIPT_VERIFY_CAT17OP_CAT
SCRIPT_VERIFY_CHECKTEMPLATEVERIFY18OP_CHECKTEMPLATEVERIFY
SCRIPT_VERIFY_CHECKSIGFROMSTACK19OP_CHECKSIGFROMSTACK (widens element cap)
SCRIPT_VERIFY_TXHASH20OP_TXHASH
SCRIPT_VERIFY_64BIT_INTEGERS2864-bit arithmetic; OP_MUL/DIV/MOD
SCRIPT_VERIFY_REFINPUTS31OP_REFINPUT* family
SCRIPT_VERIFY_OUTPUTAUTHCOMMITMENT32OP_OUTPUTAUTHCOMMITMENT
SCRIPT_VERIFY_CHAINCONTEXT34OP_CHAINCONTEXT (co-requires bit 28)
SCRIPT_VERIFY_MERKLE_INCLUSION36OP_CHECKMERKLEINCLUSION (widens element cap)
SCRIPT_VERIFY_POSEIDON38OP_POSEIDON
SCRIPT_VERIFY_ED2551939OP_CHECKSIG_ED25519
SCRIPT_VERIFY_CHECKSIGADD40OP_CHECKSIGADD (widens element cap)

(Introspection, byte-op and asset-field opcodes occupy the intervening bits 21–30.)


Native Merkle Inclusion

OP_CHECKMERKLEINCLUSION (0xc1, NIP-031) verifies that a leaf belongs to a Merkle tree at a given root, with the stack effect (leaf scheme_id proof root -- bool). It supports four tree schemes:

SchemeIdNode hash
Bitcoin/Neurai0x01double SHA-256
SHA-256 plain0x02single SHA-256
Keccak-256 plain0x03Keccak-256 (requires NIP-030)
BLAKE2b plain0x04BLAKE2b-256 (requires NIP-030)

The proof encodes a 1-byte depth (max 32), the sibling hashes, and a direction bitmap. This gives covenants an efficient, native way to validate membership (allow-lists, airdrops, state commitments) without re-implementing the hashing in script.


Proposed (Not Yet Implemented)

The following are specified in NIPs but are not present in the node code and are documented here for forward visibility:

  • Reference Scripts (NIP-015) — publish a witnessScript once in a carrier UTXO and reuse it across transactions via vrefin and a new AUTHSCRIPT_REF witness mode, instead of inlining the script in every spend. Depends on reference inputs (already active).
  • Zero-knowledge opcode family (NIP-016 / NIP-018)OP_ZKVERIFY, OP_ZKVERIFYREF and OP_ZKTXBIND to verify Groth16/BN254 proofs on-chain and bind them to transaction context. No verifier backend or opcode handlers exist yet.

See Also

  • POSITRONIC upgrade overview
  • Assets — the asset types these opcodes introspect
  • Developer Hub — SDKs and libraries, including @neuraiproject/neurai-scripts (a ScriptBuilder with these opcode constants) and @neuraiproject/neurai-sign-transaction (NoAuth / PQ / Legacy signing)