Skip to content

Commit

Permalink
feat(docs): update demo (#1205)
Browse files Browse the repository at this point in the history
  • Loading branch information
hussein-aitlahcen authored Jan 25, 2024
2 parents 15254d3 + f0e14e6 commit b00ddc3
Show file tree
Hide file tree
Showing 10 changed files with 181 additions and 88 deletions.
1 change: 1 addition & 0 deletions dictionary.txt
Original file line number Diff line number Diff line change
Expand Up @@ -761,6 +761,7 @@ txpool
typecheck
typemap
typenum
typeof
typesparams
uatom
ucli
Expand Down
140 changes: 80 additions & 60 deletions evm/contracts/apps/ucs/00-pingpong/PingPong.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,61 +3,62 @@ pragma solidity ^0.8.23;
import "../../Base.sol";
import "../../../core/25-handler/IBCHandler.sol";

// Protocol specific packet
struct PingPongPacket {
bool ping;
uint64 counterpartyTimeoutRevisionNumber;
uint64 counterpartyTimeoutRevisionHeight;
uint64 counterpartyTimeout;
}

library PingPongPacketLib {
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.counterpartyTimeoutRevisionNumber,
packet.counterpartyTimeoutRevisionHeight
);
return abi.encode(packet.ping, packet.counterpartyTimeout);
}

function decode(
bytes memory packet
) internal pure returns (PingPongPacket memory) {
(
bool ping,
uint64 counterpartyTimeoutRevisionNumber,
uint64 counterpartyTimeoutRevisionHeight
) = abi.decode(packet, (bool, uint64, uint64));
(bool ping, uint64 counterpartyTimeout) = abi.decode(
packet,
(bool, uint64)
);
return
PingPongPacket({
ping: ping,
counterpartyTimeoutRevisionNumber: counterpartyTimeoutRevisionNumber,
counterpartyTimeoutRevisionHeight: counterpartyTimeoutRevisionHeight
counterpartyTimeout: counterpartyTimeout
});
}
}

contract PingPong is IBCAppBase {
using PingPongPacketLib for PingPongPacket;
using PingPongLib for *;

IBCHandler private ibcHandler;
string private portId;
string private channelId;
uint64 private revisionNumber;
uint64 private numberOfBlockBeforePongTimeout;

event Ring(bool ping);
event TimedOut();
uint64 private timeout;

constructor(
IBCHandler _ibcHandler,
uint64 _revisionNumber,
uint64 _numberOfBlockBeforePongTimeout
uint64 _timeout
) {
ibcHandler = _ibcHandler;
revisionNumber = _revisionNumber;
numberOfBlockBeforePongTimeout = _numberOfBlockBeforePongTimeout;
timeout = _timeout;
}

function ibcAddress() public view virtual override returns (address) {
Expand All @@ -66,21 +67,22 @@ contract PingPong is IBCAppBase {

function initiate(
PingPongPacket memory packet,
uint64 counterpartyTimeoutRevisionNumber,
uint64 counterpartyTimeoutRevisionHeight
uint64 localTimeout
) public {
require(
bytes(channelId).length != 0,
"pingpong: channel must be opened"
);
if (bytes(channelId).length == 0) {
revert PingPongLib.ErrNoChannel();
}
ibcHandler.sendPacket(
portId,
channelId,
// No height timeout
IbcCoreClientV1Height.Data({
revision_number: counterpartyTimeoutRevisionNumber,
revision_height: counterpartyTimeoutRevisionHeight
revision_number: 0,
revision_height: 0
}),
0,
// Timestamp timeout
localTimeout,
// Raw protocol packet
packet.encode()
);
}
Expand All @@ -89,36 +91,50 @@ contract PingPong is IBCAppBase {
IbcCoreChannelV1Packet.Data calldata packet,
address relayer
) external virtual override onlyIBC returns (bytes memory acknowledgement) {
PingPongPacket memory pp = PingPongPacketLib.decode(packet.data);
emit Ring(pp.ping);
uint64 counterpartyTimeoutRevisionNumber = pp
.counterpartyTimeoutRevisionNumber;
uint64 counterpartyTimeoutRevisionHeight = pp
.counterpartyTimeoutRevisionHeight;
PingPongPacket memory pp = PingPongLib.decode(packet.data);

emit PingPongLib.Ring(pp.ping);

uint64 localTimeout = pp.counterpartyTimeout;

pp.ping = !pp.ping;
pp.counterpartyTimeoutRevisionNumber = revisionNumber;
pp.counterpartyTimeoutRevisionHeight =
uint64(block.number) +
numberOfBlockBeforePongTimeout;
initiate(
pp,
counterpartyTimeoutRevisionNumber,
counterpartyTimeoutRevisionHeight
);
return hex"01";
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 {}
) 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 {
emit TimedOut();
/*
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(
Expand All @@ -129,10 +145,10 @@ contract PingPong is IBCAppBase {
IbcCoreChannelV1Counterparty.Data calldata,
string calldata
) external virtual override onlyIBC {
require(
bytes(channelId).length == 0,
"pingpong: only one channel can be opened"
);
// This protocol is only accepting a single counterparty.
if (bytes(channelId).length != 0) {
revert PingPongLib.ErrOnlyOneChannel();
}
}

function onChanOpenTry(
Expand All @@ -144,10 +160,10 @@ contract PingPong is IBCAppBase {
string calldata,
string calldata
) external virtual override onlyIBC {
require(
bytes(channelId).length == 0,
"pingpong: only one channel can be opened"
);
// Symmetric to onChanOpenInit
if (bytes(channelId).length != 0) {
revert PingPongLib.ErrOnlyOneChannel();
}
}

function onChanOpenAck(
Expand All @@ -156,6 +172,7 @@ contract PingPong is IBCAppBase {
string calldata _counterpartyChannelId,
string calldata _counterpartyVersion
) external virtual override onlyIBC {
// Store the port/channel needed to send packets.
portId = _portId;
channelId = _channelId;
}
Expand All @@ -164,6 +181,7 @@ contract PingPong is IBCAppBase {
string calldata _portId,
string calldata _channelId
) external virtual override onlyIBC {
// Symmetric to onChanOpenAck
portId = _portId;
channelId = _channelId;
}
Expand All @@ -172,13 +190,15 @@ contract PingPong is IBCAppBase {
string calldata _portId,
string calldata _channelId
) external virtual override onlyIBC {
revert("This game is infinite");
// 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 {
revert("This game is infinite");
// Symmetric to onChanCloseInit
revert PingPongLib.ErrInfiniteGame();
}
}
27 changes: 1 addition & 26 deletions evm/contracts/core/05-port/IIBCModule.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,8 @@ import "../../proto/ibc/core/channel/v1/channel.sol";

// IIBCModule defines an interface that implements all the callbacks
// that modules must define as specified in ICS-26
// https://github.com/cosmos/ibc/blob/2921c5cec7b18e4ef77677e16a6b693051ae3b35/spec/core/ics-026-routing-module/README.md
interface IIBCModule {
// onChanOpenInit will verify that the relayer-chosen parameters
// are valid and perform any custom INIT logic.
// It may return an error if the chosen parameters are invalid
// in which case the handshake is aborted.
// If the provided version string is non-empty, OnChanOpenInit should return
// the version string if valid or an error if the provided version is invalid.
// If the version string is empty, OnChanOpenInit is expected to
// return a default version string representing the version(s) it supports.
// If there is no default version string for the application,
// it should return an error if provided version is empty string.
function onChanOpenInit(
IbcCoreChannelV1GlobalEnums.Order,
string[] calldata connectionHops,
Expand All @@ -24,14 +15,6 @@ interface IIBCModule {
string calldata version
) external;

// OnChanOpenTry will verify the relayer-chosen parameters along with the
// counterparty-chosen version string and perform custom TRY logic.
// If the relayer-chosen parameters are invalid, the callback must return
// an error to abort the handshake. If the counterparty-chosen version is not
// compatible with this modules supported versions, the callback must return
// an error to abort the handshake. If the versions are compatible, the try callback
// must select the final version string and return it to core IBC.
// OnChanOpenTry may also perform custom initialization logic
function onChanOpenTry(
IbcCoreChannelV1GlobalEnums.Order,
string[] calldata connectionHops,
Expand All @@ -42,16 +25,13 @@ interface IIBCModule {
string calldata counterpartyVersion
) external;

// OnChanOpenAck will error if the counterparty selected version string
// is invalid to abort the handshake. It may also perform custom ACK logic.
function onChanOpenAck(
string calldata portId,
string calldata channelId,
string calldata counterpartyChannelId,
string calldata counterpartyVersion
) external;

// OnChanOpenConfirm will perform custom CONFIRM logic and may error to abort the handshake.
function onChanOpenConfirm(
string calldata portId,
string calldata channelId
Expand All @@ -67,11 +47,6 @@ interface IIBCModule {
string calldata channelId
) external;

// OnRecvPacket must return an acknowledgement that implements the Acknowledgement interface.
// In the case of an asynchronous acknowledgement, nil should be returned.
// If the acknowledgement returned is successful, the state changes on callback are written,
// otherwise the application state changes are discarded. In either case the packet is received
// and the acknowledgement is written (in synchronous cases).
function onRecvPacket(
IbcCoreChannelV1Packet.Data calldata,
address relayer
Expand Down
1 change: 1 addition & 0 deletions evm/evm.nix
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,7 @@
text = ''
${ensureAtRepositoryRoot}
FOUNDRY_CONFIG="${foundryConfig}/foundry.toml" \
FOUNDRY_SRC="evm/contracts" \
FOUNDRY_PROFILE="test" \
FOUNDRY_TEST="evm/tests/src" \
forge test -vvv --gas-report
Expand Down
6 changes: 6 additions & 0 deletions site/astro.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,12 @@ export default defineConfig({
},
],
},
{
label: "Integration",
autogenerate: {
directory: "/docs/integration",
},
},
{
label: "Demos",
autogenerate: {
Expand Down
5 changes: 5 additions & 0 deletions site/site.nix
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@
site = pkgs.buildNpmPackage {
npmDepsHash = "sha256-MnKC0nRMAa4tWVYXYhvfpb73BJ5GXEbATQtqzmQxI4E=";
src = ./.;
srcs = [
./.
./../evm/.
];
sourceRoot = "site";
pname = "site";
version = "0.0.1";
PUPPETEER_SKIP_DOWNLOAD = true;
Expand Down
24 changes: 24 additions & 0 deletions site/src/components/EmbedCodeSnippet.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
---
import { Code } from 'astro:components'
import { getFileContent } from '#/lib/utilities.ts'
interface Props {
filepath: string
filename: string
language: Parameters<typeof Code>[0]['lang']
}
/**
* TODO: add tab support
*/
const { filepath, filename, language } = Astro.props
const fileContent = await getFileContent({ filepath })
---

<Code
lang={language}
code={fileContent}
theme={'css-variables'}
class:list={['expressive-code']}
/>
Loading

0 comments on commit b00ddc3

Please sign in to comment.