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