Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] 488 - Moving EcPoint to ergo-chain-types crate #504

Draft
wants to merge 3 commits into
base: develop
Choose a base branch
from
Draft
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
27 changes: 26 additions & 1 deletion ergo-chain-types/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,32 @@ exclude = [
crate-type = ["cdylib", "rlib"]

[dependencies]
thiserror = "1"
k256 = { version = "0.9.6", features = ["zeroize", "arithmetic", "ecdsa"] }
base16 = "0.2.1"
derive_more = "0.99"
sigma-ser = { version = "^0.3.0", path = "../sigma-ser" }
elliptic-curve = {version = "0.10.6", features = ["zeroize", "ff"]}
proptest-derive = {version = "0.3.0", optional = true }
num-bigint = "0.4.0"

[dependencies.proptest]
# wasm support, via https://altsysrq.github.io/proptest-book/proptest/wasm.html
version = "1.0.0"
# The default feature set includes things like process forking which are not
# supported in Web Assembly.
default-features = false
# Enable using the `std` crate.
features = ["std"]
optional = true

[dependencies.serde_with]
version = "1.9.1"
features = [ "json" ]
optional = true

[dev-dependencies]
rand = "0.8.3"
pretty_assertions = "0.7.2"

[features]
arbitrary = ["proptest", "proptest-derive"]
161 changes: 161 additions & 0 deletions ergo-chain-types/src/ecpoint.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
//! This is the general interface for the discrete logarithm prime-order group.
//!
//! The discrete logarithm problem is as follows: given a generator g of a finite
//! group G and a random element h in G, find the (unique) integer x such that
//! `g^x = h`.
//!
//! In cryptography, we are interested in groups for which the discrete logarithm problem
//! (Dlog for short) is assumed to be hard. The most known groups of that kind are some Elliptic curve groups.
//!
//! Another issue pertaining elliptic curves is the need to find a suitable mapping that will convert an arbitrary
//! message (that is some binary string) to an element of the group and vice-versa.
//!
//! Only a subset of the messages can be effectively mapped to a group element in such a way that there is a one-to-one
//! injection that converts the string to a group element and vice-versa.
//!
//! On the other hand, any group element can be mapped to some string.

//use elliptic_curve::group::prime::PrimeCurveAffine;
use elliptic_curve::group::prime::PrimeCurveAffine;
use k256::elliptic_curve::sec1::ToEncodedPoint;
use k256::{PublicKey};
use k256::{ProjectivePoint, Scalar};

//use std::convert::TryFrom;
use std::ops::{Add, Mul, Neg};

use sigma_ser::{ScorexSerializable, ScorexParsingError, ScorexSerializeResult};
use sigma_ser::vlq_encode::ReadSigmaVlqExt;
use sigma_ser::vlq_encode::WriteSigmaVlqExt;

/// Elliptic curve point
#[derive(PartialEq, Clone, Default)]
pub struct EcPoint(ProjectivePoint);


#[allow(clippy::unwrap_used)]
impl std::fmt::Debug for EcPoint {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
f.write_str("EC:")?;
f.write_str(&base16::encode_lower(
&self.scorex_serialize_bytes().unwrap(),
))
}
}

impl EcPoint {
/// Number of bytes to represent any group element as byte array
pub const GROUP_SIZE: usize = 33;


/// Attempts to parse from Base16-encoded string
pub fn from_base16_str(str: String) -> Option<Self> {
base16::decode(&str)
.ok()
.map(|bytes| Self::scorex_parse_bytes(&bytes).ok())
.flatten()
}

}

impl Eq for EcPoint {}

impl Mul<&EcPoint> for EcPoint {
type Output = EcPoint;

fn mul(self, other: &EcPoint) -> EcPoint {
EcPoint(ProjectivePoint::add(self.0, &other.0))
}
}

impl Neg for EcPoint {
type Output = EcPoint;

fn neg(self) -> EcPoint {
EcPoint(ProjectivePoint::neg(self.0))
}
}

/// The generator g of the group is an element of the group such that, when written multiplicatively, every element
/// of the group is a power of g.
pub fn generator() -> EcPoint {
EcPoint(ProjectivePoint::generator())
}

/// The identity(infinity) element
pub const fn identity() -> EcPoint {
EcPoint(ProjectivePoint::identity())
}

/// Check if point is identity(infinity) element
pub fn is_identity(ge: &EcPoint) -> bool {
*ge == identity()
}

/// Calculates the inverse of the given group element
pub fn inverse(ec: &EcPoint) -> EcPoint {
-ec.clone()
}

/// Raises the base GroupElement to the exponent. The result is another GroupElement.
pub fn exponentiate(base: &EcPoint, exponent: &Scalar) -> EcPoint {
if !is_identity(base) {
// we treat EC as a multiplicative group, therefore, exponentiate point is multiply.
EcPoint(base.0 * exponent)
} else {
base.clone()
}
}


impl ScorexSerializable for EcPoint {

fn scorex_serialize<W: WriteSigmaVlqExt>(&self, w: &mut W) -> ScorexSerializeResult {
let caff = self.0.to_affine();
if caff.is_identity().into() {
// infinity point
let zeroes = [0u8; EcPoint::GROUP_SIZE];
w.write_all(&zeroes)?;
} else {
w.write_all(caff.to_encoded_point(true).as_bytes())?;
}
Ok(())
}

fn scorex_parse<R: ReadSigmaVlqExt>(r: &mut R) -> Result<Self, ScorexParsingError>{
let mut buf = [0; EcPoint::GROUP_SIZE];
r.read_exact(&mut buf[..])?;
if buf[0] != 0 {
let pubkey = PublicKey::from_sec1_bytes(&buf[..]).map_err(|e| {
ScorexParsingError::Misc(format!("failed to parse PK from bytes: {:?}", e))
})?;
Ok(EcPoint(pubkey.to_projective()))
} else {
// infinity point
Ok(EcPoint(ProjectivePoint::identity()))
}
}

}


/// Arbitrary impl for EcPoint
#[cfg(feature = "arbitrary")]
mod arbitrary {
use super::*;
use proptest::prelude::*;

impl Arbitrary for EcPoint {
type Parameters = ();
type Strategy = BoxedStrategy<Self>;

fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
prop_oneof![
Just(generator()),
Just(identity()), /*Just(random_element()),*/
]
.boxed()
}
}
}

3 changes: 3 additions & 0 deletions ergo-chain-types/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,6 @@
#![deny(clippy::todo)]
#![deny(clippy::unimplemented)]
#![deny(clippy::panic)]

pub mod ecpoint;

21 changes: 21 additions & 0 deletions ergotree-ir/src/serialization/serializable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ use std::convert::TryInto;
use std::io;
use thiserror::Error;

use sigma_ser::ScorexSerializable;

/// Ways serialization might fail
#[derive(Error, Eq, PartialEq, Debug, Clone)]
pub enum SigmaSerializationError {
Expand Down Expand Up @@ -220,3 +222,22 @@ pub fn sigma_serialize_roundtrip<T: SigmaSerializable>(v: &T) -> T {
let mut sr = SigmaByteReader::new(cursor, ConstantStore::empty());
T::sigma_parse(&mut sr).expect("parse failed")
}

/*
impl<T> SigmaSerializable for T
where
T: ScorexSerializable,
{

fn sigma_serialize<W: SigmaByteWrite>(&self, w: &mut W) -> SigmaSerializeResult {
// self.scorex_serialize()
}

fn sigma_parse<R: SigmaByteRead>(r: &mut R) -> Result<Self, SigmaParsingError> {
// self.scorex_parse()
}
}
*/