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) +}