PingPong
Overview
El PingPong contract implementa un simple protocolo de ping pong que alterna entre el envío de mensajes “ping” y “pong” entre dos contratos implementados en dos cadenas de bloques diferentes conectadas a través de IBC.
Estructura del Contrato
El contrato consta de los siguientes componentes:
Estructuras de Datos y Biblioteca: Define a
PingPongPacketestructura para representar los datos del paquete de ping pong.Definición de Contrato: El
PingPongel contrato hereda deIBCAppBasee implementa las devoluciones de llamada deIIBCModule.Constructor: Inicializa el contrato con el controlador IBC, el número de revisión y el número de bloques antes del tiempo de espera del pong.
IBCAppBase Anula: Anula las funciones requeridas por el protocolo IBC, incluyendo
ibcAddress,onRecvPacket, y canal de devoluciones de llamada abiertas/cerradas.Iniciación: El
initiatela función envía un paquete de ping o pong a la cadena de contraparte.
Protocolo de Flujo de trabajo
El contrato se implementa con el controlador IBC, el número de revisión y la información de tiempo de espera.
Un lado del canal inicia el protocolo de ping pong llamando al
initiatefunción.Al recibir un paquete, el
onRecvPacketla función se activa, emitiendo unRingevento y envío de un paquete de respuesta a la cadena de contrapartes.El protocolo continúa alternando entre los mensajes de ping y pong a medida que cada lado del canal procesa los paquetes entrantes.
Conclusión
El PingPong contract muestra un ejemplo básico de comunicación entre cadenas a través de IBC utilizando Solidity. Demuestra cómo los desarrolladores pueden implementar un protocolo de ping pong simple, intercambiando paquetes y alternando entre mensajes de ping y pong. Este ejemplo sirve como punto de partida para construir aplicaciones de cadena cruzada más complejas utilizando IBC y Solidity.
Implementación
La interfaz utilizada por la implementación es la descrita en la sección de integración de Solidity.
pragma solidity ^0.8.23;
import "../../Base.sol";
import "../../../core/25-handler/IBCHandler.sol";
// Protocol specific packet
struct PingPongPacket {
bool ping;
uint64 counterpartyTimeout;
}
library PingPongLib {
bytes1 public constant ACK_SUCCESS = 0x01;
error ErrOnlyOneChannel();
error ErrInvalidAck();
error ErrNoChannel();
error ErrInfiniteGame();
event Ring(bool ping);
event TimedOut();
event Acknowledged();
function encode(PingPongPacket memory packet)
internal
pure
returns (bytes memory)
{
return abi.encode(packet.ping, packet.counterpartyTimeout);
}
function decode(bytes memory packet)
internal
pure
returns (PingPongPacket memory)
{
(bool ping, uint64 counterpartyTimeout) =
abi.decode(packet, (bool, uint64));
return PingPongPacket({
ping: ping,
counterpartyTimeout: counterpartyTimeout
});
}
}
contract PingPong is IBCAppBase {
using PingPongLib for *;
IBCHandler private ibcHandler;
string private portId;
string private channelId;
uint64 private revisionNumber;
uint64 private timeout;
constructor(
IBCHandler _ibcHandler,
uint64 _revisionNumber,
uint64 _timeout
) {
ibcHandler = _ibcHandler;
revisionNumber = _revisionNumber;
timeout = _timeout;
}
function ibcAddress() public view virtual override returns (address) {
return address(ibcHandler);
}
function initiate(
PingPongPacket memory packet,
uint64 localTimeout
) public {
if (bytes(channelId).length == 0) {
revert PingPongLib.ErrNoChannel();
}
ibcHandler.sendPacket(
portId,
channelId,
// No height timeout
IbcCoreClientV1Height.Data({revision_number: 0, revision_height: 0}),
// Timestamp timeout
localTimeout,
// Raw protocol packet
packet.encode()
);
}
function onRecvPacket(
IbcCoreChannelV1Packet.Data calldata packet,
address relayer
)
external
virtual
override
onlyIBC
returns (bytes memory acknowledgement)
{
PingPongPacket memory pp = PingPongLib.decode(packet.data);
emit PingPongLib.Ring(pp.ping);
uint64 localTimeout = pp.counterpartyTimeout;
pp.ping = !pp.ping;
pp.counterpartyTimeout = uint64(block.timestamp) + timeout;
// Send back the packet after having reversed the bool and set the counterparty timeout
initiate(pp, localTimeout);
// Return protocol specific successful acknowledgement
return abi.encodePacked(PingPongLib.ACK_SUCCESS);
}
function onAcknowledgementPacket(
IbcCoreChannelV1Packet.Data calldata packet,
bytes calldata acknowledgement,
address relayer
) external virtual override onlyIBC {
/*
In practice, a more sophisticated protocol would check
and execute code depending on the counterparty outcome (refund etc...).
In our case, the acknowledgement will always be ACK_SUCCESS
*/
if (
keccak256(acknowledgement)
!= keccak256(abi.encodePacked(PingPongLib.ACK_SUCCESS))
) {
revert PingPongLib.ErrInvalidAck();
}
emit PingPongLib.Acknowledged();
}
function onTimeoutPacket(
IbcCoreChannelV1Packet.Data calldata packet,
address relayer
) external virtual override onlyIBC {
/*
Similarly to the onAcknowledgementPacket function, this indicates a failure to deliver the packet in expected time.
A sophisticated protocol would revert the action done before sending this packet.
*/
emit PingPongLib.TimedOut();
}
function onChanOpenInit(
IbcCoreChannelV1GlobalEnums.Order,
string[] calldata,
string calldata,
string calldata,
IbcCoreChannelV1Counterparty.Data calldata,
string calldata
) external virtual override onlyIBC {
// This protocol is only accepting a single counterparty.
if (bytes(channelId).length != 0) {
revert PingPongLib.ErrOnlyOneChannel();
}
}
function onChanOpenTry(
IbcCoreChannelV1GlobalEnums.Order,
string[] calldata,
string calldata,
string calldata,
IbcCoreChannelV1Counterparty.Data calldata,
string calldata,
string calldata
) external virtual override onlyIBC {
// Symmetric to onChanOpenInit
if (bytes(channelId).length != 0) {
revert PingPongLib.ErrOnlyOneChannel();
}
}
function onChanOpenAck(
string calldata _portId,
string calldata _channelId,
string calldata _counterpartyChannelId,
string calldata _counterpartyVersion
) external virtual override onlyIBC {
// Store the port/channel needed to send packets.
portId = _portId;
channelId = _channelId;
}
function onChanOpenConfirm(
string calldata _portId,
string calldata _channelId
) external virtual override onlyIBC {
// Symmetric to onChanOpenAck
portId = _portId;
channelId = _channelId;
}
function onChanCloseInit(
string calldata _portId,
string calldata _channelId
) external virtual override onlyIBC {
// The ping-pong is infinite, closing the channel is disallowed.
revert PingPongLib.ErrInfiniteGame();
}
function onChanCloseConfirm(
string calldata _portId,
string calldata _channelId
) external virtual override onlyIBC {
// Symmetric to onChanCloseInit
revert PingPongLib.ErrInfiniteGame();
}
}Last updated