Back
KASPA.NEWS Articles
Kaspa Core R&D Channel

Toccata Becomes Fork-Aware

Wednesday, May 13, 2026

Kaspa ($KAS) core R&D - what developers discussed, debated, and shipped. May 1-May 13, 2026.

TN12 Moves From Relaunch to Cleanup

The first hours after TN12 relaunch #3 were noisy, but not broken. Michael Sutton told node operators that new TN12 nodes were seeing heavy incoming bandwidth because non-updated nodes kept requesting IBD header proofs. He said the extra orphan/unorphan logs were "all just noise" and that the network was "progressing fully in sync." (source)

The cleanup started quickly. Sutton approved Maxim Biryukov's advisory-fix PR #970, then merged it into master and merged PR #970 plus PR #978 into the toccata and tn12 branches. He described both as advisory-only fixes, including the serde issue raised in the channel. (source) (source) (PR #970) (PR #978)

A few operational patches followed. Sutton told anyone whose new TN12 node crashed with InsufficientDaaWindowSize(0) to pull the latest tn12 branch and restart normally. (source) Tiram88 added a TN12 DNS seeder, and Sutton asked for filtering by minimum kaspad user-agent version 1.1.1, so after release and fork the seeder would already register only new nodes. (source) (source) (source)

That filter became configurable through PR #981. Sutton first called the PR "a handy tool," then merged it and noted that TN12 nodes can now use --ua-rule='reject;ver:kaspad<1.1.1' to filter non-updated nodes from previous TN12 launches. (source) (source) (PR #981) IzioDev also shared temporary TN12 endpoints for quick testing while PNN work continues: wss://tn12reset-wrpc.kasia.fyi and https://tn12reset-grpc.kasia.fyi. (source)

The Toccata Branch Starts Preparing for Master

Ori Newman opened PR #987, Add activation logic for script engine consts, on May 6 and marked it ready for review. (source) (PR #987) Sutton reviewed it the same day, asking for a failing test to be fixed and for integration code such as build_zk_script to use script-builder flags with covenants enabled. (source)

The merge landed on May 7. Sutton called it "the first out of 2-3 PRs for getting the toccata codebase to the point where it can be merged into master," with the goal that pre-activation code is identical to current master in consensus terms. (source)

The practical consequence is that APIs now have to understand the fork boundary. Sutton said ScriptBuilder can be configured for pre- or post-Toccata activation, while ScriptBuilder::new remains pre-activation by default so users do not accidentally build invalid mainnet scripts. Builders working on the toccata branch for post-activation scripts should use ScriptBuilder::with_flags; the tn12 branch can continue as usual because it targets the post-activation engine. (source)

There is still follow-up work around WASM APIs. Sutton noted that the toccata branch does not yet expose a way to call ScriptBuilder::with_flags from WASM, and said those gaps would be handled based on ecosystem need. (source)

KIPs Get Pulled Back Toward the Code

The documentation side also moved from broad design toward final alignment. coderofstuff opened a topic for the KIPs that need to be finalized and merged for Toccata, listing KIP-16, KIP-17, KIP-20, KIP-21, plus a pending Toccata KIP. (source) (source)

Sutton said KIP-20 only needs a very minor update, while KIP-21 requires more work. His preferred shape is a more abstract formal spec plus a separate document explaining the specific rusty-kaspa design, hopefully written by Maxim. (source) (source)

Alexander Saefstroem said KIP-16 needs adjustments because the implementation has changed, especially after switching to arkworks-generic Groth16. (source) By May 11 he had updated it, but wanted review on the consensus parameter updates because script units had changed the original figures. coderofstuff answered that the KIP should reflect the implementation as it looks now. (source) (source) (source)

KIP-17 raised a smaller scoping question. coderofstuff asked Ori whether KIP-17 was up to date and ready to move toward merge. Ori asked whether general opcodes such as opBlake3 should be included there, since they are in Toccata. coderofstuff said it is fine in KIP-17 unless opBlake3 needs its own explanation, in which case it could live in the overall Toccata KIP or a separate KIP. (source) (source) (source) (source) (source)

Script Units Meet ZK SDK Reality

The channel also clarified the script-unit model that sits under Toccata pricing. Sutton summarized the rough accounting: 100,000 script units per signature unit, a Stark verifier at 250x, one script unit per byte pushed during computation, and one-half script units per hashed byte for Blake3 or Blake2b respectively. To get the actual number, he said to run the engine with a max u64 limit and read used_script_units after execution. (source) Ori added the scale conversion: 100 script units is one gram, and the block mass limit is 500,000 grams. (source)

That turned into an immediate developer-experience discussion. Alexander hit a Groth16 script-units failure on local devnet, then realized he had not set the compute budget. Sutton said the commitment has to be determined locally first, and Alexander said he expected Silverscript or SDK tooling to handle this kind of thing for users. (source) (source) (source) (source) (source)

The reason it cannot be purely static is that scripts must actually run. Maxim said if-else branches are impossible to predict without execution, and IzioDev added that even without branches the engine may need opcode outputs that feed later compute-budget measurement. (source) (source)

The ZK SDK kept moving in parallel. Alexander said the SDK had changed, had been made WASM-compatible, and was nearly ready for review again while he finalized Succinct JavaScript examples. (source) (PR #953) Two days later he said all JavaScript examples had been updated and tested. (source) He then pointed Sutton to Groth16 and Succinct JS examples using the new SDK builder, including the part where the custom Groth16 script computes the claim hash in script to bind the journal hash and image ID to the proof. (source) (source) (source) (source)

KIP-21 Proofs Get a Cleaner Light-Client Shape

PiDAGorass reopened an important KIP-21 question: if an untrusted peer sends a ChainBlock with merged transactions and lane proofs, how does a client know the peer did not skip a lane? He suggested that one mitigation could be adding a merged_lane_count field to the commitment. (source)

Maxim pointed back to PR #961, which retrieves a proof of inclusion of a lane into the lanes root and sequencing commitment. He said single-lane verification becomes straightforward, and that it would also make sense to extend the RPC endpoint to obtain SMT proofs for multiple lanes. (source) (source) (source) (PR #961)

The next day, the discussion narrowed around whether committing to the count of active lanes would help data availability proofs. Maxim said the count exists internally but is not exposed or committed, while Sutton argued that if a verifier already has all leaves, rebuilding the tree is cheaper and more complete than checking N separate inclusion proofs. (source) (source) (source) (source)

The important implementation detail was the suffix-leaf optimization, called SLO in code. Sutton said the SMT uses this optimization, Maxim clarified that the lane ID is the first four bytes of the subnetwork ID and the tree key is blake3(lane_id), and Sutton added that max unique lanes per block is limited to 50, or 500 lanes per second, in addition to SLO. (source) (source) (source) (source)

The clearest answer came on May 10. PiDAGorass framed the issue as statefulness: before KIP-21, a client could validate a ChainBlock mergeset with the current sequencing commitment, the mergeset, and the selected-parent sequencing commitment; after KIP-21, a wallet-like light client should not have to maintain the full incremental SMT or receive all leaves. (source)

Sutton answered that those are not the only two options. A client can validate a single ChainBlock transition with a standard SMT write witness: selected-parent root, current ChainBlock root, and for each written lane the key, previous-value witness under the selected-parent root, and new value. Creation is an exclusion proof, expiration is a new empty value, and the verifier batch-applies the writes to check that the resulting root equals the current ChainBlock root. Sutton emphasized that the witness is proportional to lane changes in that transition, not to all active SMT leaves, and does not require processing all ChainBlocks over time. (source)

UTXO Queries Become a Real API Design Problem

The previous article covered the data-service pressure around historical data. This week, that pressure turned into concrete API work. D-Stakes described the UTXO query he wanted: addresses, from_daa_score, to_daa_score, and a limit, but noted that fairly limiting across many addresses is awkward if the database is still organized by script public key. (source) His preferred option became a cursor consisting of a start address plus DAA score, which is not as neat to use but gets around the main pagination problems. (source)

By May 6, D-Stakes opened PR #991 with a cursor-based spk + daa_score solution. (source) (PR #991) He later described a client-side synchronization pattern: subscribe to UTXO changes, buffer subscription responses, use the new API to paginate and sync up to the first response's DAA score, drain the buffer, then run only from subscription messages. He argued that doing that buffering inside the node would create too many edge cases because buffer growth depends on outside variables. (source)

The covenant-index question came next. Sutton asked whether <known covenant id, unknown script public key> is a real use case, then narrowed the likely useful version to <known script public key, filter by covenant id>. He listed options from client-side filtering, to API-level filtering, to adding covenant ID into the index key structure, while noting possible tension with DAA-score scanning. (source) (source)

Maxim gave the rollup version of the use case: if he follows a rollup and later wants the latest state, he may know only the covenant ID and not the script public key. His query is to find the UTXO with that covenant ID and the latest blue score, then use the data availability layer to fetch state, compare it against the root stored in the UTXO, and sync forward. (source) (source) (source) Sutton pushed the other side of the argument: if the user cannot calculate the script-public-key preimage, they cannot do much with the UTXO; if they can, they can calculate the preimage first and query by script public key. (source)

Indexing Capacity Is Still the Immediate Bottleneck

The broader indexer conversation also got sharper. Supertypo said the current api.kaspa.org backend served more than 25 million positive API responses in the previous 24 hours, and argued that the immediate problem is not the schema but high load on too few backend servers due to underfunding or lack of volunteered hardware. He still agreed that long-term architecture must change because keeping all historical data quickly queryable on fast NVMe grows more expensive over time. (source)

Hashdag asked whether the real issue was insufficient servers, architecture change, or inconsistencies from historical gaps. Supertypo distilled it this way: more servers are priority one, long-term architecture work is priority two, and the early historical gaps are likely unsolvable due to lack of early archival nodes. (source) (source) (source) He later repeated that long-term architecture should definitely be worked on, but fixing the immediate server-capacity issue is the most pressing concern. (source)

5bb 55b also posted an early version of go-kaspabook, a local RocksDB-backed indexer using kaspad vspc2 data, protobuf storage, DAA-score-to-live pruning, and reorg-aware index keys. The exposed API includes status, DAA-score and blue-score chain-block lookups, block and transaction lookup, address transaction history, balance and UTXO lookup, transaction submission, and RBF. (source) (repo)

Python SDK Adds Wallet Support

The Python SDK closed one of its biggest gaps. smartgoo_v said wallet and supporting components were added via bindings to the same native rusty-kaspa wallet components used by the WASM SDK, with a Python API similar to the WASM SDK. (source)

He also added the caveat that the feature is not on PyPI yet and must be built from source on the repo main branch. The wallet functionality is expected in the next release, likely around the Toccata hard fork, and users should remember that data is exposed to the Python interpreter and memory model, which is not ideal for secret handling. (source)

Silverscript Gets Tuple-Shaped Split Results

Silverscript kept shaving off language rough edges. Ori pointed to PR #120, which adds tuple field access and tuple-shaped split results. (source) (PR #120) Sutton's immediate reaction was: "so both ternary support and numbered tuple access. you really are building the dream language." (source)

Ori said tuples are currently supported through function return types rather than as distinct assignable types. His main motivation was to remove the incoherent leftover split syntax from CashScript and move toward a unified tuple approach. Explicit tuples may come later, but not in v1. (source) (source)

Transient Mass Becomes a Post-Fork Cleanup Item

The last technical thread was about transient mass. coderofstuff asked why TN12 increased transient mass to 1 million instead of lowering TRANSIENT_BYTE_TO_MASS_FACTOR from 4 to 2. Sutton said the main reason was to keep mass calculation isolated and a function of the plain transaction alone. (source) (source)

coderofstuff argued that the factor has outlived its purpose because it forces future transient-mass limits to move in factors of four. Sutton first worried about rounding edge cases, then checked the reasoning and agreed there were no rounding edge cases. He said the factor can be removed post-fork and the transient block limit changed to 250,000, with care around mempool minimum standard fee calculation. (source) (source) (source) (source) (source) coderofstuff opened issue #996 to track it. (source) (issue #996)

Sutton tied the discussion back to the newer block-limit model. Splitting block limits by type proved more robust, and the current mempool challenge now includes multi-dimensional mass, lanes per block, and gas per lane. The implementation is still the most vanilla solution for now. (source) (source) (source) (source)

PR #995 then picked up the activation-side refinement. Sutton linked a commit that keeps transient-mass fee pricing stable before and after activation, and keeps it from being affected by the future removal of the constant factor. coderofstuff's response was short and deserved: "Very nice." (source) (source) (PR #995)


All sources link to public messages in the Kaspa Core R&D (public) Telegram channel.

More Kaspa Articles