molpha_verifier/
message.rs1use solana_keccak_hasher::hashv;
4
5use crate::payload::AttestationPayload;
6
7pub const MESSAGE_PREFIX: [u8; 32] = [
11 0xa7, 0x55, 0x23, 0xa2, 0xab, 0x7b, 0x71, 0x8d, 0x9c, 0xff, 0xd2, 0xfa, 0x97, 0xed, 0x06, 0x9f,
12 0xc1, 0x21, 0x84, 0xea, 0xbe, 0xe7, 0xd5, 0x07, 0x85, 0x4d, 0x09, 0x22, 0xf7, 0x0e, 0x7f, 0xe7,
13];
14
15pub fn compute_message_hash(payload: &AttestationPayload, signers_bitmap: [u8; 32]) -> [u8; 32] {
25 let registry_version_bytes = payload.registry_version.to_be_bytes();
26 let signatures_required_bytes = payload.signatures_required.to_be_bytes();
27 let canonical_timestamp_bytes = payload.canonical_timestamp.to_be_bytes();
28
29 hashv(&[
30 MESSAGE_PREFIX.as_slice(),
31 payload.value.as_slice(),
32 payload.source_id.as_slice(),
33 registry_version_bytes.as_slice(),
34 signatures_required_bytes.as_slice(),
35 canonical_timestamp_bytes.as_slice(),
36 signers_bitmap.as_slice(),
37 ])
38 .to_bytes()
39}
40
41#[cfg(test)]
42mod tests {
43 use super::*;
44 use crate::fixtures::{
45 CANONICAL_TIMESTAMP, MESSAGE_HASH, REGISTRY_VERSION, SIGNATURES_REQUIRED, SIGNERS_BITMAP,
46 SOURCE_ID, VALUE,
47 };
48
49 fn fixture_payload() -> AttestationPayload {
50 AttestationPayload {
51 value: VALUE,
52 source_id: SOURCE_ID,
53 registry_version: REGISTRY_VERSION,
54 signatures_required: SIGNATURES_REQUIRED,
55 canonical_timestamp: CANONICAL_TIMESTAMP,
56 }
57 }
58
59 fn fixture_signers_bitmap() -> [u8; 32] {
60 SIGNERS_BITMAP
61 }
62
63 #[test]
64 fn message_prefix_is_keccak_of_domain() {
65 let expected = hashv(&[b"MOLPHA_MESSAGE_V1"]).to_bytes();
66 assert_eq!(MESSAGE_PREFIX, expected);
67 }
68
69 #[test]
70 fn compute_message_hash_is_deterministic() {
71 let p = fixture_payload();
72 assert_eq!(
73 compute_message_hash(&p, fixture_signers_bitmap()),
74 compute_message_hash(&p, fixture_signers_bitmap())
75 );
76 }
77
78 #[test]
79 fn compute_message_hash_matches_evm_fixture() {
80 let p = fixture_payload();
81 assert_eq!(
82 compute_message_hash(&p, fixture_signers_bitmap()),
83 MESSAGE_HASH,
84 );
85 }
86
87 #[test]
88 fn compute_message_hash_is_sensitive_to_each_field() {
89 let base = fixture_payload();
90 let base_hash = compute_message_hash(&base, fixture_signers_bitmap());
91
92 let mut a = fixture_payload();
93 a.registry_version += 1;
94 assert_ne!(
95 compute_message_hash(&a, fixture_signers_bitmap()),
96 base_hash
97 );
98
99 let mut b = fixture_payload();
100 b.signatures_required += 1;
101 assert_ne!(
102 compute_message_hash(&b, fixture_signers_bitmap()),
103 base_hash
104 );
105
106 let c = fixture_payload();
107 let mut signers_bitmap = fixture_signers_bitmap();
108 signers_bitmap[31] ^= 0x01;
109 assert_ne!(compute_message_hash(&c, signers_bitmap), base_hash);
110
111 let mut d = fixture_payload();
112 d.value[0] ^= 0xff;
113 assert_ne!(
114 compute_message_hash(&d, fixture_signers_bitmap()),
115 base_hash
116 );
117
118 let mut e = fixture_payload();
119 e.canonical_timestamp += 1;
120 assert_ne!(
121 compute_message_hash(&e, fixture_signers_bitmap()),
122 base_hash
123 );
124
125 let mut f = fixture_payload();
126 f.source_id[0] ^= 0xff;
127 assert_ne!(
128 compute_message_hash(&f, fixture_signers_bitmap()),
129 base_hash
130 );
131 }
132}