use crate::{
avalanche::vms::subnet_evm::warp::{AddressedPayload, SubnetEVMWarpMessage},
errors::*,
};
use avalanche_types::ids::{node::Id as NodeId, Id};
use serde::{Deserialize, Serialize};
use sha2::{Digest, Sha256};
pub const WARP_ANYCAST_ID: &str = "2wkBET2rRgE8pahuaczxKbmv7ciehqsne57F9gtzf1PVcUJEQG";
#[derive(Default, Clone, Debug, Serialize, Deserialize, PartialEq)]
pub struct WarpUnsignedMessage {
pub id: Id,
#[serde(rename = "networkID")]
pub network_id: u32,
#[serde(rename = "sourceChainID")]
pub source_chain_id: Id,
pub payload: WarpMessagePayload,
#[serde(skip)]
pub bytes: Vec<u8>,
}
impl WarpUnsignedMessage {
pub fn try_from_subnet_evm_log_data(bytes: &[u8]) -> Result<Self, AshError> {
let mut warp_message = Self::from(bytes);
let warp_payload = match warp_message.payload {
WarpMessagePayload::Unknown(bytes) => bytes,
_ => panic!("Warp message payload is not Unknown"),
};
warp_message.payload = WarpMessagePayload::SubnetEVMAddressedPayload(
AddressedPayload::try_from(warp_payload)?,
);
Ok(warp_message)
}
}
impl From<&[u8]> for WarpUnsignedMessage {
fn from(bytes: &[u8]) -> Self {
let network_id = u32::from_be_bytes(bytes[2..6].try_into().unwrap());
let source_chain_id = Id::from_slice(&bytes[6..38]);
let payload = WarpMessagePayload::Unknown(bytes[38..].to_vec());
let mut hasher = Sha256::new();
hasher.update(bytes);
Self {
id: Id::from_slice(&hasher.finalize()[..]),
bytes: bytes.to_vec(),
network_id,
source_chain_id,
payload,
}
}
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub enum WarpMessagePayload {
Unknown(Vec<u8>),
SubnetEVMAddressedPayload(AddressedPayload),
}
impl Default for WarpMessagePayload {
fn default() -> Self {
WarpMessagePayload::Unknown(vec![])
}
}
#[derive(Default, Clone, Debug, Serialize, Deserialize, PartialEq)]
pub enum WarpMessageStatus {
#[default]
Sent,
Signed(u16),
}
#[derive(Default, Clone, Debug, Serialize, Deserialize, PartialEq)]
pub enum VerifiedWarpMessage {
#[default]
Unknown,
SubnetEVM(SubnetEVMWarpMessage),
}
#[derive(Default, Clone, Debug, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct WarpMessage {
pub unsigned_message: WarpUnsignedMessage,
pub verified_message: VerifiedWarpMessage,
pub status: WarpMessageStatus,
pub node_signatures: Vec<WarpMessageNodeSignature>,
}
impl WarpMessage {
pub fn add_node_signature(&mut self, node_signature: WarpMessageNodeSignature) {
if !self
.node_signatures
.iter()
.any(|sig| sig.node_id == node_signature.node_id)
{
self.node_signatures.push(node_signature);
}
if !self.node_signatures.is_empty() {
self.status = WarpMessageStatus::Signed(self.node_signatures.len() as u16);
} else {
self.status = WarpMessageStatus::Sent;
}
}
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub struct WarpMessageNodeSignature {
pub node_id: NodeId,
#[serde(
serialize_with = "ethers::types::serialize_bytes",
deserialize_with = "hex::deserialize"
)]
pub signature: [u8; 96],
}
impl Default for WarpMessageNodeSignature {
fn default() -> Self {
Self {
node_id: NodeId::default(),
signature: [0; 96],
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use ethers::types::{Address, Bytes, H256};
use std::str::FromStr;
const WARP_MESSAGE_HEX: &str = "00000000303976dccb39c21a43aad4ffa98d4dd86a9ca29f5038a13b87658ef856bc161dbb470000005e0000000000008db97c7cece249c2b98bdc0226cc4c2a57bf52fcffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8db97c7cece249c2b98bdc0226cc4c2a57bf52fc0000000c48656c6c6f20776f726c6421";
#[test]
fn test_warp_message_from_bytes() {
let warp_message =
WarpUnsignedMessage::from(hex::decode(WARP_MESSAGE_HEX).unwrap().as_slice());
assert_eq!(warp_message.network_id, 12345);
assert_eq!(
warp_message.source_chain_id,
Id::from_str("uMBaf3Nb62N2xajmxo9ZL5VcSw87tuB3snQEJb8nsyxyLq68f").unwrap()
);
}
#[test]
fn test_warp_message_try_from_subnet_evm_log_data() {
let warp_message = WarpUnsignedMessage::try_from_subnet_evm_log_data(
hex::decode(WARP_MESSAGE_HEX).unwrap().as_slice(),
)
.unwrap();
assert_eq!(warp_message.network_id, 12345);
assert_eq!(
warp_message.source_chain_id,
Id::from_str("uMBaf3Nb62N2xajmxo9ZL5VcSw87tuB3snQEJb8nsyxyLq68f").unwrap()
);
assert_eq!(
warp_message.payload,
WarpMessagePayload::SubnetEVMAddressedPayload(AddressedPayload {
source_address: Address::from_str("0x8db97c7cece249c2b98bdc0226cc4c2a57bf52fc")
.unwrap(),
destination_chain_id: H256::from_str(
"0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
)
.unwrap(),
destination_address: Address::from_str(
"0x8db97c7cece249c2b98bdc0226cc4c2a57bf52fc"
)
.unwrap(),
payload: Bytes::from_str("0x0000000c48656c6c6f20776f726c6421").unwrap(),
})
)
}
}