om_primitives_types/transaction/
mod.rs1pub mod hashing;
2
3use alloy_primitives::{TxHash, keccak256};
4use alloy_rlp::Encodable;
5pub use hashing::*;
6pub mod openapi;
7pub use openapi::*;
8
9pub mod envelope;
10pub mod payload;
11
12pub mod validate;
13pub use validate::*;
14
15mod signed;
16pub use signed::{B264, MultiSigSignatureEntry, SigError, Signature, SignatureType, Signed, SignerRecoverable};
17
18pub trait TxHashable: Encodable {
19 fn tx_hash(&self, signature: &Signature) -> TxHash {
22 let mut buf = Vec::new();
23 let out = &mut buf;
24
25 let payload_length = self.length() + signature.rlp_rs_len() + signature.v().length();
26 let header = alloy_rlp::Header {
27 list: true,
28 payload_length,
29 };
30 header.encode(out);
31
32 self.encode(out);
33 signature.write_rlp_vrs(out, signature.v());
34
35 keccak256(&buf)
36 }
37
38 fn tx_hash_with_signature_type(&self, signature_type: &SignatureType) -> TxHash {
45 match signature_type {
46 SignatureType::Single(sig) => self.tx_hash(sig),
47 SignatureType::Multi { signatures, .. } => {
48 let mut buf = Vec::new();
50 self.encode(&mut buf);
51 for sig_entry in signatures {
52 buf.extend_from_slice(&sig_entry.signature.as_bytes());
53 }
54 keccak256(&buf)
55 }
56 }
57 }
58}
59
60impl<T: Encodable> TxHashable for T {}
63
64#[cfg(test)]
65mod tests {
66 use alloy_primitives::{Address, U256};
67 use alloy_rlp::Encodable;
68
69 use super::*;
70 use crate::transaction::payload::PaymentPayload;
71
72 fn sample_payload() -> PaymentPayload {
73 PaymentPayload {
74 chain_id: 1,
75 nonce: 7,
76 recipient: Address::from([0x11u8; 20]),
77 value: U256::from(42u64),
78 token: Address::from([0x22u8; 20]),
79 }
80 }
81
82 fn make_signature(v: u8) -> Signature {
83 let mut bytes = [0u8; 65];
84 bytes[0] = 1;
85 bytes[32] = 2;
86 bytes[64] = v;
87 Signature::try_from(bytes.as_slice()).expect("signature bytes should parse")
88 }
89
90 #[test]
91 fn test_tx_hash_single_vs_multi() {
92 let payload = sample_payload();
93 let single_sig = make_signature(27);
94 let single_hash = payload.tx_hash(&single_sig);
95
96 let signatures = vec![
97 MultiSigSignatureEntry::new(B264::repeat_byte(0x02), make_signature(27)),
98 MultiSigSignatureEntry::new(B264::repeat_byte(0x03), make_signature(28)),
99 ];
100 let multi_sig = SignatureType::Multi {
101 account: Address::from([0x33u8; 20]),
102 signatures: signatures.clone(),
103 };
104 let multi_hash = payload.tx_hash_with_signature_type(&multi_sig);
105
106 let mut buf = Vec::new();
107 payload.encode(&mut buf);
108 for entry in &signatures {
109 buf.extend_from_slice(&entry.signature.as_bytes());
110 }
111 let expected_multi_hash = keccak256(&buf);
112
113 assert_eq!(multi_hash, expected_multi_hash);
114 assert_ne!(single_hash, multi_hash);
115 }
116
117 #[test]
118 fn test_tx_hash_multi_no_signatures() {
119 let payload = sample_payload();
120 let multi_sig = SignatureType::Multi {
121 account: Address::from([0x44u8; 20]),
122 signatures: Vec::new(),
123 };
124 let hash = payload.tx_hash_with_signature_type(&multi_sig);
125
126 let mut buf = Vec::new();
127 payload.encode(&mut buf);
128 let expected = keccak256(&buf);
129
130 assert_eq!(hash, expected);
131 }
132}