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

refactor instantiation logic #94

Draft
wants to merge 12 commits into
base: main
Choose a base branch
from
Next Next commit
init
SebastianElvis committed Dec 6, 2024
commit 31a2e51400852b903eb9e6a6f28d20e7818fd2ca
3 changes: 2 additions & 1 deletion Cargo.lock

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

4 changes: 0 additions & 4 deletions contracts/babylon/benches/main.rs
Original file line number Diff line number Diff line change
@@ -49,10 +49,6 @@ pub fn setup_instance() -> Instance<MockApi, MockStorage, MockQuerier> {
btc_confirmation_depth: 10,
checkpoint_finalization_timeout: 1,
notify_cosmos_zone: false,
btc_staking_code_id: None,
btc_staking_msg: None,
btc_finality_code_id: None,
btc_finality_msg: None,
admin: None,
};
let info = mock_info(CREATOR, &[]);
119 changes: 11 additions & 108 deletions contracts/babylon/src/contract.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use babylon_bindings::babylon_sdk::{QueryParamsResponse, QUERY_PARAMS_PATH};
use cosmwasm_std::{
to_json_binary, Addr, Binary, Deps, DepsMut, Empty, Env, MessageInfo, QueryResponse, Reply,
Response, SubMsg, SubMsgResponse, WasmMsg,
@@ -40,56 +41,15 @@ pub fn instantiate(
btc_confirmation_depth: msg.btc_confirmation_depth,
checkpoint_finalization_timeout: msg.checkpoint_finalization_timeout,
notify_cosmos_zone: msg.notify_cosmos_zone,
btc_staking: None, // Will be set in `reply` if `btc_staking_code_id` is provided
btc_finality: None, // Will be set in `reply` if `btc_finality_code_id` is provided
consumer_name: None,
consumer_description: None,
};

let mut res = Response::new().add_attribute("action", "instantiate");
let res = Response::new().add_attribute("action", "instantiate");

if let Some(btc_staking_code_id) = msg.btc_staking_code_id {
// Update config with consumer information
cfg.consumer_name = msg.consumer_name;
cfg.consumer_description = msg.consumer_description;

// Instantiate BTC staking contract
let init_msg = WasmMsg::Instantiate {
admin: msg.admin.clone(),
code_id: btc_staking_code_id,
msg: msg.btc_staking_msg.unwrap_or(Binary::from(b"{}")),
funds: vec![],
label: "BTC Staking".into(),
};
let init_msg = SubMsg::reply_on_success(init_msg, REPLY_ID_INSTANTIATE_STAKING);

// Test code sets a channel, so that we can better approximate IBC in test code
#[cfg(any(test, all(feature = "library", not(target_arch = "wasm32"))))]
{
let channel = cosmwasm_std::testing::mock_ibc_channel(
"channel-123",
cosmwasm_std::IbcOrder::Ordered,
"babylon",
);
IBC_CHANNEL.save(deps.storage, &channel)?;
}

res = res.add_submessage(init_msg);
}

if let Some(btc_finality_code_id) = msg.btc_finality_code_id {
// Instantiate BTC finality contract
let init_msg = WasmMsg::Instantiate {
admin: msg.admin,
code_id: btc_finality_code_id,
msg: msg.btc_finality_msg.unwrap_or(Binary::from(b"{}")),
funds: vec![],
label: "BTC Finality".into(),
};
let init_msg = SubMsg::reply_on_success(init_msg, REPLY_ID_INSTANTIATE_FINALITY);

res = res.add_submessage(init_msg);
}
// Update config with consumer information
cfg.consumer_name = msg.consumer_name;
cfg.consumer_description = msg.consumer_description;

// Save the config after potentially updating it
CONFIG.save(deps.storage, &cfg)?;
@@ -98,18 +58,6 @@ pub fn instantiate(
Ok(res)
}

pub fn reply(
deps: DepsMut,
_env: Env,
reply: Reply,
) -> Result<Response<BabylonMsg>, ContractError> {
match reply.id {
REPLY_ID_INSTANTIATE_STAKING => reply_init_callback_staking(deps, reply.result.unwrap()),
REPLY_ID_INSTANTIATE_FINALITY => reply_init_finality_callback(deps, reply.result.unwrap()),
_ => Err(ContractError::InvalidReplyId(reply.id)),
}
}

/// Tries to get contract address from events in reply
fn reply_init_get_contract_address(reply: SubMsgResponse) -> Result<Addr, ContractError> {
for event in reply.events {
@@ -126,47 +74,6 @@ fn reply_init_get_contract_address(reply: SubMsgResponse) -> Result<Addr, Contra
)))
}

/// Store BTC staking address
fn reply_init_callback_staking(
deps: DepsMut,
reply: SubMsgResponse,
) -> Result<Response<BabylonMsg>, ContractError> {
// Try to get contract address from events in reply
let addr = reply_init_get_contract_address(reply)?;
CONFIG.update(deps.storage, |mut cfg| {
cfg.btc_staking = Some(addr);
Ok::<_, ContractError>(cfg)
})?;
Ok(Response::new())
}

/// Store BTC finality address
fn reply_init_finality_callback(
deps: DepsMut,
reply: SubMsgResponse,
) -> Result<Response<BabylonMsg>, ContractError> {
// Try to get contract address from events in reply
let finality_addr = reply_init_get_contract_address(reply)?;
CONFIG.update(deps.storage, |mut cfg| {
cfg.btc_finality = Some(finality_addr.clone());
Ok::<_, ContractError>(cfg)
})?;
// Set the BTC staking contract address to the BTC finality contract
let cfg = CONFIG.load(deps.storage)?;
let msg = finality_api::ExecuteMsg::UpdateStaking {
staking: cfg
.btc_staking
.ok_or(ContractError::BtcStakingNotSet {})?
.to_string(),
};
let wasm_msg = WasmMsg::Execute {
contract_addr: finality_addr.to_string(),
msg: to_json_binary(&msg)?,
funds: vec![],
};
Ok(Response::new().add_message(wasm_msg))
}

pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> Result<QueryResponse, ContractError> {
match msg {
QueryMsg::Config {} => Ok(to_json_binary(&queries::config(deps)?)?),
@@ -228,18 +135,18 @@ pub fn execute(
Ok(Response::new())
}
ExecuteMsg::Slashing { evidence } => {
// This is an internal routing message from the `btc_finality` contract
let cfg = CONFIG.load(deps.storage)?;
// Check sender
let btc_finality = cfg
.btc_finality
.ok_or(ContractError::BtcFinalityNotSet {})?;
let params = deps
.querier
.query_grpc(QUERY_PARAMS_PATH.to_owned(), Binary::new("".into()))?;
let params = QueryParamsResponse::from(params).params;
let btc_finality = params.btc_finality_contract_address;
if info.sender != btc_finality {
return Err(ContractError::Unauthorized {});
}
// Send to the staking contract for processing
let mut res = Response::new();
let btc_staking = cfg.btc_staking.ok_or(ContractError::BtcStakingNotSet {})?;
let btc_staking = params.btc_staking_contract_address;
// Slashes this finality provider, i.e., sets its slashing height to the block height
// and its power to zero
let msg = btc_staking_api::ExecuteMsg::Slash {
@@ -298,10 +205,6 @@ mod tests {
btc_confirmation_depth: 10,
checkpoint_finalization_timeout: 100,
notify_cosmos_zone: false,
btc_staking_code_id: None,
btc_staking_msg: None,
btc_finality_code_id: None,
btc_finality_msg: None,
admin: None,
consumer_name: None,
consumer_description: None,
15 changes: 6 additions & 9 deletions contracts/babylon/src/ibc.rs
Original file line number Diff line number Diff line change
@@ -162,6 +162,7 @@ pub(crate) mod ibc_packet {
ActiveBtcDelegation, NewFinalityProvider, UnbondedBtcDelegation,
};
use babylon_apis::finality_api::Evidence;
use babylon_bindings::babylon_sdk::{QueryParamsResponse, QUERY_PARAMS_PATH};
use babylon_proto::babylon::btcstaking::v1::BtcStakingIbcPacket;
use babylon_proto::babylon::zoneconcierge::v1::zoneconcierge_packet_data::Packet::ConsumerSlashing;
use babylon_proto::babylon::zoneconcierge::v1::ConsumerSlashingIbcPacket;
@@ -201,13 +202,13 @@ pub(crate) mod ibc_packet {
_caller: String,
btc_staking: &BtcStakingIbcPacket,
) -> StdResult<IbcReceiveResponse<BabylonMsg>> {
let storage = deps.storage;
let cfg = CONFIG.load(storage)?;
let params = deps
.querier
.query_grpc(QUERY_PARAMS_PATH.to_owned(), Binary::new("".into()))?;
let params = QueryParamsResponse::from(params).params;

// Route the packet to the btc-staking contract
let btc_staking_addr = cfg
.btc_staking
.ok_or(StdError::generic_err("btc_staking contract not set"))?;
let btc_staking_addr = params.btc_staking_contract_address;

// Build the message to send to the BTC staking contract
let msg = babylon_apis::btc_staking_api::ExecuteMsg::BtcStaking {
@@ -331,10 +332,6 @@ mod tests {
btc_confirmation_depth: 10,
checkpoint_finalization_timeout: 100,
notify_cosmos_zone: false,
btc_staking_code_id: None,
btc_staking_msg: None,
btc_finality_code_id: None,
btc_finality_msg: None,
admin: None,
consumer_name: None,
consumer_description: None,
5 changes: 0 additions & 5 deletions contracts/babylon/src/lib.rs
Original file line number Diff line number Diff line change
@@ -33,11 +33,6 @@ pub fn instantiate(
contract::instantiate(deps, env, info, msg)
}

#[cfg_attr(not(feature = "library"), entry_point)]
pub fn reply(deps: DepsMut, env: Env, reply: Reply) -> Result<Response<BabylonMsg>, ContractError> {
contract::reply(deps, env, reply)
}

#[cfg_attr(not(feature = "library"), entry_point)]
pub fn query(deps: Deps, env: Env, msg: msg::contract::QueryMsg) -> Result<Binary, ContractError> {
contract::query(deps, env, msg)
31 changes: 0 additions & 31 deletions contracts/babylon/src/msg/contract.rs
Original file line number Diff line number Diff line change
@@ -33,18 +33,6 @@ pub struct InstantiateMsg {
/// NOTE: If set to true, then the Cosmos zone needs to integrate the corresponding message handler
/// as well
pub notify_cosmos_zone: bool,
/// If set, this will instantiate a BTC staking contract for BTC re-staking
pub btc_staking_code_id: Option<u64>,
/// If set, this will define the instantiation message for the BTC staking contract.
/// This message is opaque to the Babylon contract, and depends on the specific staking contract
/// being instantiated
pub btc_staking_msg: Option<Binary>,
/// If set, this will instantiate a BTC finality contract
pub btc_finality_code_id: Option<u64>,
/// If set, this will define the instantiation message for the BTC finality contract.
/// This message is opaque to the Babylon contract, and depends on the specific finality contract
/// being instantiated
pub btc_finality_msg: Option<Binary>,
/// If set, this will be the Wasm migration / upgrade admin of the BTC staking contract and the
/// BTC finality contract
pub admin: Option<String>,
@@ -64,25 +52,6 @@ impl ContractMsg for InstantiateMsg {
}
let _ = self.babylon_tag_to_bytes()?;

if self.btc_staking_code_id.is_some() {
if let (Some(consumer_name), Some(consumer_description)) =
(&self.consumer_name, &self.consumer_description)
{
if consumer_name.trim().is_empty() {
return Err(StdError::generic_err("Consumer name cannot be empty"));
}
if consumer_description.trim().is_empty() {
return Err(StdError::generic_err(
"Consumer description cannot be empty",
));
}
} else {
return Err(StdError::generic_err(
"Consumer name and description are required when btc_staking_code_id is set",
));
}
}

Ok(())
}

23 changes: 4 additions & 19 deletions contracts/babylon/src/multitest.rs
Original file line number Diff line number Diff line change
@@ -3,12 +3,6 @@ mod suite;
use cosmwasm_std::Addr;
use suite::SuiteBuilder;

// Some multi-test default settings
// TODO: Replace these with their address generators
const CONTRACT0_ADDR: &str = "cosmwasm19mfs8tl4s396u7vqw9rrnsmrrtca5r66p7v8jvwdxvjn3shcmllqupdgxu";
const CONTRACT1_ADDR: &str = "cosmwasm14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9s8jef58";
const CONTRACT2_ADDR: &str = "cosmwasm1nc5tatafv6eyq7llkr2gv50ff9e22mnf70qgjlv737ktmt4eswrqt8utkp";

#[test]
fn initialization() {
let suite = SuiteBuilder::new().build();
@@ -23,16 +17,8 @@ fn initialization() {
assert_eq!(config.btc_confirmation_depth, 1);
assert_eq!(config.checkpoint_finalization_timeout, 10);
assert!(!config.notify_cosmos_zone);
assert_eq!(config.btc_staking, Some(Addr::unchecked(CONTRACT1_ADDR)));
assert_eq!(config.btc_finality, Some(Addr::unchecked(CONTRACT2_ADDR)));

// Check that the btc-staking contract was initialized correctly
let btc_staking_config = suite.get_btc_staking_config();
assert_eq!(btc_staking_config.babylon, Addr::unchecked(CONTRACT0_ADDR));

// Check that the btc-finality contract was initialized correctly
let btc_finality_config = suite.get_btc_finality_config();
assert_eq!(btc_finality_config.babylon, Addr::unchecked(CONTRACT0_ADDR));
assert!(!suite.btc_staking_contract.to_string().is_empty());
assert!(!suite.btc_finality_contract.to_string().is_empty());
}

mod instantiation {
@@ -43,10 +29,9 @@ mod instantiation {
let suite = SuiteBuilder::new().build();

// Confirm the btc-staking contract has been instantiated and set
let config = suite.get_config();
assert_eq!(config.btc_staking, Some(Addr::unchecked(CONTRACT1_ADDR)));
assert!(!suite.btc_staking_contract.to_string().is_empty());
// Confirm the btc-finality contract has been instantiated and set
assert_eq!(config.btc_finality, Some(Addr::unchecked(CONTRACT2_ADDR)));
assert!(!suite.btc_finality_contract.to_string().is_empty());
}
}

69 changes: 52 additions & 17 deletions contracts/babylon/src/multitest/suite.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
use crate::msg::contract::{InstantiateMsg, QueryMsg};
use crate::multitest::{CONTRACT1_ADDR, CONTRACT2_ADDR};
use crate::state::config::Config;
use anyhow::Result as AnyResult;
use babylon_bindings::BabylonMsg;
@@ -29,7 +28,6 @@ fn contract_btc_finality() -> Box<dyn Contract<BabylonMsg>> {

fn contract_babylon() -> Box<dyn Contract<BabylonMsg>> {
let contract = ContractWrapper::new(crate::execute, crate::instantiate, crate::query)
.with_reply(crate::reply)
.with_migrate(crate::migrate);
Box::new(contract)
}
@@ -61,12 +59,9 @@ impl SuiteBuilder {
app.init_modules(|_router, _api, _storage| -> AnyResult<()> { Ok(()) })
.unwrap();

let btc_staking_code_id =
app.store_code_with_creator(owner.clone(), contract_btc_staking());
let btc_finality_code_id =
app.store_code_with_creator(owner.clone(), contract_btc_finality());
// Store and instantiate the Babylon contract
let contract_code_id = app.store_code_with_creator(owner.clone(), contract_babylon());
let contract = app
let babylon_contract = app
.instantiate_contract(
contract_code_id,
owner.clone(),
@@ -76,10 +71,6 @@ impl SuiteBuilder {
btc_confirmation_depth: 1,
checkpoint_finalization_timeout: 10,
notify_cosmos_zone: false,
btc_staking_code_id: Some(btc_staking_code_id),
btc_staking_msg: None,
btc_finality_code_id: Some(btc_finality_code_id),
btc_finality_msg: None,
admin: Some(owner.to_string()),
consumer_name: Some("TestConsumer".to_string()),
consumer_description: Some("Test Consumer Description".to_string()),
@@ -90,10 +81,44 @@ impl SuiteBuilder {
)
.unwrap();

let btc_staking_code_id =
app.store_code_with_creator(owner.clone(), contract_btc_staking());
let btc_staking_contract = app
.instantiate_contract(
btc_staking_code_id,
owner.clone(),
&btc_staking::msg::InstantiateMsg {
admin: None,
params: None,
},
&[],
"btc-staking",
Some(owner.to_string()),
)
.unwrap();

let btc_finality_code_id =
app.store_code_with_creator(owner.clone(), contract_btc_finality());
let btc_finality_contract = app
.instantiate_contract(
btc_finality_code_id,
owner.clone(),
&btc_finality::msg::InstantiateMsg {
admin: None,
params: None,
},
&[],
"btc-finality",
Some(owner.to_string()),
)
.unwrap();

Suite {
app,
code_id: contract_code_id,
contract,
babylon_contract: babylon_contract,
btc_staking_contract: btc_staking_contract,
btc_finality_contract: btc_finality_contract,
owner,
}
}
@@ -107,7 +132,11 @@ pub struct Suite {
/// The code id of the babylon contract
code_id: u64,
/// Babylon contract address
pub contract: Addr,
pub babylon_contract: Addr,
/// BTC staking contract address
pub btc_staking_contract: Addr,
/// BTC finality contract address
pub btc_finality_contract: Addr,
/// Admin of babylon and btc-staking contracts
pub owner: Addr,
}
@@ -121,30 +150,36 @@ impl Suite {
pub fn get_config(&self) -> Config {
self.app
.wrap()
.query_wasm_smart(self.contract.clone(), &QueryMsg::Config {})
.query_wasm_smart(self.babylon_contract.clone(), &QueryMsg::Config {})
.unwrap()
}

#[track_caller]
pub fn get_btc_staking_config(&self) -> btc_staking::state::config::Config {
self.app
.wrap()
.query_wasm_smart(CONTRACT1_ADDR, &btc_staking::msg::QueryMsg::Config {})
.query_wasm_smart(
self.btc_staking_contract.clone(),
&btc_staking::msg::QueryMsg::Config {},
)
.unwrap()
}

#[track_caller]
pub fn get_btc_finality_config(&self) -> btc_finality::state::config::Config {
self.app
.wrap()
.query_wasm_smart(CONTRACT2_ADDR, &btc_finality::msg::QueryMsg::Config {})
.query_wasm_smart(
self.btc_finality_contract.clone(),
&btc_finality::msg::QueryMsg::Config {},
)
.unwrap()
}

pub fn migrate(&mut self, addr: &str, msg: Empty) -> AnyResult<AppResponse> {
self.app.migrate_contract(
Addr::unchecked(addr),
self.contract.clone(),
self.babylon_contract.clone(),
&msg,
self.code_id,
)
2 changes: 0 additions & 2 deletions contracts/babylon/src/state/btc_light_client.rs
Original file line number Diff line number Diff line change
@@ -356,8 +356,6 @@ pub(crate) mod tests {
btc_confirmation_depth: 1,
checkpoint_finalization_timeout: w,
notify_cosmos_zone: false,
btc_staking: None,
btc_finality: None,
consumer_name: None,
consumer_description: None,
};
4 changes: 0 additions & 4 deletions contracts/babylon/src/state/config.rs
Original file line number Diff line number Diff line change
@@ -15,10 +15,6 @@ pub struct Config {
/// NOTE: if set to true, then the Cosmos zone needs to integrate the corresponding message
/// handler as well
pub notify_cosmos_zone: bool,
/// If set, this stores a BTC staking contract used for BTC re-staking
pub btc_staking: Option<Addr>,
/// If set, this stores a BTC finality contract used for BTC finality on the Consumer
pub btc_finality: Option<Addr>,
/// Consumer name
pub consumer_name: Option<String>,
/// Consumer description
8 changes: 0 additions & 8 deletions contracts/babylon/tests/integration.rs
Original file line number Diff line number Diff line change
@@ -47,10 +47,6 @@ fn setup() -> Instance<MockApi, MockStorage, MockQuerier> {
btc_confirmation_depth: 10,
checkpoint_finalization_timeout: 99,
notify_cosmos_zone: false,
btc_staking_code_id: None,
btc_staking_msg: None,
btc_finality_code_id: None,
btc_finality_msg: None,
admin: None,
};
let info = message_info(&Addr::unchecked(CREATOR), &[]);
@@ -101,10 +97,6 @@ fn instantiate_works() {
btc_confirmation_depth: 10,
checkpoint_finalization_timeout: 100,
notify_cosmos_zone: false,
btc_staking_code_id: None,
btc_staking_msg: None,
btc_finality_code_id: None,
btc_finality_msg: None,
admin: None,
};
let info = message_info(&Addr::unchecked(CREATOR), &[]);
1 change: 1 addition & 0 deletions packages/bindings/Cargo.toml
Original file line number Diff line number Diff line change
@@ -9,5 +9,6 @@ homepage = "https://babylonlabs.io"
license = "Apache-2.0"

[dependencies]
anybuf = { workspace = true }
cosmwasm-std = { workspace = true }
cosmwasm-schema = { workspace = true }
47 changes: 47 additions & 0 deletions packages/bindings/src/babylon_sdk.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
use anybuf::Bufany;
use cosmwasm_schema::cw_serde;
use cosmwasm_std::{Addr, Binary};

pub const QUERY_PARAMS_PATH: &str = "/babylonlabs/babylon/v1beta1/params";

#[cw_serde]
pub struct QueryParamsResponse {
pub params: Params,
}

#[cw_serde]
pub struct Params {
/// babylon_contract_code_id is the code ID of the Babylon contract
pub babylon_contract_code_id: u64,
/// btc_staking_contract_code_id is the code ID of the BTC staking contract
pub btc_staking_contract_code_id: u64,
/// btc_finality_contract_code_id is the code ID of the BTC finality contract
pub btc_finality_contract_code_id: u64,
/// babylon_contract_address is the address of the Babylon contract
pub babylon_contract_address: Addr,
/// btc_staking_contract_address is the address of the BTC staking contract
pub btc_staking_contract_address: Addr,
/// btc_finality_contract_address is the address of the BTC finality contract
pub btc_finality_contract_address: Addr,
/// max_gas_begin_blocker defines the maximum gas that can be spent in a contract sudo callback
pub max_gas_begin_blocker: u32,
}

impl From<Binary> for QueryParamsResponse {
fn from(binary: Binary) -> Self {
let res_decoded = Bufany::deserialize(&binary).unwrap();
// See https://github.com/babylonlabs-io/babylon/blob/base/consumer-chain-support/proto/babylon/btcstaking/v1/query.proto#L35
let res_params = res_decoded.message(1).unwrap();
QueryParamsResponse {
params: Params {
babylon_contract_code_id: res_params.uint64(1).unwrap(),
btc_staking_contract_code_id: res_params.uint64(2).unwrap(),
btc_finality_contract_code_id: res_params.uint64(3).unwrap(),
babylon_contract_address: Addr::unchecked(res_params.string(4).unwrap()),
btc_staking_contract_address: Addr::unchecked(res_params.string(5).unwrap()),
btc_finality_contract_address: Addr::unchecked(res_params.string(6).unwrap()),
max_gas_begin_blocker: res_params.uint32(7).unwrap(),
},
}
}
}
1 change: 1 addition & 0 deletions packages/bindings/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
pub mod babylon_sdk;
pub mod msg;

pub use msg::{BabylonMsg, BabylonQuery, BabylonSudoMsg};