Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

distributions: LQT pool budget #5026

Merged
merged 3 commits into from
Jan 30, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 62 additions & 12 deletions crates/core/component/distributions/src/component.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,23 +48,28 @@ impl Component for Distributions {
#[instrument(name = "distributions", skip(state))]
async fn end_epoch<S: StateWrite + 'static>(state: &mut Arc<S>) -> Result<()> {
let state = Arc::get_mut(state).context("state should be unique")?;
let new_issuance = state.compute_new_issuance().await?;
tracing::debug!(?new_issuance, "computed new issuance for epoch");
Ok(state.distribute(new_issuance).await)

// Define staking budget.
state.define_staking_budget().await?;

// Define LQT budget.
state.define_lqt_budget().await?;

Ok(())
}
}

#[async_trait]
trait DistributionManager: StateWriteExt {
/// Compute the total new issuance of staking tokens for this epoch.
async fn compute_new_issuance(&self) -> Result<Amount> {
async fn compute_new_staking_issuance(&self) -> Result<Amount> {
use penumbra_sdk_sct::component::clock::EpochRead;

let current_block_height = self.get_block_height().await?;
let current_epoch = self.get_current_epoch().await?;
let num_blocks = current_block_height
.checked_sub(current_epoch.start_height)
.unwrap_or_else(|| panic!("epoch start height is less than or equal to current block height (epoch_start={}, current_height={}", current_epoch.start_height, current_block_height));
.unwrap_or_else(|| panic!("epoch start height is greater than current block height (epoch_start={}, current_height={}", current_epoch.start_height, current_block_height));

// TODO(erwan): Will make the distribution chain param an `Amount`
// in a subsequent PR. Want to avoid conflicts with other in-flight changes.
Expand All @@ -76,21 +81,66 @@ trait DistributionManager: StateWriteExt {
tracing::debug!(
number_of_blocks_in_epoch = num_blocks,
staking_issuance_per_block,
"calculating issuance per epoch"
"calculating staking issuance per epoch"
);

let new_issuance_for_epoch = staking_issuance_per_block
let new_staking_issuance_for_epoch = staking_issuance_per_block
.checked_mul(num_blocks as u128) /* Safe to cast a `u64` to `u128` */
.expect("infaillible unless issuance is pathological");
.expect("infallible unless issuance is pathological");

tracing::debug!(?new_issuance_for_epoch, "computed new issuance for epoch");
tracing::debug!(
?new_staking_issuance_for_epoch,
"computed new staking issuance for epoch"
);

Ok(Amount::from(new_issuance_for_epoch))
Ok(Amount::from(new_staking_issuance_for_epoch))
}

/// Update the object store with the new issuance of staking tokens for this epoch.
async fn distribute(&mut self, new_issuance: Amount) {
self.set_staking_token_issuance_for_epoch(new_issuance)
async fn define_staking_budget(&mut self) -> Result<()> {
let new_issuance = self.compute_new_staking_issuance().await?;
tracing::debug!(?new_issuance, "computed new staking issuance for epoch");
Ok(self.set_staking_token_issuance_for_epoch(new_issuance))
}

/// Computes total LQT reward issuance for the epoch.
async fn compute_new_lqt_issuance(&self) -> Result<Amount> {
use penumbra_sdk_sct::component::clock::EpochRead;

let current_block_height = self.get_block_height().await?;
let current_epoch = self.get_current_epoch().await?;
let epoch_length = current_block_height
.checked_sub(current_epoch.start_height)
.unwrap_or_else(|| panic!("epoch start height is greater than current block height (epoch_start={}, current_height={}", current_epoch.start_height, current_block_height));

let lqt_block_reward_rate = self
.get_distributions_params()
.await?
.liquidity_tournament_incentive_per_block as u64;
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

why is this chain param defined as u64 again?

Copy link
Member

Choose a reason for hiding this comment

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

I don't know if there's a principled reason for it to be a u64 rather than Amount, but this seems fine.


tracing::debug!(
number_of_blocks_in_epoch = epoch_length,
lqt_block_reward_rate,
"calculating lqt reward issuance per epoch"
);

let total_pool_size_for_epoch = lqt_block_reward_rate
.checked_mul(epoch_length as u64)
.expect("infallible unless issuance is pathological");

tracing::debug!(
?total_pool_size_for_epoch,
"computed new reward lqt issuance for epoch"
);

Ok(Amount::from(total_pool_size_for_epoch))
}

/// Update the nonverifiable storage with the newly issued LQT rewards for the current epoch.
async fn define_lqt_budget(&mut self) -> Result<()> {
let new_issuance = self.compute_new_lqt_issuance().await?;
tracing::debug!(?new_issuance, "computed new lqt reward issuance for epoch");
Ok(self.set_lqt_reward_issuance_for_epoch(new_issuance))
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@ pub fn staking_token_issuance_for_epoch() -> &'static str {
"distributions/staking_token_issuance_for_epoch"
}

// The amount of LQT rewards issued for this epoch.
pub fn lqt_reward_issuance_for_epoch() -> &'static str {
"distributions/lqt_reward_issuance_for_epoch"
}

pub fn distributions_parameters() -> &'static str {
"distributions/parameters"
}
11 changes: 11 additions & 0 deletions crates/core/component/distributions/src/component/view.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,15 @@ pub trait StateReadExt: StateRead {
.ok_or_else(|| anyhow::anyhow!("Missing DistributionsParameters"))
}

// Get the total amount of staking tokens issued for this epoch.
fn get_staking_token_issuance_for_epoch(&self) -> Option<Amount> {
self.object_get(&state_key::staking_token_issuance_for_epoch())
}

// Get the total amount of LQT rewards issued for this epoch.
fn get_lqt_reward_issuance_for_epoch(&self) -> Option<Amount> {
self.object_get(&state_key::lqt_reward_issuance_for_epoch())
}
}

impl<T: StateRead + ?Sized> StateReadExt for T {}
Expand All @@ -33,5 +39,10 @@ pub trait StateWriteExt: StateWrite + StateReadExt {
fn put_distributions_params(&mut self, params: DistributionsParameters) {
self.put(state_key::distributions_parameters().into(), params)
}

/// Set the total amount of LQT rewards issued for this epoch.
fn set_lqt_reward_issuance_for_epoch(&mut self, issuance: Amount) {
self.object_put(state_key::lqt_reward_issuance_for_epoch(), issuance);
}
}
impl<T: StateWrite + ?Sized> StateWriteExt for T {}