mirror of
https://github.com/serai-dex/serai.git
synced 2024-12-25 13:09:30 +00:00
Add selector collisions to Router to make it IRouter compatible
This commit is contained in:
parent
8de42cc2d4
commit
2f5c0c68d0
5 changed files with 65 additions and 36 deletions
|
@ -20,7 +20,9 @@ workspace = true
|
||||||
group = { version = "0.13", default-features = false }
|
group = { version = "0.13", default-features = false }
|
||||||
|
|
||||||
alloy-core = { version = "0.8", default-features = false }
|
alloy-core = { version = "0.8", default-features = false }
|
||||||
|
|
||||||
alloy-sol-types = { version = "0.8", default-features = false }
|
alloy-sol-types = { version = "0.8", default-features = false }
|
||||||
|
alloy-sol-macro = { version = "0.8", default-features = false }
|
||||||
|
|
||||||
alloy-consensus = { version = "0.3", default-features = false }
|
alloy-consensus = { version = "0.3", default-features = false }
|
||||||
|
|
||||||
|
|
|
@ -33,7 +33,7 @@ fn main() {
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
// This cannot be handled with the sol! macro. The Solidity requires an import
|
// This cannot be handled with the sol! macro. The Router requires an import
|
||||||
// https://github.com/alloy-rs/core/issues/602
|
// https://github.com/alloy-rs/core/issues/602
|
||||||
sol(
|
sol(
|
||||||
&[
|
&[
|
||||||
|
|
|
@ -171,10 +171,14 @@ contract Router {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @notice Update the key representing Serai's Ethereum validators
|
/// @notice Update the key representing Serai's Ethereum validators
|
||||||
/// @dev This assumes the key is correct. No checks on it are performed
|
/**
|
||||||
|
* @dev This assumes the key is correct. No checks on it are performed.
|
||||||
|
*
|
||||||
|
* The hex bytes are to cause a collision with `IRouter.updateSeraiKey`.
|
||||||
|
*/
|
||||||
// @param signature The signature by the current key authorizing this update
|
// @param signature The signature by the current key authorizing this update
|
||||||
// @param newSeraiKey The key to update to
|
// @param newSeraiKey The key to update to
|
||||||
function updateSeraiKey() external {
|
function updateSeraiKey5A8542A2() external {
|
||||||
(uint256 nonceUsed, bytes memory args,) = verifySignature();
|
(uint256 nonceUsed, bytes memory args,) = verifySignature();
|
||||||
/*
|
/*
|
||||||
We could replace this with a length check (if we don't simply assume the calldata is valid as
|
We could replace this with a length check (if we don't simply assume the calldata is valid as
|
||||||
|
@ -341,7 +345,9 @@ contract Router {
|
||||||
/// @notice Execute a batch of `OutInstruction`s
|
/// @notice Execute a batch of `OutInstruction`s
|
||||||
/**
|
/**
|
||||||
* @dev All `OutInstruction`s in a batch are only for a single coin to simplify handling of the
|
* @dev All `OutInstruction`s in a batch are only for a single coin to simplify handling of the
|
||||||
* fee
|
* fee.
|
||||||
|
*
|
||||||
|
* The hex bytes are to cause a function selector collision with `IRouter.execute`.
|
||||||
*/
|
*/
|
||||||
// @param signature The signature by the current key for Serai's Ethereum validators
|
// @param signature The signature by the current key for Serai's Ethereum validators
|
||||||
// @param coin The coin all of these `OutInstruction`s are for
|
// @param coin The coin all of these `OutInstruction`s are for
|
||||||
|
@ -349,7 +355,7 @@ contract Router {
|
||||||
// @param outs The `OutInstruction`s to act on
|
// @param outs The `OutInstruction`s to act on
|
||||||
// Each individual call is explicitly metered to ensure there isn't a DoS here
|
// Each individual call is explicitly metered to ensure there isn't a DoS here
|
||||||
// slither-disable-next-line calls-loop
|
// slither-disable-next-line calls-loop
|
||||||
function execute() external {
|
function execute4DE42904() external {
|
||||||
(uint256 nonceUsed, bytes memory args, bytes32 message) = verifySignature();
|
(uint256 nonceUsed, bytes memory args, bytes32 message) = verifySignature();
|
||||||
(,, address coin, uint256 fee, IRouter.OutInstruction[] memory outs) =
|
(,, address coin, uint256 fee, IRouter.OutInstruction[] memory outs) =
|
||||||
abi.decode(args, (bytes32, bytes32, address, uint256, IRouter.OutInstruction[]));
|
abi.decode(args, (bytes32, bytes32, address, uint256, IRouter.OutInstruction[]));
|
||||||
|
@ -418,10 +424,14 @@ contract Router {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @notice Escapes to a new smart contract
|
/// @notice Escapes to a new smart contract
|
||||||
/// @dev This should be used upon an invariant being reached or new functionality being needed
|
/**
|
||||||
|
* @dev This should be used upon an invariant being reached or new functionality being needed.
|
||||||
|
*
|
||||||
|
* The hex bytes are to cause a collision with `IRouter.updateSeraiKey`.
|
||||||
|
*/
|
||||||
// @param signature The signature by the current key for Serai's Ethereum validators
|
// @param signature The signature by the current key for Serai's Ethereum validators
|
||||||
// @param escapeTo The address to escape to
|
// @param escapeTo The address to escape to
|
||||||
function escapeHatch() external {
|
function escapeHatchDCDD91CC() external {
|
||||||
// Verify the signature
|
// Verify the signature
|
||||||
(, bytes memory args,) = verifySignature();
|
(, bytes memory args,) = verifySignature();
|
||||||
|
|
||||||
|
|
|
@ -28,15 +28,23 @@ use serai_client::networks::ethereum::Address as SeraiAddress;
|
||||||
#[expect(clippy::all)]
|
#[expect(clippy::all)]
|
||||||
#[expect(clippy::ignored_unit_patterns)]
|
#[expect(clippy::ignored_unit_patterns)]
|
||||||
#[expect(clippy::redundant_closure_for_method_calls)]
|
#[expect(clippy::redundant_closure_for_method_calls)]
|
||||||
mod _abi {
|
pub mod _irouter_abi {
|
||||||
|
alloy_sol_macro::sol!("contracts/IRouter.sol");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rustfmt::skip]
|
||||||
|
#[expect(warnings)]
|
||||||
|
#[expect(needless_pass_by_value)]
|
||||||
|
#[expect(clippy::all)]
|
||||||
|
#[expect(clippy::ignored_unit_patterns)]
|
||||||
|
#[expect(clippy::redundant_closure_for_method_calls)]
|
||||||
|
mod _router_abi {
|
||||||
include!(concat!(env!("OUT_DIR"), "/serai-processor-ethereum-router/router.rs"));
|
include!(concat!(env!("OUT_DIR"), "/serai-processor-ethereum-router/router.rs"));
|
||||||
}
|
}
|
||||||
|
|
||||||
mod abi {
|
mod abi {
|
||||||
pub use super::_abi::IRouter::{
|
pub use super::_router_abi::IRouter::*;
|
||||||
Signature, DestinationType, CodeDestination, OutInstruction, SeraiKeyUpdated, InInstruction,
|
pub use super::_router_abi::Router::constructorCall;
|
||||||
Executed, EscapeHatch, Escaped,
|
|
||||||
};
|
|
||||||
pub use super::_abi::Router::*;
|
|
||||||
}
|
}
|
||||||
use abi::{
|
use abi::{
|
||||||
SeraiKeyUpdated as SeraiKeyUpdatedEvent, InInstruction as InInstructionEvent,
|
SeraiKeyUpdated as SeraiKeyUpdatedEvent, InInstruction as InInstructionEvent,
|
||||||
|
@ -315,23 +323,22 @@ impl Router {
|
||||||
|
|
||||||
/// Get the message to be signed in order to update the key for Serai.
|
/// Get the message to be signed in order to update the key for Serai.
|
||||||
pub fn update_serai_key_message(nonce: u64, key: &PublicKey) -> Vec<u8> {
|
pub fn update_serai_key_message(nonce: u64, key: &PublicKey) -> Vec<u8> {
|
||||||
[
|
abi::updateSeraiKeyCall::new((
|
||||||
abi::updateSeraiKeyCall::SELECTOR.as_slice(),
|
abi::Signature { c: U256::try_from(nonce).unwrap().into(), s: U256::ZERO.into() },
|
||||||
&(U256::try_from(nonce).unwrap(), U256::ZERO, key.eth_repr()).abi_encode_params(),
|
key.eth_repr().into(),
|
||||||
]
|
))
|
||||||
.concat()
|
.abi_encode()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Construct a transaction to update the key representing Serai.
|
/// Construct a transaction to update the key representing Serai.
|
||||||
pub fn update_serai_key(&self, public_key: &PublicKey, sig: &Signature) -> TxLegacy {
|
pub fn update_serai_key(&self, public_key: &PublicKey, sig: &Signature) -> TxLegacy {
|
||||||
TxLegacy {
|
TxLegacy {
|
||||||
to: TxKind::Call(self.1),
|
to: TxKind::Call(self.1),
|
||||||
input: [
|
input: abi::updateSeraiKeyCall::new((
|
||||||
abi::updateSeraiKeyCall::SELECTOR.as_slice(),
|
abi::Signature::from(sig),
|
||||||
&(abi::Signature::from(sig), public_key.eth_repr()).abi_encode_params(),
|
public_key.eth_repr().into(),
|
||||||
]
|
))
|
||||||
.concat()
|
.abi_encode().into(),
|
||||||
.into(),
|
|
||||||
gas_limit: 40_889 * 120 / 100,
|
gas_limit: 40_889 * 120 / 100,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
|
@ -339,12 +346,13 @@ impl Router {
|
||||||
|
|
||||||
/// Get the message to be signed in order to execute a series of `OutInstruction`s.
|
/// Get the message to be signed in order to execute a series of `OutInstruction`s.
|
||||||
pub fn execute_message(nonce: u64, coin: Coin, fee: U256, outs: OutInstructions) -> Vec<u8> {
|
pub fn execute_message(nonce: u64, coin: Coin, fee: U256, outs: OutInstructions) -> Vec<u8> {
|
||||||
[
|
abi::executeCall::new((
|
||||||
abi::executeCall::SELECTOR.as_slice(),
|
abi::Signature { c: U256::try_from(nonce).unwrap().into(), s: U256::ZERO.into() },
|
||||||
&(U256::try_from(nonce).unwrap(), U256::ZERO, coin.address(), fee, outs.0)
|
coin.address(),
|
||||||
.abi_encode_params(),
|
fee,
|
||||||
]
|
outs.0,
|
||||||
.concat()
|
))
|
||||||
|
.abi_encode()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Construct a transaction to execute a batch of `OutInstruction`s.
|
/// Construct a transaction to execute a batch of `OutInstruction`s.
|
||||||
|
@ -352,12 +360,9 @@ impl Router {
|
||||||
let outs_len = outs.0.len();
|
let outs_len = outs.0.len();
|
||||||
TxLegacy {
|
TxLegacy {
|
||||||
to: TxKind::Call(self.1),
|
to: TxKind::Call(self.1),
|
||||||
input: [
|
input: abi::executeCall::new((abi::Signature::from(sig), coin.address(), fee, outs.0))
|
||||||
abi::executeCall::SELECTOR.as_slice(),
|
.abi_encode()
|
||||||
&(abi::Signature::from(sig), coin.address(), fee, outs.0).abi_encode_params(),
|
.into(),
|
||||||
]
|
|
||||||
.concat()
|
|
||||||
.into(),
|
|
||||||
// TODO
|
// TODO
|
||||||
gas_limit: (45_501 + ((200_000 + 10_000) * u128::try_from(outs_len).unwrap())) * 120 / 100,
|
gas_limit: (45_501 + ((200_000 + 10_000) * u128::try_from(outs_len).unwrap())) * 120 / 100,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
|
|
|
@ -22,6 +22,18 @@ use ethereum_deployer::Deployer;
|
||||||
|
|
||||||
use crate::{Coin, OutInstructions, Router};
|
use crate::{Coin, OutInstructions, Router};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn selector_collisions() {
|
||||||
|
assert_eq!(
|
||||||
|
crate::_irouter_abi::IRouter::executeCall::SELECTOR,
|
||||||
|
crate::_router_abi::Router::execute4DE42904Call::SELECTOR
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
crate::_irouter_abi::IRouter::updateSeraiKeyCall::SELECTOR,
|
||||||
|
crate::_router_abi::Router::updateSeraiKey5A8542A2Call::SELECTOR
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn test_key() -> (Scalar, PublicKey) {
|
pub(crate) fn test_key() -> (Scalar, PublicKey) {
|
||||||
loop {
|
loop {
|
||||||
let key = Scalar::random(&mut OsRng);
|
let key = Scalar::random(&mut OsRng);
|
||||||
|
|
Loading…
Reference in a new issue