Toccata 101 · Part 1 · card 4
What protects your funds? Every Kaspa address is a lock on UTXOs, and Kaspa has two kinds of lock.
A Kaspa address is not an account, it doesn't hold funds. An address's mission is to spell out the lock an output will carry. Kaspa has two kinds of these locks:
From a p address alone you cannot read the script, only its fingerprint, until someone spends it or publishes it. Your wallet shows both kinds as UTXOs. The lock decides what the spend must show.
| Key lock | Script lock | |
|---|---|---|
| The address starts with | q | p |
| The address carries | a public key, 32 bytes | the fingerprint of a script, 32 bytes |
| The spend must show | a signature by that key | the script, matching the fingerprint, and whatever the script demands |
| Who uses it | every wallet | every covenant |
Two transactions:
60a2f205…3f145914 created:
2436d2fc…f7501f3d spent the p output.
Transaction 60a2f205… made two outputs: 0.3 KAS behind a p address, and the user's change behind a q address. A p address is the fingerprint (the hash) of a script. At that moment nobody on the network could see the script itself, only its fingerprint.
Then 2436d2fc… spent that 0.3 KAS. To spend a p output you must put two things in the input: the script itself, and whatever that script demands. So the input carries the script (187 bytes) and a signature (65 bytes).
The node did two things. First it hashed the script it was handed and compared the result to the 32 bytes inside the p address. They matched, so this is the script the address was made from and not a substitute. Only then did it run the script. The script says one thing: key K must sign. The signature was K's, so the script said yes and the 0.3 KAS moved.
Two extra facts make the example interesting. The script also carries a dead branch (OP_FALSE OP_IF … OP_ENDIF) that the node never executes, because it's a place to stash a text note that off-chain KRC-20 software reads. That is why this p address is not a Toccata covenant, it's the older trick of hiding data inside a script. And key K is the very same public key the q address is built from. One owner, one key, two locks: the q output is locked by the key directly, the p output is locked by a script that demands the key. That is "Same key, two locks."
An address is a prefix, a version byte and a payload, with an 8-character checksum, in rusty-kaspa v2.0.1 crypto/addresses/src/lib.rs (versions 0 PubKey, 32 bytes, 1 PubKeyECDSA, 33 bytes, 8 ScriptHash, 32 bytes) and bech32.rs (the charset and the checksum). The lock each address stands for is built in crypto/txscript/src/standard.rs: OpData32 <key> OpCheckSig, OpData33 <key> OpCheckSigECDSA, OpBlake2b OpData32 <hash> OpEqual, the hash being blake2b-256 of the script. Opcode values are in opcodes/mod.rs. The decoder on this page is a line-by-line port of bech32.rs, checked against the node's own test vectors. The two transactions were read from api.kaspa.org on 6 September 2026. The revealed script was hashed with blake2b-256 and matched to the p address.