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.
183 lines
5.6 KiB
183 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";
|
||
|
// ============ Internal Imports ============
|
||
|
import {PingPongMessage} from "./PingPongMessage.sol";
|
||
|
import {Router} from "../Router.sol";
|
||
|
import {XAppConnectionClient} from "../XAppConnectionClient.sol";
|
||
|
|
||
|
/*
|
||
|
============ PingPong xApp ============
|
||
|
The PingPong xApp is capable of initiating PingPong "matches" between two chains.
|
||
|
A match consists of "volleys" sent back-and-forth between the two chains via Optics.
|
||
|
|
||
|
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.
|
||
|
*/
|
||
|
contract PingPongRouter is Router, XAppConnectionClient {
|
||
|
// ============ 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 ============
|
||
|
|
||
|
constructor(address _xAppConnectionManager)
|
||
|
XAppConnectionClient(_xAppConnectionManager)
|
||
|
{}
|
||
|
|
||
|
// ============ Handle message functions ============
|
||
|
|
||
|
/**
|
||
|
* @notice Handle "volleys" sent via Optics from other remote PingPong Routers
|
||
|
* @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
|
||
|
)
|
||
|
external
|
||
|
override
|
||
|
onlyReplica
|
||
|
onlyRemoteRouter(_origin, _sender)
|
||
|
returns (bytes memory)
|
||
|
{
|
||
|
bytes29 _msg = _message.ref(0);
|
||
|
|
||
|
if (_msg.isPing()) {
|
||
|
return _handlePing(_origin, _msg);
|
||
|
} else if (_msg.isPong()) {
|
||
|
return _handlePong(_origin, _msg);
|
||
|
}
|
||
|
|
||
|
// if _message doesn't match any valid actions, revert
|
||
|
require(false, "!valid action");
|
||
|
return hex"";
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @notice Handle a Ping volley
|
||
|
* @param _origin The domain that sent the volley
|
||
|
* @param _message The message in the form of raw bytes
|
||
|
*/
|
||
|
function _handlePing(uint32 _origin, bytes29 _message)
|
||
|
internal
|
||
|
returns (bytes memory)
|
||
|
{
|
||
|
bool _isPing = true;
|
||
|
return _handle(_origin, _isPing, _message);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @notice Handle a Pong volley
|
||
|
* @param _origin The domain that sent the volley
|
||
|
* @param _message The message in the form of raw bytes
|
||
|
*/
|
||
|
function _handlePong(uint32 _origin, bytes29 _message)
|
||
|
internal
|
||
|
returns (bytes memory)
|
||
|
{
|
||
|
bool _isPing = false;
|
||
|
return _handle(_origin, _isPing, _message);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @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
|
||
|
) internal returns (bytes memory) {
|
||
|
// 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);
|
||
|
|
||
|
return hex"";
|
||
|
}
|
||
|
|
||
|
// ============ 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
|
||
|
bytes32 _remoteRouterAddress = _mustHaveRemote(_destinationDomain);
|
||
|
|
||
|
// format the ping message
|
||
|
bytes memory _message =
|
||
|
_isPing
|
||
|
? PingPongMessage.formatPing(_match, _count)
|
||
|
: PingPongMessage.formatPong(_match, _count);
|
||
|
|
||
|
// send the message to the xApp Router
|
||
|
(_home()).enqueue(_destinationDomain, _remoteRouterAddress, _message);
|
||
|
|
||
|
// emit a Sent event
|
||
|
emit Sent(_destinationDomain, _match, _count, _isPing);
|
||
|
}
|
||
|
}
|