You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
166 lines
5.6 KiB
166 lines
5.6 KiB
4 years ago
|
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||
|
pragma solidity >=0.6.11;
|
||
|
|
||
|
// ============ External Imports ============
|
||
|
import {TypedMemView} from "@summa-tx/memview-sol/contracts/TypedMemView.sol";
|
||
3 years ago
|
import {Router} from "@abacus-network/core/contracts/router/Router.sol";
|
||
|
import {XAppConnectionClient} from "@abacus-network/core/contracts/router/XAppConnectionClient.sol";
|
||
4 years ago
|
// ============ Internal Imports ============
|
||
|
import {PingPongMessage} from "./PingPongMessage.sol";
|
||
|
|
||
|
/*
|
||
|
============ PingPong xApp ============
|
||
|
The PingPong xApp is capable of initiating PingPong "matches" between two chains.
|
||
3 years ago
|
A match consists of "volleys" sent back-and-forth between the two chains via Abacus.
|
||
4 years ago
|
|
||
|
The first volley in a match is always a Ping volley.
|
||
|
When a Router receives a Ping volley, it returns a Pong.
|
||
|
When a Router receives a Pong volley, it returns a Ping.
|
||
|
|
||
|
The Routers keep track of the number of volleys in a given match,
|
||
|
and emit events for each Sent and Received volley so that spectators can watch.
|
||
|
*/
|
||
3 years ago
|
contract PingPongRouter is Router {
|
||
4 years ago
|
// ============ Libraries ============
|
||
|
|
||
|
using TypedMemView for bytes;
|
||
|
using TypedMemView for bytes29;
|
||
|
using PingPongMessage for bytes29;
|
||
|
|
||
|
// ============ Mutable State ============
|
||
|
uint32 nextMatch;
|
||
|
|
||
|
// ============ Events ============
|
||
|
|
||
|
event Received(
|
||
|
uint32 indexed domain,
|
||
|
uint32 indexed matchId,
|
||
|
uint256 count,
|
||
|
bool isPing
|
||
|
);
|
||
|
event Sent(
|
||
|
uint32 indexed domain,
|
||
|
uint32 indexed matchId,
|
||
|
uint256 count,
|
||
|
bool isPing
|
||
|
);
|
||
|
|
||
|
// ============ Constructor ============
|
||
3 years ago
|
constructor(address _xAppConnectionManager) {
|
||
3 years ago
|
require(false, "example xApp, do not deploy");
|
||
|
|
||
3 years ago
|
__XAppConnectionClient_initialize(_xAppConnectionManager);
|
||
3 years ago
|
}
|
||
4 years ago
|
|
||
|
// ============ Handle message functions ============
|
||
|
|
||
|
/**
|
||
3 years ago
|
* @notice Handle "volleys" sent via Abacus from other remote PingPong Routers
|
||
4 years ago
|
* @param _origin The domain the message is coming from
|
||
|
* @param _sender The address the message is coming from
|
||
|
* @param _message The message in the form of raw bytes
|
||
|
*/
|
||
|
function handle(
|
||
|
uint32 _origin,
|
||
|
bytes32 _sender,
|
||
|
bytes memory _message
|
||
3 years ago
|
) external override onlyInbox onlyRemoteRouter(_origin, _sender) {
|
||
4 years ago
|
bytes29 _msg = _message.ref(0);
|
||
|
if (_msg.isPing()) {
|
||
3 years ago
|
_handlePing(_origin, _msg);
|
||
4 years ago
|
} else if (_msg.isPong()) {
|
||
3 years ago
|
_handlePong(_origin, _msg);
|
||
|
} else {
|
||
|
// if _message doesn't match any valid actions, revert
|
||
|
require(false, "!valid action");
|
||
4 years ago
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @notice Handle a Ping volley
|
||
|
* @param _origin The domain that sent the volley
|
||
|
* @param _message The message in the form of raw bytes
|
||
|
*/
|
||
3 years ago
|
function _handlePing(uint32 _origin, bytes29 _message) internal {
|
||
4 years ago
|
bool _isPing = true;
|
||
3 years ago
|
_handle(_origin, _isPing, _message);
|
||
4 years ago
|
}
|
||
|
|
||
|
/**
|
||
|
* @notice Handle a Pong volley
|
||
|
* @param _origin The domain that sent the volley
|
||
|
* @param _message The message in the form of raw bytes
|
||
|
*/
|
||
3 years ago
|
function _handlePong(uint32 _origin, bytes29 _message) internal {
|
||
4 years ago
|
bool _isPing = false;
|
||
3 years ago
|
_handle(_origin, _isPing, _message);
|
||
4 years ago
|
}
|
||
|
|
||
|
/**
|
||
|
* @notice Upon receiving a volley, emit an event, increment the count and return a the opposite volley
|
||
|
* @param _origin The domain that sent the volley
|
||
|
* @param _isPing True if the volley received is a Ping, false if it is a Pong
|
||
|
* @param _message The message in the form of raw bytes
|
||
|
*/
|
||
|
function _handle(
|
||
|
uint32 _origin,
|
||
|
bool _isPing,
|
||
|
bytes29 _message
|
||
3 years ago
|
) internal {
|
||
4 years ago
|
// get the volley count for this game
|
||
|
uint256 _count = _message.count();
|
||
|
uint32 _match = _message.matchId();
|
||
|
// emit a Received event
|
||
|
emit Received(_origin, _match, _count, _isPing);
|
||
|
// send the opposite volley back
|
||
|
_send(_origin, !_isPing, _match, _count + 1);
|
||
|
}
|
||
|
|
||
|
// ============ Dispatch message functions ============
|
||
|
|
||
|
/**
|
||
|
* @notice Initiate a PingPong match with the destination domain
|
||
|
* by sending the first Ping volley.
|
||
|
* @param _destinationDomain The domain to initiate the match with
|
||
|
*/
|
||
|
function initiatePingPongMatch(uint32 _destinationDomain) external {
|
||
|
// the PingPong match always begins with a Ping volley
|
||
|
bool _isPing = true;
|
||
|
// increment match counter
|
||
|
uint32 _match = nextMatch;
|
||
|
nextMatch = _match + 1;
|
||
|
// send the first volley to the destination domain
|
||
|
_send(_destinationDomain, _isPing, _match, 0);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @notice Send a Ping or Pong volley to the destination domain
|
||
|
* @param _destinationDomain The domain to send the volley to
|
||
|
* @param _isPing True if the volley to send is a Ping, false if it is a Pong
|
||
|
* @param _count The number of volleys in this match
|
||
|
*/
|
||
|
function _send(
|
||
|
uint32 _destinationDomain,
|
||
|
bool _isPing,
|
||
|
uint32 _match,
|
||
|
uint256 _count
|
||
|
) internal {
|
||
|
// get the xApp Router at the destinationDomain
|
||
3 years ago
|
bytes32 _remoteRouterAddress = _mustHaveRemoteRouter(
|
||
|
_destinationDomain
|
||
|
);
|
||
4 years ago
|
// format the ping message
|
||
3 years ago
|
bytes memory _message = _isPing
|
||
|
? PingPongMessage.formatPing(_match, _count)
|
||
|
: PingPongMessage.formatPong(_match, _count);
|
||
4 years ago
|
// send the message to the xApp Router
|
||
3 years ago
|
(_outbox()).dispatch(
|
||
|
_destinationDomain,
|
||
|
_remoteRouterAddress,
|
||
|
_message
|
||
|
);
|
||
4 years ago
|
// emit a Sent event
|
||
|
emit Sent(_destinationDomain, _match, _count, _isPing);
|
||
|
}
|
||
|
}
|