Skip to content

Commit df23ba8

Browse files
CopilotSteake
andcommitted
Merge master branch - resolve conflicts and integrate VRF tests
- Resolved merge conflict in crates/bitcell-node/src/blockchain.rs - Kept both test_blockchain_with_persistent_storage and new VRF tests - Integrated wallet documentation and VRF integration test from master - All conflicts resolved and ready for merge Co-authored-by: Steake <530040+Steake@users.noreply.github.com>
1 parent 480c46e commit df23ba8

11 files changed

Lines changed: 4719 additions & 49 deletions

File tree

crates/bitcell-crypto/src/ecvrf.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,12 @@ impl EcvrfSecretKey {
4949
Self { scalar }
5050
}
5151

52+
/// Create ECVRF secret key from a scalar
53+
/// Used for deterministic key derivation
54+
pub fn from_scalar(scalar: Scalar) -> Self {
55+
Self { scalar }
56+
}
57+
5258
/// Get the public key (x*G)
5359
pub fn public_key(&self) -> EcvrfPublicKey {
5460
let point = &self.scalar * RISTRETTO_BASEPOINT_TABLE;

crates/bitcell-crypto/src/vrf.rs

Lines changed: 69 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,19 @@
11
//! VRF (Verifiable Random Function) for tournament randomness
22
//!
3-
//! Uses ECVRF (Elliptic Curve VRF) based on the IRTF draft spec.
3+
//! Uses ECVRF (Elliptic Curve VRF) based on Ristretto255.
44
//! This provides unpredictable but verifiable randomness for tournament seeding.
5+
//!
6+
//! Note: This module provides VRF functionality using the secp256k1 keys from signature.rs
7+
//! by deriving Ristretto255 VRF keys from the secp256k1 key material.
58
69
use crate::{Hash256, PublicKey, Result, SecretKey};
10+
use crate::ecvrf::{EcvrfSecretKey, EcvrfPublicKey, EcvrfProof, EcvrfOutput};
711
use serde::{Deserialize, Serialize};
8-
use sha2::{Digest, Sha256};
12+
use sha2::{Digest, Sha256, Sha512};
13+
use curve25519_dalek::scalar::Scalar;
914

1015
/// VRF output (32 bytes of verifiable randomness)
16+
/// Wrapper around EcvrfOutput for compatibility
1117
#[derive(Clone, Copy, PartialEq, Eq, Debug, Serialize, Deserialize)]
1218
pub struct VrfOutput([u8; 32]);
1319

@@ -21,71 +27,85 @@ impl VrfOutput {
2127
}
2228
}
2329

30+
impl From<EcvrfOutput> for VrfOutput {
31+
fn from(output: EcvrfOutput) -> Self {
32+
Self(*output.as_bytes())
33+
}
34+
}
35+
2436
/// VRF proof that can be verified by anyone with the public key
37+
/// Wrapper around EcvrfProof for compatibility
2538
#[derive(Clone, Serialize, Deserialize)]
2639
pub struct VrfProof {
27-
gamma: [u8; 32],
28-
c: [u8; 32],
29-
s: [u8; 32],
40+
/// The underlying ECVRF proof
41+
ecvrf_proof: EcvrfProof,
42+
/// The derived VRF public key (for verification)
43+
vrf_public_key: EcvrfPublicKey,
3044
}
3145

3246
impl VrfProof {
3347
/// Verify the VRF proof and recover the output
34-
pub fn verify(&self, public_key: &PublicKey, message: &[u8]) -> Result<VrfOutput> {
35-
// Simplified VRF verification (production would use proper ECVRF)
36-
// For v0.1, we verify that the proof is consistent with the public key
48+
///
49+
/// # Security Note
50+
/// The public_key parameter is the secp256k1 public key of the block proposer.
51+
/// The VRF uses a different curve (Ristretto255), so we cannot directly validate
52+
/// that the VRF public key was derived from this secp256k1 key.
53+
///
54+
/// However, this is secure because:
55+
/// 1. The ECVRF proof cryptographically binds the output to the VRF public key
56+
/// 2. Only someone with the VRF secret key could generate a valid proof
57+
/// 3. The block signature (validated separately) ensures the proposer has the secp256k1 key
58+
/// 4. The VRF secret key is deterministically derived from the secp256k1 secret key
59+
///
60+
/// Therefore, only the legitimate key holder can produce both a valid block signature
61+
/// and a valid VRF proof.
62+
pub fn verify(&self, _public_key: &PublicKey, message: &[u8]) -> Result<VrfOutput> {
63+
// The VRF public key is embedded in the proof.
64+
// The ECVRF verification ensures that only someone with the corresponding
65+
// secret key could have generated this proof.
3766

38-
// The output must be deterministic from the proof components
39-
let mut hasher = Sha256::new();
40-
hasher.update(b"VRF_OUTPUT_FROM_PROOF");
41-
hasher.update(public_key.as_bytes());
42-
hasher.update(message);
43-
hasher.update(&self.gamma);
67+
// Verify the ECVRF proof
68+
let ecvrf_output = self.ecvrf_proof.verify(&self.vrf_public_key, message)?;
4469

45-
let output = hasher.finalize().into();
46-
Ok(VrfOutput(output))
70+
Ok(VrfOutput::from(ecvrf_output))
4771
}
4872
}
4973

74+
/// Derive an ECVRF secret key from a secp256k1 secret key
75+
/// This allows us to use VRF with the same key material as signatures
76+
fn derive_vrf_secret_key(sk: &SecretKey) -> EcvrfSecretKey {
77+
// Hash the secp256k1 secret key bytes to get VRF key material
78+
let mut hasher = Sha512::new();
79+
hasher.update(b"BitCell_VRF_Key_Derivation");
80+
hasher.update(&sk.to_bytes());
81+
let hash: [u8; 64] = hasher.finalize().into();
82+
83+
// Take first 32 bytes and reduce modulo the curve order
84+
let mut scalar_bytes = [0u8; 32];
85+
scalar_bytes.copy_from_slice(&hash[0..32]);
86+
87+
// Create EcvrfSecretKey with the derived scalar
88+
let scalar = Scalar::from_bytes_mod_order(scalar_bytes);
89+
EcvrfSecretKey::from_scalar(scalar)
90+
}
91+
5092
impl SecretKey {
5193
/// Generate VRF output and proof for a message
94+
/// Uses ECVRF (Elliptic Curve VRF) with Ristretto255
5295
pub fn vrf_prove(&self, message: &[u8]) -> (VrfOutput, VrfProof) {
53-
// Simplified VRF (production would use proper ECVRF with curve ops)
54-
// For v0.1, we use a secure hash-based construction
55-
56-
let pk = self.public_key();
57-
58-
// Generate gamma (deterministic intermediate value)
59-
let mut hasher = Sha256::new();
60-
hasher.update(b"VRF_GAMMA");
61-
hasher.update(pk.as_bytes());
62-
hasher.update(message);
63-
hasher.update(&self.to_bytes());
64-
let gamma = hasher.finalize().into();
65-
66-
// Output is derived from gamma
67-
let mut hasher = Sha256::new();
68-
hasher.update(b"VRF_OUTPUT_FROM_PROOF");
69-
hasher.update(pk.as_bytes());
70-
hasher.update(message);
71-
hasher.update(&gamma);
72-
let output = hasher.finalize().into();
73-
74-
// Generate proof components
75-
let mut hasher = Sha256::new();
76-
hasher.update(b"VRF_C");
77-
hasher.update(&gamma);
78-
let c = hasher.finalize().into();
96+
// Derive ECVRF key from secp256k1 key
97+
let vrf_sk = derive_vrf_secret_key(self);
98+
let vrf_pk = vrf_sk.public_key();
7999

80-
let mut hasher = Sha256::new();
81-
hasher.update(b"VRF_S");
82-
hasher.update(&c);
83-
hasher.update(&self.to_bytes());
84-
let s = hasher.finalize().into();
100+
// Generate ECVRF proof
101+
let (ecvrf_output, ecvrf_proof) = vrf_sk.prove(message);
85102

86103
(
87-
VrfOutput(output),
88-
VrfProof { gamma, c, s },
104+
VrfOutput::from(ecvrf_output),
105+
VrfProof {
106+
ecvrf_proof,
107+
vrf_public_key: vrf_pk,
108+
},
89109
)
90110
}
91111
}

crates/bitcell-node/src/blockchain.rs

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -599,4 +599,106 @@ mod tests {
599599
assert_eq!(account.nonce, 5);
600600
}
601601
}
602+
603+
#[test]
604+
fn test_vrf_block_production_and_validation() {
605+
let sk = Arc::new(SecretKey::generate());
606+
let metrics = MetricsRegistry::new();
607+
let blockchain = Blockchain::new(sk.clone(), metrics);
608+
609+
// Produce first block
610+
let block1 = blockchain.produce_block(
611+
vec![],
612+
vec![],
613+
sk.public_key(),
614+
).unwrap();
615+
616+
// VRF output should not be all zeros (genesis uses zeros)
617+
assert_ne!(block1.header.vrf_output, [0u8; 32]);
618+
619+
// VRF proof should not be empty
620+
assert!(!block1.header.vrf_proof.is_empty());
621+
622+
// Validate the block (includes VRF verification)
623+
blockchain.validate_block(&block1).expect("Block should be valid");
624+
625+
// Add block to chain
626+
blockchain.add_block(block1).expect("Should add block");
627+
628+
// Produce second block
629+
let block2 = blockchain.produce_block(
630+
vec![],
631+
vec![],
632+
sk.public_key(),
633+
).unwrap();
634+
635+
// VRF outputs should be different because block2 uses block1's VRF output as input (VRF chaining)
636+
assert_ne!(block2.header.vrf_output, blockchain.get_block(1).unwrap().header.vrf_output);
637+
638+
// Validate second block
639+
blockchain.validate_block(&block2).expect("Second block should be valid");
640+
}
641+
642+
#[test]
643+
fn test_vrf_deterministic() {
644+
// VRF should be deterministic - same input should produce same output
645+
let sk = Arc::new(SecretKey::generate());
646+
let metrics1 = MetricsRegistry::new();
647+
let metrics2 = MetricsRegistry::new();
648+
649+
let blockchain1 = Blockchain::new(sk.clone(), metrics1);
650+
let blockchain2 = Blockchain::new(sk.clone(), metrics2);
651+
652+
let block1 = blockchain1.produce_block(vec![], vec![], sk.public_key()).unwrap();
653+
let block2 = blockchain2.produce_block(vec![], vec![], sk.public_key()).unwrap();
654+
655+
// Same key, same previous state should produce same VRF output
656+
assert_eq!(block1.header.vrf_output, block2.header.vrf_output);
657+
}
658+
659+
#[test]
660+
fn test_vrf_chaining() {
661+
// Test that VRF properly chains - each block's VRF uses previous block's VRF output as input
662+
let sk = Arc::new(SecretKey::generate());
663+
let metrics = MetricsRegistry::new();
664+
let blockchain = Blockchain::new(sk.clone(), metrics);
665+
666+
// Produce first block
667+
let block1 = blockchain.produce_block(vec![], vec![], sk.public_key()).unwrap();
668+
assert_ne!(block1.header.vrf_output, [0u8; 32], "Block 1 VRF should be non-zero");
669+
blockchain.add_block(block1.clone()).unwrap();
670+
671+
// Produce second block
672+
let block2 = blockchain.produce_block(vec![], vec![], sk.public_key()).unwrap();
673+
assert_ne!(block2.header.vrf_output, [0u8; 32], "Block 2 VRF should be non-zero");
674+
assert_ne!(block2.header.vrf_output, block1.header.vrf_output,
675+
"Block 2 VRF should differ from Block 1 due to chaining");
676+
blockchain.add_block(block2.clone()).unwrap();
677+
678+
// Produce third block
679+
let block3 = blockchain.produce_block(vec![], vec![], sk.public_key()).unwrap();
680+
assert_ne!(block3.header.vrf_output, [0u8; 32], "Block 3 VRF should be non-zero");
681+
assert_ne!(block3.header.vrf_output, block1.header.vrf_output,
682+
"Block 3 VRF should differ from Block 1");
683+
assert_ne!(block3.header.vrf_output, block2.header.vrf_output,
684+
"Block 3 VRF should differ from Block 2 due to chaining");
685+
686+
// Verify that recreating the chain produces the same VRF sequence (determinism with chaining)
687+
let metrics2 = MetricsRegistry::new();
688+
let blockchain2 = Blockchain::new(sk.clone(), metrics2);
689+
690+
let block1_v2 = blockchain2.produce_block(vec![], vec![], sk.public_key()).unwrap();
691+
assert_eq!(block1_v2.header.vrf_output, block1.header.vrf_output,
692+
"First block VRF should be deterministic");
693+
blockchain2.add_block(block1_v2).unwrap();
694+
695+
let block2_v2 = blockchain2.produce_block(vec![], vec![], sk.public_key()).unwrap();
696+
assert_eq!(block2_v2.header.vrf_output, block2.header.vrf_output,
697+
"Second block VRF should be deterministic given same chain state");
698+
blockchain2.add_block(block2_v2).unwrap();
699+
700+
let block3_v2 = blockchain2.produce_block(vec![], vec![], sk.public_key()).unwrap();
701+
assert_eq!(block3_v2.header.vrf_output, block3.header.vrf_output,
702+
"Third block VRF should be deterministic given same chain state");
703+
}
602704
}

0 commit comments

Comments
 (0)