Skip to content

[ARC-0049] Introduce Redelegation and Extend Unbonding Time#3228

Draft
raychu86 wants to merge 14 commits into
stagingfrom
feat/extend-unbond
Draft

[ARC-0049] Introduce Redelegation and Extend Unbonding Time#3228
raychu86 wants to merge 14 commits into
stagingfrom
feat/extend-unbond

Conversation

@raychu86
Copy link
Copy Markdown
Collaborator

Motivation

This PR implements the features proposed in ARC-0049.

The current 360-block unbonding period provides weak economic finality and limited deterrence against short-term stake mobility. Extending it to ~14 days strengthens economic security, but introduces capital inefficiency for honest delegators wanting to switch validators. A new redelegate function addresses this by allowing instant stake reassignment between validators without leaving the bonded state, subject to a matching cooldown.

This is done by:

  1. Extending the unbonding period in credits.aleo/unbond_public from 360u32 to 403200u32 blocks. The circuit and VK are unchanged since only the finalize scope is modified.
  2. Adding a new redelegate function to credits.aleo that reassigns a delegator's stake between validators while remaining bonded.
  3. Enforcing a 403,200-block per-delegator cooldown on redelegate, matching the unbonding period.
  4. Gating the changes behind a new ConsensusVersion for activation at a specific block height.

Test Plan

Tests have been added covering the new unbonding period, redelegate success and failure cases, cooldown boundary enforcement, and ConsensusVersion gating.

V14 = 14,
/// V15: Introduces the record-existence check and `commit.*.raw` instruction variants.
/// Increase the anchor time to 35.
/// V15: Introduces the record-existence check, `commit.*.raw` instruction variants,
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: depending on when this gets merged, likely needs to be updated


/// The `CreditsVersion` is used to track the version of the `credits.aleo` program.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum CreditsVersion {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we comment at which ConsensusVersion they become active. Or even better strongly type this in the Network trait.

That trait being unpleasant to import is a known painpoint, but we dont have a comprehensive solution for it and anything required the credits version will probably also need the Network trait.


/**********************************************************************************************************************/

// The `redelegated` mapping contains the delegator with their redelegation information.
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we add invariants at the top of this file?

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Implements ARC-0049: extends the unbonding period in credits.aleo/unbond_public from 360 to 403,200 blocks and adds a new redelegate function that lets a delegator switch validators while remaining bonded, subject to a 403,200-block per-delegator cooldown. The activation is gated behind a new ConsensusVersion::V15, which requires introducing a credits_v1 snapshot of credits.aleo (the V1 keys/program, plus V2 keys including the new redelegate circuit) and a CreditsVersion enum so the VM/process can load the appropriate program edition for each consensus range.

Changes:

  • Add redelegate (function + finalize + redelegated mapping + redelegate_state struct) to credits.aleo, extend unbond cooldown to 403,200, and remove redelegated[r1] when a delegator is fully removed.
  • Introduce CreditsVersion::{V0,V1,V2}, Stack::new_credits, Stack::insert_credits_verifying_keys, Process::load_v1, Program::credits_v0 / credits_v1, and gate the V2 stack swap on the V15 block height in VM::advance.
  • Carve out V1 credits keys (mainnet/testnet/canary) and add new V2 redelegate keys, plus Transition::is_redelegate and a V15 gate in VM::check_transaction_execution; add tests for the new unbond period, the redelegate happy path, cooldown enforcement, validator/self-redelegate rejection, and unbond cleanup.

Reviewed changes

Copilot reviewed 25 out of 126 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
synthesizer/program/src/resources/credits.aleo Adds redelegated mapping/struct and redelegate finalize; extends unbond cooldown to 403,200; removes redelegated state on full unbond.
synthesizer/program/src/resources/credits_v0.aleo Snapshots the pre-V8 credits program for V0 loading.
synthesizer/program/src/lib.rs Adds credits_v0() / credits_v1() constructors.
synthesizer/process/src/stack/mod.rs Introduces CreditsVersion and helpers new_credits / insert_credits_verifying_keys.
synthesizer/process/src/lib.rs Refactors Process::load* to a shared load_with_credits helper; adds load_v1.
synthesizer/process/src/tests/test_credits.rs Adds tests for redelegate state, cooldown, validator-cannot-redelegate, etc.
synthesizer/src/vm/mod.rs Loads the correct credits version per consensus height and swaps program to V1/V2 at the V8/V15 boundary.
synthesizer/src/vm/verify.rs Gates redelegate calls behind ConsensusVersion::V15.
synthesizer/src/vm/tests/test_v15/{mod.rs,update_credits.rs} New tests around credits.aleo stack update and unbond/redelegate behavior at the V15 boundary.
synthesizer/src/vm/tests/test_v14/snark_verify.rs Imports adjusted for new test helpers.
ledger/store/src/transaction/deployment.rs Populates editions 0/1/2 of credits.aleo in the deployment store.
ledger/block/src/transition/mod.rs Adds Transition::is_redelegate helper.
console/network/src/{lib.rs,mainnet_v0.rs,testnet_v0.rs,canary_v0.rs,consensus_heights.rs} Adds get_credits_v1_*_key accessors, V1 key maps, and updates the V15 docstring.
parameters/src/{mainnet,testnet,canary}/mod.rs + resources/credits_v1/, resources/credits/redelegate. Splits existing credits keys into credits_v1 and adds new V2 redelegate keys; adds insert_*_credit_v1_keys! macro and tests for redelegate keys.


// Perform checks if the execution contains `credits.aleo/redelegate`.
if execution.transitions().any(|t| t.is_redelegate()) {
// Do not allow `credits.aleo/upgrade` calls on the previous inclusion version or until after the migration block has passed.
Comment thread synthesizer/src/vm/mod.rs
self.update_credits_verifying_keys()?;
self.update_credits_program(CreditsVersion::V1)?;
}
// If the block advances to `ConsensusVersion::v15`, update the credits program to V2.
Comment thread synthesizer/process/src/stack/mod.rs
Comment on lines +465 to +468
ensure!(
program_edition == credits_version_as_edition,
"The provided credits version ({program_edition}) should match the stack's program edition ({credits_version_as_edition})."
);
Comment on lines +249 to +256
.ok_or_else(|| anyhow!("Proving key for credits.aleo/{function_name}' not found"))
}

/// Returns the verifying key for the given function name in the v1 version of `credits.aleo`.
fn get_credits_v1_verifying_key(function_name: String) -> Result<&'static Arc<VarunaVerifyingKey<Self>>> {
CREDITS_V1_VERIFYING_KEYS
.get(&function_name)
.ok_or_else(|| anyhow!("Verifying key for credits.aleo/{function_name}' not found"))
Comment on lines +243 to +250
.ok_or_else(|| anyhow!("Proving key (v0) for credits.aleo/{function_name}' not found"))
}

/// Returns the verifying key for the given function name in the v1 version of `credits.aleo`.
fn get_credits_v1_verifying_key(function_name: String) -> Result<&'static Arc<VarunaVerifyingKey<Self>>> {
TESTNET_CREDITS_V1_VERIFYING_KEYS
.get(&function_name)
.ok_or_else(|| anyhow!("Verifying key (v0) for credits_v0.aleo/{function_name}' not found"))
Comment on lines +243 to +250
.ok_or_else(|| anyhow!("Proving key (v0) for credits.aleo/{function_name}' not found"))
}

/// Returns the verifying key for the given function name in the v1 version of `credits.aleo`.
fn get_credits_v1_verifying_key(function_name: String) -> Result<&'static Arc<VarunaVerifyingKey<Self>>> {
CANARY_CREDITS_V1_VERIFYING_KEYS
.get(&function_name)
.ok_or_else(|| anyhow!("Verifying key (v0) for credits_v0.aleo/{function_name}' not found"))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants