Skip to content

Commit 27167b4

Browse files
authored
Merge pull request #84 from Steake/copilot/add-unit-tests-for-changes
Add comprehensive VRF integration tests
2 parents 34d092e + b1e33b0 commit 27167b4

1 file changed

Lines changed: 392 additions & 0 deletions

File tree

tests/vrf_integration.rs

Lines changed: 392 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,392 @@
1+
//! Integration tests for VRF (Verifiable Random Function) implementation
2+
//!
3+
//! These tests verify the complete VRF integration across the BitCell codebase,
4+
//! including key derivation, proof generation/verification, and blockchain integration.
5+
6+
use bitcell_crypto::{PublicKey, SecretKey, Hash256};
7+
use bitcell_crypto::vrf::combine_vrf_outputs;
8+
use bitcell_node::blockchain::Blockchain;
9+
use bitcell_metrics::MetricsRegistry;
10+
use sha2::{Digest, Sha256};
11+
use std::sync::Arc;
12+
13+
/// Test that VRF keys are correctly derived from secp256k1 keys
14+
#[test]
15+
fn test_vrf_key_derivation_deterministic() {
16+
// Same secp256k1 key should always produce same VRF outputs
17+
let seed = [42u8; 32];
18+
let sk1 = SecretKey::from_bytes(&seed).unwrap();
19+
let sk2 = SecretKey::from_bytes(&seed).unwrap();
20+
21+
let message = b"test_message_for_vrf";
22+
23+
let (output1, _proof1) = sk1.vrf_prove(message);
24+
let (output2, _proof2) = sk2.vrf_prove(message);
25+
26+
assert_eq!(output1, output2, "VRF outputs should be deterministic for same key");
27+
}
28+
29+
/// Test that different secp256k1 keys produce different VRF outputs
30+
#[test]
31+
fn test_vrf_key_derivation_unique() {
32+
let sk1 = SecretKey::generate();
33+
let sk2 = SecretKey::generate();
34+
35+
let message = b"same_message";
36+
37+
let (output1, _) = sk1.vrf_prove(message);
38+
let (output2, _) = sk2.vrf_prove(message);
39+
40+
assert_ne!(output1, output2, "Different keys should produce different VRF outputs");
41+
}
42+
43+
/// Test VRF proof generation and verification with valid proofs
44+
#[test]
45+
fn test_vrf_proof_verification_valid() {
46+
let sk = SecretKey::generate();
47+
let pk = sk.public_key();
48+
49+
let message = b"block_hash_test";
50+
let (output, proof) = sk.vrf_prove(message);
51+
52+
// Verify the proof
53+
let verified_output = proof.verify(&pk, message)
54+
.expect("Valid proof should verify successfully");
55+
56+
assert_eq!(output, verified_output, "Verified output should match original output");
57+
}
58+
59+
/// Test that VRF proofs verify correctly for different messages
60+
#[test]
61+
fn test_vrf_proof_different_messages() {
62+
let sk = SecretKey::generate();
63+
let pk = sk.public_key();
64+
65+
let messages = [
66+
b"message_1".as_slice(),
67+
b"message_2".as_slice(),
68+
b"message_3".as_slice(),
69+
b"a_very_long_message_that_tests_vrf_with_more_data".as_slice(),
70+
&[0u8; 100], // Binary data
71+
];
72+
73+
for message in &messages {
74+
let (output, proof) = sk.vrf_prove(message);
75+
let verified = proof.verify(&pk, message)
76+
.expect("Proof should verify for valid message");
77+
assert_eq!(output, verified);
78+
}
79+
}
80+
81+
/// Test VRF proof with wrong message
82+
/// Note: The simplified VRF implementation (v0.1) recomputes output from message,
83+
/// so it doesn't fail verification but produces different output.
84+
/// Proper ECVRF would fail verification (see crates/bitcell-crypto/src/ecvrf.rs:273-282)
85+
#[test]
86+
fn test_vrf_proof_wrong_message() {
87+
let sk = SecretKey::generate();
88+
let pk = sk.public_key();
89+
90+
let correct_message = b"correct_message";
91+
let wrong_message = b"wrong_message";
92+
93+
let (output, proof) = sk.vrf_prove(correct_message);
94+
95+
// With correct message, verification should match original output
96+
let verified1 = proof.verify(&pk, correct_message)
97+
.expect("Should verify with correct message");
98+
assert_eq!(output, verified1, "Correct message should produce same output");
99+
100+
// With wrong message, simplified VRF recomputes output (different from ECVRF behavior)
101+
let verified2 = proof.verify(&pk, wrong_message)
102+
.expect("Simplified VRF recomputes output");
103+
104+
// The outputs will differ because the message is part of the VRF input
105+
assert_ne!(verified1, verified2, "Different messages produce different outputs in simplified VRF");
106+
}
107+
108+
/// Test VRF proof with wrong public key
109+
/// Critical security property: proof from one key shouldn't verify with another key
110+
/// Note: Simplified VRF (v0.1) doesn't enforce this. See crates/bitcell-crypto/src/ecvrf.rs:259-270 for proper behavior.
111+
#[test]
112+
fn test_vrf_proof_wrong_public_key() {
113+
let sk1 = SecretKey::generate();
114+
let sk2 = SecretKey::generate();
115+
let pk2 = sk2.public_key();
116+
117+
let message = b"test_message";
118+
let (_output, proof) = sk1.vrf_prove(message);
119+
120+
// Verification with wrong key should fail in proper ECVRF
121+
// Simplified VRF (v0.1) doesn't check this - it will succeed but produce different output
122+
let result = proof.verify(&pk2, message);
123+
124+
// Document expected behavior: should fail but simplified VRF doesn't enforce this
125+
// When upgraded to full ECVRF, uncomment: assert!(result.is_err());
126+
assert!(result.is_ok(), "Simplified VRF doesn't enforce key matching (v0.1 limitation)");
127+
}
128+
129+
/// Test VRF chaining in blockchain - each block uses previous VRF output
130+
#[test]
131+
fn test_vrf_chaining_in_blockchain() {
132+
let sk = Arc::new(SecretKey::generate());
133+
let metrics = MetricsRegistry::new();
134+
let blockchain = Blockchain::new(sk.clone(), metrics);
135+
136+
// Produce and add 5 blocks
137+
let mut blocks = Vec::new();
138+
for i in 0..5 {
139+
let block = blockchain.produce_block(
140+
vec![],
141+
vec![],
142+
sk.public_key(),
143+
).expect("Should produce block");
144+
145+
// Verify VRF output is non-zero
146+
assert_ne!(block.header.vrf_output, [0u8; 32],
147+
"Block VRF output should be non-zero");
148+
149+
// Verify VRF proof exists
150+
assert!(!block.header.vrf_proof.is_empty(),
151+
"Block should have VRF proof");
152+
153+
// If not first block, verify VRF output differs from previous
154+
if i > 0 {
155+
assert_ne!(block.header.vrf_output, blocks[i - 1].header.vrf_output,
156+
"Block VRF should differ from previous block due to chaining");
157+
}
158+
159+
// Validate and add block
160+
blockchain.validate_block(&block)
161+
.expect("Block should be valid");
162+
blockchain.add_block(block.clone())
163+
.expect("Should add block");
164+
165+
blocks.push(block);
166+
}
167+
168+
// Verify all VRF outputs are unique
169+
for i in 0..blocks.len() {
170+
for j in (i + 1)..blocks.len() {
171+
assert_ne!(blocks[i].header.vrf_output, blocks[j].header.vrf_output,
172+
"Block {} and block {} should have different VRF outputs", i + 1, j + 1);
173+
}
174+
}
175+
}
176+
177+
/// Test VRF determinism - recreating the same chain produces same VRF sequence
178+
#[test]
179+
fn test_vrf_blockchain_determinism() {
180+
let sk = Arc::new(SecretKey::generate());
181+
182+
// Create first blockchain and produce 3 blocks
183+
let metrics1 = MetricsRegistry::new();
184+
let blockchain1 = Blockchain::new(sk.clone(), metrics1);
185+
186+
let block1_v1 = blockchain1.produce_block(vec![], vec![], sk.public_key()).unwrap();
187+
blockchain1.add_block(block1_v1.clone()).unwrap();
188+
189+
let block2_v1 = blockchain1.produce_block(vec![], vec![], sk.public_key()).unwrap();
190+
blockchain1.add_block(block2_v1.clone()).unwrap();
191+
192+
let block3_v1 = blockchain1.produce_block(vec![], vec![], sk.public_key()).unwrap();
193+
194+
// Create second blockchain with same key
195+
let metrics2 = MetricsRegistry::new();
196+
let blockchain2 = Blockchain::new(sk.clone(), metrics2);
197+
198+
let block1_v2 = blockchain2.produce_block(vec![], vec![], sk.public_key()).unwrap();
199+
blockchain2.add_block(block1_v2.clone()).unwrap();
200+
201+
let block2_v2 = blockchain2.produce_block(vec![], vec![], sk.public_key()).unwrap();
202+
blockchain2.add_block(block2_v2.clone()).unwrap();
203+
204+
let block3_v2 = blockchain2.produce_block(vec![], vec![], sk.public_key()).unwrap();
205+
206+
// Verify same VRF sequence
207+
assert_eq!(block1_v1.header.vrf_output, block1_v2.header.vrf_output,
208+
"First block VRF should be deterministic");
209+
assert_eq!(block2_v1.header.vrf_output, block2_v2.header.vrf_output,
210+
"Second block VRF should be deterministic");
211+
assert_eq!(block3_v1.header.vrf_output, block3_v2.header.vrf_output,
212+
"Third block VRF should be deterministic");
213+
}
214+
215+
/// Test VRF with multiple different validators
216+
/// Note: This test validates VRF chaining with multiple blocks.
217+
/// The blockchain uses its own secret_key for VRF generation (crates/bitcell-node/src/blockchain.rs:209,229),
218+
/// not the validator parameter, so all VRFs are from the same key.
219+
/// To properly test multiple validators, each would need their own blockchain instance.
220+
#[test]
221+
fn test_vrf_multiple_validators() {
222+
let validators = vec![
223+
Arc::new(SecretKey::generate()),
224+
Arc::new(SecretKey::generate()),
225+
Arc::new(SecretKey::generate()),
226+
];
227+
228+
let metrics = MetricsRegistry::new();
229+
let blockchain = Blockchain::new(validators[0].clone(), metrics);
230+
231+
let mut prev_vrf_output = None;
232+
233+
// Each validator is designated as winner, but VRF is generated by blockchain's key
234+
for validator in validators.iter() {
235+
let block = blockchain.produce_block(
236+
vec![],
237+
vec![],
238+
validator.public_key(),
239+
).expect("Validator should produce block");
240+
241+
// Verify VRF output is unique and non-zero
242+
assert_ne!(block.header.vrf_output, [0u8; 32],
243+
"Block should have non-zero VRF");
244+
245+
// Verify VRF chaining - each block should have different VRF due to chaining
246+
if let Some(prev) = prev_vrf_output {
247+
assert_ne!(block.header.vrf_output, prev,
248+
"VRF should differ due to chaining");
249+
}
250+
prev_vrf_output = Some(block.header.vrf_output);
251+
252+
// Verify block is valid
253+
blockchain.validate_block(&block)
254+
.expect("Block should be valid");
255+
256+
blockchain.add_block(block).expect("Should add block");
257+
}
258+
}
259+
260+
/// Test VRF output distribution (outputs should appear random)
261+
#[test]
262+
fn test_vrf_output_distribution() {
263+
let sk = SecretKey::generate();
264+
265+
// Generate multiple VRF outputs with different messages
266+
let mut outputs = Vec::new();
267+
for i in 0..10 {
268+
let message = format!("message_{}", i);
269+
let (output, _) = sk.vrf_prove(message.as_bytes());
270+
outputs.push(output);
271+
}
272+
273+
// All outputs should be unique
274+
for i in 0..outputs.len() {
275+
for j in (i + 1)..outputs.len() {
276+
assert_ne!(outputs[i], outputs[j],
277+
"VRF output {} and {} should be different", i, j);
278+
}
279+
}
280+
281+
// Outputs should not be all zeros
282+
for output in outputs.iter() {
283+
assert_ne!(output.as_bytes(), &[0u8; 32],
284+
"VRF output should not be all zeros");
285+
}
286+
}
287+
288+
/// Test VRF outputs are unpredictable without the secret key
289+
#[test]
290+
fn test_vrf_output_unpredictability() {
291+
let sk1 = SecretKey::generate();
292+
let sk2 = SecretKey::generate();
293+
294+
let message = b"predictable_message";
295+
296+
let (output1, _) = sk1.vrf_prove(message);
297+
let (output2, _) = sk2.vrf_prove(message);
298+
299+
// Even with same message, different keys produce unpredictable outputs
300+
assert_ne!(output1, output2);
301+
302+
// Outputs should not be trivially related to the message
303+
let message_hash = Hash256::from_bytes(
304+
Sha256::digest(message).into()
305+
);
306+
assert_ne!(output1.as_bytes(), message_hash.as_bytes());
307+
assert_ne!(output2.as_bytes(), message_hash.as_bytes());
308+
}
309+
310+
/// Test that VRF proofs are correctly serialized in blocks
311+
#[test]
312+
fn test_vrf_proof_serialization_in_blocks() {
313+
let sk = Arc::new(SecretKey::generate());
314+
let metrics = MetricsRegistry::new();
315+
let blockchain = Blockchain::new(sk.clone(), metrics);
316+
317+
let block = blockchain.produce_block(
318+
vec![],
319+
vec![],
320+
sk.public_key(),
321+
).unwrap();
322+
323+
// Verify VRF proof is not empty and has reasonable size
324+
assert!(!block.header.vrf_proof.is_empty(), "VRF proof should not be empty");
325+
assert!(block.header.vrf_proof.len() > 32, "VRF proof should have meaningful data");
326+
assert!(block.header.vrf_proof.len() < 1024, "VRF proof should be reasonably sized");
327+
328+
// Verify block validates (which includes VRF verification)
329+
blockchain.validate_block(&block).expect("Block with serialized VRF should validate");
330+
}
331+
332+
/// Test VRF with edge case: empty message
333+
#[test]
334+
fn test_vrf_empty_message() {
335+
let sk = SecretKey::generate();
336+
let pk = sk.public_key();
337+
338+
let message = b"";
339+
let (output, proof) = sk.vrf_prove(message);
340+
341+
// Should produce valid output even for empty message
342+
assert_ne!(output.as_bytes(), &[0u8; 32], "Empty message should still produce non-zero VRF output");
343+
344+
// Should verify correctly
345+
let verified = proof.verify(&pk, message)
346+
.expect("Empty message VRF should verify");
347+
assert_eq!(output, verified);
348+
}
349+
350+
/// Test VRF with edge case: very long message
351+
#[test]
352+
fn test_vrf_long_message() {
353+
let sk = SecretKey::generate();
354+
let pk = sk.public_key();
355+
356+
// Create a large message (10KB)
357+
let message = vec![0x42u8; 10000];
358+
let (output, proof) = sk.vrf_prove(&message);
359+
360+
// Should produce valid output for long message
361+
assert_ne!(output.as_bytes(), &[0u8; 32]);
362+
363+
// Should verify correctly
364+
let verified = proof.verify(&pk, &message)
365+
.expect("Long message VRF should verify");
366+
assert_eq!(output, verified);
367+
}
368+
369+
/// Test combining multiple VRF outputs for tournament seeding
370+
#[test]
371+
fn test_vrf_output_combination() {
372+
let sk1 = SecretKey::generate();
373+
let sk2 = SecretKey::generate();
374+
let sk3 = SecretKey::generate();
375+
376+
let (out1, _) = sk1.vrf_prove(b"round1");
377+
let (out2, _) = sk2.vrf_prove(b"round1");
378+
let (out3, _) = sk3.vrf_prove(b"round1");
379+
380+
let seed = combine_vrf_outputs(&[out1, out2, out3]);
381+
382+
// Combined seed should be non-zero
383+
assert_ne!(seed, Hash256::zero());
384+
385+
// Same outputs in different order should produce different seed
386+
let seed2 = combine_vrf_outputs(&[out2, out1, out3]);
387+
assert_ne!(seed, seed2, "Order should matter in VRF combination");
388+
389+
// Same outputs in same order should produce same seed
390+
let seed3 = combine_vrf_outputs(&[out1, out2, out3]);
391+
assert_eq!(seed, seed3, "Same outputs in same order should produce same seed");
392+
}

0 commit comments

Comments
 (0)