Script Pricing

How Toccata turns compute budgets into script-unit limits, and why transactions fail when the budget is wrong.

A covenant transaction can be structurally correct and still fail because the input did not buy enough execution room.

If the active input runs out of committed execution room, validation fails.

spent input -> compute_budget -> script-unit limit -> script execution

Toccata replaces the old signature-count budget with a runtime meter that can price the work covenants actually do.

The unit ladder

There are three names in play.

NameWhere it livesCurrent conversion
compute_budgetv1 transaction input1 budget = 100 compute grams
compute gramtransaction mass accounting1 gram = 100 script units
script unitscript-engine runtime metercharged as the VM runs

So one v1 compute-budget unit buys:

1 compute_budget = 100 compute grams = 10,000 script units

A v0 sig_op_count remains a compatibility path:

1 sig_op_count = 1,000 compute grams = 100,000 script units

Each input also gets a fixed free allowance of 9,999 script units.

For v1:

allowed_script_units = compute_budget * 10,000 + 9,999

The allowance is one unit less than a full compute-budget step. That gives tiny scripts some room without letting budget N - 1 cover exactly N budget units of work.

Required script unitsRequired v1 compute_budget
00
9,9990
10,0001
19,9991
20,0002

What the meter charges

The meter follows runtime work, not only signatures.

It charges:

  • newly pushed stack bytes;
  • hashing opcodes;
  • signature verification attempts;
  • ZK precompile verification;
  • spent UTXO script public key bytes above the standard size.

That is why v1 needs compute_budget. A static sig-op count cannot price OpBlake3, covenant byte construction, stack growth, or OpZkPrecompile.

Small scripts fail too

A common debugging path looks like this:

flowchart LR
    classDef ok fill:#eef9ef,stroke:#4d9f5b,color:#132016
    classDef bad fill:#fff0f0,stroke:#c65d5d,color:#2b1111
    classDef tx fill:#fff7e8,stroke:#c58b2a,color:#1f1b12

    TX["v1 input<br/>compute_budget = 3"]:::tx
    LIMIT["limit = 39,999 SU"]:::tx
    RUN["script consumes<br/>41,200 SU"]:::bad
    FAIL["validation fails"]:::bad

    TX --> LIMIT --> RUN --> FAIL

The script did not fail because the state rule was wrong. It failed because the input did not buy enough execution room.

Budget too high has the opposite cost: mass goes up, fees go up, and block admission gets worse. Generated transaction builders should estimate script units and set the input budget, not ask users to guess.

Current costs

The constants below are the current Toccata pricing rules.

Hashing:

OpcodeCost
OP_SHA2561 SU per hashed byte
OpBlake2bWithKey2 SU per hashed data byte
OpBlake2b2 SU per hashed byte
OpBlake31 SU per hashed byte
OpBlake3WithKey1 SU per hashed data byte

Stack and script-public-key events:

EventCost
Bytes newly pushed into execution stacks1 SU per byte
Spent UTXO script public key above standard 35 bytes100 SU per extra byte

Signature verification:

1 verification attempt = 100,000 SU

That cost applies to Schnorr/ECDSA signature checks and to each cryptographic verification attempt inside multisig-style opcodes.

ZK precompile verification:

TagProof systemCost
0x20Groth1614,000,000 SU + 250,000 SU * (public inputs + 1)
0x21RISC Zero Succinct25,000,000 SU

For Groth16, the variable part follows the gamma_abc element count, which is public inputs plus one.

Budget sizing by hand

Suppose an input script uses:

  • one signature verification: 100,000 SU;
  • 2 KB of BLAKE3 hashing: 2,048 SU;
  • about 1 KB of new stack bytes: 1,024 SU.

Total:

103,072 SU

Apply the free allowance:

charged = 103,072 - 9,999 = 93,073 SU
budget  = ceil(93,073 / 10,000) = 10

That input should carry:

compute_budget = 10

For ZK, the numbers are intentionally large. A RISC Zero Succinct verification alone costs:

25,000,000 SU
budget = ceil((25,000,000 - 9,999) / 10,000) = 2,500

A Groth16 proof with 5 public inputs costs:

14,000,000 + 250,000 * (5 + 1) = 15,500,000 SU
budget = ceil((15,500,000 - 9,999) / 10,000) = 1,550

Those examples are sizing intuition, not a substitute for tooling. Production transaction builders should compute the budget from the generated script and proof path they are actually submitting.

If you are building inside rusty-kaspa, or mirroring its rules in another transaction builder, the canonical helper is:

ComputeBudget::checked_covering_script_units(required_script_units)

It subtracts the per-input free allowance, rounds up to the smallest compute_budget that covers the required script units, and returns None if the result cannot fit in the v1 u16 budget field.

Why budget is outside the signature

V1 signatures do not sign compute_budget.

This is a consensus design choice. It keeps user intent separate from resource fitting. A relayer, wallet, or miner can adjust the execution budget so the transaction has enough room to validate without changing the txid or invalidating signatures.

The block-level transaction hash still commits to the budget. Once mined, the block commits to the resource choice.

What to do in tooling

For covenant tooling:

  • estimate script units during transaction construction;
  • use ComputeBudget::checked_covering_script_units(...) or an exact equivalent for the tightest valid budget;
  • if implementing the conversion yourself, account for the 9,999 SU allowance before converting to compute_budget;
  • keep budget adjustment outside the user's signed policy;
  • show budget failures as resource failures, not state-transition failures;
  • size ZK settlement transactions from the actual proof tag and public input count.

Continue with Inline ZK for proof verification inside covenants, or Based Apps for proof-batched app execution.