Skip to main content

seedelf_crypto/
register.rs

1use crate::schnorr::random_scalar;
2use anyhow::{Context, Result, anyhow};
3use blstrs::{G1Affine, G1Projective, Scalar};
4use hex;
5use hex::FromHex;
6use pallas_primitives::{
7    BoundedBytes, Fragment,
8    alonzo::{Constr, MaybeIndefArray, PlutusData},
9};
10use serde::{Deserialize, Serialize};
11
12/// Represents a cryptographic register containing a generator and a public value.
13///
14/// The `Register` struct holds two points in compressed hex string format:
15/// - `generator`: A generator point in G1.
16/// - `public_value`: A public value computed as `generator * sk` where `sk` is a scalar.
17///
18/// It provides methods for creating, serializing, rerandomizing, and verifying ownership of the register.
19#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Hash, Clone, Default)]
20pub struct Register {
21    pub generator: String,
22    pub public_value: String,
23}
24
25impl Register {
26    /// Creates a new `Register` with the specified generator and public value.
27    ///
28    /// # Arguments
29    ///
30    /// * `generator` - A compressed hex string representing the generator point.
31    /// * `public_value` - A compressed hex string representing the public value point.
32    pub fn new(generator: String, public_value: String) -> Self {
33        Self {
34            generator,
35            public_value,
36        }
37    }
38
39    /// Generates a `Register` using a provided scalar (`sk`).
40    ///
41    /// This method decompresses a hardcoded generator point, multiplies it by the scalar
42    /// to compute the public value, and compresses both points into hex strings.
43    ///
44    /// # Arguments
45    ///
46    /// * `sk` - A scalar used to compute the public value.
47    ///
48    /// # Returns
49    ///
50    /// * A new `Register` with compressed generator and public value.
51    pub fn create(sk: Scalar) -> Result<Self> {
52        // Decode and decompress generator
53        let compressed_g1_generator: &str = "97F1D3A73197D7942695638C4FA9AC0FC3688C4F9774B905A14E3A3F171BAC586C55E83FF97A1AEFFB3AF00ADB22C6BB";
54
55        let g1_generator: G1Affine = G1Affine::from_compressed(
56            &hex::decode(compressed_g1_generator)
57                .context("Failed to decode generator hex")?
58                .try_into()
59                .map_err(|e| anyhow::anyhow!("{e:?}"))?,
60        )
61        .into_option()
62        .ok_or_else(|| anyhow!("Failed to decompress generator"))?;
63
64        let public_value: G1Projective = G1Projective::from(g1_generator) * sk;
65
66        // Compress points and return them as hex strings
67        Ok(Self {
68            generator: hex::encode(g1_generator.to_compressed()),
69            public_value: hex::encode(public_value.to_compressed()),
70        })
71    }
72
73    /// Converts the `Register` into a serialized vector of bytes using PlutusData encoding.
74    ///
75    /// # Returns
76    ///
77    /// * `Vec<u8>` - A serialized byte vector representing the `Register`.
78    ///
79    /// # Panics
80    ///
81    /// * If the generator or public value are invalid hex strings.
82    pub fn to_vec(&self) -> Result<Vec<u8>> {
83        // convert the strings into vectors
84        let generator_vector: Vec<u8> =
85            Vec::from_hex(&self.generator).context("Invalid hex string")?;
86        let public_value_vector: Vec<u8> =
87            Vec::from_hex(&self.public_value).context("Invalid hex string")?;
88        // construct the plutus data
89        let plutus_data: PlutusData = PlutusData::Constr(Constr {
90            tag: 121,
91            any_constructor: None,
92            fields: MaybeIndefArray::Indef(vec![
93                PlutusData::BoundedBytes(BoundedBytes::from(generator_vector)),
94                PlutusData::BoundedBytes(BoundedBytes::from(public_value_vector)),
95            ]),
96        });
97        plutus_data
98            .encode_fragment()
99            .map_err(|e| anyhow!("Failed to encode PlutusData fragment: {e}"))
100    }
101
102    /// Rerandomizes the `Register` using a new random scalar.
103    ///
104    /// This method multiplies both the generator and the public value by a new random scalar,
105    /// producing a rerandomized `Register`.
106    ///
107    /// # Returns
108    ///
109    /// * A new `Register` instance with rerandomized points.
110    pub fn rerandomize(self) -> Result<Self> {
111        // Decode and decompress generator
112        let g1: G1Affine = G1Affine::from_compressed(
113            &hex::decode(self.generator)
114                .context("Failed to decode generator hex")?
115                .try_into()
116                .map_err(|e| anyhow::anyhow!("{e:?}"))?,
117        )
118        .into_option()
119        .ok_or_else(|| anyhow!("Failed to decompress generator"))?;
120
121        // Decode and decompress public_value
122        let u: G1Affine = G1Affine::from_compressed(
123            &hex::decode(self.public_value)
124                .context("Failed to decode public value hex")?
125                .try_into()
126                .map_err(|e| anyhow::anyhow!("{e:?}"))?,
127        )
128        .into_option()
129        .ok_or_else(|| anyhow!("Failed to decompress generator"))?;
130
131        // get a random scalar
132        let d: Scalar = random_scalar();
133
134        // Multiply points by the scalar in G1Projective
135        let g1_randomized: G1Projective = G1Projective::from(g1) * d;
136        let u_randomized: G1Projective = G1Projective::from(u) * d;
137
138        // Compress points and return them as hex strings
139        Ok(Self {
140            generator: hex::encode(g1_randomized.to_compressed()),
141            public_value: hex::encode(u_randomized.to_compressed()),
142        })
143    }
144
145    /// Verifies ownership of the `Register` using a provided scalar (`sk`).
146    ///
147    /// This method checks if the public value in the `Register` matches the generator
148    /// multiplied by the scalar.
149    ///
150    /// # Arguments
151    ///
152    /// * `sk` - The scalar to verify ownership.
153    ///
154    /// # Returns
155    ///
156    /// * `true` - If the scalar matches and proves ownership.
157    /// * `false` - Otherwise.
158    pub fn is_owned(&self, sk: Scalar) -> Result<bool> {
159        let g1: G1Affine = G1Affine::from_compressed(
160            &hex::decode(&self.generator)
161                .context("Failed to decode generator hex")?
162                .try_into()
163                .map_err(|e| anyhow::anyhow!("{e:?}"))?,
164        )
165        .into_option()
166        .ok_or_else(|| anyhow!("Failed to decompress generator"))?;
167
168        let g_x: G1Projective = G1Projective::from(g1) * sk;
169
170        Ok(hex::encode(g_x.to_compressed()) == self.public_value)
171    }
172
173    pub fn is_valid(&self) -> Result<bool> {
174        // Decode and decompress generator
175        let g1: G1Affine = G1Affine::from_compressed(
176            &hex::decode(&self.generator)
177                .context("Failed to decode generator hex")?
178                .try_into()
179                .map_err(|e| anyhow::anyhow!("{e:?}"))?,
180        )
181        .into_option()
182        .ok_or_else(|| anyhow!("Failed to decompress generator"))?;
183
184        // Decode and decompress public_value
185        let u: G1Affine = G1Affine::from_compressed(
186            &hex::decode(&self.public_value)
187                .context("Failed to decode public value hex")?
188                .try_into()
189                .map_err(|e| anyhow::anyhow!("{e:?}"))?,
190        )
191        .into_option()
192        .ok_or_else(|| anyhow!("Failed to decompress generator"))?;
193
194        Ok(g1.is_on_curve().into()
195            && g1.is_torsion_free().into()
196            && u.is_on_curve().into()
197            && u.is_torsion_free().into())
198    }
199}