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

feat(docs): update demo #1205

Merged
merged 9 commits into from
Jan 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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-4YxL4ubvVgwi+30Q7Cd9xJGFxFBlylLrE0eTgy7jSi4=";
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
Loading