@@ -111,3 +111,91 @@ impl ApiClient {
111
111
}
112
112
}
113
113
}
114
+
115
+ #[ cfg( test) ]
116
+ mod tests {
117
+ use secp256k1:: {
118
+ ecdsa:: { RecoverableSignature , RecoveryId } ,
119
+ PublicKey ,
120
+ } ;
121
+ use serde_json:: Value ;
122
+ use serde_wormhole:: RawMessage ;
123
+ use wormhole_sdk:: { Address , Chain } ;
124
+
125
+ use super :: * ;
126
+
127
+ #[ test]
128
+ fn test_new_signed_observation ( ) {
129
+ let secret_key = SecretKey :: from_byte_array ( [ 1u8 ; 32 ] ) . expect ( "Invalid secret key length" ) ;
130
+ let body = Body {
131
+ timestamp : 1234567890 ,
132
+ nonce : 42 ,
133
+ emitter_chain : Chain :: Solana ,
134
+ emitter_address : Address ( [ 1u8 ; 32 ] ) ,
135
+ sequence : 1000 ,
136
+ consistency_level : 1 ,
137
+ payload : vec ! [ 1 , 2 , 3 , 4 , 5 ] ,
138
+ } ;
139
+ let observation =
140
+ Observation :: try_new ( body. clone ( ) , secret_key) . expect ( "Failed to create observation" ) ;
141
+ assert_eq ! ( observation. version, 1 ) ;
142
+ assert_eq ! ( observation. body, body) ;
143
+
144
+ // Signature verification
145
+ let secp = Secp256k1 :: new ( ) ;
146
+ let digest = body. digest ( ) . expect ( "Failed to compute digest" ) ;
147
+ let message = Message :: from_digest ( digest. secp256k_hash ) ;
148
+
149
+ let recovery_id: RecoveryId = ( observation. signature [ 64 ] as i32 )
150
+ . try_into ( )
151
+ . expect ( "Invalid recovery ID" ) ;
152
+ let recoverable_sig =
153
+ RecoverableSignature :: from_compact ( & observation. signature [ ..64 ] , recovery_id)
154
+ . expect ( "Invalid recoverable signature" ) ;
155
+
156
+ let pubkey = secp
157
+ . recover_ecdsa ( message, & recoverable_sig)
158
+ . expect ( "Failed to recover pubkey" ) ;
159
+
160
+ let expected_pubkey = PublicKey :: from_secret_key ( & secp, & secret_key) ;
161
+ assert_eq ! ( pubkey, expected_pubkey) ;
162
+ }
163
+
164
+ #[ test]
165
+ fn test_observation_serialization ( ) {
166
+ let payload = vec ! [ 5 , 1 , 2 , 3 , 4 , 5 ] ;
167
+ let observation = Observation {
168
+ version : 1 ,
169
+ signature : [ 1u8 ; 65 ] ,
170
+ body : Body {
171
+ timestamp : 1234567890 ,
172
+ nonce : 42 ,
173
+ emitter_chain : Chain :: Solana ,
174
+ emitter_address : Address ( [ 1u8 ; 32 ] ) ,
175
+ sequence : 1000 ,
176
+ consistency_level : 1 ,
177
+ payload : RawMessage :: new ( payload. as_slice ( ) ) ,
178
+ } ,
179
+ } ;
180
+
181
+ let serialized =
182
+ serde_json:: to_string ( & observation) . expect ( "Failed to serialize observation" ) ;
183
+ let parsed: Value = serde_json:: from_str ( & serialized) . expect ( "Failed to parse JSON" ) ;
184
+
185
+ assert_eq ! ( parsed[ "version" ] , 1 ) ;
186
+ let sig = parsed[ "signature" ]
187
+ . as_str ( )
188
+ . expect ( "Signature should be a string" ) ;
189
+ let decoded = hex:: decode ( sig) . expect ( "Should be valid hex" ) ;
190
+ assert_eq ! ( decoded, observation. signature) ;
191
+
192
+ let message = parsed[ "body" ] . as_array ( ) . expect ( "Body should be an array" ) ;
193
+ let bytes = message
194
+ . iter ( )
195
+ . map ( |v| v. as_u64 ( ) . expect ( "Body elements should be u64" ) as u8 )
196
+ . collect :: < Vec < u8 > > ( ) ;
197
+ let deserialized: Body < & RawMessage > =
198
+ serde_wormhole:: from_slice ( & bytes) . expect ( "Failed to deserialize body" ) ;
199
+ assert_eq ! ( deserialized, observation. body) ;
200
+ }
201
+ }
0 commit comments