diff --git a/ergo-chain-types/Cargo.toml b/ergo-chain-types/Cargo.toml index 3c1dbf5a4..9911d1950 100644 --- a/ergo-chain-types/Cargo.toml +++ b/ergo-chain-types/Cargo.toml @@ -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"] \ No newline at end of file diff --git a/ergo-chain-types/src/ecpoint.rs b/ergo-chain-types/src/ecpoint.rs new file mode 100644 index 000000000..b0b121fd9 --- /dev/null +++ b/ergo-chain-types/src/ecpoint.rs @@ -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 { + 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(&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: &mut R) -> Result{ + 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; + + fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy { + prop_oneof![ + Just(generator()), + Just(identity()), /*Just(random_element()),*/ + ] + .boxed() + } + } +} + diff --git a/ergo-chain-types/src/lib.rs b/ergo-chain-types/src/lib.rs index d63e437a5..5c4fbb1a5 100644 --- a/ergo-chain-types/src/lib.rs +++ b/ergo-chain-types/src/lib.rs @@ -16,3 +16,6 @@ #![deny(clippy::todo)] #![deny(clippy::unimplemented)] #![deny(clippy::panic)] + +pub mod ecpoint; + diff --git a/ergotree-ir/src/serialization/serializable.rs b/ergotree-ir/src/serialization/serializable.rs index 8dfe99a53..324f02a36 100644 --- a/ergotree-ir/src/serialization/serializable.rs +++ b/ergotree-ir/src/serialization/serializable.rs @@ -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 { @@ -220,3 +222,22 @@ pub fn sigma_serialize_roundtrip(v: &T) -> T { let mut sr = SigmaByteReader::new(cursor, ConstantStore::empty()); T::sigma_parse(&mut sr).expect("parse failed") } + +/* +impl SigmaSerializable for T +where + T: ScorexSerializable, +{ + + fn sigma_serialize(&self, w: &mut W) -> SigmaSerializeResult { + // self.scorex_serialize() + } + + fn sigma_parse(r: &mut R) -> Result { + // self.scorex_parse() + } +} +*/ + + +