Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Appendix A: Domain Separators

Every hash in the system uses domain separation to prevent cross-protocol collisions. This appendix lists all domain tags, their hash function, and purpose.

Hash domain tags

Domain stringHashKeyed?ModulePurpose
"SMTLeaf"SHA-256No (prefix)core/src/smt.rsAccount leaf: sha256("SMTLeaf" || pubkey || balance)
"SMTEmpty"SHA-256No (prefix)core/src/smt.rsEmpty account slot sentinel: sha256("SMTEmpty")
"SMTBranch"SHA-256No (prefix)core/src/smt.rsSMT internal node: sha256("SMTBranch" || left || right)
"PermLeaf"SHA-256No (prefix)core/src/permission_tree.rsWithdrawal leaf: sha256("PermLeaf" || spk || amount)
"PermEmpty"SHA-256No (prefix)core/src/permission_tree.rsEmpty withdrawal slot: sha256("PermEmpty")
"PermBranch"SHA-256No (prefix)core/src/permission_tree.rsPermission tree node: sha256("PermBranch" || left || right)
"SeqCommitmentMerkleLeafHash"BLAKE3Yes (key)core/src/seq_commit.rsSeq commitment leaf: blake3_keyed(key, tx_id || version)
"SeqCommitmentMerkleBranchHash"BLAKE3Yes (key)core/src/seq_commit.rsSeq commitment node: blake3_keyed(key, left || right)
"PayloadDigest"BLAKE3Yes (key)core/src/lib.rsV1 tx payload hash
"TransactionRest"BLAKE3Yes (key)core/src/lib.rsV1 tx rest-of-data hash
"TransactionV1Id"BLAKE3Yes (key)core/src/lib.rsV1 tx_id: blake3_keyed(key, payload_digest || rest_digest)
"TransactionID"BLAKE2b-256Yes (.key())core/src/lib.rsV0 tx_id: blake2b_keyed(key, full_preimage)

Non-hash domain tags

TagValueTypeModulePurpose
ACTION_TX_ID_PREFIX0x41 ('A')Byte prefixcore/src/lib.rsFirst byte of tx_id identifies action transactions
State verification suffix[0x00, 0x75]Opcode pairhost/src/bridge.rs[OP_0, OP_DROP] tags state verification scripts
Permission suffix[0x51, 0x75]Opcode pairhost/src/bridge.rs[OP_1, OP_DROP] tags permission scripts

Hashing strategy

The system uses three hash functions, chosen to match Kaspa’s protocol:

SHA-256 — Used for Merkle trees that must be replicated on-chain via OP_SHA256. Both the account SMT and permission tree use SHA-256 with domain-prefix separation (sha256(tag || data)).

BLAKE3 — Used for transaction IDs and sequence commitments. Kaspa’s V1 transaction ID scheme uses BLAKE3 with keyed hashing. The domain_to_key() function zero-pads a domain string into a 32-byte BLAKE3 key:

#![allow(unused)]
fn main() {

}

BLAKE2b-256 — Used for V0 transaction IDs (legacy) and P2SH script hashing. V0 tx_id uses keyed BLAKE2b; P2SH uses unkeyed BLAKE2b matching kaspa_txscript::pay_to_script_hash_script.

Why separate tree domains?

The SMT and permission tree intentionally use different domain strings ("SMTLeaf" vs "PermLeaf", etc.) even though both use SHA-256. This prevents a valid proof in one tree from being accepted in the other. A test in permission_tree.rs explicitly asserts:

#![allow(unused)]
fn main() {
assert_ne!(perm_empty_leaf_hash(), crate::smt::empty_leaf_hash());
}