KIP: 14
Layer: Consensus (hard fork)
Title: The Crescendo Hardfork
Type: Consensus change (block rate, script engine)
Author: Michael Sutton <[email protected]>
Comments-URI: https://research.kas.pa/t/crescendo-hardfork-discussion-thread/279
created: 2025-01-21
updated: 2025-01-21
Status: Draft
This KIP proposes the implementation of the Crescendo Hardfork for the Kaspa network, detailing the consensus changes and transition strategy for various components. The primary change in this KIP is the increase in Blocks Per Second (BPS) from 1 to 10, a significant adjustment with wide-ranging implications for node performance as well as storage and bandwidth requirements. This necessitates updates to many consensus parameters and, in some cases, a rethinking of existing mechanisms to ensure they remain efficient at the increased block rate (e.g., the sparse DAA window introduced in KIP-4).
Additionally, this hardfork includes the activation of other significant improvements to Kaspa. Starting with KIP-9, which introduces a critical mechanism for managing and mitigating state bloat, thereby regulating persistent storage requirements. This is complemented by KIP-13, which regulates transient storage requirements for a node. Another major component activated in this hardfork is KIP-10, which introduces new introspection opcodes to Kaspa's script engine. Through such introspection, these opcodes enable the concept of covenants, allowing for advanced transaction controls, including the design of additive addresses that support microtransactions and complement KIP-9.
This hardfork also marks the closure of KIP-1, the Kaspa Rust Rewrite. The performance improvements enabled by Rusty Kaspa (RK) provide the foundation necessary to support this upgrade, allowing the network to handle the increased demands of 10 BPS and beyond.
The Crescendo Hardfork is a proactive upgrade to the Kaspa network, made possible through RK. With TN11—a testnet operating at 10 BPS—running stably for over a year, the network has demonstrated its readiness for this transition. By increasing capacity and speed, this upgrade positions Kaspa to support anticipated demand from emerging technologies, including smart contract layers enabled via the ongoing based ZK bridge design [1].
All of the changes described in this KIP (excluding KIP-13) have already been implemented and tested in TN11, providing valuable insights into their performance and stability. These results ensure that the proposed modifications are ready for deployment on the Kaspa mainnet.
The following details the changes solely related to the bps increase.
-
Increasing BPS from 1 to 10:
- This change is governed by a consensus parameter named
target_time_per_block
(milliseconds), which controls the expected time between blocks. To increase bps from 1 to 10, thetarget_time_per_block
will be reduced from 1000 ms to 100 ms. - This adjustment will in turn cause the difficulty adjustment algorithm to reduce the difficulty by a factor of 10, thus accelerating block creation tenfold. Further details on this switch are provided below under "Transitioning strategy".
- This change is governed by a consensus parameter named
-
Re-adjusting the Ghostdag K parameter:
- Reducing block time leads to a higher rate of parallel blocks. Consequently, the Ghostdag K parameter, which is a function of
$2 \lambda D$ (where$\lambda$ is the block rate and$D$ is the a priori delay bound), must be recalibrated to maintain network security adhering to the Ghostdag formula (see eq. 1 from section 4.2 of the PHANTOM-GHOSTDAG paper [2]). - Setting
$D=5, \delta=0.01$ , the new Ghostdag K is recalculated to be 124 based on the Poisson tail cutoff therein.
- Reducing block time leads to a higher rate of parallel blocks. Consequently, the Ghostdag K parameter, which is a function of
-
Scaling time-based consensus parameters:
- Several parameters conceptually defined by time duration but applied via block count must be scaled with the new bps:
-
Finality Depth (
$\phi$ ): Previously defined for a 24-hour duration at 1 bps (86,400 blocks), it will now correspond to a 12-hour duration at 10 bps (432,000 blocks). -
Merge Depth Bound (
$M$ ): Defined for a 1-hour duration, it will now increase from 3600 blocks at 1 bps to 36,000 blocks at 10 bps. -
Pruning Depth: Calculated as
$\phi + 2M + 4KL + 2K + 2$ [3], where:-
$\phi$ : Finality Depth -
$M$ : Merge Depth Bound -
$L$ : Mergeset Size Limit (see below) -
$K$ : Ghostdag K - The pruning depth formula provides a lower bound, yet the actual pruning period can be set longer. Plugging in the scaled parameters, the lower bound is calculated to be 627,258 blocks, representing approximately ~17.4238 hours. We suggest rounding this up to 30 hours for simplicity and practical application. A 30-hour period is closer to the current mainnet pruning period (~51 hours) and aligns closely with the value used and benchmarked throughout TN11 (~31 hours).
-
- Coinbase Maturity: Originally defined as 100 seconds or ~100 blocks at 1 bps, this will now correspond to 1000 blocks at 10 bps.
-
Finality Depth (
- Several parameters conceptually defined by time duration but applied via block count must be scaled with the new bps:
-
Conservative scaling of performance-impacting parameters:
- Max Block Parents: Increased from 10 to 16. Based on continuous TN11 data, 16 remains well above the average number of DAG tips, ensuring that all tips are normally merged by subsequent blocks.
-
Mergeset Size Limit (
$L$ ): Increased from 180 to 248 ($2K$ ) to accommodate the higher bps while maintaining storage efficiency.
-
Adjustments to the Coinbase reward mechanism:
- The scheme described below keeps the reward system and the emission schedule precisely intact by conceptually transferring the current reward per block to a reward per second with equal value.
- Specifically, the reward table will continue to map from months to rewards, but the reward is now considered a per-second reward. To calculate the per-block reward, the per-second reward is divided by the bps.
- Special care must be taken to correctly calculate the current emission month. Previously, the DAA score (essentially a block count) mapped directly to seconds since blocks were produced at a rate of 1 block per second. Post-hardfork, with 10 bps, the DAA score at activation must be used to maintain accurate second counting.
- Two key values are used in the subsidy month calculation:
-
deflationary_phase_daa_score
: A constant from the current consensus rules that marks the start of the deflationary phase. -
crescendo_activation_daa_score
: The DAA score at the time of the Crescendo activation, which will be set as part of the hardfork's implementation.
-
- The following code depicts the required permanent change in subsidy month calculation. The returned
subsidy_month
value can then be used as before to extract the reward from the subsidy-by-month table.
// We define a year as 365.25 days and a month as 365.25 / 12 = 30.4375
// SECONDS_PER_MONTH = 30.4375 * 24 * 60 * 60
const SECONDS_PER_MONTH: u64 = 2629800;
fn subsidy_month(daa_score: u64) -> u64 {
if daa_score < crescendo_activation_daa_score {
// Pre activation, we simply assume block count represents second units (since block per second = 1)
return (daa_score - deflationary_phase_daa_score) / SECONDS_PER_MONTH;
}
// Else, count seconds differently before and after Crescendo activation
let seconds_since_deflationary_phase_started =
(crescendo_activation_daa_score - deflationary_phase_daa_score) +
(daa_score - crescendo_activation_daa_score) / bps;
return seconds_since_deflationary_phase_started / SECONDS_PER_MONTH;
}
-
Activation of KIP-4: Sparse DAA and Median Time Windows:
- Transitioning to sparse Difficulty Adjustment Algorithm (DAA) and sparse Median Time (MT) windows while maintaining their previous durations (2641 seconds for DAA; 263 seconds for Median Time).
- The size of these sparse windows (in blocks) is determined by dividing their durations by chosen sampling intervals. For DAA, we choose a sampling interval of 4 seconds, resulting in a window size of
$\lceil 2641/4 \rceil = 661$ . For MT, we choose a sampling interval of 10 seconds, resulting in$\lceil 263/10 \rceil = 27$ . Notably, these window sizes are now independent of bps. - Sampling intervals are scaled by bps to calculate block sample rates:
-
past_median_time_sample_rate = 100
(fromMEDIAN_TIME_SAMPLE_INTERVAL=10
). -
difficulty_adjustment_sample_rate = 40
(fromDIFFICULTY_WINDOW_SAMPLE_INTERVAL=4
).
-
-
KIP-9 (Storage Mass): Introduces a storage mass formula to mitigate and regulate UTXO set growth in both organic and adversarial conditions.
-
KIP-13 (Transient Storage Mass): Implements transient storage mass to regulate short-term storage usage.
-
KIP-10 (Script Engine Enhancements): Introduces direct introspection within the script engine, enabling covenants and advanced transaction controls.
The following details additional minor changes that do not warrant a separate KIP.
This hardfork introduces support for arbitrary data in the payload
field of native (non-coinbase) transactions. Native transactions, which represent the standard transaction type, now support payloads, while coinbase transactions retain their existing restricted format. Transactions already include a reserved byte array field named payload
, which was previously required to remain empty for native transactions. This restriction is now lifted, enabling native transactions to carry arbitrary data.
To ensure proper handling, the sighash
mechanism must be adapted to include the payload
field, proving authorization over the payload. This is achieved by hashing the payload
into a payload_hash
and incorporating it into the overall sighash
. For backwards compatibility, if the payload
is empty, the zero hash is returned as the payload_hash
.
The payload
field is already included in the transaction hash
and id
in current Kaspa implementations. However, other clients may have assumed this field is always empty and must verify their implementations to account for it.
This change enables preliminary second-layer smart contract implementations, leveraging Kaspa for sequencing and data availability without settlement functionality yet. Abuse or spam risks are mitigated by the transient storage regulation introduced in KIP-13. This functionality has already been implemented in RK and activated for TN11 (pull request).
A new consensus header-validity rule is introduced where the first direct block parent must be the Ghostdag selected parent. This simplifies bringing a witness of the selected chain, particularly for scenarios pre-KIP-6 (which is unplanned for this hardfork).
In Kaspa, the DAA score of a block typically determines when forking rules are activated. However, certain consensus changes can affect the DAA score of the block itself, resulting in circular logic. One notable example is the activation of KIP-4 (sparse DAA window), which modifies how the DAA score is calculated. To avoid this cycle, we propose using the DAA score of the block's selected parent to determine activation. Another scenario where the selected parent's score must be considered instead is the increase in Ghostdag K, since Ghostdag is computed before the DAA score is known.
To simplify implementation, we suggest extending this method to all header-related changes, which includes bps-related changes (excluding coinbase rewards) and the first-parent rule.
For KIPs 9, 10, and 13, as well as payload activation and coinbase reward changes (all block-body-related), we recommend using the usual, more straightforward approach of relying on the DAA score of the block itself.
When transitioning to 10 bps, a significant challenge arises because the DAA window post-activation spans both the 1 bps and 10 bps eras. Careful consideration is required to prevent an overly large decrease in difficulty caused by the slower block production rate at the beginning of the window. Furthermore, the adoption of KIP-4 introduces an additional layer of complexity, as we are shifting from a full DAA window to a sparse one.
To address these challenges, the following approach is proposed:
- Reset the DAA window: The DAA window should be reset at the activation point and should include only blocks mined post-activation.
- Difficulty calculation for initial blocks:
- Empty window: When the window is empty (i.e., the selected parent was mined before activation), increase the difficulty target by a factor of 10, reflecting the new bps. This adjustment reduces the amount of work required to find a block by tenfold, resulting in a tenfold increase in block rate with the same hashrate.
- Partial window: For blocks where the window contains some post-activation blocks but has not yet reached the minimum required size, use the selected parent's difficulty as is.
- Minimum window size: The minimum window size should be set to a value in the range of 60–661 blocks, corresponding to approximately 4–44 minutes, to balance stability and responsiveness.
Upon activation, the pruning depth increases to accommodate the higher bps. However, the pruning point itself should not regress to meet the new depth immediately. Instead, it must transition gradually under the updated rules, remaining fixed at its current location until a point above it accumulates sufficient depth. This ensures pruning point monotonicity.
- Denote
$B_n$ as the block at depth$n$ on its selected chain. Note that the definition of depth remains unchanged from current rules. - For every block
$B$ mined post-activation, let$pre(B)$ denote the pruning point of its last chain ancestor mined pre-activation. - Denote the new pruning depth as
$P$ . - The pruning point for a post-activation block
$B$ , denoted$\pi(B)$ , is determined by the following rule:$\pi(B) := \max(pre(B), B_P)$ .
I thank @coderofstuff for helping with the management of the hardfork detailed in this KIP, and all of Kaspa's core developers and researchers for their diligent work toward this massive endeavor.