CosmWasm y CIB

Casos de uso

El protocolo IBC proporciona una forma sin permisos de retransmitir paquetes de datos entre cadenas de bloques, y la combinación de IBC con CosmWasm permite potentes casos de uso, como:

Transferencia de activos entre cadenas

IBC permite la transferencia de activos entre diferentes redes blockchain. Con CosmWasm, los desarrolladores pueden crear contratos inteligentes que admitan transferencias de activos entre cadenas, lo que permite a los usuarios enviar y recibir tokens a través de múltiples redes de blockchain.

Infraestructura interoperable

IBC y CosmWasm crean conjuntamente una infraestructura altamente interoperable que permite la comunicación y el intercambio de datos sin fisuras entre diferentes redes de cadenas de bloques. Esto ayuda a promover la colaboración y la innovación entre cadenas, así como a mejorar la seguridad y la escalabilidad del ecosistema en general.

Infraestructura interoperable

IBC permite que múltiples blockchains participen en un sistema de gobierno compartido. CosmWasm permite a los desarrolladores crear contratos inteligentes que facilitan la gobernanza multicadena, permitiendo a los usuarios participar en procesos de toma de decisiones que afectan a múltiples redes blockchain simultáneamente.

Utilización de IBC con CosmWasm

Normas ICS

Puede utilizar ICS20 e ICS721, ya que son estándares IBC establecidos y actualmente están implementados en CosmWasm.

De todos modos, también puede implementar sus propios protocolos ICS dentro del contrato (por ejemplo, cw20-ics20).

Canales IBC

Debes establecer un canal IBC entre los dos contratos para poder conectarlos. En esta sección, denominaremos cadena A y cadena B a las cadenas conectadas por el canal IBC.

El proceso de establecimiento del canal IBC utiliza un handshake de cuatro vías:

OpenInit permite a la cadena A declarar su información a la cadena B, y a la cadena A pedir a la cadena B que verifique dicha información.

OpenTry permite a la cadena B responder a la cadena A con su información (que debe ser verificada por la cadena A).

OpenAck permite a la cadena A abrir el canal IBC y enviar el acuse de recibo a la cadena B.

OpenConfirm permite a la cadena B recibir el acuse de recibo y abrir el canal che desde su lado.

Puntos de entrada

Para ejecutar el handshake IBC, necesitarás implementar seis puntos de entrada en tu contrato inteligente. Estos puntos de entrada son necesarios para permitir la comunicación IBC, y están relacionados con los cuatro pasos del proceso de apretón de manos:

ibc_channel_open se utiliza para gestionar los pasos MsgChannelOpenInit y MsgChannelOpenTry del handshake del canal.

ibc_channel_connect se utiliza para manejar los pasos MsgChannelOpenAck y MsgChannelOpenConfirm del handshake del canal.

ibc_channel_close se utiliza para manejar el cierre del canal.

ibc_packet_receive se utiliza para manejar MsgRecvPacket.

ibc_packet_ack se utiliza para manejar MsgAcknowledgement.

ibc_packet_timeout se utiliza para manejar MsgTimeout.

Ejemplo de contador IBC

A modo de ejemplo, vamos a ver cómo implementar un contrato inteligente simple de contador IBC con CosmWasm. Puedes encontrar el código de este ejemplo en este repositorio de Github.

Cuando se solicita en una cadena, el contador IBC Contrato Inteligente haría:

  • Enviar mensajes a su cadena homóloga.

  • Cuenta el número de veces que se han recibido mensajes en ambos lados.

Como IBC nos permite enviar paquetes de la cadena A a la cadena B y recibir respuestas, el flujo es:

  • Que el contrato/módulo de la cadena A solicite el envío de un paquete.

  • A continuación, el paquete se transmite a la cadena B, que lo recibe y calcula un acuse de recibo. El acuse de recibo puede contener un resultado satisfactorio o un mensaje de error, que son bytes que luego interpreta el contrato receptor.

  • A continuación, el acuse de recibo se transmite de nuevo a la cadena A, con lo que se completa el ciclo.

  • Como es posible que el paquete nunca se entregue antes del tiempo de espera, llamar al manejador de tiempo de espera en la cadena A aborta el proceso.

  • En el caso de que la cadena A envíe el paquete y reciba un mensaje de "timeout", nunca se ejecutan las llamadas de retorno de recepción ni de acuse de recibo.

Para utilizar este contrato, necesitaría:

  • Almacenar e instanciar el contrato en dos cadenas habilitadas para IBC (A y B).

  • Utilice los canales activos que figuran en los canales IBC, o ejecute su propio repetidor.

  • Ejecuta el método Incrementar {} en un contrato para incrementar el enviar un mensaje e incrementar la cuenta en el otro.

  • Utilice la consulta GetCount { connection } para determinar el recuento de mensajes de una conexión determinada.

Veamos con más detalle algunas partes clave del código.

contract.rs

Cuando se ejecuta el mensaje Incrementar para un canal concreto en una instancia de contrato, se crea una respuesta que contiene un mensaje IbcMsg::SendPacket con el mensaje Incrementar para ejecutar en la contraparte:

#[cfg_attr(not(feature = "library"), entry_point)]
pub fn execute(
    _deps: DepsMut,
    env: Env,
    _info: MessageInfo,
    msg: ExecuteMsg,
) -> Result<Response, ContractError> {
    match msg {
        ExecuteMsg::Increment { channel } => Ok(Response::new()
            .add_attribute("method", "execute_increment")
            .add_attribute("channel", channel.clone())
            // outbound IBC message, where packet is then received on other chain
            .add_message(IbcMsg::SendPacket {
                channel_id: channel,
                data: to_binary(&IbcExecuteMsg::Increment {})?,
                // default timeout of two minutes.
                timeout: IbcTimeout::with_timestamp(env.block.time.plus_seconds(120)),
            })),
    }
}
/// called on IBC packet receive in other chain
pub fn try_increment(deps: DepsMut, channel: String) -> Result<u32, StdError> {
    CONNECTION_COUNTS.update(deps.storage, channel, |count| -> StdResult<_> {
        Ok(count.unwrap_or_default() + 1)
    })
}

Puede consultar la instancia del contrato para obtener el valor actual del contador:

/// called on IBC packet receive in other chain
pub fn try_increment(deps: DepsMut, channel: String) -> Result<u32, StdError> {
    CONNECTION_COUNTS.update(deps.storage, channel, |count| -> StdResult<_> {
        Ok(count.unwrap_or_default() + 1)
    })
}

ibc.rs

Aquí habría que implementar los seis puntos de entrada.

Cuando la instancia del contrato en la cadena de contrapartida recibe el mensaje Incrementar, se ejecuta la función ibc_packet_receive:

pub fn do_ibc_packet_receive(
    deps: DepsMut,
    _env: Env,
    msg: IbcPacketReceiveMsg,
) -> Result<IbcReceiveResponse, ContractError> {
    // The channel this packet is being relayed along on this chain.
    let channel = msg.packet.dest.channel_id;
    let msg: IbcExecuteMsg = from_binary(&msg.packet.data)?;
    match msg {
        IbcExecuteMsg::Increment {} => execute_increment(deps, channel),
    }
}

Finalmente, el código intenta incrementar el contador en el estado de la instancia del contrato y devuelve un IbcReceiveResponse que establece el acuse de recibo:


fn execute_increment(deps: DepsMut, channel: String) -> Result<IbcReceiveResponse, ContractError> {
    let count = try_increment(deps, channel)?;
    Ok(IbcReceiveResponse::new()
        .add_attribute("method", "execute_increment")
        .add_attribute("count", count.to_string())
        .set_ack(make_ack_success()))
}

state.rs

El estado del contrato es simplemente un mapa con el ID del canal como clave y el contador de ese canal como valor:

use cw_storage_plus::Map;
/// (channel_id) -> count. Reset on channel closure.
pub const CONNECTION_COUNTS: Map<String, u32> = Map::new("connection_counts");
/// (channel_id) -> timeout_count. Reset on channel closure.
pub const TIMEOUT_COUNTS: Map<String, u32> = Map::new("timeout_count");

msg.rs

The msg.rs defines the messages to instantiate the contract, execute the increment message on the contract, send the increment message to the counterparty and query the contract In this case, the contract only sends one type of packet data (i.e. the Increment message):

#[cw_serde]
#[derive(QueryResponses)]
pub enum QueryMsg {
    // GetCount returns the current count as a json-encoded number
    #[returns(crate::msg::GetCountResponse)]
    GetCount {
        // The ID of the LOCAL channel you'd like to query the count
        // for.
        channel: String,
    },
    // GetTimeoutCount returns the number of timeouts have occurred on
    // the LOCAL channel `channel`.
    #[returns(crate::msg::GetCountResponse)]
    GetTimeoutCount { channel: String },
}
// We define a custom struct for each query response
#[cw_serde]
pub struct GetCountResponse {
    pub count: u32,
}

Resources

Last updated