vrf-contract-verifier 0.8.0

Minimal VRF proof verification for smart contracts
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
//! Cross-verification tests between vrf-wasm (proof generation) and vrf-contract-verifier (proof verification)
//!
//! These tests ensure that VRF proofs generated by the main vrf-wasm library can be
//! verified by the vrf-contract-verifier library

#[cfg(test)]
mod tests {
    use crate::verifiers;
    use crate::types::VerificationError;

    // Import the main vrf-wasm library for proof generation
    use vrf_wasm::vrf::ecvrf::ECVRFKeyPair;
    use vrf_wasm::vrf::{VRFKeyPair, VRFProof};
    use vrf_wasm::serde_helpers::ToFromByteArray;
    use vrf_wasm::rng;

    #[test]
    fn test_cross_verification_basic() {
        // Generate a VRF keypair using the main vrf-wasm library
        let mut rng_instance = rng::WasmRng;
        let keypair = ECVRFKeyPair::generate(&mut rng_instance);

        // Create test input
        let input = b"test_input_for_vrf";

        // Generate VRF proof using vrf-wasm
        let proof = keypair.prove(input);
        let vrf_output = proof.to_hash();

        // Extract public key bytes
        let pk_bytes = keypair.public_key().to_byte_array();

        // Extract proof components
        let (gamma_bytes, challenge_bytes, scalar_bytes) = proof.to_components();

        // Reconstruct proof bytes in the format expected by vrf-contract-verifier
        // Format: gamma(32) + challenge(16) + scalar(32) = 80 bytes total
        let mut proof_bytes = Vec::with_capacity(80);
        proof_bytes.extend_from_slice(&gamma_bytes);
        proof_bytes.extend_from_slice(&challenge_bytes);
        proof_bytes.extend_from_slice(&scalar_bytes);

        // Verify using vrf-contract-verifier
        let verification_result = verifiers::verify_vrf(
            &proof_bytes,
            &pk_bytes,
            input,
        );

        // Assert verification succeeds
        assert!(verification_result.is_ok(), "VRF proof verification failed: {:?}", verification_result.err());

        // Assert the output matches
        let verified_output = verification_result.unwrap();
        assert_eq!(verified_output, vrf_output, "VRF outputs do not match");
    }

    #[test]
    fn test_cross_verification_multiple_inputs() {
        // Test with multiple different inputs to ensure robustness
        let test_inputs = [
            b"test_input_1".as_slice(),
            b"test_input_2",
            b"different_seed_value",
            b"",  // Empty input
            b"very_long_input_that_exceeds_normal_length_to_test_edge_cases_and_ensure_robustness",
            &[0u8; 32],  // All zeros
            &[255u8; 16], // All ones (shorter)
        ];

        // Generate a single keypair for all tests
        let mut rng_instance = rng::WasmRng;
        let keypair = ECVRFKeyPair::generate(&mut rng_instance);
        let pk_bytes = keypair.public_key().to_byte_array();

        for (i, input) in test_inputs.iter().enumerate() {
            // Generate VRF proof using vrf-wasm
            let proof = keypair.prove(input);
            let vrf_output = proof.to_hash();

            // Extract proof components and reconstruct proof bytes
            let (gamma_bytes, challenge_bytes, scalar_bytes) = proof.to_components();
            let mut proof_bytes = Vec::with_capacity(80);
            proof_bytes.extend_from_slice(&gamma_bytes);
            proof_bytes.extend_from_slice(&challenge_bytes);
            proof_bytes.extend_from_slice(&scalar_bytes);

            // Verify using vrf-contract-verifier
            let verification_result = verifiers::verify_vrf(
                &proof_bytes,
                &pk_bytes,
                input,
            );

            // Assert verification succeeds
            assert!(
                verification_result.is_ok(),
                "VRF proof verification failed for input {}: {:?}",
                i,
                verification_result.err()
            );

            // Assert the output matches
            let verified_output = verification_result.unwrap();
            assert_eq!(
                verified_output, vrf_output,
                "VRF outputs do not match for input {}",
                i
            );
        }
    }

    #[test]
    fn test_cross_verification_different_keypairs() {
        // Test with multiple different keypairs to ensure consistency
        let input = b"consistent_test_input";

        for i in 0..5 {
            // Generate a new keypair for each iteration
            let mut rng_instance = rng::WasmRng;
            let keypair = ECVRFKeyPair::generate(&mut rng_instance);
            let pk_bytes = keypair.public_key().to_byte_array();

            // Generate VRF proof using vrf-wasm
            let proof = keypair.prove(input);
            let vrf_output = proof.to_hash();

            // Extract proof components and reconstruct proof bytes
            let (gamma_bytes, challenge_bytes, scalar_bytes) = proof.to_components();
            let mut proof_bytes = Vec::with_capacity(80);
            proof_bytes.extend_from_slice(&gamma_bytes);
            proof_bytes.extend_from_slice(&challenge_bytes);
            proof_bytes.extend_from_slice(&scalar_bytes);

            // Verify using vrf-contract-verifier
            let verification_result = verifiers::verify_vrf(
                &proof_bytes,
                &pk_bytes,
                input,
            );

            // Assert verification succeeds
            assert!(
                verification_result.is_ok(),
                "VRF proof verification failed for keypair {}: {:?}",
                i,
                verification_result.err()
            );

            // Assert the output matches
            let verified_output = verification_result.unwrap();
            assert_eq!(
                verified_output, vrf_output,
                "VRF outputs do not match for keypair {}",
                i
            );
        }
    }

    #[test]
    fn test_cross_verification_invalid_proof() {
        // Test that invalid proofs are correctly rejected
        let mut rng_instance = rng::WasmRng;
        let keypair = ECVRFKeyPair::generate(&mut rng_instance);
        let pk_bytes = keypair.public_key().to_byte_array();

        let input = b"test_input";

        // Generate a valid proof
        let proof = keypair.prove(input);
        let (gamma_bytes, challenge_bytes, scalar_bytes) = proof.to_components();

        // Test 1: Corrupt the gamma component
        let mut corrupted_proof_bytes = Vec::with_capacity(80);
        let mut corrupted_gamma = gamma_bytes;
        corrupted_gamma[0] ^= 0xFF; // Flip first byte
        corrupted_proof_bytes.extend_from_slice(&corrupted_gamma);
        corrupted_proof_bytes.extend_from_slice(&challenge_bytes);
        corrupted_proof_bytes.extend_from_slice(&scalar_bytes);

        let result = verifiers::verify_vrf(
            &corrupted_proof_bytes,
            &pk_bytes,
            input,
        );
        assert!(result.is_err(), "Corrupted gamma should fail verification");

        // Test 2: Corrupt the challenge component
        let mut corrupted_proof_bytes = Vec::with_capacity(80);
        let mut corrupted_challenge = challenge_bytes;
        corrupted_challenge[0] ^= 0xFF; // Flip first byte
        corrupted_proof_bytes.extend_from_slice(&gamma_bytes);
        corrupted_proof_bytes.extend_from_slice(&corrupted_challenge);
        corrupted_proof_bytes.extend_from_slice(&scalar_bytes);

        let result = verifiers::verify_vrf(
            &corrupted_proof_bytes,
            &pk_bytes,
            input,
        );
        assert!(result.is_err(), "Corrupted challenge should fail verification");

        // Test 3: Corrupt the scalar component
        let mut corrupted_proof_bytes = Vec::with_capacity(80);
        let mut corrupted_scalar = scalar_bytes;
        corrupted_scalar[0] ^= 0xFF; // Flip first byte
        corrupted_proof_bytes.extend_from_slice(&gamma_bytes);
        corrupted_proof_bytes.extend_from_slice(&challenge_bytes);
        corrupted_proof_bytes.extend_from_slice(&corrupted_scalar);

        let result = verifiers::verify_vrf(
            &corrupted_proof_bytes,
            &pk_bytes,
            input,
        );
        assert!(result.is_err(), "Corrupted scalar should fail verification");
    }

    #[test]
    fn test_cross_verification_wrong_public_key() {
        // Test that proofs fail verification with wrong public key
        let mut rng_instance = rng::WasmRng;

        // Generate two different keypairs
        let keypair1 = ECVRFKeyPair::generate(&mut rng_instance);
        let keypair2 = ECVRFKeyPair::generate(&mut rng_instance);

        let input = b"test_input";

        // Generate proof with keypair1
        let proof = keypair1.prove(input);
        let (gamma_bytes, challenge_bytes, scalar_bytes) = proof.to_components();
        let mut proof_bytes = Vec::with_capacity(80);
        proof_bytes.extend_from_slice(&gamma_bytes);
        proof_bytes.extend_from_slice(&challenge_bytes);
        proof_bytes.extend_from_slice(&scalar_bytes);

        // Try to verify with keypair2's public key (should fail)
        let wrong_pk_bytes = keypair2.public_key().to_byte_array();
        let result = verifiers::verify_vrf(
            &proof_bytes,
            &wrong_pk_bytes,
            input,
        );

        assert!(result.is_err(), "Verification with wrong public key should fail");
        assert!(matches!(result.err(), Some(VerificationError::InvalidProof)));
    }

    #[test]
    fn test_cross_verification_wrong_input() {
        // Test that proofs fail verification with wrong input
        let mut rng_instance = rng::WasmRng;
        let keypair = ECVRFKeyPair::generate(&mut rng_instance);
        let pk_bytes = keypair.public_key().to_byte_array();

        let original_input = b"original_input";
        let wrong_input = b"wrong_input";

        // Generate proof with original input
        let proof = keypair.prove(original_input);
        let (gamma_bytes, challenge_bytes, scalar_bytes) = proof.to_components();
        let mut proof_bytes = Vec::with_capacity(80);
        proof_bytes.extend_from_slice(&gamma_bytes);
        proof_bytes.extend_from_slice(&challenge_bytes);
        proof_bytes.extend_from_slice(&scalar_bytes);

        // Try to verify with wrong input (should fail)
        let result = verifiers::verify_vrf(
            &proof_bytes,
            &pk_bytes,
            wrong_input,
        );

        assert!(result.is_err(), "Verification with wrong input should fail");
        assert!(matches!(result.err(), Some(VerificationError::InvalidProof)));
    }

    #[test]
    fn test_cross_verification_fixed_array_api() {
        // Test the fixed-array API as well
        let mut rng_instance = rng::WasmRng;
        let keypair = ECVRFKeyPair::generate(&mut rng_instance);

        let input = b"test_input_for_fixed_api";

        // Generate VRF proof using vrf-wasm
        let proof = keypair.prove(input);
        let vrf_output = proof.to_hash();

        // Extract components and create fixed arrays
        let pk_bytes = keypair.public_key().to_byte_array();
        let (gamma_bytes, challenge_bytes, scalar_bytes) = proof.to_components();

        // Create 80-byte proof array
        let mut proof_array = [0u8; 80];
        proof_array[0..32].copy_from_slice(&gamma_bytes);
        proof_array[32..48].copy_from_slice(&challenge_bytes);
        proof_array[48..80].copy_from_slice(&scalar_bytes);

        // Verify using the fixed-array API
        let verification_result = verifiers::verify_vrf_fixed(
            &proof_array,
            &pk_bytes,
            input,
        );

        // Assert verification succeeds
        assert!(verification_result.is_ok(), "Fixed-array VRF proof verification failed: {:?}", verification_result.err());

        // Assert the output matches
        let verified_output = verification_result.unwrap();
        assert_eq!(verified_output, vrf_output, "Fixed-array VRF outputs do not match");
    }

    #[test]
    fn test_cross_verification_boolean_api() {
        // Test the boolean verification API
        let mut rng_instance = rng::WasmRng;
        let keypair = ECVRFKeyPair::generate(&mut rng_instance);
        let pk_bytes = keypair.public_key().to_byte_array();

        let input = b"test_input_for_boolean_api";

        // Generate VRF proof using vrf-wasm
        let proof = keypair.prove(input);
        let (gamma_bytes, challenge_bytes, scalar_bytes) = proof.to_components();

        // Create proof bytes
        let mut proof_bytes = Vec::with_capacity(80);
        proof_bytes.extend_from_slice(&gamma_bytes);
        proof_bytes.extend_from_slice(&challenge_bytes);
        proof_bytes.extend_from_slice(&scalar_bytes);

        // Test valid proof
        let is_valid = verifiers::verify_vrf_bool(
            proof_bytes.clone(),
            pk_bytes.to_vec(),
            input.to_vec(),
        );
        assert!(is_valid, "Valid proof should return true");

        // Test invalid proof (corrupted)
        let mut invalid_proof = proof_bytes;
        invalid_proof[0] ^= 0xFF; // Corrupt first byte
        let is_valid = verifiers::verify_vrf_bool(
            invalid_proof,
            pk_bytes.to_vec(),
            input.to_vec(),
        );
        assert!(!is_valid, "Invalid proof should return false");
    }

    #[test]
    fn test_proof_component_structure() {
        // Generate a VRF keypair using the main vrf-wasm library
        let mut rng_instance = rng::WasmRng;
        let keypair = ECVRFKeyPair::generate(&mut rng_instance);

        // Create test input
        let input = b"test_input_for_vrf";

        // Generate VRF proof using vrf-wasm
        let proof = keypair.prove(input);
        let vrf_output = proof.to_hash();

        // Extract proof components
        let (gamma_bytes, challenge_bytes, scalar_bytes) = proof.to_components();

        // Verify the component structure matches what vrf-contract-verifier expects
        assert_eq!(gamma_bytes.len(), 32, "Gamma component should be 32 bytes");
        assert_eq!(challenge_bytes.len(), 16, "Challenge component should be 16 bytes");
        assert_eq!(scalar_bytes.len(), 32, "Scalar component should be 32 bytes");

        // Verify we can reconstruct the 80-byte proof format
        let mut proof_bytes = Vec::with_capacity(80);
        proof_bytes.extend_from_slice(&gamma_bytes);
        proof_bytes.extend_from_slice(&challenge_bytes);
        proof_bytes.extend_from_slice(&scalar_bytes);

        assert_eq!(proof_bytes.len(), 80, "Complete proof should be exactly 80 bytes");

        // Verify the VRF output is 64 bytes as expected
        assert_eq!(vrf_output.len(), 64, "VRF output should be 64 bytes");

        println!("✅ Proof structure test passed - suite string compatibility verified");
    }

    #[test]
    fn test_suite_string_constants() {
        // This test verifies that both libraries use the same domain separation approach
        // by checking that the constants match

        // The suite string used in vrf-wasm should be "sui_vrf" (7 bytes)
        // The challenge domain separator should be 0x02
        // The output domain separator should be 0x03

        // We can't directly compare constants across crates, but we can verify
        // that our verification logic expects the same structure

        let test_gamma = [1u8; 32];
        let test_challenge = [2u8; 16];
        let test_scalar = [3u8; 32];

        // Construct a test proof in the expected format
        let mut test_proof_bytes = Vec::with_capacity(80);
        test_proof_bytes.extend_from_slice(&test_gamma);
        test_proof_bytes.extend_from_slice(&test_challenge);
        test_proof_bytes.extend_from_slice(&test_scalar);

        assert_eq!(test_proof_bytes.len(), 80);

        // Test that the verifier correctly parses the proof structure
        let test_pk = [4u8; 32];
        let test_input = b"test";

        // This should fail verification (invalid proof), but should parse correctly
        let result = verifiers::verify_vrf(
            &test_proof_bytes,
            &test_pk,
            test_input,
        );

        // We expect this to fail, but not due to parsing errors
        assert!(result.is_err(), "Invalid test proof should fail verification");

        println!("✅ Suite string structure test passed");
    }
}