mirror of
https://github.com/serai-dex/serai.git
synced 2024-12-24 12:39:33 +00:00
63521f6a96
* start Router contract * use calldata for function args * var name changes * start testing router contract * test with and without abi.encode * cleanup * why tf isn't tests/utils working * cleanup tests * remove unused files * wip * fix router contract and tests, add set/update public keys funcs * impl some Froms * make execute non-reentrant * cleanup * update Router to use ReentrancyGuard * update contract to use errors, use bitfield in Executed event, minor other fixes * wip * fix build issues from merge, tests ok * Router.sol cleanup * cleanup, uncomment stuff * bump ethers.rs version to latest * make contract functions take generic middleware * update build script to assert no compiler errors * hardcode pubkey parity into contract, update tests * Polish coins/ethereum in various ways --------- Co-authored-by: Luke Parker <lukeparker5132@gmail.com>
90 lines
2.9 KiB
Solidity
90 lines
2.9 KiB
Solidity
// SPDX-License-Identifier: AGPLv3
|
|
pragma solidity ^0.8.0;
|
|
|
|
import "./Schnorr.sol";
|
|
|
|
contract Router is Schnorr {
|
|
// Contract initializer
|
|
// TODO: Replace with a MuSig of the genesis validators
|
|
address public initializer;
|
|
|
|
// Nonce is incremented for each batch of transactions executed
|
|
uint256 public nonce;
|
|
|
|
// fixed parity for the public keys used in this contract
|
|
uint8 constant public KEY_PARITY = 27;
|
|
|
|
// current public key's x-coordinate
|
|
// note: this key must always use the fixed parity defined above
|
|
bytes32 public seraiKey;
|
|
|
|
struct OutInstruction {
|
|
address to;
|
|
uint256 value;
|
|
bytes data;
|
|
}
|
|
|
|
struct Signature {
|
|
bytes32 c;
|
|
bytes32 s;
|
|
}
|
|
|
|
// success is a uint256 representing a bitfield of transaction successes
|
|
event Executed(uint256 nonce, bytes32 batch, uint256 success);
|
|
|
|
// error types
|
|
error NotInitializer();
|
|
error AlreadyInitialized();
|
|
error InvalidKey();
|
|
error TooManyTransactions();
|
|
|
|
constructor() {
|
|
initializer = msg.sender;
|
|
}
|
|
|
|
// initSeraiKey can be called by the contract initializer to set the first
|
|
// public key, only if the public key has yet to be set.
|
|
function initSeraiKey(bytes32 _seraiKey) external {
|
|
if (msg.sender != initializer) revert NotInitializer();
|
|
if (seraiKey != 0) revert AlreadyInitialized();
|
|
if (_seraiKey == bytes32(0)) revert InvalidKey();
|
|
seraiKey = _seraiKey;
|
|
}
|
|
|
|
// updateSeraiKey validates the given Schnorr signature against the current public key,
|
|
// and if successful, updates the contract's public key to the given one.
|
|
function updateSeraiKey(
|
|
bytes32 _seraiKey,
|
|
Signature memory sig
|
|
) public {
|
|
if (_seraiKey == bytes32(0)) revert InvalidKey();
|
|
bytes32 message = keccak256(abi.encodePacked("updateSeraiKey", _seraiKey));
|
|
if (!verify(KEY_PARITY, seraiKey, message, sig.c, sig.s)) revert InvalidSignature();
|
|
seraiKey = _seraiKey;
|
|
}
|
|
|
|
// execute accepts a list of transactions to execute as well as a Schnorr signature.
|
|
// if signature verification passes, the given transactions are executed.
|
|
// if signature verification fails, this function will revert.
|
|
function execute(
|
|
OutInstruction[] calldata transactions,
|
|
Signature memory sig
|
|
) public {
|
|
if (transactions.length > 256) revert TooManyTransactions();
|
|
|
|
bytes32 message = keccak256(abi.encode("execute", nonce, transactions));
|
|
// This prevents re-entrancy from causing double spends yet does allow
|
|
// out-of-order execution via re-entrancy
|
|
nonce++;
|
|
if (!verify(KEY_PARITY, seraiKey, message, sig.c, sig.s)) revert InvalidSignature();
|
|
|
|
uint256 successes;
|
|
for(uint256 i = 0; i < transactions.length; i++) {
|
|
(bool success, ) = transactions[i].to.call{value: transactions[i].value, gas: 200_000}(transactions[i].data);
|
|
assembly {
|
|
successes := or(successes, shl(i, success))
|
|
}
|
|
}
|
|
emit Executed(nonce, message, successes);
|
|
}
|
|
}
|