Work on testing the Router

Completes the `Executed` enum in the router. Adds an `Escape` struct. Both are
needed for testing purposes.

Documents the gas constants in intent and reasoning.

Adds modernized tests around key rotation and the escape hatch.

Also updates the rest of the codebase which had accumulated errors.
This commit is contained in:
Luke Parker
2025-01-23 01:59:24 -05:00
parent 6508957cbc
commit 669b8b776b
13 changed files with 765 additions and 355 deletions

View File

@@ -17,8 +17,8 @@ use crate::{output::OutputId, machine::ClonableTransctionMachine};
#[derive(Clone, PartialEq, Debug)]
pub(crate) enum Action {
SetKey { nonce: u64, key: PublicKey },
Batch { nonce: u64, coin: Coin, fee: U256, outs: Vec<(Address, U256)> },
SetKey { chain_id: U256, nonce: u64, key: PublicKey },
Batch { chain_id: U256, nonce: u64, coin: Coin, fee: U256, outs: Vec<(Address, U256)> },
}
#[derive(Clone, PartialEq, Eq, Debug)]
@@ -33,17 +33,25 @@ impl Action {
pub(crate) fn message(&self) -> Vec<u8> {
match self {
Action::SetKey { nonce, key } => Router::update_serai_key_message(*nonce, key),
Action::Batch { nonce, coin, fee, outs } => {
Router::execute_message(*nonce, *coin, *fee, OutInstructions::from(outs.as_ref()))
Action::SetKey { chain_id, nonce, key } => {
Router::update_serai_key_message(*chain_id, *nonce, key)
}
Action::Batch { chain_id, nonce, coin, fee, outs } => Router::execute_message(
*chain_id,
*nonce,
*coin,
*fee,
OutInstructions::from(outs.as_ref()),
),
}
}
pub(crate) fn eventuality(&self) -> Eventuality {
Eventuality(match self {
Self::SetKey { nonce, key } => Executed::SetKey { nonce: *nonce, key: key.eth_repr() },
Self::Batch { nonce, .. } => {
Self::SetKey { chain_id: _, nonce, key } => {
Executed::NextSeraiKeySet { nonce: *nonce, key: key.eth_repr() }
}
Self::Batch { chain_id: _, nonce, .. } => {
Executed::Batch { nonce: *nonce, message_hash: keccak256(self.message()) }
}
})
@@ -77,6 +85,10 @@ impl SignableTransaction for Action {
Err(io::Error::other("unrecognized Action type"))?;
}
let mut chain_id = [0; 32];
reader.read_exact(&mut chain_id)?;
let chain_id = U256::from_be_bytes(chain_id);
let mut nonce = [0; 8];
reader.read_exact(&mut nonce)?;
let nonce = u64::from_le_bytes(nonce);
@@ -88,10 +100,10 @@ impl SignableTransaction for Action {
let key =
PublicKey::from_eth_repr(key).ok_or_else(|| io::Error::other("invalid key in Action"))?;
Action::SetKey { nonce, key }
Action::SetKey { chain_id, nonce, key }
}
1 => {
let coin = Coin::read(reader)?;
let coin = borsh::from_reader(reader)?;
let mut fee = [0; 32];
reader.read_exact(&mut fee)?;
@@ -111,22 +123,24 @@ impl SignableTransaction for Action {
outs.push((address, amount));
}
Action::Batch { nonce, coin, fee, outs }
Action::Batch { chain_id, nonce, coin, fee, outs }
}
_ => unreachable!(),
})
}
fn write(&self, writer: &mut impl io::Write) -> io::Result<()> {
match self {
Self::SetKey { nonce, key } => {
Self::SetKey { chain_id, nonce, key } => {
writer.write_all(&[0])?;
writer.write_all(&chain_id.to_be_bytes::<32>())?;
writer.write_all(&nonce.to_le_bytes())?;
writer.write_all(&key.eth_repr())
}
Self::Batch { nonce, coin, fee, outs } => {
Self::Batch { chain_id, nonce, coin, fee, outs } => {
writer.write_all(&[1])?;
writer.write_all(&chain_id.to_be_bytes::<32>())?;
writer.write_all(&nonce.to_le_bytes())?;
coin.write(writer)?;
borsh::BorshSerialize::serialize(coin, writer)?;
writer.write_all(&fee.as_le_bytes())?;
writer.write_all(&u32::try_from(outs.len()).unwrap().to_le_bytes())?;
for (address, amount) in outs {
@@ -167,9 +181,9 @@ impl primitives::Eventuality for Eventuality {
}
fn read(reader: &mut impl io::Read) -> io::Result<Self> {
Executed::read(reader).map(Self)
Ok(Self(borsh::from_reader(reader)?))
}
fn write(&self, writer: &mut impl io::Write) -> io::Result<()> {
self.0.write(writer)
borsh::BorshSerialize::serialize(&self.0, writer)
}
}