Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ plonky2 = { git = "https://github.com/0xPolygonZero/plonky2", optional = true }
serde = "1.0.219"
serde_json = "1.0.140"
base64 = "0.22.1"
bs58 = "0.5.1"
schemars = "0.8.22"
num = { version = "0.4.3", features = ["num-bigint"] }
num-bigint = { version = "0.4.6", features = ["rand"] }
Expand Down
2 changes: 1 addition & 1 deletion examples/signed_pod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
// Create a schnorr key pair to sign the pod
let sk = SecretKey::new_rand();
let pk = sk.public_key();
println!("Public key: {:?}\n", pk);
println!("Public key: {}\n", pk);

let mut signer = Signer(sk);

Expand Down
89 changes: 83 additions & 6 deletions src/backends/plonky2/primitives/ec/curve.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
//! We roughly follow pornin/ecgfp5.
use core::ops::{Add, Mul};
use std::{
array,
array, fmt,
ops::{AddAssign, Neg, Sub},
sync::LazyLock,
};
Expand All @@ -23,7 +23,7 @@ use plonky2::{
util::serialization::{Read, Write},
};
use rand::rngs::OsRng;
use serde::{Deserialize, Serialize};
use serde::{Deserialize, Deserializer, Serialize, Serializer};

use crate::backends::plonky2::{
circuits::common::ValueTarget,
Expand Down Expand Up @@ -94,12 +94,65 @@ fn ec_field_from_bytes(b: &[u8]) -> Result<ECField, Error> {
Ok(QuinticExtension(array::from_fn(|i| fields[i])))
}

#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Point {
pub x: ECField,
pub u: ECField,
}

impl fmt::Display for Point {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
#[allow(clippy::collapsible_else_if)]
if f.alternate() {
write!(f, "({}, {})", self.x, self.u)
} else {
if self.is_in_subgroup() {
// Compressed
let u_bytes = self.as_bytes_from_subgroup().expect("point in subgroup");
let u_b58 = bs58::encode(u_bytes).into_string();
write!(f, "{}", u_b58)
} else {
// Non-compressed
let xu_bytes = [ec_field_to_bytes(&self.x), ec_field_to_bytes(&self.u)].concat();
let xu_b58 = bs58::encode(xu_bytes).into_string();
write!(f, "{}", xu_b58)
}
}
}
}

impl Serialize for Point {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let point_b58 = format!("{}", self);
serializer.serialize_str(&point_b58)
}
}

impl<'de> Deserialize<'de> for Point {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let point_b58 = String::deserialize(deserializer)?;
let point_bytes: Vec<u8> = bs58::decode(point_b58)
.into_vec()
.map_err(serde::de::Error::custom)?;
if point_bytes.len() == 80 {
// Non-compressed
Ok(Point {
x: ec_field_from_bytes(&point_bytes[..40]).map_err(serde::de::Error::custom)?,
u: ec_field_from_bytes(&point_bytes[40..]).map_err(serde::de::Error::custom)?,
})
} else {
// Compressed
Self::from_bytes_into_subgroup(&point_bytes).map_err(serde::de::Error::custom)
}
}
}

impl Point {
pub fn new_rand_from_subgroup() -> Self {
&OsRng.gen_biguint_below(&GROUP_ORDER) * Self::generator()
Expand All @@ -111,8 +164,8 @@ impl Point {
match self.is_in_subgroup() {
true => Ok(self.u),
false => Err(Error::custom(format!(
"Point must lie in EC subgroup: ({}, {})",
self.x, self.u
"Point must lie in EC subgroup: {}",
self
))),
}
}
Expand Down Expand Up @@ -810,7 +863,7 @@ mod test {
match p == q {
true => Ok(()),
false => Err(Error::custom(format!(
"Roundtrip compression failed: {:?} ≠ {:?}",
"Roundtrip compression failed: {} ≠ {}",
p, q
))),
}
Expand Down Expand Up @@ -897,4 +950,28 @@ mod test {
assert!(data.prove(pw).is_err());
Ok(())
}

#[test]
fn test_point_serialize_deserialize() -> Result<(), anyhow::Error> {
// In subgroup
let g = Point::generator();

let serialized = serde_json::to_string_pretty(&g)?;
println!("g = {}", serialized);
let deserialized = serde_json::from_str(&serialized)?;
assert_eq!(g, deserialized);

// Not in subgroup
let not_sub = Point {
x: Point::b() / g.x,
u: g.u,
};

let serialized = serde_json::to_string_pretty(&not_sub)?;
println!("not_sub = {}", serialized);
let deserialized = serde_json::from_str(&serialized)?;
assert_eq!(not_sub, deserialized);

Ok(())
}
}
3 changes: 2 additions & 1 deletion src/middleware/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ impl fmt::Display for TypedValue {
TypedValue::Set(s) => write!(f, "set:{}", s.commitment()),
TypedValue::Array(a) => write!(f, "arr:{}", a.commitment()),
TypedValue::Raw(v) => write!(f, "{}", v),
TypedValue::PublicKey(p) => write!(f, "ecGFp5_pt:({},{})", p.x, p.u),
TypedValue::PublicKey(p) => write!(f, "pk:{}", p),
TypedValue::PodId(id) => write!(f, "pod_id:{}", id),
}
}
Expand Down Expand Up @@ -849,6 +849,7 @@ pub struct MainPodInputs<'a> {
/// Statements that need to be made public (they can come from input pods or input
/// statements)
pub public_statements: &'a [Statement],
// TODO: REMOVE THIS
pub vd_set: VDSet,
}

Expand Down
Loading