-
Notifications
You must be signed in to change notification settings - Fork 72
Description
Issue description
The crate defines ExternalNetworkId and ExternalCoin as logically associated types, but it does not enforce their consistency when constructing or processing external transfer instructions. For example, ExternalNetworkId::Ethereum is implicitly expected to be used with ExternalCoin::Ether, but no runtime check ensures that a coin actually belongs to the declared network. As a result, a transfer instruction may claim to send Ether on the Bitcoin network or XMR on Ethereum, violating domain constraints.
src/coin.rs
pub enum ExternalCoin {
Btc,
Ether,
Dai,
Xmr,
}
impl ExternalCoin {
pub fn network(&self) -> ExternalNetworkId {
match self {
ExternalCoin::Bitcoin => ExternalNetworkId::Bitcoin,
ExternalCoin::Ether | ExternalCoin::Dai => ExternalNetworkId::Ethereum,
ExternalCoin::Monero => ExternalNetworkId::Monero,
}
}
}
src/network_id.rs:
pub enum ExternalNetworkId {
Bitcoin,
Ethereum,
Monero,
}
impl ExternalNetworkId {
pub fn coins(&self) -> &'static [ExternalCoin] {
match self {
ExternalNetworkId::Bitcoin => &[ExternalCoin::Btc],
ExternalNetworkId::Ethereum => &[ExternalCoin::Ether, ExternalCoin::Dai],
ExternalNetworkId::Monero => &[ExternalCoin::Xmr],
}
}
}
However, beyond these mappings, the code does not enforce validity checks at runtime. In particular, there is no dedicated function like validate_coin_for_network(coin, network) that would reject mismatched pairs. The two mapping functions above simply provide the canonical pairing but do not guard other code paths against misuse.
We found no validity checks in the functionality that processes ExternalNetworkId and/or ExternalCoin.
This structural gap could allow semantically incorrect or misleading messages to propagate if not being caught at higher level APIs outside of serai-primitives.
However, we expect the higher level APIs to enforce this logic. As we are currently not auditing these we still felt we should raise the issue.
Risk
An attacker could potentially craft a transfer instruction that combines mismatched network and coin identifiers. For example, they could declare a Bitcoin network withdrawal of Ether, potentially tricking off-chain relayers or bridge infrastructure into executing incorrect asset movements. This mismatch could lead to loss of funds, misrouting of value, or security violations in external systems that rely on this data. If no higher-level enforcement exists, these inconsistencies can silently propagate, violating assumptions across modules or between on-chain and off-chain logic.
Mitigation
Introduce runtime checks wherever ExternalCoin and ExternalNetworkId appear together to ensure the coin belongs to the declared network. Refactor the type system to encapsulate coin-network relationships more tightly, possibly by introducing a struct that pairs a network with its valid coins and disallows invalid constructions at the type level. Validate incoming instructions during decoding or execution to reject mismatched combinations.