sdk-py: Move to src/sdk/python

This commit is contained in:
parazyd
2023-06-13 08:42:42 +02:00
committed by parazyd
parent 533594bbab
commit badcb123d3
16 changed files with 12 additions and 11 deletions

72
src/sdk/python/.gitignore vendored Normal file
View File

@@ -0,0 +1,72 @@
/target
# Byte-compiled / optimized / DLL files
__pycache__/
.pytest_cache/
*.py[cod]
# C extensions
*.so
# Distribution / packaging
.Python
.venv/
env/
bin/
build/
develop-eggs/
dist/
eggs/
lib/
lib64/
parts/
sdist/
var/
include/
man/
venv/
*.egg-info/
.installed.cfg
*.egg
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
pip-selfcheck.json
# Unit test / coverage reports
htmlcov/
.tox/
.coverage
.cache
nosetests.xml
coverage.xml
# Translations
*.mo
# Mr Developer
.mr.developer.cfg
.project
.pydevproject
# Rope
.ropeproject
# Django stuff:
*.log
*.pot
.DS_Store
# Sphinx documentation
docs/_build/
# PyCharm
.idea/
# VSCode
.vscode/
# Pyenv
.python-version

22
src/sdk/python/Cargo.toml Normal file
View File

@@ -0,0 +1,22 @@
[package]
name = "darkfi-sdk-py"
description = "Python bindings for Darkfi SDK"
version = "0.4.1"
edition = "2021"
authors = ["Dyne.org foundation <foundation@dyne.org>"]
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"
crate-type = ["cdylib"]
[dependencies]
darkfi = { path = "../../../", features = ["zk", "zkas"] }
darkfi-sdk = { path = "../" }
halo2_gadgets = "0.3.0"
pasta_curves = "0.5.1"
pyo3 = "0.19.0"
rand = "0.8.5"

3
src/sdk/python/README.md Normal file
View File

@@ -0,0 +1,3 @@
# Installation
Follow virtualenv and pyo3 setup guide: https://pyo3.rs/v0.15.1/#using-rust-from-python

View File

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

View File

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

195
src/sdk/python/src/base.rs Normal file
View File

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

76
src/sdk/python/src/lib.rs Normal file
View File

@@ -0,0 +1,76 @@
/* This file is part of DarkFi (https://dark.fi)
*
* Copyright (C) 2020-2023 Dyne.org foundation
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
mod affine;
mod base;
mod point;
mod proof;
mod proving_key;
mod scalar;
mod verifying_key;
mod zk_binary;
mod zk_circuit;
#[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");
m.add_submodule(submodule)?;
let submodule = base::create_module(py)?;
pyo3::py_run!(py, submodule, "import sys; sys.modules['darkfi_sdk_py.base'] = submodule");
m.add_submodule(submodule)?;
let submodule = scalar::create_module(py)?;
pyo3::py_run!(py, submodule, "import sys; sys.modules['darkfi_sdk_py.scalar'] = submodule");
m.add_submodule(scalar::create_module(py)?)?;
let submodule = point::create_module(py)?;
pyo3::py_run!(py, submodule, "import sys; sys.modules['darkfi_sdk_py.point'] = submodule");
m.add_submodule(point::create_module(py)?)?;
let submodule = proof::create_module(py)?;
pyo3::py_run!(py, submodule, "import sys; sys.modules['darkfi_sdk_py.proof'] = submodule");
m.add_submodule(proof::create_module(py)?)?;
let submodule = proving_key::create_module(py)?;
pyo3::py_run!(
py,
submodule,
"import sys; sys.modules['darkfi_sdk_py.proving_key'] = submodule"
);
m.add_submodule(proving_key::create_module(py)?)?;
let submodule = verifying_key::create_module(py)?;
pyo3::py_run!(
py,
submodule,
"import sys; sys.modules['darkfi_sdk_py.verifying_key'] = submodule"
);
m.add_submodule(verifying_key::create_module(py)?)?;
let submodule = zk_binary::create_module(py)?;
pyo3::py_run!(py, submodule, "import sys; sys.modules['darkfi_sdk_py.zk_binary'] = submodule");
m.add_submodule(zk_binary::create_module(py)?)?;
let submodule = 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)?)?;
Ok(())
}

100
src/sdk/python/src/point.rs Normal file
View File

@@ -0,0 +1,100 @@
/* This file is part of DarkFi (https://dark.fi)
*
* Copyright (C) 2020-2023 Dyne.org foundation
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
use crate::{affine::Affine, base::Base, scalar::Scalar};
use darkfi_sdk::{
crypto::{
constants::fixed_bases::{
NullifierK, VALUE_COMMITMENT_PERSONALIZATION, VALUE_COMMITMENT_R_BYTES,
VALUE_COMMITMENT_V_BYTES,
},
util::mod_r_p,
ValueCommit,
},
pasta::{
arithmetic::CurveExt,
group::{Curve, Group},
},
};
use halo2_gadgets::ecc::chip::FixedPoint;
use pasta_curves::pallas;
use pyo3::prelude::*;
use std::ops::{Add, Mul};
/// 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 = ValueCommit::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 = ValueCommit::hash_to_curve(VALUE_COMMITMENT_PERSONALIZATION);
let r = hasher(&VALUE_COMMITMENT_R_BYTES);
let r = Self(r);
r.mul(blind)
}
#[pyo3(name = "__str__")]
fn __str__(&self) -> String {
format!("Point({:?})", self.0)
}
fn to_affine(&self) -> Affine {
Affine(self.0.to_affine())
}
fn add(&self, rhs: &Self) -> Self {
Self(self.0.add(rhs.0))
}
fn mul(&self, scalar: &Scalar) -> Self {
Self(self.0.mul(scalar.0))
}
}
pub fn create_module(py: pyo3::Python<'_>) -> pyo3::PyResult<&PyModule> {
let submod = PyModule::new(py, "point")?;
submod.add_class::<Point>()?;
Ok(submod)
}

View File

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

View File

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

View File

@@ -0,0 +1,99 @@
/* This file is part of DarkFi (https://dark.fi)
*
* Copyright (C) 2020-2023 Dyne.org foundation
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
use darkfi_sdk::crypto::pasta_prelude::{Field, PrimeField};
use pasta_curves::pallas;
use pyo3::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 {
#[new]
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 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())
}
#[pyo3(name = "__str__")]
fn __str__(&self) -> String {
format!("Scalar({:?})", self.0)
}
#[pyo3(name = "__repr__")]
fn __repr__(&self) -> String {
format!("Scalar({:?})", self.0)
}
fn add(&self, rhs: &Self) -> Self {
Self(self.0.add(&rhs.0))
}
fn sub(&self, rhs: &Self) -> Self {
Self(self.0.sub(&rhs.0))
}
fn double(&self) -> Self {
Self(self.0.double())
}
fn mul(&self, rhs: &Self) -> Self {
Self(self.0.mul(&rhs.0))
}
fn neg(&self) -> Self {
Self(self.0.neg())
}
fn square(&self) -> Self {
Self(self.0.square())
}
}
pub fn create_module(py: pyo3::Python<'_>) -> pyo3::PyResult<&PyModule> {
let submod = PyModule::new(py, "scalar")?;
submod.add_class::<Scalar>()?;
Ok(submod)
}

View File

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

View File

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

View File

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