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

F/rewards distribution 2 #97

Merged
merged 8 commits into from
Dec 16, 2024
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
11 changes: 8 additions & 3 deletions contracts/btc-finality/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ use btc_staking::msg::ActivatedHeightResponse;

use crate::error::ContractError;
use crate::finality::{
compute_active_finality_providers, handle_finality_signature, handle_public_randomness_commit,
compute_active_finality_providers, distribute_rewards, handle_finality_signature,
handle_public_randomness_commit,
};
use crate::msg::{ExecuteMsg, InstantiateMsg, QueryMsg};
use crate::state::config::{Config, ADMIN, CONFIG, PARAMS};
Expand Down Expand Up @@ -222,10 +223,14 @@ fn handle_update_staking(
}

fn handle_begin_block(deps: &mut DepsMut, env: Env) -> Result<Response<BabylonMsg>, ContractError> {
// Distribute rewards
distribute_rewards(deps, &env)?;

// Compute active finality provider set
let max_active_fps = PARAMS.load(deps.storage)?.max_active_finality_providers as usize;
compute_active_finality_providers(deps, env, max_active_fps)?;
compute_active_finality_providers(deps, env.block.height, max_active_fps)?;

// TODO: Add events
Ok(Response::new())
}

Expand All @@ -245,7 +250,7 @@ fn handle_end_block(
let ev = finality::index_block(deps, env.block.height, &hex::decode(app_hash_hex)?)?;
res = res.add_event(ev);
// Tally all non-finalised blocks
let (msg, events) = finality::tally_blocks(deps, activated_height, env.block.height)?;
let (msg, events) = finality::tally_blocks(deps, &env, activated_height)?;
if let Some(msg) = msg {
res = res.add_message(msg);
}
Expand Down
55 changes: 44 additions & 11 deletions contracts/btc-finality/src/finality.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
use crate::contract::encode_smart_query;
use crate::error::ContractError;
use crate::state::config::{Config, CONFIG, PARAMS};
use crate::state::finality::{BLOCKS, EVIDENCES, FP_SET, NEXT_HEIGHT, SIGNATURES, TOTAL_POWER};
use crate::state::finality::{
BLOCKS, EVIDENCES, FP_SET, NEXT_HEIGHT, REWARDS, SIGNATURES, TOTAL_REWARDS,
};
use crate::state::public_randomness::{
get_last_pub_rand_commit, get_pub_rand_commit_for_height, PUB_RAND_COMMITS, PUB_RAND_VALUES,
};
Expand All @@ -13,7 +15,7 @@ use btc_staking::msg::{FinalityProviderInfo, FinalityProvidersByPowerResponse};
use cosmwasm_std::Order::Ascending;
use cosmwasm_std::{
to_json_binary, Addr, Coin, Decimal, DepsMut, Env, Event, QuerierWrapper, Response, StdResult,
Storage, WasmMsg,
Storage, Uint128, WasmMsg,
};
use k256::ecdsa::signature::Verifier;
use k256::schnorr::{Signature, VerifyingKey};
Expand Down Expand Up @@ -420,8 +422,8 @@ pub fn index_block(
/// It must be invoked only after the BTC staking protocol is activated.
pub fn tally_blocks(
deps: &mut DepsMut,
env: &Env,
activated_height: u64,
height: u64,
) -> Result<(Option<BabylonMsg>, Vec<Event>), ContractError> {
// Start finalising blocks since max(activated_height, next_height)
let next_height = NEXT_HEIGHT.may_load(deps.storage)?.unwrap_or(0);
Expand All @@ -438,7 +440,7 @@ pub fn tally_blocks(
// non-finalisable
let mut events = vec![];
let mut finalized_blocks = 0;
for h in start_height..=height {
for h in start_height..=env.block.height {
let mut indexed_block = BLOCKS.load(deps.storage, h)?;
// Get the finality provider set of this block
let fp_set = FP_SET.may_load(deps.storage, h)?;
Expand Down Expand Up @@ -490,7 +492,7 @@ pub fn tally_blocks(
// Assemble mint message
let mint_msg = BabylonMsg::MintRewards {
amount: rewards,
recipient: cfg.staking.into(),
recipient: env.contract.address.to_string(),
};
Some(mint_msg)
} else {
Expand Down Expand Up @@ -528,8 +530,6 @@ fn finalize_block(
// Set the next height to finalise as height+1
NEXT_HEIGHT.save(store, &(block.height + 1))?;

// TODO: Distribute rewards to BTC staking delegators

// Record the last finalized height metric
let ev = Event::new("finalize_block")
.add_attribute("module", "finality")
Expand Down Expand Up @@ -568,7 +568,7 @@ const QUERY_LIMIT: Option<u32> = Some(30);
/// power of top finality providers, and records them in the contract state
pub fn compute_active_finality_providers(
deps: &mut DepsMut,
env: Env,
height: u64,
max_active_fps: usize,
) -> Result<(), ContractError> {
let cfg = CONFIG.load(deps.storage)?;
Expand Down Expand Up @@ -602,9 +602,7 @@ pub fn compute_active_finality_providers(
// TODO: Filter out slashed / offline / jailed FPs
// Save the new set of active finality providers
// TODO: Purge old (height - finality depth) FP_SET entries to avoid bloating the storage
FP_SET.save(deps.storage, env.block.height, &finality_providers)?;
// Save the total voting power of the top n finality providers
TOTAL_POWER.save(deps.storage, &total_power)?;
FP_SET.save(deps.storage, height, &finality_providers)?;

Ok(())
}
Expand All @@ -622,3 +620,38 @@ pub fn list_fps_by_power(
let res: FinalityProvidersByPowerResponse = querier.query(&query)?;
Ok(res.fps)
}

/// `distribute_rewards` distributes rewards to finality providers who are in the active set at `height`
pub fn distribute_rewards(deps: &mut DepsMut, env: &Env) -> Result<(), ContractError> {
// Try to use the finality provider set at the previous height
let active_fps = FP_SET.may_load(deps.storage, env.block.height - 1)?;
// Short-circuit if there are no active finality providers
let active_fps = match active_fps {
Some(active_fps) => active_fps,
None => return Ok(()),
};
Comment on lines +626 to +632
Copy link
Member

Choose a reason for hiding this comment

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

Maybe add a TODO here that we will have to use the voting power table of the height that is being finalised, rather than the latest height, for fair distribution.

Btw, Babylon side has implemented the exact design for reward distribution at the FP level -- https://github.com/babylonlabs-io/babylon/blob/main/x/incentive/keeper/btc_staking_gauge.go

// Get the voting power of the active FPS
let total_voting_power = active_fps.iter().map(|fp| fp.power as u128).sum::<u128>();
// Get the rewards to distribute (bank balance of the finality contract)
let cfg = CONFIG.load(deps.storage)?;
let rewards_amount = deps
.querier
.query_balance(env.contract.address.clone(), cfg.denom)?
.amount;
// Compute the rewards for each active FP
let mut total_rewards = Uint128::zero();
for fp in active_fps {
let reward = (Decimal::from_ratio(fp.power as u128, total_voting_power)
* Decimal::from_ratio(rewards_amount, 1u128))
.to_uint_floor();
// Update the rewards for this FP
REWARDS.update(deps.storage, &fp.btc_pk_hex, |r| {
Ok::<Uint128, ContractError>(r.unwrap_or_default() + reward)
})?;
// Compute the total rewards
total_rewards += reward;
}
// Update the total rewards
TOTAL_REWARDS.save(deps.storage, &total_rewards)?;
Ok(())
}
12 changes: 8 additions & 4 deletions contracts/btc-finality/src/state/finality.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use cosmwasm_std::Uint128;

use cw_storage_plus::{Item, Map};

use babylon_apis::finality_api::{Evidence, IndexedBlock};
Expand All @@ -15,9 +17,11 @@ pub const NEXT_HEIGHT: Item<u64> = Item::new("next_height");
/// `FP_SET` is the calculated list of the active finality providers by height
pub const FP_SET: Map<u64, Vec<FinalityProviderInfo>> = Map::new("fp_set");

/// `TOTAL_POWER` is the total power of all finality providers
// FIXME: Store by height? Remove? Not currently being used in the contract
pub const TOTAL_POWER: Item<u64> = Item::new("total_power");

/// Map of double signing evidence by FP and block height
pub const EVIDENCES: Map<(&str, u64), Evidence> = Map::new("evidences");

/// Map of pending finality provider rewards
pub const REWARDS: Map<&str, Uint128> = Map::new("rewards");

/// Total pending rewards
pub const TOTAL_REWARDS: Item<Uint128> = Item::new("total_rewards");
Loading