pod_types/cryptography/
signer.rs

1use alloy_consensus::{
2    SignableTransaction, TxEip1559, serde_bincode_compat, transaction::RlpEcdsaEncodableTx,
3};
4use alloy_primitives::{Signature, SignatureError};
5use alloy_sol_types::SolValue;
6use serde::{Deserialize, Deserializer, Serialize, Serializer};
7use serde_with::serde_as;
8use std::{ops::Deref, sync::OnceLock};
9
10use alloy_primitives::Address;
11
12use super::{Hash, Hashable, Merkleizable, merkle_tree::MerkleBuilder};
13use crate::Transaction;
14
15pub trait TxSigner {
16    fn sign_tx(&self, tx: Transaction) -> Result<Signed<Transaction>, alloy_signer::Error>;
17}
18
19pub trait Signer {
20    fn sign<T: Hashable>(&self, msg: T) -> Result<Signed<T>, alloy_signer::Error>;
21}
22
23impl<S> TxSigner for S
24where
25    S: alloy_signer::SignerSync<Signature>,
26    S: alloy_signer::Signer<Signature>,
27{
28    fn sign_tx(&self, tx: Transaction) -> Result<Signed<Transaction>, alloy_signer::Error> {
29        self.sign(tx)
30    }
31}
32
33impl<S> Signer for S
34where
35    S: alloy_signer::SignerSync<Signature>,
36    S: alloy_signer::Signer<Signature>,
37{
38    fn sign<T: Hashable>(&self, msg: T) -> Result<Signed<T>, alloy_signer::Error> {
39        let signature = self.sign_hash_sync(&msg.hash_custom())?;
40        Ok(Signed {
41            signed: msg,
42            signature,
43            signer: self.address(),
44            hash: OnceLock::new(),
45        })
46    }
47}
48
49// Guarantees Signed<T>.signer == Signed<T>.signature.recover_address(T.hash())
50// by the fact that it can only be constructed by functions that guarantee the address.
51// Only works with ECDSA signatures for now
52// The signature check happens on both rounds. For the second round, it *might* not be
53// necessary but we still want to do it to ensure that if something ever went wrong, we
54// have a second line of defence.
55#[non_exhaustive]
56#[derive(Clone, Debug, PartialEq, Eq)]
57pub struct Signed<T> {
58    pub signed: T,
59    pub signature: Signature,
60    pub signer: Address,
61    pub hash: OnceLock<Hash>,
62}
63
64impl<T> Signed<T>
65where
66    T: SignableTransaction<Signature>,
67{
68    /// Creates a new `Signed<T>` with the given `signed`, `signature`, and `signer`.
69    ///
70    /// # Safety
71    /// The caller must ensure that the `signer` is the correct address that corresponds to the
72    /// `signature`
73    pub unsafe fn new_unchecked(signed: T, signature: Signature, signer: Address) -> Self {
74        debug_assert_eq!(
75            signer,
76            signature
77                .recover_address_from_prehash(&signed.signature_hash())
78                .unwrap(),
79            "Signer address does not match the signature's recovered address"
80        );
81        Signed {
82            signed,
83            signature,
84            signer,
85            hash: OnceLock::new(),
86        }
87    }
88}
89
90impl Serialize for Signed<Transaction> {
91    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
92    where
93        S: Serializer,
94    {
95        #[serde_as]
96        #[derive(Serialize)]
97        struct Helper<'a> {
98            #[serde_as(as = "serde_bincode_compat::transaction::TxEip1559")]
99            signed: &'a Transaction,
100            signature: &'a Signature,
101        }
102
103        Helper {
104            signed: &self.signed,
105            signature: &self.signature,
106        }
107        .serialize(serializer)
108    }
109}
110
111impl<'de> Deserialize<'de> for Signed<Transaction> {
112    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
113    where
114        D: Deserializer<'de>,
115    {
116        #[serde_as]
117        #[derive(Deserialize)]
118        struct Helper {
119            #[serde_as(as = "serde_bincode_compat::transaction::TxEip1559")]
120            signed: Transaction,
121            signature: Signature,
122        }
123        let Helper { signed, signature } = Helper::deserialize(deserializer)?;
124
125        let signer =
126            alloy_consensus::Signed::new_unchecked(signed.clone(), signature, signed.hash_custom())
127                .recover_signer()
128                .map_err(serde::de::Error::custom)?;
129
130        Ok(Signed {
131            signed,
132            signature,
133            signer,
134            hash: OnceLock::new(),
135        })
136    }
137}
138
139impl<T: Hashable> Deref for Signed<T> {
140    type Target = T;
141
142    fn deref(&self) -> &Self::Target {
143        &self.signed
144    }
145}
146
147// the actual hash used for identifying a transaction
148impl<T: RlpEcdsaEncodableTx> Hashable for Signed<T> {
149    fn hash_custom(&self) -> Hash {
150        *self
151            .hash
152            .get_or_init(|| self.signed.tx_hash(&self.signature))
153    }
154}
155
156impl TryFrom<alloy_consensus::Signed<TxEip1559, Signature>> for Signed<Transaction> {
157    type Error = SignatureError;
158
159    fn try_from(value: alloy_consensus::Signed<TxEip1559>) -> Result<Self, Self::Error> {
160        let signer = value.recover_signer()?;
161
162        Ok(Signed {
163            signature: *value.signature(),
164            signed: value.strip_signature(),
165            signer,
166            hash: OnceLock::new(),
167        })
168    }
169}
170
171impl<T: Merkleizable + Hashable> Merkleizable for Signed<T> {
172    fn append_leaves(&self, builder: &mut MerkleBuilder) {
173        builder.add_merkleizable("signed", &self.signed);
174        builder.add_field("signer", self.signer.abi_encode().hash_custom());
175    }
176}
177
178#[cfg(feature = "arbitrary")]
179impl<'a, T: arbitrary::Arbitrary<'a> + Hashable + SignableTransaction<Signature>>
180    arbitrary::Arbitrary<'a> for Signed<T>
181{
182    fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
183        use alloy_signer::SignerSync;
184        let signed = T::arbitrary(u)?;
185        let signer = alloy_signer_local::PrivateKeySigner::random();
186        let signature = signer.sign_hash_sync(&signed.signature_hash()).unwrap();
187        Ok(Signed {
188            signed,
189            signature,
190            signer: signer.address(),
191            hash: OnceLock::new(),
192        })
193    }
194}
195
196#[cfg(test)]
197mod tests {
198    use super::*;
199    use arbitrary::Arbitrary;
200    use bincode::config::standard;
201
202    fn arbitrary_signed_tx() -> Signed<Transaction> {
203        let bytes: [u8; 1024] = rand::random();
204        Signed::arbitrary(&mut arbitrary::Unstructured::new(&bytes)).unwrap()
205    }
206
207    #[test]
208    fn signed_serialization() {
209        let mut signed = arbitrary_signed_tx();
210        let signer = signed.signer;
211        signed.signer = Default::default(); // Clear signer to test serialization without it
212
213        let serialized = serde_json::to_string(&signed).unwrap();
214        let deserialized: Signed<Transaction> = serde_json::from_str(&serialized).unwrap();
215
216        assert_eq!(signed.signed, deserialized.signed);
217        assert_eq!(signer, deserialized.signer);
218        assert_eq!(signed.signature, deserialized.signature);
219    }
220
221    #[test]
222    fn signed_serialization_with_bincode() {
223        let mut signed = arbitrary_signed_tx();
224        let signer = signed.signer;
225        signed.signer = Default::default(); // Clear signer to test serialization without it
226
227        let serialized = bincode::serde::encode_to_vec(&signed, standard()).unwrap();
228        let (deserialized, _): (Signed<Transaction>, _) =
229            bincode::serde::decode_from_slice(&serialized, standard()).unwrap();
230
231        assert_eq!(signed.signed, deserialized.signed);
232        assert_eq!(signer, deserialized.signer);
233        assert_eq!(signed.signature, deserialized.signature);
234    }
235
236    #[test]
237    fn signed_new_unchecked() {
238        let signed = arbitrary_signed_tx();
239
240        let new_signed = unsafe {
241            Signed::new_unchecked(signed.signed.clone(), signed.signature, signed.signer)
242        };
243
244        assert_eq!(new_signed, signed);
245    }
246}