Skip to content

Commit 8875436

Browse files
authored
Add tests for observation (#7)
1 parent 5591dcb commit 8875436

File tree

4 files changed

+124
-2
lines changed

4 files changed

+124
-2
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,6 @@ tokio = "1.45.1"
2020
tokio-stream = "0.1.17"
2121
tracing = "0.1.41"
2222
wormhole-vaas-serde = "0.1.0"
23+
24+
[dev-dependencies]
25+
serde_json = "1.0.140"

src/api_client.rs

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,3 +111,91 @@ impl ApiClient {
111111
}
112112
}
113113
}
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+
}

src/posted_message.rs

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,14 @@ use {
77
},
88
};
99

10-
#[derive(Default, Debug, Clone)]
10+
#[derive(Default, Debug, Clone, PartialEq)]
1111
pub struct PostedMessageUnreliableData {
1212
pub message: MessageData,
1313
}
1414

15-
#[derive(Debug, Default, BorshSerialize, BorshDeserialize, Clone, Serialize, Deserialize)]
15+
#[derive(
16+
Debug, Default, BorshSerialize, BorshDeserialize, Clone, Serialize, Deserialize, PartialEq,
17+
)]
1618
pub struct MessageData {
1719
/// Header of the posted VAA
1820
pub vaa_version: u8,
@@ -89,3 +91,31 @@ impl DerefMut for PostedMessageUnreliableData {
8991
&mut self.message
9092
}
9193
}
94+
95+
#[cfg(test)]
96+
mod tests {
97+
use super::*;
98+
99+
#[test]
100+
fn test_borsh_roundtrip() {
101+
let post_message_unreliable_data = PostedMessageUnreliableData {
102+
message: MessageData {
103+
vaa_version: 1,
104+
consistency_level: 2,
105+
vaa_time: 3,
106+
vaa_signature_account: [4u8; 32],
107+
submission_time: 5,
108+
nonce: 6,
109+
sequence: 7,
110+
emitter_chain: 8,
111+
emitter_address: [9u8; 32],
112+
payload: vec![10u8; 32],
113+
},
114+
};
115+
116+
let encoded = borsh::to_vec(&post_message_unreliable_data).unwrap();
117+
118+
let decoded = PostedMessageUnreliableData::try_from_slice(encoded.as_slice()).unwrap();
119+
assert_eq!(decoded, post_message_unreliable_data);
120+
}
121+
}

0 commit comments

Comments
 (0)