Просмотр исходного кода

sdk-py: Implement python operator functions for Base and Scalar.

parazyd 3 лет назад
Родитель
Сommit
d424f57995
3 измененных файлов с 78 добавлено и 40 удалено
  1. 8 0
      src/sdk/python/README.md
  2. 32 22
      src/sdk/python/src/base.rs
  3. 38 18
      src/sdk/python/src/scalar.rs

+ 8 - 0
src/sdk/python/README.md

@@ -9,3 +9,11 @@ $ python3 -m venv venv
 $ source venv/bin/activate
 $ maturin develop --release
 ```
+
+```
+$ python3
+from darkfi_sdk_py.base import Base
+a = Base.from_u64(42)
+b = Base.from_u64(69)
+a + b == Base.from_u64(111)
+```

+ 32 - 22
src/sdk/python/src/base.rs

@@ -26,22 +26,28 @@ use darkfi_sdk::{
         pallas,
     },
 };
-use pyo3::prelude::*;
+use pyo3::{basic::CompareOp, prelude::*};
 use rand::rngs::OsRng;
 
 /// The base field of the Pallas and iso-Pallas curves.
 /// Randomness is provided by the OS and on the Rust side.
 #[pyclass]
+#[derive(Clone, Copy, Debug, PartialEq)]
 pub struct Base(pub(crate) pallas::Base);
 
 #[pymethods]
 impl Base {
     // Why is this not callable?
-    #[new]
+    #[staticmethod]
     fn from_u64(v: u64) -> Self {
         Self(pallas::Base::from(v))
     }
 
+    #[staticmethod]
+    fn from_u128(v: u128) -> Self {
+        Self(pallas::Base::from_u128(v))
+    }
+
     #[staticmethod]
     fn from_raw(v: [u64; 4]) -> Self {
         Self(pallas::Base::from_raw(v))
@@ -148,40 +154,44 @@ impl Base {
         Self(root)
     }
 
-    // For some reason, the name needs to be explictely stated
-    // for Python to correctly implement
-    #[pyo3(name = "__str__")]
-    fn __str_(&self) -> String {
-        format!("Base({:?})", self.0)
+    fn __str__(&self) -> String {
+        format!("{:?}", self.0)
     }
 
-    #[pyo3(name = "__repr__")]
-    fn __repr_(&self) -> String {
-        format!("Base({:?})", self.0)
+    fn __repr__(slf: &PyCell<Self>) -> PyResult<String> {
+        let class_name: &str = slf.get_type().name()?;
+        Ok(format!("{}({:?})", class_name, slf.borrow().0))
     }
 
-    fn eq(&self, rhs: &Self) -> bool {
-        self.0.eq(&rhs.0)
+    fn __add__(&self, other: &Self) -> Self {
+        Self(self.0 + other.0)
     }
 
-    fn add(&self, rhs: &Self) -> Self {
-        Self(self.0.add(&rhs.0))
+    fn __sub__(&self, other: &Self) -> Self {
+        Self(self.0 - other.0)
     }
 
-    fn sub(&self, rhs: &Self) -> Self {
-        Self(self.0.sub(&rhs.0))
+    fn __mul__(&self, other: &Self) -> Self {
+        Self(self.0 * other.0)
     }
 
-    fn double(&self) -> Self {
-        Self(self.0.double())
+    fn __neg__(&self) -> Self {
+        Self(self.0.neg())
     }
 
-    fn mul(&self, rhs: &Self) -> Self {
-        Self(self.0.mul(&rhs.0))
+    fn __richcmp__(&self, other: &Self, op: CompareOp) -> PyResult<bool> {
+        match op {
+            CompareOp::Lt => Ok(self.0 < other.0),
+            CompareOp::Le => Ok(self.0 <= other.0),
+            CompareOp::Eq => Ok(self.0 == other.0),
+            CompareOp::Ne => Ok(self.0 != other.0),
+            CompareOp::Gt => Ok(self.0 > other.0),
+            CompareOp::Ge => Ok(self.0 >= other.0),
+        }
     }
 
-    fn neg(&self) -> Self {
-        Self(self.0.neg())
+    fn double(&self) -> Self {
+        Self(self.0.double())
     }
 
     fn square(&self) -> Self {

+ 38 - 18
src/sdk/python/src/scalar.rs

@@ -18,9 +18,9 @@
 
 use darkfi_sdk::{
     crypto::pasta_prelude::{Field, PrimeField},
-    pasta::pallas,
+    pasta::{group::ff::FromUniformBytes, pallas},
 };
-use pyo3::prelude::*;
+use pyo3::{basic::CompareOp, prelude::*};
 use rand::rngs::OsRng;
 
 /// The scalar field of the Pallas and iso-Pallas curves.
@@ -29,7 +29,12 @@ pub struct Scalar(pub(crate) pallas::Scalar);
 
 #[pymethods]
 impl Scalar {
-    #[new]
+    #[staticmethod]
+    fn from_u64(v: u64) -> Self {
+        Self(pallas::Scalar::from(v))
+    }
+
+    #[staticmethod]
     fn from_u128(v: u128) -> Self {
         Self(pallas::Scalar::from_u128(v))
     }
@@ -39,6 +44,11 @@ impl Scalar {
         Self(pallas::Scalar::from_raw(v))
     }
 
+    #[staticmethod]
+    fn from_uniform_bytes(bytes: [u8; 64]) -> Self {
+        Self(pallas::Scalar::from_uniform_bytes(&bytes))
+    }
+
     #[staticmethod]
     fn random() -> Self {
         Self(pallas::Scalar::random(&mut OsRng))
@@ -59,34 +69,44 @@ impl Scalar {
         Self(pallas::Scalar::one())
     }
 
-    #[pyo3(name = "__str__")]
     fn __str__(&self) -> String {
-        format!("Scalar({:?})", self.0)
+        format!("{:?}", self.0)
     }
 
-    #[pyo3(name = "__repr__")]
-    fn __repr__(&self) -> String {
-        format!("Scalar({:?})", self.0)
+    fn __repr__(slf: &PyCell<Self>) -> PyResult<String> {
+        let class_name: &str = slf.get_type().name()?;
+        Ok(format!("{}({:?})", class_name, slf.borrow().0))
     }
 
-    fn add(&self, rhs: &Self) -> Self {
-        Self(self.0.add(&rhs.0))
+    fn __add__(&self, other: &Self) -> Self {
+        Self(self.0 + other.0)
     }
 
-    fn sub(&self, rhs: &Self) -> Self {
-        Self(self.0.sub(&rhs.0))
+    fn __sub__(&self, other: &Self) -> Self {
+        Self(self.0 - other.0)
     }
 
-    fn double(&self) -> Self {
-        Self(self.0.double())
+    fn __mul__(&self, other: &Self) -> Self {
+        Self(self.0 * other.0)
     }
 
-    fn mul(&self, rhs: &Self) -> Self {
-        Self(self.0.mul(&rhs.0))
+    fn __neg__(&self) -> Self {
+        Self(self.0.neg())
     }
 
-    fn neg(&self) -> Self {
-        Self(self.0.neg())
+    fn __richcmp__(&self, other: &Self, op: CompareOp) -> PyResult<bool> {
+        match op {
+            CompareOp::Lt => Ok(self.0 < other.0),
+            CompareOp::Le => Ok(self.0 <= other.0),
+            CompareOp::Eq => Ok(self.0 == other.0),
+            CompareOp::Ne => Ok(self.0 != other.0),
+            CompareOp::Gt => Ok(self.0 > other.0),
+            CompareOp::Ge => Ok(self.0 >= other.0),
+        }
+    }
+
+    fn double(&self) -> Self {
+        Self(self.0.double())
     }
 
     fn square(&self) -> Self {