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

sdk/python: Perform full code cleanup and make everything work.

parazyd 3 лет назад
Родитель
Сommit
988455f79b

+ 4 - 5
src/sdk/python/Cargo.toml

@@ -1,6 +1,6 @@
 [package]
 name = "darkfi-sdk-py"
-description = "Python bindings for Darkfi SDK"
+description = "Python bindings for the DarkFi SDK"
 version = "0.4.1"
 edition = "2021"
 authors = ["Dyne.org foundation <foundation@dyne.org>"]
@@ -8,14 +8,13 @@ license = "AGPL-3.0-only"
 homepage = "https://dark.fi"
 repository = "https://github.com/darkrenaissance/darkfi"
 
-# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
 [lib]
-name = "darkfi_sdk_py"
+name = "darkfi_sdk"
 crate-type = ["cdylib"]
 
 [dependencies]
-darkfi = { path = "../../../", features = ["zk", "zkas"] }
-darkfi-sdk = { path = "../" }
+darkfi = {path = "../../../", features = ["zk", "zkas"]}
+darkfi-sdk = {path = "../"}
 halo2_gadgets = "0.3.0"
 pyo3 = "0.19.2"
 rand = "0.8.5"

+ 0 - 1
src/sdk/python/Makefile

@@ -9,5 +9,4 @@ all:
 dev:
 	$(MATURIN) develop --release
 
-
 .PHONY: all

+ 6 - 6
src/sdk/python/README.md

@@ -22,14 +22,14 @@ $ source venv/bin/activate
 
 ```
 $ python
->>> import darkfi_sdk_py
->>> from darkfi_sdk_py.base import Base
->>> a = Base.from_u64(42)
->>> b = Base.from_u64(69)
->>> a + b == Base.from_u64(111)
+>>> import darkfi_sdk
+>>> from darkfi_sdk.pasta import Fp
+>>> a = Fp.from_u64(42)
+>>> b = Fp.from_u64(69)
+>>> a + b == Fp.from_u64(111)
 ```
 
-### Randomness
+## Randomness
 
 Note that the `random` methods take randomness 
 from the OS on the Rust side.

+ 2 - 3
src/sdk/python/pyproject.toml

@@ -3,14 +3,13 @@ requires = ["maturin>=1.0,<2.0"]
 build-backend = "maturin"
 
 [project]
-name = "darkfi-sdk-py"
-requires-python = ">=3.8"
+name = "darkfi-sdk"
+requires-python = ">=3.9"
 classifiers = [
     "Programming Language :: Rust",
     "Programming Language :: Python :: Implementation :: CPython",
     "Programming Language :: Python :: Implementation :: PyPy",
 ]
 
-
 [tool.maturin]
 features = ["pyo3/extension-module"]

+ 0 - 44
src/sdk/python/src/affine.rs

@@ -1,44 +0,0 @@
-/* This file is part of DarkFi (https://dark.fi)
- *
- * Copyright (C) 2020-2023 Dyne.org foundation
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <https://www.gnu.org/licenses/>.
- */
-
-use darkfi_sdk::pasta::{arithmetic::CurveAffine, pallas};
-use pyo3::prelude::*;
-
-use super::base::Base;
-
-/// A Pallas point in the affine coordinate space (or the point at infinity).
-#[pyclass]
-pub struct Affine(pub(crate) pallas::Affine);
-
-#[pymethods]
-impl Affine {
-    fn __str__(&self) -> String {
-        format!("Affine({:?})", self.0)
-    }
-
-    fn coordinates(&self) -> (Base, Base) {
-        let coords = self.0.coordinates().unwrap();
-        (Base(*coords.x()), Base(*coords.y()))
-    }
-}
-
-pub fn create_module(py: pyo3::Python<'_>) -> pyo3::PyResult<&PyModule> {
-    let submod = PyModule::new(py, "affine")?;
-    submod.add_class::<Affine>()?;
-    Ok(submod)
-}

+ 0 - 206
src/sdk/python/src/base.rs

@@ -1,206 +0,0 @@
-/* This file is part of DarkFi (https://dark.fi)
- *
- * Copyright (C) 2020-2023 Dyne.org foundation
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <https://www.gnu.org/licenses/>.
- */
-
-use std::ops::Deref;
-
-use darkfi_sdk::{
-    bridgetree::Hashable,
-    crypto::{poseidon_hash, MerkleNode},
-    pasta::{
-        group::ff::{Field, FromUniformBytes, PrimeField},
-        pallas,
-    },
-};
-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?
-    #[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))
-    }
-
-    #[staticmethod]
-    fn from_uniform_bytes(bytes: [u8; 64]) -> Self {
-        Self(pallas::Base::from_uniform_bytes(&bytes))
-    }
-
-    #[staticmethod]
-    fn random() -> Self {
-        Self(pallas::Base::random(&mut OsRng))
-    }
-
-    #[staticmethod]
-    fn modulus() -> String {
-        pallas::Base::MODULUS.to_string()
-    }
-
-    #[staticmethod]
-    fn zero() -> Self {
-        Self(pallas::Base::zero())
-    }
-
-    #[staticmethod]
-    fn one() -> Self {
-        Self(pallas::Base::one())
-    }
-
-    #[staticmethod]
-    fn poseidon_hash(messages: Vec<&PyCell<Self>>) -> Self {
-        let l = messages.len();
-        let messages: Vec<pallas::Base> = messages.iter().map(|m| m.borrow().deref().0).collect();
-        if l == 1 {
-            let m: [pallas::Base; 1] = messages.try_into().unwrap();
-            Self(poseidon_hash(m))
-        } else if l == 2 {
-            let m: [pallas::Base; 2] = messages.try_into().unwrap();
-            Self(poseidon_hash(m))
-        } else if l == 3 {
-            let m: [pallas::Base; 3] = messages.try_into().unwrap();
-            Self(poseidon_hash(m))
-        } else if l == 4 {
-            let m: [pallas::Base; 4] = messages.try_into().unwrap();
-            Self(poseidon_hash(m))
-        } else if l == 5 {
-            let m: [pallas::Base; 5] = messages.try_into().unwrap();
-            Self(poseidon_hash(m))
-        } else if l == 6 {
-            let m: [pallas::Base; 6] = messages.try_into().unwrap();
-            Self(poseidon_hash(m))
-        } else if l == 7 {
-            let m: [pallas::Base; 7] = messages.try_into().unwrap();
-            Self(poseidon_hash(m))
-        } else if l == 8 {
-            let m: [pallas::Base; 8] = messages.try_into().unwrap();
-            Self(poseidon_hash(m))
-        } else if l == 9 {
-            let m: [pallas::Base; 9] = messages.try_into().unwrap();
-            Self(poseidon_hash(m))
-        } else if l == 10 {
-            let m: [pallas::Base; 10] = messages.try_into().unwrap();
-            Self(poseidon_hash(m))
-        } else if l == 11 {
-            let m: [pallas::Base; 11] = messages.try_into().unwrap();
-            Self(poseidon_hash(m))
-        } else if l == 12 {
-            let m: [pallas::Base; 12] = messages.try_into().unwrap();
-            Self(poseidon_hash(m))
-        } else if l == 13 {
-            let m: [pallas::Base; 13] = messages.try_into().unwrap();
-            Self(poseidon_hash(m))
-        } else if l == 14 {
-            let m: [pallas::Base; 14] = messages.try_into().unwrap();
-            Self(poseidon_hash(m))
-        } else if l == 15 {
-            let m: [pallas::Base; 15] = messages.try_into().unwrap();
-            Self(poseidon_hash(m))
-        } else if l == 16 {
-            let m: [pallas::Base; 16] = messages.try_into().unwrap();
-            Self(poseidon_hash(m))
-        } else {
-            panic!("Messages length violation, must be: 1 <= len <= 16");
-        }
-    }
-
-    /// pos(ition) encodes the left/right position on each level
-    /// path is the the silbling on each level
-    #[staticmethod]
-    fn merkle_root(i: u64, p: Vec<&PyCell<Base>>, a: &Base) -> Self {
-        // TOOD: consider adding length check, for i and path, for extra defensiness
-        let mut current = MerkleNode::new(a.0);
-        for (level, sibling) in p.iter().enumerate() {
-            let level = level as u8;
-            let sibling = MerkleNode::new(sibling.borrow().deref().0);
-            current = if i & (1 << level) == 0 {
-                MerkleNode::combine(level.into(), &current, &sibling)
-            } else {
-                MerkleNode::combine(level.into(), &sibling, &current)
-            };
-        }
-        let root = current.inner();
-        Self(root)
-    }
-
-    fn __str__(&self) -> String {
-        format!("{:?}", 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, other: &Self) -> Self {
-        Self(self.0 + other.0)
-    }
-
-    fn __sub__(&self, other: &Self) -> Self {
-        Self(self.0 - other.0)
-    }
-
-    fn __mul__(&self, other: &Self) -> Self {
-        Self(self.0 * other.0)
-    }
-
-    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 {
-        Self(self.0.square())
-    }
-}
-
-pub fn create_module(py: pyo3::Python<'_>) -> pyo3::PyResult<&PyModule> {
-    let submod = PyModule::new(py, "base")?;
-    submod.add_class::<Base>()?;
-    Ok(submod)
-}

+ 73 - 0
src/sdk/python/src/crypto.rs

@@ -0,0 +1,73 @@
+/* This file is part of DarkFi (https://dark.fi)
+ *
+ * Copyright (C) 2020-2023 Dyne.org foundation
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+
+use std::ops::Deref;
+
+use darkfi_sdk::{crypto, pasta::pallas};
+use pyo3::{pyfunction, types::PyModule, wrap_pyfunction, PyCell, PyResult, Python};
+
+use super::pasta::{Ep, Fp, Fq};
+
+/// Calculate the Poseidon hash of given `Fp` elements.
+#[pyfunction]
+pub fn poseidon_hash(messages: Vec<&PyCell<Fp>>) -> Fp {
+    let messages: Vec<pallas::Base> = messages.iter().map(|x| x.borrow().deref().0).collect();
+    match messages.len() {
+        1 => Fp(crypto::util::poseidon_hash::<1>(messages.try_into().unwrap())),
+        2 => Fp(crypto::util::poseidon_hash::<2>(messages.try_into().unwrap())),
+        3 => Fp(crypto::util::poseidon_hash::<3>(messages.try_into().unwrap())),
+        4 => Fp(crypto::util::poseidon_hash::<4>(messages.try_into().unwrap())),
+        5 => Fp(crypto::util::poseidon_hash::<5>(messages.try_into().unwrap())),
+        6 => Fp(crypto::util::poseidon_hash::<6>(messages.try_into().unwrap())),
+        7 => Fp(crypto::util::poseidon_hash::<7>(messages.try_into().unwrap())),
+        8 => Fp(crypto::util::poseidon_hash::<8>(messages.try_into().unwrap())),
+        9 => Fp(crypto::util::poseidon_hash::<9>(messages.try_into().unwrap())),
+        10 => Fp(crypto::util::poseidon_hash::<10>(messages.try_into().unwrap())),
+        11 => Fp(crypto::util::poseidon_hash::<11>(messages.try_into().unwrap())),
+        12 => Fp(crypto::util::poseidon_hash::<12>(messages.try_into().unwrap())),
+        13 => Fp(crypto::util::poseidon_hash::<13>(messages.try_into().unwrap())),
+        14 => Fp(crypto::util::poseidon_hash::<14>(messages.try_into().unwrap())),
+        15 => Fp(crypto::util::poseidon_hash::<15>(messages.try_into().unwrap())),
+        16 => Fp(crypto::util::poseidon_hash::<16>(messages.try_into().unwrap())),
+        _ => unimplemented!(),
+    }
+}
+
+/// Calculate a Pedersen commitment with an u64 value.
+#[pyfunction]
+pub fn pedersen_commitment_u64(value: u64, blind: &PyCell<Fq>) -> Ep {
+    Ep(crypto::pedersen::pedersen_commitment_u64(value, blind.borrow().deref().0))
+}
+
+/// Calculate a Pedersen commitment with an Fp value.
+#[pyfunction]
+pub fn pedersen_commitment_base(value: &PyCell<Fp>, blind: &PyCell<Fq>) -> Ep {
+    Ep(crypto::pedersen::pedersen_commitment_base(
+        value.borrow().deref().0,
+        blind.borrow().deref().0,
+    ))
+}
+
+/// Wrapper function for creating this Python module.
+pub(crate) fn create_module(py: Python<'_>) -> PyResult<&PyModule> {
+    let submod = PyModule::new(py, "crypto")?;
+    submod.add_function(wrap_pyfunction!(poseidon_hash, submod)?)?;
+    submod.add_function(wrap_pyfunction!(pedersen_commitment_u64, submod)?)?;
+    submod.add_function(wrap_pyfunction!(pedersen_commitment_base, submod)?)?;
+    Ok(submod)
+}

+ 21 - 57
src/sdk/python/src/lib.rs

@@ -16,71 +16,35 @@
  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
  */
 
-/// Pallas point in affine space
-mod affine;
-/// Pallas base field element
-mod base;
-/// Pallas point in projective space
-mod point;
-/// Pallas scalar field element
-mod scalar;
+/// Pallas and Vesta curves
+mod pasta;
 
-/// ZK proof creation
-mod proof;
-/// Proving key creation
-mod proving_key;
-/// Verifying key creation
-mod verifying_key;
-/// zkas ZkBinary wrappers
-mod zk_binary;
-/// zkvm wrappers
-mod zk_circuit;
+/// Merkle tree utilities
+mod merkle;
+
+/// Cryptographic utilities
+mod crypto;
+
+/// zkas definitions
+mod zkas;
 
 #[pyo3::prelude::pymodule]
-fn darkfi_sdk_py(py: pyo3::Python<'_>, m: &pyo3::types::PyModule) -> pyo3::PyResult<()> {
-    let submodule = affine::create_module(py)?;
-    pyo3::py_run!(py, submodule, "import sys; sys.modules['darkfi_sdk_py.affine'] = submodule");
+fn darkfi_sdk(py: pyo3::Python<'_>, m: &pyo3::types::PyModule) -> pyo3::PyResult<()> {
+    let submodule = pasta::create_module(py)?;
+    pyo3::py_run!(py, submodule, "import sys; sys.modules['darkfi_sdk.pasta'] = submodule");
     m.add_submodule(submodule)?;
 
-    let submodule = base::create_module(py)?;
-    pyo3::py_run!(py, submodule, "import sys; sys.modules['darkfi_sdk_py.base'] = submodule");
+    let submodule = merkle::create_module(py)?;
+    pyo3::py_run!(py, submodule, "import sys; sys.modules['darkfi_sdk.merkle'] = submodule");
     m.add_submodule(submodule)?;
 
-    let submodule = scalar::create_module(py)?;
-    pyo3::py_run!(py, submodule, "import sys; sys.modules['darkfi_sdk_py.scalar'] = submodule");
-    m.add_submodule(scalar::create_module(py)?)?;
-
-    let submodule = point::create_module(py)?;
-    pyo3::py_run!(py, submodule, "import sys; sys.modules['darkfi_sdk_py.point'] = submodule");
-    m.add_submodule(point::create_module(py)?)?;
-
-    let submodule = proof::create_module(py)?;
-    pyo3::py_run!(py, submodule, "import sys; sys.modules['darkfi_sdk_py.proof'] = submodule");
-    m.add_submodule(proof::create_module(py)?)?;
-
-    let submodule = proving_key::create_module(py)?;
-    pyo3::py_run!(
-        py,
-        submodule,
-        "import sys; sys.modules['darkfi_sdk_py.proving_key'] = submodule"
-    );
-    m.add_submodule(proving_key::create_module(py)?)?;
-
-    let submodule = verifying_key::create_module(py)?;
-    pyo3::py_run!(
-        py,
-        submodule,
-        "import sys; sys.modules['darkfi_sdk_py.verifying_key'] = submodule"
-    );
-    m.add_submodule(verifying_key::create_module(py)?)?;
-
-    let submodule = zk_binary::create_module(py)?;
-    pyo3::py_run!(py, submodule, "import sys; sys.modules['darkfi_sdk_py.zk_binary'] = submodule");
-    m.add_submodule(zk_binary::create_module(py)?)?;
+    let submodule = crypto::create_module(py)?;
+    pyo3::py_run!(py, submodule, "import sys; sys.modules['darkfi_sdk.crypto'] = submodule");
+    m.add_submodule(submodule)?;
 
-    let submodule = zk_circuit::create_module(py)?;
-    pyo3::py_run!(py, submodule, "import sys; sys.modules['darkfi_sdk_py.zk_circuit'] = submodule");
-    m.add_submodule(zk_circuit::create_module(py)?)?;
+    let submodule = zkas::create_module(py)?;
+    pyo3::py_run!(py, submodule, "import sys; sys.modules['darkfi_sdk.zkas'] = submodule");
+    m.add_submodule(submodule)?;
 
     Ok(())
 }

+ 60 - 0
src/sdk/python/src/merkle.rs

@@ -0,0 +1,60 @@
+/* This file is part of DarkFi (https://dark.fi)
+ *
+ * Copyright (C) 2020-2023 Dyne.org foundation
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+use std::ops::Deref;
+
+use darkfi_sdk::crypto::{merkle_node, MerkleNode};
+use pyo3::{pyclass, pymethods, types::PyModule, PyCell, PyResult};
+
+use super::pasta::Fp;
+
+#[pyclass]
+/// Class representing a bridgetree
+pub struct MerkleTree(merkle_node::MerkleTree);
+
+#[pymethods]
+impl MerkleTree {
+    #[new]
+    fn new() -> Self {
+        Self(merkle_node::MerkleTree::new(100))
+    }
+
+    fn append(&mut self, node: &PyCell<Fp>) -> PyResult<bool> {
+        Ok(self.0.append(MerkleNode::from(node.borrow().deref().0)))
+    }
+
+    fn mark(&mut self) -> PyResult<u32> {
+        Ok(u64::from(self.0.mark().unwrap()) as u32)
+    }
+
+    fn root(&self, checkpoint_depth: usize) -> PyResult<Fp> {
+        let root = self.0.root(checkpoint_depth).unwrap();
+        Ok(Fp(root.inner()))
+    }
+
+    fn witness(&self, position: u32, checkpoint_depth: usize) -> PyResult<Vec<Fp>> {
+        let path = self.0.witness((position as u64).into(), checkpoint_depth).unwrap();
+        Ok(path.iter().map(|x| Fp(x.inner())).collect())
+    }
+}
+
+/// Wrapper function for creating this Python module.
+pub(crate) fn create_module(py: pyo3::Python<'_>) -> PyResult<&PyModule> {
+    let submod = PyModule::new(py, "merkle")?;
+    submod.add_class::<MerkleTree>()?;
+    Ok(submod)
+}

+ 316 - 0
src/sdk/python/src/pasta.rs

@@ -0,0 +1,316 @@
+/* This file is part of DarkFi (https://dark.fi)
+ *
+ * Copyright (C) 2020-2023 Dyne.org foundation
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+
+use std::ops::Deref;
+
+use darkfi_sdk::{
+    crypto::{constants::NullifierK, pasta_prelude::*, util},
+    pasta::{group::ff::FromUniformBytes, pallas, vesta},
+};
+use halo2_gadgets::ecc::chip::FixedPoint;
+use pyo3::{
+    basic::CompareOp, pyclass, pyfunction, pymethods, types::PyModule, wrap_pyfunction, PyCell,
+    PyResult,
+};
+use rand::rngs::OsRng;
+
+macro_rules! impl_elem {
+    ($x:ty, $inner:ty) => {
+        #[pymethods]
+        impl $x {
+            #[new]
+            fn new(v: &str) -> PyResult<Self> {
+                assert!(v.starts_with("0x") && v.len() == 66);
+                let v = v.trim_start_matches("0x");
+                let (a, b) = v.split_at(32);
+                let (le_1, le_0) = b.split_at(16);
+                let (le_3, le_2) = a.split_at(16);
+
+                let le_0 = u64::from_str_radix(le_0, 16)?;
+                let le_1 = u64::from_str_radix(le_1, 16)?;
+                let le_2 = u64::from_str_radix(le_2, 16)?;
+                let le_3 = u64::from_str_radix(le_3, 16)?;
+
+                Ok(Self(<$inner>::from_raw([le_0, le_1, le_2, le_3])))
+            }
+
+            #[staticmethod]
+            fn from_u64(v: u64) -> Self {
+                Self(<$inner>::from(v))
+            }
+
+            #[staticmethod]
+            fn from_u128(v: u128) -> Self {
+                Self(<$inner>::from_u128(v))
+            }
+
+            #[staticmethod]
+            const fn from_raw(v: [u64; 4]) -> Self {
+                Self(<$inner>::from_raw(v))
+            }
+
+            #[staticmethod]
+            fn from_uniform_bytes(bytes: [u8; 64]) -> Self {
+                Self(<$inner>::from_uniform_bytes(&bytes))
+            }
+
+            #[staticmethod]
+            fn random() -> Self {
+                Self(<$inner>::random(&mut OsRng))
+            }
+
+            #[staticmethod]
+            fn modulus() -> &'static str {
+                <$inner>::MODULUS
+            }
+
+            #[staticmethod]
+            fn zero() -> Self {
+                Self(<$inner>::ZERO)
+            }
+
+            #[staticmethod]
+            fn one() -> Self {
+                Self(<$inner>::ONE)
+            }
+
+            fn double(&self) -> Self {
+                Self(self.0.double())
+            }
+
+            fn square(&self) -> Self {
+                Self(self.0.square())
+            }
+
+            fn __str__(&self) -> PyResult<String> {
+                Ok(format!("{:?}", 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, other: &Self) -> Self {
+                Self(self.0 + other.0)
+            }
+
+            fn __sub__(&self, other: &Self) -> Self {
+                Self(self.0 - other.0)
+            }
+
+            fn __mul__(&self, other: &Self) -> Self {
+                Self(self.0 * other.0)
+            }
+
+            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 to_json(&self) -> PyResult<String> {
+                self.__str__()
+            }
+        }
+    };
+}
+
+macro_rules! impl_affine {
+    ($x:ty, $inner:ty, $base:ident, $projective:ty) => {
+        #[pymethods]
+        impl $x {
+            fn coordinates(&self) -> ($base, $base) {
+                let coords = self.0.coordinates().unwrap();
+                ($base(*coords.x()), $base(*coords.y()))
+            }
+
+            fn coordinates_str(&self) -> PyResult<Vec<String>> {
+                let coords = self.0.coordinates().unwrap();
+                let x = $base(*coords.x()).__str__()?;
+                let y = $base(*coords.y()).__str__()?;
+                Ok(vec![x, y])
+            }
+
+            #[staticmethod]
+            fn from_xy(x: &PyCell<$base>, y: &PyCell<$base>) -> PyResult<Self> {
+                let affine_point =
+                    <$inner>::from_xy(x.borrow().deref().0, y.borrow().deref().0).unwrap();
+                Ok(Self(affine_point))
+            }
+
+            #[staticmethod]
+            fn from_projective(x: &PyCell<$projective>) -> Self {
+                Self(<$inner>::from(x.borrow().deref().0))
+            }
+
+            fn __str__(&self) -> String {
+                format!("{:?}", self.0)
+            }
+
+            fn __repr__(slf: &PyCell<Self>) -> PyResult<String> {
+                let class_name: &str = slf.get_type().name()?;
+                Ok(format!("{}({:?})", class_name, slf.borrow().0))
+            }
+        }
+    };
+}
+
+macro_rules! impl_point {
+    ($x:ty, $inner:ty, $base:ty, $scalar:ty, $affine:ty) => {
+        #[pymethods]
+        impl $x {
+            #[new]
+            fn new(x: &PyCell<$base>, y: &PyCell<$base>) -> PyResult<Self> {
+                let affine_point = <$affine>::from_xy(x, y).unwrap();
+                Ok(Self::from_affine(affine_point))
+            }
+
+            #[staticmethod]
+            fn identity() -> Self {
+                Self(<$inner>::identity())
+            }
+
+            #[staticmethod]
+            fn generator() -> Self {
+                Self(<$inner>::generator())
+            }
+
+            #[staticmethod]
+            fn random() -> Self {
+                Self(<$inner>::random(&mut OsRng))
+            }
+
+            #[staticmethod]
+            fn from_affine(p: $affine) -> Self {
+                Self(<$inner>::from(p.0))
+            }
+
+            fn __str__(slf: &PyCell<Self>) -> PyResult<String> {
+                let affine = <$affine>::from_projective(slf);
+                let (x, y) = affine.coordinates();
+                Ok(format!("[{}, {}]", x.__str__()?, y.__str__()?))
+            }
+
+            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 + rhs.0)
+            }
+
+            fn __sub__(&self, rhs: &Self) -> Self {
+                Self(self.0 - rhs.0)
+            }
+
+            fn __mul__(&self, scalar: &$scalar) -> Self {
+                Self(self.0 * scalar.0)
+            }
+
+            fn __neg__(&self) -> Self {
+                Self(-self.0)
+            }
+
+            fn __richcmp__(&self, other: &Self, op: CompareOp) -> PyResult<bool> {
+                match op {
+                    CompareOp::Eq => Ok(self.0 == other.0),
+                    CompareOp::Ne => Ok(self.0 != other.0),
+                    CompareOp::Lt => unimplemented!(),
+                    CompareOp::Le => unimplemented!(),
+                    CompareOp::Gt => unimplemented!(),
+                    CompareOp::Ge => unimplemented!(),
+                }
+            }
+        }
+    };
+}
+
+/// The base field of the Pallas curve and the scalar field of the Vesta curve.
+#[pyclass(dict)]
+#[derive(Copy, Clone, PartialEq, std::cmp::Eq, Ord, PartialOrd, Debug)]
+pub struct Fp(pub(crate) pallas::Base);
+impl_elem!(Fp, pallas::Base);
+
+/// The scalar field of the Pallas curve and the base field of the Vesta curve.
+#[pyclass]
+#[derive(Copy, Clone, PartialEq, std::cmp::Eq, Ord, PartialOrd, Debug)]
+pub struct Fq(pub(crate) pallas::Scalar);
+impl_elem!(Fq, pallas::Scalar);
+
+/// A Pallas curve point in the projective space
+#[pyclass]
+#[derive(Copy, Clone, PartialEq, std::cmp::Eq, Debug)]
+pub struct Ep(pub(crate) pallas::Point);
+impl_point!(Ep, pallas::Point, Fp, Fq, EpAffine);
+
+/// A Pallas curve point in the affine space
+#[pyclass]
+#[derive(Copy, Clone, PartialEq, std::cmp::Eq, Debug)]
+pub struct EpAffine(pub(crate) pallas::Affine);
+impl_affine!(EpAffine, pallas::Affine, Fp, Ep);
+
+/// A Vesta curve point in the projective space
+#[pyclass]
+#[derive(Copy, Clone, PartialEq, std::cmp::Eq, Debug)]
+pub struct Eq(pub(crate) vesta::Point);
+impl_point!(Eq, vesta::Point, Fq, Fp, EqAffine);
+
+/// A Vesta curve point in the affine space
+#[pyclass]
+#[derive(Copy, Clone, PartialEq, std::cmp::Eq, Debug)]
+pub struct EqAffine(pub(crate) vesta::Affine);
+impl_affine!(EqAffine, vesta::Affine, Fq, Eq);
+
+#[pyfunction]
+/// Return the NullifierK generator point as EpAffine.
+pub fn nullifier_k() -> EpAffine {
+    EpAffine(NullifierK.generator())
+}
+
+#[pyfunction]
+/// Convert Fp to Fq safely.
+pub fn mod_r_p(x: &PyCell<Fp>) -> PyResult<Fq> {
+    Ok(Fq(util::mod_r_p(x.borrow().deref().0)))
+}
+
+pub fn create_module(py: pyo3::Python<'_>) -> PyResult<&PyModule> {
+    let submod = PyModule::new(py, "pasta")?;
+
+    submod.add_class::<Fp>()?;
+    submod.add_class::<Fq>()?;
+    submod.add_class::<Ep>()?;
+    submod.add_class::<EpAffine>()?;
+    submod.add_class::<Eq>()?;
+    submod.add_class::<EqAffine>()?;
+
+    submod.add_function(wrap_pyfunction!(nullifier_k, submod)?)?;
+    submod.add_function(wrap_pyfunction!(mod_r_p, submod)?)?;
+
+    Ok(submod)
+}

+ 0 - 128
src/sdk/python/src/point.rs

@@ -1,128 +0,0 @@
-/* This file is part of DarkFi (https://dark.fi)
- *
- * Copyright (C) 2020-2023 Dyne.org foundation
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <https://www.gnu.org/licenses/>.
- */
-
-use darkfi_sdk::{
-    crypto::{
-        constants::fixed_bases::{
-            NullifierK, VALUE_COMMITMENT_PERSONALIZATION, VALUE_COMMITMENT_R_BYTES,
-            VALUE_COMMITMENT_V_BYTES,
-        },
-        util::mod_r_p,
-    },
-    pasta::{
-        arithmetic::CurveExt,
-        group::{Curve, Group},
-        pallas,
-    },
-};
-use halo2_gadgets::ecc::chip::FixedPoint;
-use pyo3::{basic::CompareOp, prelude::*};
-use rand::rngs::OsRng;
-
-use super::{affine::Affine, base::Base, scalar::Scalar};
-
-/// A Pallas point in the projective coordinate space.
-#[pyclass]
-pub struct Point(pub(crate) pallas::Point);
-
-#[pymethods]
-impl Point {
-    #[staticmethod]
-    fn identity() -> Self {
-        Self(pallas::Point::identity())
-    }
-
-    #[staticmethod]
-    fn generator() -> Self {
-        Self(pallas::Point::generator())
-    }
-
-    #[staticmethod]
-    fn mul_short(value: &Base) -> Self {
-        let hasher = pallas::Point::hash_to_curve(VALUE_COMMITMENT_PERSONALIZATION);
-        let v = hasher(&VALUE_COMMITMENT_V_BYTES);
-        Self(v * mod_r_p(value.0))
-    }
-
-    // Why value doesn't need to be a Pycell?
-    #[staticmethod]
-    fn mul_base(value: &Base) -> Self {
-        let v = NullifierK.generator();
-        Self(v * mod_r_p(value.0))
-    }
-
-    // Why not a pycell?
-    #[staticmethod]
-    fn mul_r_generator(blind: &Scalar) -> Self {
-        let hasher = pallas::Point::hash_to_curve(VALUE_COMMITMENT_PERSONALIZATION);
-        let r = hasher(&VALUE_COMMITMENT_R_BYTES);
-        let r = Self(r);
-        Self(r.0 * blind.0)
-    }
-
-    #[staticmethod]
-    fn random() -> Self {
-        Self(pallas::Point::random(&mut OsRng))
-    }
-
-    fn __str__(&self) -> String {
-        format!("{:?}", 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 to_affine(&self) -> Affine {
-        Affine(self.0.to_affine())
-    }
-
-    fn __add__(&self, rhs: &Self) -> Self {
-        Self(self.0 + rhs.0)
-    }
-
-    fn __sub__(&self, rhs: &Self) -> Self {
-        Self(self.0 - rhs.0)
-    }
-
-    fn __mul__(&self, scalar: &Scalar) -> Self {
-        Self(self.0 * scalar.0)
-    }
-
-    fn __neg__(&self) -> Self {
-        Self(-self.0)
-    }
-
-    fn __richcmp__(&self, other: &Self, op: CompareOp) -> PyResult<bool> {
-        match op {
-            CompareOp::Eq => Ok(self.0 == other.0),
-            CompareOp::Ne => Ok(self.0 != other.0),
-            CompareOp::Lt => todo!(),
-            CompareOp::Le => todo!(),
-            CompareOp::Gt => todo!(),
-            CompareOp::Ge => todo!(),
-        }
-    }
-}
-
-pub fn create_module(py: pyo3::Python<'_>) -> pyo3::PyResult<&PyModule> {
-    let submod = PyModule::new(py, "point")?;
-    submod.add_class::<Point>()?;
-    Ok(submod)
-}

+ 0 - 63
src/sdk/python/src/proof.rs

@@ -1,63 +0,0 @@
-/* This file is part of DarkFi (https://dark.fi)
- *
- * Copyright (C) 2020-2023 Dyne.org foundation
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <https://www.gnu.org/licenses/>.
- */
-
-use std::ops::Deref;
-
-use darkfi::zk::{proof, vm};
-use darkfi_sdk::pasta::pallas;
-use pyo3::prelude::*;
-use rand::rngs::OsRng;
-
-use super::{
-    base::Base, proving_key::ProvingKey, verifying_key::VerifyingKey, zk_circuit::ZkCircuit,
-};
-
-#[pyclass]
-pub struct Proof(pub(crate) proof::Proof);
-
-#[pymethods]
-impl Proof {
-    #[staticmethod]
-    fn create(
-        pk: &PyCell<ProvingKey>,
-        circuits: Vec<&PyCell<ZkCircuit>>,
-        instances: Vec<&PyCell<Base>>,
-    ) -> Self {
-        let pk = pk.borrow().deref().0.clone();
-        let circuits: Vec<vm::ZkCircuit> =
-            circuits.iter().map(|c| c.borrow().deref().0.clone()).collect();
-        let instances: Vec<pallas::Base> = instances.iter().map(|i| i.borrow().deref().0).collect();
-        let proof =
-            proof::Proof::create(&pk, circuits.as_slice(), instances.as_slice(), &mut OsRng);
-        let proof = proof.unwrap();
-        Self(proof)
-    }
-
-    fn verify(&self, vk: &PyCell<VerifyingKey>, instances: Vec<&PyCell<Base>>) {
-        let vk = vk.borrow().deref().0.clone();
-        let proof = self.0.clone();
-        let instances: Vec<pallas::Base> = instances.iter().map(|i| i.borrow().deref().0).collect();
-        proof.verify(&vk, instances.as_slice()).unwrap();
-    }
-}
-
-pub fn create_module(py: pyo3::Python<'_>) -> pyo3::PyResult<&PyModule> {
-    let submod = PyModule::new(py, "proof")?;
-    submod.add_class::<Proof>()?;
-    Ok(submod)
-}

+ 0 - 44
src/sdk/python/src/proving_key.rs

@@ -1,44 +0,0 @@
-/* This file is part of DarkFi (https://dark.fi)
- *
- * Copyright (C) 2020-2023 Dyne.org foundation
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <https://www.gnu.org/licenses/>.
- */
-
-use std::ops::Deref;
-
-use darkfi::zk::{proof, vm};
-use pyo3::prelude::*;
-
-use super::zk_circuit::ZkCircuit;
-
-#[pyclass]
-pub struct ProvingKey(pub(crate) proof::ProvingKey);
-
-#[pymethods]
-impl ProvingKey {
-    #[staticmethod]
-    fn build(k: u32, circuit: &PyCell<ZkCircuit>) -> Self {
-        let circuit_ref = circuit.borrow();
-        let circuit: &vm::ZkCircuit = &circuit_ref.deref().0;
-        let proving_key = proof::ProvingKey::build(k, circuit);
-        Self(proving_key)
-    }
-}
-
-pub fn create_module(py: pyo3::Python<'_>) -> pyo3::PyResult<&PyModule> {
-    let submod = PyModule::new(py, "proving_key")?;
-    submod.add_class::<ProvingKey>()?;
-    Ok(submod)
-}

+ 0 - 121
src/sdk/python/src/scalar.rs

@@ -1,121 +0,0 @@
-/* This file is part of DarkFi (https://dark.fi)
- *
- * Copyright (C) 2020-2023 Dyne.org foundation
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <https://www.gnu.org/licenses/>.
- */
-
-use darkfi_sdk::{
-    crypto::pasta_prelude::{Field, PrimeField},
-    pasta::{group::ff::FromUniformBytes, pallas},
-};
-use pyo3::{basic::CompareOp, prelude::*};
-use rand::rngs::OsRng;
-
-/// The scalar field of the Pallas and iso-Pallas curves.
-#[pyclass]
-pub struct Scalar(pub(crate) pallas::Scalar);
-
-#[pymethods]
-impl Scalar {
-    #[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))
-    }
-
-    #[staticmethod]
-    fn from_raw(v: [u64; 4]) -> Self {
-        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))
-    }
-
-    #[staticmethod]
-    fn modulus() -> String {
-        pallas::Scalar::MODULUS.to_string()
-    }
-
-    #[staticmethod]
-    fn zero() -> Self {
-        Self(pallas::Scalar::zero())
-    }
-
-    #[staticmethod]
-    fn one() -> Self {
-        Self(pallas::Scalar::one())
-    }
-
-    fn __str__(&self) -> String {
-        format!("{:?}", 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, other: &Self) -> Self {
-        Self(self.0 + other.0)
-    }
-
-    fn __sub__(&self, other: &Self) -> Self {
-        Self(self.0 - other.0)
-    }
-
-    fn __mul__(&self, other: &Self) -> Self {
-        Self(self.0 * other.0)
-    }
-
-    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 {
-        Self(self.0.square())
-    }
-}
-
-pub fn create_module(py: pyo3::Python<'_>) -> pyo3::PyResult<&PyModule> {
-    let submod = PyModule::new(py, "scalar")?;
-    submod.add_class::<Scalar>()?;
-    Ok(submod)
-}

+ 0 - 44
src/sdk/python/src/verifying_key.rs

@@ -1,44 +0,0 @@
-/* This file is part of DarkFi (https://dark.fi)
- *
- * Copyright (C) 2020-2023 Dyne.org foundation
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <https://www.gnu.org/licenses/>.
- */
-
-use std::ops::Deref;
-
-use darkfi::zk::proof;
-use pyo3::prelude::*;
-
-use super::zk_circuit::ZkCircuit;
-
-#[pyclass]
-pub struct VerifyingKey(pub(crate) proof::VerifyingKey);
-
-#[pymethods]
-impl VerifyingKey {
-    #[staticmethod]
-    fn build(k: u32, circuit: &PyCell<ZkCircuit>) -> Self {
-        let circuit_ref = circuit.borrow();
-        let circuit = &circuit_ref.deref().0;
-        let proving_key = proof::VerifyingKey::build(k, circuit);
-        Self(proving_key)
-    }
-}
-
-pub fn create_module(py: pyo3::Python<'_>) -> pyo3::PyResult<&PyModule> {
-    let submod = PyModule::new(py, "verifying_key")?;
-    submod.add_class::<VerifyingKey>()?;
-    Ok(submod)
-}

+ 0 - 77
src/sdk/python/src/zk_binary.rs

@@ -1,77 +0,0 @@
-/* This file is part of DarkFi (https://dark.fi)
- *
- * Copyright (C) 2020-2023 Dyne.org foundation
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <https://www.gnu.org/licenses/>.
- */
-
-use darkfi::zkas::decoder;
-use pyo3::prelude::*;
-
-#[pyclass]
-pub struct ZkBinary(pub(crate) decoder::ZkBinary);
-
-/// There is no need for constants, as bindings for ec_mul_short and ec_mul_base
-/// don't actually take the constants.
-/// The constants are hardcoded on the Rust side.
-#[pymethods]
-impl ZkBinary {
-    #[staticmethod]
-    fn decode(bytes: Vec<u8>) -> Self {
-        let bincode = decoder::ZkBinary::decode(bytes.as_slice()).unwrap();
-        Self(bincode)
-    }
-
-    fn namespace(&self) -> String {
-        self.0.namespace.clone()
-    }
-
-    fn literals(&self) -> Vec<(String, String)> {
-        let l = self.0.literals.clone();
-        l.iter().map(|(lit, value)| (format!("{lit:?}"), value.clone())).collect()
-    }
-
-    fn witnesses(&self) -> Vec<String> {
-        let w = self.0.witnesses.clone();
-        w.iter().map(|v| format!("{v:?}")).collect()
-    }
-
-    fn constant_count(&self) -> usize {
-        self.0.constants.len()
-    }
-
-    fn opcodes(&self) -> Vec<(String, Vec<(String, usize)>)> {
-        let o = self.0.opcodes.clone();
-        o.iter()
-            .map(|(opcode_, args_)| {
-                let opcode = format!("{opcode_:?}");
-                let args = args_
-                    .iter()
-                    .map(|(heap_type, heap_idx)| (format!("{heap_type:?}"), *heap_idx))
-                    .collect();
-                (opcode, args)
-            })
-            .collect()
-    }
-
-    fn k(&self) -> u32 {
-        self.0.k
-    }
-}
-
-pub fn create_module(py: pyo3::Python<'_>) -> pyo3::PyResult<&PyModule> {
-    let submod = PyModule::new(py, "zk_binary")?;
-    submod.add_class::<ZkBinary>()?;
-    Ok(submod)
-}

+ 0 - 103
src/sdk/python/src/zk_circuit.rs

@@ -1,103 +0,0 @@
-/* This file is part of DarkFi (https://dark.fi)
- *
- * Copyright (C) 2020-2023 Dyne.org foundation
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <https://www.gnu.org/licenses/>.
- */
-
-use std::ops::Deref;
-
-use darkfi::zk::{halo2::Value, vm, vm_heap::empty_witnesses};
-use darkfi_sdk::crypto::MerkleNode;
-use pyo3::prelude::*;
-
-use super::{base::Base, point::Point, scalar::Scalar, zk_binary::ZkBinary};
-
-#[pyclass]
-pub struct ZkCircuit(pub(crate) vm::ZkCircuit, pub(crate) Vec<vm::Witness>);
-
-/// Like Builder Object
-#[pymethods]
-impl ZkCircuit {
-    #[new]
-    fn new(circuit_code: &PyCell<ZkBinary>) -> Self {
-        let circuit_code = circuit_code.borrow().deref().0.clone();
-        // DUMMY CIRCUIT
-        let circuit = vm::ZkCircuit::new(vec![], &circuit_code);
-        Self(circuit, vec![])
-    }
-
-    fn build(&self, circuit_code: &PyCell<ZkBinary>) -> Self {
-        let circuit_code = circuit_code.borrow().deref().0.clone();
-        let circuit = vm::ZkCircuit::new(self.1.clone(), &circuit_code);
-        Self(circuit, self.1.clone())
-    }
-
-    fn verifier_build(&self, circuit_code: &PyCell<ZkBinary>) -> Self {
-        let circuit_code = circuit_code.borrow().deref().0.clone();
-        let circuit = vm::ZkCircuit::new(empty_witnesses(&circuit_code).unwrap(), &circuit_code);
-        Self(circuit, self.1.clone())
-    }
-
-    fn witness_point(&mut self, v: &PyCell<Point>) {
-        let v = v.borrow();
-        let v = v.deref();
-        self.1.push(vm::Witness::EcPoint(Value::known(v.0)));
-    }
-
-    fn witness_ni_point(&mut self, v: &PyCell<Point>) {
-        let v = v.borrow();
-        let v = v.deref();
-        self.1.push(vm::Witness::EcNiPoint(Value::known(v.0)));
-    }
-
-    fn witness_fixed_point(&mut self, v: &PyCell<Point>) {
-        let v = v.borrow();
-        let v = v.deref();
-        self.1.push(vm::Witness::EcFixedPoint(Value::known(v.0)));
-    }
-
-    fn witness_scalar(&mut self, v: &PyCell<Scalar>) {
-        let v = v.borrow();
-        let v = v.deref();
-        self.1.push(vm::Witness::Scalar(Value::known(v.0)));
-    }
-
-    fn witness_base(&mut self, v: &PyCell<Base>) {
-        let v = v.borrow();
-        let v = v.deref();
-        self.1.push(vm::Witness::Base(Value::known(v.0)));
-    }
-
-    fn witness_merkle_path(&mut self, v: Vec<&PyCell<Base>>) {
-        let v: Vec<MerkleNode> = v.iter().map(|v| MerkleNode::new(v.borrow().deref().0)).collect();
-        let v: [MerkleNode; 32] = v.try_into().unwrap();
-        let v = Value::known(v);
-        self.1.push(vm::Witness::MerklePath(v));
-    }
-
-    fn witness_u32(&mut self, v: u32) {
-        self.1.push(vm::Witness::Uint32(Value::known(v)));
-    }
-
-    fn witness_u64(&mut self, v: u64) {
-        self.1.push(vm::Witness::Uint64(Value::known(v)));
-    }
-}
-
-pub fn create_module(py: pyo3::Python<'_>) -> pyo3::PyResult<&PyModule> {
-    let submod = PyModule::new(py, "zk_circuit")?;
-    submod.add_class::<ZkCircuit>()?;
-    Ok(submod)
-}

+ 209 - 0
src/sdk/python/src/zkas.rs

@@ -0,0 +1,209 @@
+/* This file is part of DarkFi (https://dark.fi)
+ *
+ * Copyright (C) 2020-2023 Dyne.org foundation
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+
+use std::ops::Deref;
+
+use darkfi::{
+    zk::{self, empty_witnesses, halo2::Value},
+    zkas::decoder,
+};
+use darkfi_sdk::{crypto::MerkleNode, pasta::pallas};
+use pyo3::{pyclass, pymethods, types::PyModule, PyCell, PyResult, Python};
+use rand::rngs::OsRng;
+
+use super::pasta::{Ep, Fp, Fq};
+
+#[pyclass]
+/// Decoded zkas bincode
+pub struct ZkBinary(decoder::ZkBinary);
+
+#[pymethods]
+impl ZkBinary {
+    #[new]
+    fn new(bytes: Vec<u8>) -> Self {
+        Self::decode(bytes)
+    }
+
+    #[staticmethod]
+    fn decode(bytes: Vec<u8>) -> Self {
+        let bincode = decoder::ZkBinary::decode(bytes.as_slice()).unwrap();
+        Self(bincode)
+    }
+
+    fn k(&self) -> u32 {
+        self.0.k
+    }
+}
+
+#[pyclass]
+/// Class representing a zkVM circuit, the witness values, and the zkas binary
+/// defining the circuit code path.
+pub struct ZkCircuit(zk::vm::ZkCircuit, Vec<zk::vm::Witness>, decoder::ZkBinary);
+
+#[pymethods]
+impl ZkCircuit {
+    #[new]
+    fn new(zkbin: &PyCell<ZkBinary>) -> Self {
+        let zkbin = zkbin.borrow().deref().0.clone();
+        let circuit = zk::vm::ZkCircuit::new(vec![], &zkbin);
+        Self(circuit, vec![], zkbin)
+    }
+
+    fn prover_build(&self) -> Self {
+        let circuit = zk::vm::ZkCircuit::new(self.1.clone(), &self.2);
+        Self(circuit, self.1.clone(), self.2.clone())
+    }
+
+    fn verifier_build(&self) -> Self {
+        let witnesses = empty_witnesses(&self.2).unwrap();
+        let circuit = zk::vm::ZkCircuit::new(witnesses.clone(), &self.2);
+        Self(circuit, witnesses, self.2.clone())
+    }
+
+    fn witness_ecpoint(&mut self, w: &PyCell<Ep>) {
+        let w = w.borrow();
+        let w = w.deref();
+        self.1.push(zk::vm::Witness::EcPoint(Value::known(w.0)));
+    }
+
+    fn witness_ecnipoint(&mut self, w: &PyCell<Ep>) {
+        let w = w.borrow();
+        let w = w.deref();
+        self.1.push(zk::vm::Witness::EcNiPoint(Value::known(w.0)));
+    }
+
+    fn witness_base(&mut self, w: &PyCell<Fp>) {
+        let w = w.borrow();
+        let w = w.deref();
+        self.1.push(zk::vm::Witness::Base(Value::known(w.0)));
+    }
+
+    fn witness_scalar(&mut self, w: &PyCell<Fq>) {
+        let w = w.borrow();
+        let w = w.deref();
+        self.1.push(zk::vm::Witness::Scalar(Value::known(w.0)));
+    }
+
+    fn witness_merklepath(&mut self, w: Vec<&PyCell<Fp>>) {
+        assert!(w.len() == 32);
+        let path: Vec<MerkleNode> =
+            w.iter().map(|x| MerkleNode::from(x.borrow().deref().0)).collect();
+        self.1.push(zk::vm::Witness::MerklePath(Value::known(path.try_into().unwrap())));
+    }
+
+    fn witness_uint32(&mut self, w: u32) {
+        self.1.push(zk::vm::Witness::Uint32(Value::known(w)));
+    }
+
+    fn witness_uint64(&mut self, w: u64) {
+        self.1.push(zk::vm::Witness::Uint64(Value::known(w)));
+    }
+}
+
+#[pyclass]
+/// Verifying key for a zkVM proof
+pub struct VerifyingKey(zk::proof::VerifyingKey);
+
+#[pymethods]
+impl VerifyingKey {
+    #[staticmethod]
+    fn build(k: u32, circuit: &PyCell<ZkCircuit>) -> Self {
+        let circuit_ref = circuit.borrow();
+        let circuit = &circuit_ref.deref().0;
+        let vk = zk::proof::VerifyingKey::build(k, circuit);
+        Self(vk)
+    }
+}
+
+#[pyclass]
+/// Proving key for a zkVM proof
+pub struct ProvingKey(zk::proof::ProvingKey);
+
+#[pymethods]
+impl ProvingKey {
+    #[staticmethod]
+    fn build(k: u32, circuit: &PyCell<ZkCircuit>) -> Self {
+        let circuit_ref = circuit.borrow();
+        let circuit = &circuit_ref.deref().0;
+        let pk = zk::proof::ProvingKey::build(k, circuit);
+        Self(pk)
+    }
+}
+
+#[pyclass]
+/// A zkVM proof
+pub struct Proof(zk::proof::Proof);
+
+#[pymethods]
+impl Proof {
+    #[staticmethod]
+    fn create(
+        pk: &PyCell<ProvingKey>,
+        circuits: Vec<&PyCell<ZkCircuit>>,
+        instances: Vec<&PyCell<Fp>>,
+    ) -> Self {
+        let pk = pk.borrow().deref().0.clone();
+        let circuits: Vec<zk::vm::ZkCircuit> =
+            circuits.iter().map(|c| c.borrow().deref().0.clone()).collect();
+        let instances: Vec<pallas::Base> = instances.iter().map(|i| i.borrow().deref().0).collect();
+
+        let proof =
+            zk::proof::Proof::create(&pk, circuits.as_slice(), instances.as_slice(), &mut OsRng)
+                .unwrap();
+        Self(proof)
+    }
+
+    fn verify(&self, vk: &PyCell<VerifyingKey>, instances: Vec<&PyCell<Fp>>) {
+        let vk = vk.borrow().deref().0.clone();
+        let instances: Vec<pallas::Base> = instances.iter().map(|i| i.borrow().deref().0).collect();
+        self.0.verify(&vk, instances.as_slice()).unwrap();
+    }
+}
+
+#[pyclass]
+/// MockProver class used for fast proof creation and verification.
+/// Doesn't offer any security and should not be used in production.
+pub struct MockProver(zk::halo2::dev::MockProver<pallas::Base>);
+
+#[pymethods]
+impl MockProver {
+    #[staticmethod]
+    fn run(k: u32, circuit: &PyCell<ZkCircuit>, instances: Vec<&PyCell<Fp>>) -> Self {
+        let circuit = circuit.borrow().deref().0.clone();
+        let instances: Vec<pallas::Base> = instances.iter().map(|i| i.borrow().deref().0).collect();
+        let prover = zk::halo2::dev::MockProver::run(k, &circuit, vec![instances]).unwrap();
+        Self(prover)
+    }
+
+    fn verify(&self) {
+        self.0.assert_satisfied();
+    }
+}
+
+pub fn create_module(py: Python<'_>) -> PyResult<&PyModule> {
+    let submod = PyModule::new(py, "zkas")?;
+
+    submod.add_class::<ZkBinary>()?;
+    submod.add_class::<ZkCircuit>()?;
+    submod.add_class::<VerifyingKey>()?;
+    submod.add_class::<ProvingKey>()?;
+    submod.add_class::<Proof>()?;
+    submod.add_class::<MockProver>()?;
+
+    Ok(submod)
+}