From 988455f79b374c3bfb67b91c0a8d979de1356a17 Mon Sep 17 00:00:00 2001
From: parazyd
Date: Sun, 13 Aug 2023 15:31:08 +0200
Subject: [PATCH] sdk/python: Perform full code cleanup and make everything
work.
---
src/sdk/python/Cargo.toml | 9 +-
src/sdk/python/Makefile | 1 -
src/sdk/python/README.md | 12 +-
src/sdk/python/pyproject.toml | 5 +-
src/sdk/python/src/affine.rs | 44 ----
src/sdk/python/src/base.rs | 206 ------------------
src/sdk/python/src/crypto.rs | 73 +++++++
src/sdk/python/src/lib.rs | 78 ++-----
src/sdk/python/src/merkle.rs | 60 ++++++
src/sdk/python/src/pasta.rs | 316 ++++++++++++++++++++++++++++
src/sdk/python/src/point.rs | 128 -----------
src/sdk/python/src/proof.rs | 63 ------
src/sdk/python/src/proving_key.rs | 44 ----
src/sdk/python/src/scalar.rs | 121 -----------
src/sdk/python/src/verifying_key.rs | 44 ----
src/sdk/python/src/zk_binary.rs | 77 -------
src/sdk/python/src/zk_circuit.rs | 103 ---------
src/sdk/python/src/zkas.rs | 209 ++++++++++++++++++
18 files changed, 691 insertions(+), 902 deletions(-)
delete mode 100644 src/sdk/python/src/affine.rs
delete mode 100644 src/sdk/python/src/base.rs
create mode 100644 src/sdk/python/src/crypto.rs
create mode 100644 src/sdk/python/src/merkle.rs
create mode 100644 src/sdk/python/src/pasta.rs
delete mode 100644 src/sdk/python/src/point.rs
delete mode 100644 src/sdk/python/src/proof.rs
delete mode 100644 src/sdk/python/src/proving_key.rs
delete mode 100644 src/sdk/python/src/scalar.rs
delete mode 100644 src/sdk/python/src/verifying_key.rs
delete mode 100644 src/sdk/python/src/zk_binary.rs
delete mode 100644 src/sdk/python/src/zk_circuit.rs
create mode 100644 src/sdk/python/src/zkas.rs
diff --git a/src/sdk/python/Cargo.toml b/src/sdk/python/Cargo.toml
index b59371904..972e948cc 100644
--- a/src/sdk/python/Cargo.toml
+++ b/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 "]
@@ -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"
diff --git a/src/sdk/python/Makefile b/src/sdk/python/Makefile
index 0dbeedc62..19909042a 100644
--- a/src/sdk/python/Makefile
+++ b/src/sdk/python/Makefile
@@ -9,5 +9,4 @@ all:
dev:
$(MATURIN) develop --release
-
.PHONY: all
diff --git a/src/sdk/python/README.md b/src/sdk/python/README.md
index 896ee8511..96a3cb378 100644
--- a/src/sdk/python/README.md
+++ b/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.
diff --git a/src/sdk/python/pyproject.toml b/src/sdk/python/pyproject.toml
index 9f36ea01e..b7c9e0e80 100644
--- a/src/sdk/python/pyproject.toml
+++ b/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"]
diff --git a/src/sdk/python/src/affine.rs b/src/sdk/python/src/affine.rs
deleted file mode 100644
index 4863bed46..000000000
--- a/src/sdk/python/src/affine.rs
+++ /dev/null
@@ -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 .
- */
-
-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::()?;
- Ok(submod)
-}
diff --git a/src/sdk/python/src/base.rs b/src/sdk/python/src/base.rs
deleted file mode 100644
index caea3fb85..000000000
--- a/src/sdk/python/src/base.rs
+++ /dev/null
@@ -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 .
- */
-
-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 {
- let l = messages.len();
- let messages: Vec = 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>, 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(), ¤t, &sibling)
- } else {
- MerkleNode::combine(level.into(), &sibling, ¤t)
- };
- }
- let root = current.inner();
- Self(root)
- }
-
- fn __str__(&self) -> String {
- format!("{:?}", self.0)
- }
-
- fn __repr__(slf: &PyCell) -> PyResult {
- 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 {
- 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::()?;
- Ok(submod)
-}
diff --git a/src/sdk/python/src/crypto.rs b/src/sdk/python/src/crypto.rs
new file mode 100644
index 000000000..c4dfb3209
--- /dev/null
+++ b/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 .
+ */
+
+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 {
+ let messages: Vec = 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) -> 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, blind: &PyCell) -> 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)
+}
diff --git a/src/sdk/python/src/lib.rs b/src/sdk/python/src/lib.rs
index 75c3ff2c0..d63e1a330 100644
--- a/src/sdk/python/src/lib.rs
+++ b/src/sdk/python/src/lib.rs
@@ -16,71 +16,35 @@
* along with this program. If not, see .
*/
-/// 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 = crypto::create_module(py)?;
+ pyo3::py_run!(py, submodule, "import sys; sys.modules['darkfi_sdk.crypto'] = submodule");
+ m.add_submodule(submodule)?;
- 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 = 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(())
}
diff --git a/src/sdk/python/src/merkle.rs b/src/sdk/python/src/merkle.rs
new file mode 100644
index 000000000..116261c29
--- /dev/null
+++ b/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 .
+ */
+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) -> PyResult {
+ Ok(self.0.append(MerkleNode::from(node.borrow().deref().0)))
+ }
+
+ fn mark(&mut self) -> PyResult {
+ Ok(u64::from(self.0.mark().unwrap()) as u32)
+ }
+
+ fn root(&self, checkpoint_depth: usize) -> PyResult {
+ let root = self.0.root(checkpoint_depth).unwrap();
+ Ok(Fp(root.inner()))
+ }
+
+ fn witness(&self, position: u32, checkpoint_depth: usize) -> PyResult> {
+ 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::()?;
+ Ok(submod)
+}
diff --git a/src/sdk/python/src/pasta.rs b/src/sdk/python/src/pasta.rs
new file mode 100644
index 000000000..9e40df749
--- /dev/null
+++ b/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 .
+ */
+
+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 {
+ 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 {
+ Ok(format!("{:?}", self.0))
+ }
+
+ fn __repr__(slf: &PyCell) -> PyResult {
+ 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 {
+ 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 {
+ 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> {
+ 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 {
+ 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) -> PyResult {
+ 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 {
+ 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) -> PyResult {
+ let affine = <$affine>::from_projective(slf);
+ let (x, y) = affine.coordinates();
+ Ok(format!("[{}, {}]", x.__str__()?, y.__str__()?))
+ }
+
+ fn __repr__(slf: &PyCell) -> PyResult {
+ 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 {
+ 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) -> PyResult {
+ 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::()?;
+ submod.add_class::()?;
+ submod.add_class::()?;
+ submod.add_class::()?;
+ submod.add_class::()?;
+ submod.add_class::()?;
+
+ submod.add_function(wrap_pyfunction!(nullifier_k, submod)?)?;
+ submod.add_function(wrap_pyfunction!(mod_r_p, submod)?)?;
+
+ Ok(submod)
+}
diff --git a/src/sdk/python/src/point.rs b/src/sdk/python/src/point.rs
deleted file mode 100644
index bbcd72f53..000000000
--- a/src/sdk/python/src/point.rs
+++ /dev/null
@@ -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 .
- */
-
-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) -> PyResult {
- 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 {
- 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::()?;
- Ok(submod)
-}
diff --git a/src/sdk/python/src/proof.rs b/src/sdk/python/src/proof.rs
deleted file mode 100644
index 2ff4bd3bd..000000000
--- a/src/sdk/python/src/proof.rs
+++ /dev/null
@@ -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 .
- */
-
-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,
- circuits: Vec<&PyCell>,
- instances: Vec<&PyCell>,
- ) -> Self {
- let pk = pk.borrow().deref().0.clone();
- let circuits: Vec =
- circuits.iter().map(|c| c.borrow().deref().0.clone()).collect();
- let instances: Vec = 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, instances: Vec<&PyCell>) {
- let vk = vk.borrow().deref().0.clone();
- let proof = self.0.clone();
- let instances: Vec = 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::()?;
- Ok(submod)
-}
diff --git a/src/sdk/python/src/proving_key.rs b/src/sdk/python/src/proving_key.rs
deleted file mode 100644
index 3d68a0815..000000000
--- a/src/sdk/python/src/proving_key.rs
+++ /dev/null
@@ -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 .
- */
-
-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) -> 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::()?;
- Ok(submod)
-}
diff --git a/src/sdk/python/src/scalar.rs b/src/sdk/python/src/scalar.rs
deleted file mode 100644
index 84b0add44..000000000
--- a/src/sdk/python/src/scalar.rs
+++ /dev/null
@@ -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 .
- */
-
-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) -> PyResult {
- 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 {
- 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::()?;
- Ok(submod)
-}
diff --git a/src/sdk/python/src/verifying_key.rs b/src/sdk/python/src/verifying_key.rs
deleted file mode 100644
index bb975c6bf..000000000
--- a/src/sdk/python/src/verifying_key.rs
+++ /dev/null
@@ -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 .
- */
-
-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) -> 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::()?;
- Ok(submod)
-}
diff --git a/src/sdk/python/src/zk_binary.rs b/src/sdk/python/src/zk_binary.rs
deleted file mode 100644
index be20f565c..000000000
--- a/src/sdk/python/src/zk_binary.rs
+++ /dev/null
@@ -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 .
- */
-
-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) -> 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 {
- 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::()?;
- Ok(submod)
-}
diff --git a/src/sdk/python/src/zk_circuit.rs b/src/sdk/python/src/zk_circuit.rs
deleted file mode 100644
index a8ed496c6..000000000
--- a/src/sdk/python/src/zk_circuit.rs
+++ /dev/null
@@ -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 .
- */
-
-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);
-
-/// Like Builder Object
-#[pymethods]
-impl ZkCircuit {
- #[new]
- fn new(circuit_code: &PyCell) -> 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) -> 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) -> 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) {
- 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) {
- 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) {
- 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) {
- 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) {
- 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>) {
- let v: Vec = 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::()?;
- Ok(submod)
-}
diff --git a/src/sdk/python/src/zkas.rs b/src/sdk/python/src/zkas.rs
new file mode 100644
index 000000000..c18820491
--- /dev/null
+++ b/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 .
+ */
+
+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) -> Self {
+ Self::decode(bytes)
+ }
+
+ #[staticmethod]
+ fn decode(bytes: Vec) -> 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, decoder::ZkBinary);
+
+#[pymethods]
+impl ZkCircuit {
+ #[new]
+ fn new(zkbin: &PyCell) -> 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) {
+ 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) {
+ 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) {
+ 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) {
+ 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>) {
+ assert!(w.len() == 32);
+ let path: Vec =
+ 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) -> 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) -> 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,
+ circuits: Vec<&PyCell>,
+ instances: Vec<&PyCell>,
+ ) -> Self {
+ let pk = pk.borrow().deref().0.clone();
+ let circuits: Vec =
+ circuits.iter().map(|c| c.borrow().deref().0.clone()).collect();
+ let instances: Vec = 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, instances: Vec<&PyCell>) {
+ let vk = vk.borrow().deref().0.clone();
+ let instances: Vec = 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);
+
+#[pymethods]
+impl MockProver {
+ #[staticmethod]
+ fn run(k: u32, circuit: &PyCell, instances: Vec<&PyCell>) -> Self {
+ let circuit = circuit.borrow().deref().0.clone();
+ let instances: Vec = 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::()?;
+ submod.add_class::()?;
+ submod.add_class::()?;
+ submod.add_class::()?;
+ submod.add_class::()?;
+ submod.add_class::()?;
+
+ Ok(submod)
+}