@@ -68,6 +68,33 @@ pub struct R1CSWitness<E: Engine> {
6868 pub ( crate ) r_W : E :: Scalar ,
6969}
7070
71+ /// A blinding factor for an R1CS witness commitment.
72+ ///
73+ /// Use [`Self::random`] for ordinary proving. Protocols that support exact
74+ /// replay may reconstruct a previously derived value with
75+ /// [`Self::from_scalar`].
76+ #[ derive( Clone , Copy , Debug , PartialEq , Eq ) ]
77+ pub struct R1CSWitnessBlind < E : Engine > ( E :: Scalar ) ;
78+
79+ impl < E : Engine > R1CSWitnessBlind < E > {
80+ /// Samples a fresh witness commitment blinding factor.
81+ pub fn random ( ) -> Self {
82+ Self ( E :: Scalar :: random ( & mut OsRng ) )
83+ }
84+
85+ /// Wraps a protocol-derived blinding factor.
86+ ///
87+ /// The caller must ensure the scalar was derived from secret randomness with
88+ /// a unique domain and coordinates. Reuse it only to replay the same proof.
89+ pub fn from_scalar ( blind : E :: Scalar ) -> Self {
90+ Self ( blind)
91+ }
92+
93+ fn into_scalar ( self ) -> E :: Scalar {
94+ self . 0
95+ }
96+ }
97+
7198/// A type that holds an R1CS instance
7299#[ serde_as]
73100#[ derive( Clone , Debug , PartialEq , Eq , Serialize , Deserialize ) ]
@@ -805,14 +832,24 @@ impl<E: Engine> R1CSShape<E> {
805832}
806833
807834impl < E : Engine > R1CSWitness < E > {
808- /// A method to create a witness object using a vector of scalars
835+ /// Creates a witness with a fresh random commitment blinding factor.
836+ /// The sampled blind is available through [`Self::r_W`].
809837 pub fn new ( S : & R1CSShape < E > , W : & [ E :: Scalar ] ) -> Result < R1CSWitness < E > , NovaError > {
838+ Self :: new_with_blind ( S , W , R1CSWitnessBlind :: random ( ) )
839+ }
840+
841+ /// Creates a witness using an explicit typed commitment blinding factor.
842+ pub fn new_with_blind (
843+ S : & R1CSShape < E > ,
844+ W : & [ E :: Scalar ] ,
845+ blind : R1CSWitnessBlind < E > ,
846+ ) -> Result < R1CSWitness < E > , NovaError > {
810847 let mut W = W . to_vec ( ) ;
811848 W . resize ( S . num_vars , E :: Scalar :: ZERO ) ;
812849
813850 Ok ( R1CSWitness {
814851 W ,
815- r_W : E :: Scalar :: random ( & mut OsRng ) ,
852+ r_W : blind . into_scalar ( ) ,
816853 } )
817854 }
818855
@@ -1436,6 +1473,29 @@ mod tests {
14361473 test_random_sample_with :: < Secp256k1Engine > ( ) ;
14371474 }
14381475
1476+ #[ test]
1477+ fn test_witness_with_blind_is_deterministic ( ) {
1478+ let shape = tiny_r1cs :: < Bn256EngineKZG > ( 4 ) ;
1479+ let ck = R1CSShape :: commitment_key ( & [ & shape] , & [ & * default_ck_hint ( ) ] ) . unwrap ( ) ;
1480+ let values = vec ! [ <Bn256EngineKZG as Engine >:: Scalar :: ONE ; 3 ] ;
1481+ let blind = <Bn256EngineKZG as Engine >:: Scalar :: from ( 42_u64 ) ;
1482+
1483+ let witness_1 =
1484+ R1CSWitness :: new_with_blind ( & shape, & values, R1CSWitnessBlind :: from_scalar ( blind) ) . unwrap ( ) ;
1485+ let witness_2 =
1486+ R1CSWitness :: new_with_blind ( & shape, & values, R1CSWitnessBlind :: from_scalar ( blind) ) . unwrap ( ) ;
1487+ let witness_3 = R1CSWitness :: new_with_blind (
1488+ & shape,
1489+ & values,
1490+ R1CSWitnessBlind :: from_scalar ( <Bn256EngineKZG as Engine >:: Scalar :: from ( 43_u64 ) ) ,
1491+ )
1492+ . unwrap ( ) ;
1493+
1494+ assert_eq ! ( witness_1, witness_2) ;
1495+ assert_eq ! ( witness_1. commit( & ck) , witness_2. commit( & ck) ) ;
1496+ assert_ne ! ( witness_1. commit( & ck) , witness_3. commit( & ck) ) ;
1497+ }
1498+
14391499 fn test_multiply_vec_pair_with < E : Engine > ( ) {
14401500 // tiny_r1cs(4) has num_cons=4, num_vars=4, num_io=2
14411501 // z has length num_vars + 1 + num_io = 7
0 commit comments