ergotree_interpreter/sigma_protocol/
dht_protocol.rs

1//! Discrete logarithm signature protocol
2
3use super::wscalar::Wscalar;
4use super::ProverMessage;
5use ergo_chain_types::EcPoint;
6use ergotree_ir::serialization::SigmaSerializable;
7
8/// a = g^r, b = h^r
9#[derive(PartialEq, Eq, Debug, Clone)]
10#[cfg_attr(feature = "json", derive(serde::Serialize, serde::Deserialize))]
11#[cfg_attr(feature = "arbitrary", derive(proptest_derive::Arbitrary))]
12pub struct FirstDhTupleProverMessage {
13    #[cfg_attr(feature = "json", serde(rename = "a"))]
14    a: Box<EcPoint>,
15    #[cfg_attr(feature = "json", serde(rename = "b"))]
16    b: Box<EcPoint>,
17}
18
19impl FirstDhTupleProverMessage {
20    /// First message from the prover (message `a` and `b` of `SigmaProtocol`) for DhTuple case
21    pub fn new(a: EcPoint, b: EcPoint) -> Self {
22        Self {
23            a: a.into(),
24            b: b.into(),
25        }
26    }
27}
28
29impl ProverMessage for FirstDhTupleProverMessage {
30    #[allow(clippy::unwrap_used)] // since only EcPoint is serialized here it's safe to unwrap
31    fn bytes(&self) -> Vec<u8> {
32        let mut res = self.a.sigma_serialize_bytes().unwrap();
33        res.append(self.b.sigma_serialize_bytes().unwrap().as_mut());
34        res
35    }
36}
37
38/// Second message from the prover (message `z` of `SigmaProtocol`) for DhTuple case
39//z = r + ew mod q
40#[derive(PartialEq, Eq, Debug, Clone)]
41#[cfg_attr(feature = "arbitrary", derive(proptest_derive::Arbitrary))]
42pub struct SecondDhTupleProverMessage {
43    /// message `z`
44    pub z: Wscalar,
45}
46
47/// Interactive prover
48pub mod interactive_prover {
49
50    use std::ops::Mul;
51
52    use super::*;
53    use crate::sigma_protocol::crypto_utils;
54    use crate::sigma_protocol::private_input::DhTupleProverInput;
55    use crate::sigma_protocol::Challenge;
56    use ergotree_ir::sigma_protocol::dlog_group;
57    use ergotree_ir::sigma_protocol::sigma_boolean::ProveDhTuple;
58    use k256::Scalar;
59
60    /// Step 5 from <https://ergoplatform.org/docs/ErgoScript.pdf>
61    /// For every leaf marked “simulated”, use the simulator of the sigma protocol for that leaf
62    /// to compute the commitment "a" and the response "z", given the challenge "e" that
63    /// is already stored in the leaf
64    pub(crate) fn simulate(
65        public_input: &ProveDhTuple,
66        challenge: &Challenge,
67    ) -> (FirstDhTupleProverMessage, SecondDhTupleProverMessage) {
68        use ergo_chain_types::ec_point::exponentiate;
69        //SAMPLE a random z <- Zq
70        let z = dlog_group::random_scalar_in_group_range(crypto_utils::secure_rng());
71
72        // COMPUTE a = g^z*u^(-e) and b = h^z*v^{-e}  (where -e here means -e mod q)
73        let e: Scalar = challenge.clone().into();
74        let minus_e = e.negate();
75        let h_to_z = exponentiate(&public_input.h, &z);
76        let g_to_z = exponentiate(&public_input.g, &z);
77        let u_to_minus_e = exponentiate(&public_input.u, &minus_e);
78        let v_to_minus_e = exponentiate(&public_input.v, &minus_e);
79        let a = g_to_z.mul(&u_to_minus_e);
80        let b = h_to_z.mul(&v_to_minus_e);
81        (
82            FirstDhTupleProverMessage::new(a, b),
83            SecondDhTupleProverMessage { z: z.into() },
84        )
85    }
86
87    /// Step 6 from <https://ergoplatform.org/docs/ErgoScript.pdf>
88    /// For every leaf marked “real”, use the first prover step of the sigma protocol for
89    /// that leaf to compute the necessary randomness "r" and the commitment "a"
90    ///
91    /// In this case (DH tuple) "a" is also a tuple
92    pub fn first_message(public_input: &ProveDhTuple) -> (Wscalar, FirstDhTupleProverMessage) {
93        use ergo_chain_types::ec_point::exponentiate;
94        let r = dlog_group::random_scalar_in_group_range(crypto_utils::secure_rng());
95        let a = exponentiate(&public_input.g, &r);
96        let b = exponentiate(&public_input.h, &r);
97        (r.into(), FirstDhTupleProverMessage::new(a, b))
98    }
99
100    /// Step 9 part 2 from <https://ergoplatform.org/docs/ErgoScript.pdf>
101    /// compute its response "z" according to the second prover step(step 5 in whitepaper)
102    /// of the sigma protocol given the randomness "r"(rnd) used for the commitment "a",
103    /// the challenge "e", and witness w.
104    pub(crate) fn second_message(
105        private_input: &DhTupleProverInput,
106        rnd: &Wscalar,
107        challenge: &Challenge,
108    ) -> SecondDhTupleProverMessage {
109        let e: Scalar = challenge.clone().into();
110        // modulo multiplication, no need to explicit mod op
111        let ew = e.mul(private_input.w.as_scalar_ref());
112        // modulo addition, no need to explicit mod op
113        let z = rnd.as_scalar_ref().add(&ew);
114        SecondDhTupleProverMessage { z: z.into() }
115    }
116
117    /// The function computes initial prover's commitment to randomness
118    /// ("a" message of the sigma-protocol, which in this case has two parts "a" and "b")
119    /// based on the verifier's challenge ("e")
120    /// and prover's response ("z")
121    ///
122    /// g^z = a*u^e, h^z = b*v^e  => a = g^z/u^e, b = h^z/v^e
123    #[allow(clippy::many_single_char_names)]
124    pub fn compute_commitment(
125        proposition: &ProveDhTuple,
126        challenge: &Challenge,
127        second_message: &SecondDhTupleProverMessage,
128    ) -> (EcPoint, EcPoint) {
129        let g = proposition.g.clone();
130        let h = proposition.h.clone();
131        let u = proposition.u.clone();
132        let v = proposition.v.clone();
133
134        let z = second_message.z.clone();
135
136        let e: Scalar = challenge.clone().into();
137
138        use ergo_chain_types::ec_point::{exponentiate, inverse};
139
140        let g_to_z = exponentiate(&g, z.as_scalar_ref());
141        let h_to_z = exponentiate(&h, z.as_scalar_ref());
142
143        let u_to_e = exponentiate(&u, &e);
144        let v_to_e = exponentiate(&v, &e);
145
146        let a = g_to_z.mul(&inverse(&u_to_e));
147        let b = h_to_z.mul(&inverse(&v_to_e));
148        (a, b)
149    }
150}