Skip to content

Commit

Permalink
Merge branch 'main' into feat/contract-instantiation
Browse files Browse the repository at this point in the history
  • Loading branch information
SebastianElvis committed Dec 23, 2024
2 parents 5426f95 + 3e9f03d commit 6de5c0d
Show file tree
Hide file tree
Showing 19 changed files with 382 additions and 91 deletions.
10 changes: 10 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ integration = "./scripts/integration_test.sh"

[workspace.dependencies]
anybuf = { version = "0.5.0" }
babylon-apis = { path = "./packages/apis" }
babylon-proto = { path = "./packages/proto" }
babylon-bitcoin = { path = "./packages/bitcoin" }
babylon-btcstaking = { path = "./packages/btcstaking" }
Expand Down
11 changes: 8 additions & 3 deletions contracts/btc-finality/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,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 @@ -228,10 +229,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 @@ -251,7 +256,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
53 changes: 43 additions & 10 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 @@ -14,7 +16,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 @@ -425,8 +427,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 @@ -443,7 +445,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 @@ -535,8 +537,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 @@ -575,7 +575,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 params = get_babylon_sdk_params(&deps.querier)?;
Expand Down Expand Up @@ -621,9 +621,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 @@ -641,3 +639,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(()),
};
// 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");
10 changes: 10 additions & 0 deletions contracts/op-finality-gadget/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ library = []
[dependencies]
babylon-apis = { path = "../../packages/apis" }
babylon-merkle = { path = "../../packages/merkle" }
babylon-bindings = { path = "../../packages/bindings" }
babylon-bitcoin = { path = "../../packages/bitcoin" }
babylon-proto = { path = "../../packages/proto" }
cosmwasm-schema = { workspace = true }
cosmwasm-std = { workspace = true }
cw-controllers = { workspace = true }
Expand All @@ -33,8 +36,15 @@ eots = { path = "../../packages/eots" }
hex = { workspace = true }
k256 = { workspace = true }
thiserror = { workspace = true }
prost = { workspace = true }
anybuf = { workspace = true }

[dev-dependencies]
babylon-bindings-test = { path = "../../packages/bindings-test" }
btc-staking = { path = "../btc-staking", features = [ "library" ] }
cosmwasm-vm = { workspace = true }
test-utils = { path = "../../packages/test-utils" }
anyhow = { workspace = true }
derivative = { workspace = true }

cw-multi-test = { workspace = true }
12 changes: 8 additions & 4 deletions contracts/op-finality-gadget/src/contract.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
use crate::error::ContractError;
use crate::exec::admin::set_enabled;
use crate::exec::finality::{handle_finality_signature, handle_public_randomness_commit};
use crate::exec::finality::{
handle_finality_signature, handle_public_randomness_commit, handle_slashing,
};
use crate::msg::{ExecuteMsg, InstantiateMsg, QueryMsg};
use crate::queries::{
query_block_voters, query_config, query_first_pub_rand_commit, query_last_pub_rand_commit,
};
use crate::state::config::{Config, ADMIN, CONFIG, IS_ENABLED};
use babylon_bindings::BabylonMsg;
use cosmwasm_std::{
to_json_binary, Deps, DepsMut, Env, MessageInfo, QueryResponse, Response, StdResult,
};
Expand All @@ -16,7 +19,7 @@ pub fn instantiate(
_env: Env,
_info: MessageInfo,
msg: InstantiateMsg,
) -> StdResult<Response> {
) -> StdResult<Response<BabylonMsg>> {
let api = deps.api;
ADMIN.set(deps.branch(), Some(api.addr_validate(&msg.admin)?))?;
IS_ENABLED.save(deps.storage, &msg.is_enabled)?;
Expand Down Expand Up @@ -51,7 +54,7 @@ pub fn execute(
env: Env,
info: MessageInfo,
msg: ExecuteMsg,
) -> Result<Response, ContractError> {
) -> Result<Response<BabylonMsg>, ContractError> {
let api = deps.api;

match msg {
Expand Down Expand Up @@ -79,13 +82,15 @@ pub fn execute(
} => handle_finality_signature(
deps,
env,
info,
&fp_pubkey_hex,
height,
&pub_rand,
&proof,
&block_hash,
&signature,
),
ExecuteMsg::Slashing { sender, evidence } => handle_slashing(&sender, &evidence),
ExecuteMsg::SetEnabled { enabled } => set_enabled(deps, info, enabled),
ExecuteMsg::UpdateAdmin { admin } => ADMIN
.execute_update_admin(deps, info, Some(api.addr_validate(&admin)?))
Expand Down Expand Up @@ -145,7 +150,6 @@ pub(crate) mod tests {
let mut deps = mock_dependencies();
let init_admin = deps.api.addr_make(INIT_ADMIN);
let new_admin = deps.api.addr_make(NEW_ADMIN);

// Create an InstantiateMsg with admin set to Some(INIT_ADMIN.into())
let instantiate_msg = InstantiateMsg {
admin: init_admin.to_string(), // Admin provided
Expand Down
12 changes: 12 additions & 0 deletions contracts/op-finality-gadget/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,16 @@ pub enum ContractError {
HexError(#[from] FromHexError),
#[error("The inclusion proof for height {0} does not correspond to the given height ({1})")]
InvalidFinalitySigHeight(u64, u64),
#[error("Contract already has an open IBC channel")]
IbcChannelAlreadyOpen {},
#[error("The contract only supports ordered channels")]
IbcUnorderedChannel {},
#[error("Counterparty version must be `{version}`")]
IbcInvalidCounterPartyVersion { version: String },
#[error("IBC method is not supported")]
IbcUnsupportedMethod {},
#[error("IBC send timed out: dest: channel {0}, port {1}")]
IbcTimeout(String, String),
#[error("The total amount of public randomnesses in the proof ({0}) does not match the amount of public committed randomness ({1})")]
InvalidFinalitySigAmount(u64, u64),
#[error("The start height ({0}) has overlap with the height of the highest public randomness committed ({1})")]
Expand All @@ -30,6 +40,8 @@ pub enum ContractError {
MissingPubRandCommit(String, u64),
#[error("{0}")]
SecP256K1Error(String), // TODO: inherit errors from k256
#[error("Failed to extract secret key: {0}")]
SecretKeyExtractionError(String),
#[error("{0}")]
StdError(#[from] StdError),
#[error("Failed to query block voters for block {0} with hash {1}. {2}")]
Expand Down
3 changes: 2 additions & 1 deletion contracts/op-finality-gadget/src/exec/admin.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use babylon_bindings::BabylonMsg;
use cosmwasm_std::{DepsMut, MessageInfo, Response};

use crate::{
Expand All @@ -14,7 +15,7 @@ pub fn set_enabled(
deps: DepsMut,
info: MessageInfo,
enabled: bool,
) -> Result<Response, ContractError> {
) -> Result<Response<BabylonMsg>, ContractError> {
// Check caller is admin
check_admin(&deps, info)?;
// Check if the finality gadget is already in the desired state
Expand Down
Loading

0 comments on commit 6de5c0d

Please sign in to comment.