Skip to content

Commit

Permalink
Merge pull request #63 from osmosis-labs/ibc-validator-sync
Browse files Browse the repository at this point in the history
Implement IBC Validator Sync sub-protocol
  • Loading branch information
ethanfrey authored Jun 22, 2023
2 parents 2ef9075 + 4fa8aec commit 39712e6
Show file tree
Hide file tree
Showing 9 changed files with 426 additions and 66 deletions.
22 changes: 11 additions & 11 deletions Cargo.lock

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

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ resolver = "2"

[workspace.package]
edition = "2021"
version = "0.2.0"
version = "0.3.0-alpha.1"
license = "MIT"
repository = "https://github.com/osmosis-labs/mesh-security"

Expand Down
83 changes: 69 additions & 14 deletions contracts/consumer/converter/src/ibc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,17 @@
use cosmwasm_std::entry_point;

use cosmwasm_std::{
from_slice, DepsMut, Env, Ibc3ChannelOpenResponse, IbcBasicResponse, IbcChannel,
IbcChannelCloseMsg, IbcChannelConnectMsg, IbcChannelOpenMsg, IbcChannelOpenResponse,
IbcPacketAckMsg, IbcPacketReceiveMsg, IbcPacketTimeoutMsg, IbcReceiveResponse,
from_slice, to_binary, DepsMut, Env, Event, Ibc3ChannelOpenResponse, IbcBasicResponse,
IbcChannel, IbcChannelCloseMsg, IbcChannelConnectMsg, IbcChannelOpenMsg,
IbcChannelOpenResponse, IbcMsg, IbcPacketAckMsg, IbcPacketReceiveMsg, IbcPacketTimeoutMsg,
IbcReceiveResponse, IbcTimeout,
};
use cw_storage_plus::Item;

use mesh_apis::ibc::{validate_channel_order, ProtocolVersion, PROTOCOL_NAME};
use mesh_apis::ibc::{
validate_channel_order, AckWrapper, AddValidator, ConsumerPacket, ProtocolVersion,
PROTOCOL_NAME,
};

use crate::error::ContractError;

Expand All @@ -20,6 +24,16 @@ const MIN_IBC_PROTOCOL_VERSION: &str = "1.0.0";
// IBC specific state
const IBC_CHANNEL: Item<IbcChannel> = Item::new("ibc_channel");

// Let those validator syncs take a day...
const DEFAULT_TIMEOUT: u64 = 24 * 60 * 60;

fn packet_timeout(env: &Env) -> IbcTimeout {
// No idea about their blocktime, but 24 hours ahead of our view of the clock
// should be decently in the future.
let timeout = env.block.time.plus_seconds(DEFAULT_TIMEOUT);
IbcTimeout::with_timestamp(timeout)
}

#[cfg_attr(not(feature = "library"), entry_point)]
/// enforces ordering and versioning constraints
pub fn ibc_channel_open(
Expand Down Expand Up @@ -63,7 +77,7 @@ pub fn ibc_channel_open(
/// once it's established, we store data
pub fn ibc_channel_connect(
deps: DepsMut,
_env: Env,
env: Env,
msg: IbcChannelConnectMsg,
) -> Result<IbcBasicResponse, ContractError> {
// ensure we have no channel yet
Expand All @@ -89,8 +103,27 @@ pub fn ibc_channel_connect(
// store the channel
IBC_CHANNEL.save(deps.storage, &channel)?;

// FIXME: later we start with sending the validator sync packets
Ok(IbcBasicResponse::default())
// Send a validator sync packet to arrive with the newly established channel
let validators = deps.querier.query_all_validators()?;
let updates = validators
.into_iter()
.map(|v| AddValidator {
valoper: v.address,
// TODO: not yet available in CosmWasm APIs
pub_key: "TODO".to_string(),
// Use current height/time as start height/time (no slashing before mesh starts)
start_height: env.block.height,
start_time: env.block.time.seconds(),
})
.collect();
let packet = ConsumerPacket::AddValidators(updates);
let msg = IbcMsg::SendPacket {
channel_id: channel.endpoint.channel_id,
data: to_binary(&packet)?,
timeout: packet_timeout(&env),
};

Ok(IbcBasicResponse::new().add_message(msg))
}

#[cfg_attr(not(feature = "library"), entry_point)]
Expand All @@ -117,21 +150,43 @@ pub fn ibc_packet_receive(
}

#[cfg_attr(not(feature = "library"), entry_point)]
/// never should be called as we do not send packets
/// We get acks on sync state without much to do.
/// If it succeeded, take no action. If it errored, we can't do anything else and let it go.
/// We just log the error cases so they can be detected.
pub fn ibc_packet_ack(
_deps: DepsMut,
_env: Env,
_msg: IbcPacketAckMsg,
msg: IbcPacketAckMsg,
) -> Result<IbcBasicResponse, ContractError> {
Ok(IbcBasicResponse::new().add_attribute("action", "ibc_packet_ack"))
let ack: AckWrapper = from_slice(&msg.acknowledgement.data)?;
let mut res = IbcBasicResponse::new();
match ack {
AckWrapper::Result(_) => {}
AckWrapper::Error(e) => {
// The wasmd framework will label this with the contract_addr, which helps us find the port and issue.
// Provide info to find the actual packet.
let event = Event::new("mesh_ibc_error")
.add_attribute("error", e)
.add_attribute("channel", msg.original_packet.src.channel_id)
.add_attribute("sequence", msg.original_packet.sequence.to_string());
res = res.add_event(event);
}
}
Ok(res)
}

#[cfg_attr(not(feature = "library"), entry_point)]
/// never should be called as we do not send packets
/// The most we can do here is retry the packet, hoping it will eventually arrive.
pub fn ibc_packet_timeout(
_deps: DepsMut,
_env: Env,
_msg: IbcPacketTimeoutMsg,
env: Env,
msg: IbcPacketTimeoutMsg,
) -> Result<IbcBasicResponse, ContractError> {
Ok(IbcBasicResponse::new().add_attribute("action", "ibc_packet_timeout"))
// Play it again, Sam.
let msg = IbcMsg::SendPacket {
channel_id: msg.packet.src.channel_id,
data: msg.packet.data,
timeout: packet_timeout(&env),
};
Ok(IbcBasicResponse::new().add_message(msg))
}
19 changes: 17 additions & 2 deletions contracts/provider/external-staking/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,10 @@ use sylvia::contract;
use sylvia::types::{ExecCtx, InstantiateCtx, QueryCtx};

use crate::error::ContractError;
use crate::ibc::VAL_CRDT;
use crate::msg::{
AuthorizedEndpointResponse, ConfigResponse, IbcChannelResponse, PendingRewards,
ReceiveVirtualStake, StakeInfo, StakesResponse,
AuthorizedEndpointResponse, ConfigResponse, IbcChannelResponse, ListRemoteValidatorsResponse,
PendingRewards, ReceiveVirtualStake, StakeInfo, StakesResponse,
};
use crate::state::{Config, Distribution, PendingUnbond, Stake};

Expand Down Expand Up @@ -297,6 +298,20 @@ impl ExternalStakingContract<'_> {
Ok(IbcChannelResponse { channel })
}

/// Show all external validators that we know to be active (and can delegate to)
#[msg(query)]
pub fn list_remote_validators(
&self,
ctx: QueryCtx,
start_after: Option<String>,
limit: Option<u64>,
) -> Result<ListRemoteValidatorsResponse, ContractError> {
let limit = limit.unwrap_or(100) as usize;
let validators =
VAL_CRDT.list_active_validators(ctx.deps.storage, start_after.as_deref(), limit)?;
Ok(ListRemoteValidatorsResponse { validators })
}

/// Queries for stake info
///
/// If stake is not existing in the system is queried, the zero-stake is returned
Expand Down
Loading

0 comments on commit 39712e6

Please sign in to comment.