Michael Sutton@michaelsuttonil
Kaspa’s evolution: from local scripts to stateful systems, without losing locality
I want to try to explain, in simple words, the vision and the gradual implementation path for smart contracts and complex financial systems on Kaspa.
Instead of trying to cover everything, I am going to weave one continuous line of thought: from the most basic primitives, through the key additions we are making, toward the system-level picture.
A meta note: even in parts where the destination feels intuitively clear, conceptual clarity only emerges while building. This is not just engineering, and not pure theory either. It is system research: making the model itself clear as we walk.
A simple ladder to keep in mind:
• UTXO scripts constrain spend authorization
• Covenants constrain next outputs
• Lineage authenticates which instance is “the real one”
• ZK verifies transitions by succinct proofs, without on-chain execution
One guiding principle throughout: we want these capabilities as first-class consensus and script-engine primitives that compose cleanly, not as clever edge-case constructions.
---
UTXO as the base model: a constitution that governs a resource
The UTXO model is, at its core, a script (a “constitution”) that controls a resource.
That constitution is local in two senses:
• Local in space: the spending script sees only the inputs it spends, plus whatever data the spender provides.
• Local in time: the script is a one-shot gate. Once a spend happens, the old constitution does not persist into the future. The future is governed by whatever new scripts the coins are sent to--but there is no inherent linkage between old rules and new rules.
In the common case, the constitution is minimal: “only someone who can prove possession of a private key may spend”.
In pseudo-form it is basically: SigVerify(pk). The spender provides a signature proving they control the private key behind pk, and that they authorized this specific transaction.
---
The one thing that is enforced over time: conservation of value
There is one strong temporal law baked into the base model: conservation of value.
Consensus enforces that the total KAS value created by a transaction is less than or equal to the total KAS value it consumes. This is why “Kaspa the asset” is not just data in a UTXO. It is a native resource with a conservation law enforced by the protocol.
So Kaspa already has one temporal invariant “for free”.
---
But what if we want richer rules than “who can spend”?
Now imagine we want more complex logic. Examples:
• Coins can only be sent to a whitelist of addresses.
• Only 5% of the balance can be spent per day.
• This resource must evolve under a fixed policy over time.
This is where the right mental model becomes a state machine.
A state machine has a state and a transition function. The transition function must be able to enforce what the next state is allowed to be. In UTXO terms, “writing state” happens by creating the outputs of the spending transaction--so a real transition function must be able to constrain the outputs.
The problem is the locality constraint: in the classic btc-style scripting model, without introspection, the spending script cannot constrain what it is creating. It gates the spend, but it cannot reason about outputs. Without seeing outputs, implementing a genuine state machine is impossible.
(Notwithstanding btc’s indirect workarounds via sighash tricks, which can approximate limited introspection in specific patterns.)
---
Introspection: enabling state machines in a local-compute model
This is why transaction introspection opcodes are a foundational step. (This is what KIP-10 introduced, starting with Crescendo.)
Once the script engine can read transaction fields, and crucially inspect output scripts, the transition function can finally say: “you may spend this input only if you create outputs that satisfy these constraints”.
Conceptually, that is the birth of what we call a covenant: a spend is no longer pure ownership transfer. Spending becomes conditional on preserving a policy across time.
It lets a resource enter a covenant: the owner’s freedom becomes constrained by an on-chain policy that must remain true after the spend, not only at the moment of spending.
Note how this enables persistence without losing locality. The script only enforces a one-step look-ahead, by constraining the next outputs. But if it requires those outputs to carry the same policy forward, it becomes an inductive rule: one-step enforcement is enough to preserve the covenant across arbitrarily many future transitions.
---
Completing the state machine model: primitives and lineage
At this point we can describe covenants in principle, but to make general state machines possible we need two things: better building blocks, and a notion of authority for non-KAS state.
(1) Byte and hash primitives: even if you can see outputs, you still need the low-level tools to express robust constraints. That means byte-string construction and parsing (e.g., OpCat, OpSubstr) and strong hashing with domain separation (e.g., OpBlake2bWithKey). Without these, you can’t reliably build commitments, slice out exact fields, or enforce consistent state encodings that make transition validation composable. (This is what KIP-17 added on TN12.)
(2) Lineage (provenance): “who says this state is real?”
Once a covenant represents non-KAS state (a token, an asset, or the compressed state commitment of an off-chain application), the state is no longer self-authenticating the way KAS value is.
A short concrete story:
• I can create a UTXO whose script claims “I am TokenX with supply 1,000,000”.
• Nothing in consensus prevents me from writing that claim into a script and funding it with real KAS.
• So the real question becomes: how do wallets know which instance is the real TokenX state machine?
This problem only appears once “state” is no longer the native KAS resource, so it helps to separate the KAS case from the non-KAS case:
• If the covenant is “about KAS”, the value already has native, consensus-backed provenance via conservation. You do not need lineage to prove the KAS value was not created from thin air.
• For non-KAS state, there is no conservation law. Without lineage, you cannot prevent “fake instances” of the same-looking scheme.
So for non-KAS covenants, lineage must be part of the design: the instance has to be anchored to a recognized genesis, meaning an agreed initial state and rules for a specific state machine instance, and then continued through valid transitions. KIP-20 addresses this by introducing consensus-tracked covenant IDs for instance identity and lineage.
---
The next layer: ZK
With covenants able to enforce transitions and lineage, we can move beyond “everything must be revealed and executed in-script on-chain”. This is already the direction on TN12 with ZK verification opcodes (KIP-16).
Without ZK, each state transition must be validated on-chain by revealing what the base layer needs to check. In practice, every step tends to carry three costs: revealing the state preimage, revealing the rules preimage, and executing the transition checks in-script.
ZK verification opcodes let us keep only commitments on-chain and prepare the public transition inputs, then a proof attests that there exists a valid hidden witness and execution trace that takes the old commitment to the new commitment under the intended rules.
That gives scalability, and sometimes privacy. L1 enforces correctness without re-executing the full computation in-script, and without forcing state and rules to be re-published on every transition.
The bigger consequence is expressiveness: ZK is machinery above covenants that lifts the “on-chain execution” ceiling. The base layer verifies validity, while the full transition function can be arbitrarily complex off-chain, including loops and large computations. In that sense, covenants plus ZK give a path to general-purpose computation anchored and enforced by L1.
---
Outlook: Part 2
Part 2 will go deeper into the ZK layer and the shared-state story:
• How a zk app can be based, meaning L1 sequencing fully determines the transition history.
• How these primitives support canonical bridging of KAS.
• How we further modify the base layer so multiple zk apps or vprogs can synchronously compose without waiting for base-layer messaging roundtrips.