wavs_types/
packet.rs

1use std::borrow::Borrow;
2
3pub use crate::solidity_types::Envelope;
4use crate::{
5    Service, ServiceManagerEnvelope, ServiceManagerSignatureData, SignatureAlgorithm,
6    SignatureData, SignatureKind, SignaturePrefix, TriggerAction, TriggerData, WorkflowId,
7};
8use alloy_primitives::{eip191_hash_message, keccak256, FixedBytes, SignatureError};
9use alloy_signer::Signer;
10use alloy_signer_local::PrivateKeySigner;
11use alloy_sol_types::SolValue;
12use async_trait::async_trait;
13use ripemd::Ripemd160;
14use serde::{Deserialize, Serialize};
15use sha2::Digest;
16use thiserror::Error;
17use utoipa::ToSchema;
18
19#[derive(Serialize, Deserialize, Clone, Debug, ToSchema)]
20#[serde(rename_all = "snake_case")]
21pub struct Packet {
22    pub service: Service,
23    pub workflow_id: WorkflowId,
24    #[schema(value_type  = Object)]
25    pub envelope: Envelope,
26    pub signature: EnvelopeSignature,
27    pub trigger_data: TriggerData,
28}
29
30#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
31#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
32pub trait EnvelopeExt: Borrow<Envelope> {
33    fn prefix_eip191_hash(&self) -> FixedBytes<32> {
34        let envelope_bytes = self.borrow().abi_encode();
35        eip191_hash_message(keccak256(&envelope_bytes))
36    }
37
38    fn unprefixed_hash(&self) -> FixedBytes<32> {
39        let envelope_bytes = self.borrow().abi_encode();
40        keccak256(&envelope_bytes)
41    }
42
43    async fn sign(
44        &self,
45        signer: &PrivateKeySigner,
46        kind: SignatureKind,
47    ) -> anyhow::Result<EnvelopeSignature> {
48        let hash = match kind.algorithm {
49            SignatureAlgorithm::Secp256k1 => match kind.prefix {
50                Some(SignaturePrefix::Eip191) => self.prefix_eip191_hash(),
51                None => self.unprefixed_hash(),
52            },
53        };
54
55        Ok(signer
56            .sign_hash(&hash)
57            .await
58            .map(|signature| EnvelopeSignature {
59                data: signature.into(),
60                kind,
61            })
62            .map_err(|e| anyhow::anyhow!("Failed to sign envelope: {e:?}"))?)
63    }
64
65    fn signature_data(
66        &self,
67        signatures: Vec<EnvelopeSignature>,
68        block_height: u64,
69    ) -> std::result::Result<SignatureData, EnvelopeError> {
70        let mut signers_and_signatures: Vec<(alloy_primitives::Address, alloy_primitives::Bytes)> =
71            signatures
72                .into_iter()
73                .map(|sig| {
74                    sig.evm_signer_address(self.borrow())
75                        .map(|addr| (addr, sig.data.into()))
76                })
77                .collect::<Result<_, _>>()?;
78
79        // Solidity‑compatible ascending order (lexicographic / numeric)
80        signers_and_signatures.sort_by_key(|(addr, _)| *addr);
81
82        // unzip back into two parallel, sorted vectors
83        let (signers, signatures): (Vec<alloy_primitives::Address>, Vec<alloy_primitives::Bytes>) =
84            signers_and_signatures.into_iter().unzip();
85
86        Ok(SignatureData {
87            signers,
88            signatures,
89            referenceBlock: block_height as u32,
90        })
91    }
92}
93
94// Blanket impl for anything that borrows as Envelope
95impl<T: Borrow<Envelope> + ?Sized> EnvelopeExt for T {}
96
97impl From<Envelope> for ServiceManagerEnvelope {
98    fn from(envelope: Envelope) -> Self {
99        ServiceManagerEnvelope {
100            eventId: envelope.eventId,
101            ordering: envelope.ordering,
102            payload: envelope.payload,
103        }
104    }
105}
106
107impl From<SignatureData> for ServiceManagerSignatureData {
108    fn from(signature_data: SignatureData) -> Self {
109        ServiceManagerSignatureData {
110            signers: signature_data.signers,
111            signatures: signature_data.signatures,
112            referenceBlock: signature_data.referenceBlock,
113        }
114    }
115}
116
117#[derive(Serialize, Deserialize, Clone, Debug, ToSchema)]
118#[serde(rename_all = "snake_case")]
119pub struct EnvelopeSignature {
120    pub data: Vec<u8>,
121    pub kind: SignatureKind,
122}
123
124impl EnvelopeSignature {
125    pub fn evm_signer_address(
126        &self,
127        envelope: &Envelope,
128    ) -> std::result::Result<alloy_primitives::Address, EnvelopeError> {
129        match self.kind.algorithm {
130            SignatureAlgorithm::Secp256k1 => {
131                let signature = alloy_primitives::Signature::from_raw(&self.data)
132                    .map_err(EnvelopeError::RecoverSignerAddress)?;
133
134                match self.kind.prefix {
135                    Some(SignaturePrefix::Eip191) => signature
136                        .recover_address_from_prehash(&envelope.prefix_eip191_hash())
137                        .map_err(EnvelopeError::RecoverSignerAddress),
138                    None => signature
139                        .recover_address_from_prehash(&envelope.unprefixed_hash())
140                        .map_err(EnvelopeError::RecoverSignerAddress),
141                }
142            }
143        }
144    }
145}
146
147impl Packet {
148    pub fn event_id(&self) -> EventId {
149        self.envelope.eventId.into()
150    }
151}
152
153#[derive(
154    Serialize,
155    Deserialize,
156    Clone,
157    Eq,
158    PartialEq,
159    Debug,
160    Hash,
161    bincode::Decode,
162    bincode::Encode,
163    Ord,
164    PartialOrd,
165)]
166#[serde(transparent)]
167pub struct EventId([u8; 20]);
168
169impl From<FixedBytes<20>> for EventId {
170    fn from(value: FixedBytes<20>) -> Self {
171        Self(value.0)
172    }
173}
174
175impl From<EventId> for FixedBytes<20> {
176    fn from(value: EventId) -> Self {
177        FixedBytes(value.0)
178    }
179}
180
181impl TryFrom<(&Service, &TriggerAction)> for EventId {
182    type Error = anyhow::Error;
183
184    fn try_from(
185        (service, trigger_action): (&Service, &TriggerAction),
186    ) -> std::result::Result<EventId, Self::Error> {
187        let service_digest = service.hash()?;
188        let action_bytes = bincode::encode_to_vec(trigger_action, bincode::config::standard())?;
189
190        let mut hasher = Ripemd160::new();
191        hasher.update(&service_digest);
192        hasher.update(&action_bytes);
193        let result = hasher.finalize();
194
195        Ok(EventId(result.into()))
196    }
197}
198
199impl std::fmt::Display for EventId {
200    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
201        write!(f, "{}", const_hex::encode(self.0))
202    }
203}
204
205impl AsRef<[u8]> for EventId {
206    fn as_ref(&self) -> &[u8] {
207        &self.0
208    }
209}
210
211#[derive(Serialize, Deserialize, Clone)]
212#[serde(transparent)]
213pub struct EventOrder([u8; 12]);
214
215impl EventOrder {
216    pub fn new_u64(value: u64) -> Self {
217        let mut bytes = [0; 12];
218        bytes[0..8].copy_from_slice(&value.to_be_bytes());
219        Self(bytes)
220    }
221}
222
223impl From<FixedBytes<12>> for EventOrder {
224    fn from(value: FixedBytes<12>) -> Self {
225        Self(value.0)
226    }
227}
228
229impl From<EventOrder> for FixedBytes<12> {
230    fn from(value: EventOrder) -> Self {
231        FixedBytes(value.0)
232    }
233}
234
235impl std::fmt::Display for EventOrder {
236    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
237        write!(f, "{}", const_hex::encode(self.0))
238    }
239}
240
241impl AsRef<[u8]> for EventOrder {
242    fn as_ref(&self) -> &[u8] {
243        &self.0
244    }
245}
246
247#[derive(Debug, Error)]
248pub enum EnvelopeError {
249    #[error("Unable to recover signer address: {0:?}")]
250    RecoverSignerAddress(SignatureError),
251}