Skip to content

Commit

Permalink
Add Ciphertext, Plaintext, Network types
Browse files Browse the repository at this point in the history
  • Loading branch information
kpp committed Dec 23, 2023
1 parent 46787de commit 90ec87e
Show file tree
Hide file tree
Showing 12 changed files with 359 additions and 3 deletions.
2 changes: 2 additions & 0 deletions sdk/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions sdk/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ crate-type = ["cdylib"]

[dependencies]
anyhow = "1"
indexmap = "2.1.0"
once_cell = "1.18.0"
openssl = "0.10"
pyo3 = { version = "0.20.0", features = ["extension-module", "abi3-py37", "anyhow"] }
rand = { version = "^0.8" }
Expand Down
47 changes: 46 additions & 1 deletion sdk/python/aleo/_aleolib.pyi
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from __future__ import annotations
from typing import List, Optional, Tuple
from typing import List, Mapping, Optional, Tuple


class Account:
Expand Down Expand Up @@ -32,6 +32,13 @@ class Boolean:
def __new__(cls, b: bool) -> Boolean: ...


class Ciphertext:
@staticmethod
def from_string(s: str) -> Ciphertext: ...
def decrypt(self, view_key: ViewKey, nonce: Group) -> Plaintext: ...
def decrypt_symmetric(self, plaintext_view_key: Field) -> Plaintext: ...


class Credits:
def __new__(cls, value: float) -> Credits: ...
def micro(self) -> MicroCredits: ...
Expand Down Expand Up @@ -87,12 +94,18 @@ class Fee:


class Field:
@staticmethod
def random() -> Field: ...
@staticmethod
def from_string(s: str) -> Field: ...
@staticmethod
def domain_separator(domain: str) -> Field: ...
@staticmethod
def from_u128(u128: int) -> Field: ...
@staticmethod
def zero() -> Field: ...
def __mul__(self, other: Field) -> Field: ...
def __truediv__(self, other: Field) -> Field: ...


class Group:
Expand Down Expand Up @@ -190,16 +203,48 @@ class Locator:
def resource(self) -> Identifier: ...


class Network:
@staticmethod
def name() -> str: ...
@staticmethod
def version() -> int: ...
@staticmethod
def edition() -> int: ...
@staticmethod
def hash_psd2(input: List[Field]) -> Field: ...


class MicroCredits:
def __new__(cls, value: int) -> MicroCredits: ...
def __int__(self) -> int: ...


class Plaintext:
@staticmethod
def from_string(s: str) -> Plaintext: ...
@staticmethod
def new_literal(literal: Literal) -> Plaintext: ...
@staticmethod
def new_struct(kv: List[Tuple[Identifier, Plaintext]]) -> Plaintext: ...
@staticmethod
def new_array(kv: List[Plaintext]) -> Plaintext: ...
def encrypt(self, address: Address, randomizer: Scalar) -> Ciphertext: ...
def encrypt_symmetric(self, plaintext_view_key: Field) -> Ciphertext: ...
def is_literal(self) -> bool: ...
def is_struct(self) -> bool: ...
def is_array(self) -> bool: ...
def as_literal(self) -> Literal: ...
def as_struct(self) -> Mapping[Identifier, Plaintext]: ...
def as_array(self) -> List[Plaintext]: ...


class PrivateKey:
def address(self) -> Address: ...
def compute_key(self) -> ComputeKey: ...
@staticmethod
def from_string(private_key: str) -> PrivateKey: ...
@staticmethod
def from_seed(seed: Field) -> PrivateKey: ...
def seed(self) -> Field: ...
def sign(self, message: bytes) -> Signature: ...
def sk_sig(self) -> Scalar: ...
Expand Down
3 changes: 3 additions & 0 deletions sdk/src/account/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ pub use record::{RecordCiphertext, RecordPlaintext};
mod signature;
pub use signature::Signature;

mod text;
pub use text::{Ciphertext, Plaintext};

mod view_key;
pub use view_key::ViewKey;

Expand Down
6 changes: 6 additions & 0 deletions sdk/src/account/private_key.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,12 @@ impl PrivateKey {
ComputeKeyNative::try_from(&self.0).unwrap().into()
}

/// Returns the account private key from an account seed.
#[staticmethod]
fn from_seed(seed: Field) -> anyhow::Result<Self> {
PrivateKeyNative::try_from(seed.into()).map(Self)
}

/// Reads in an account private key from a base58 string.
#[staticmethod]
fn from_string(private_key: &str) -> anyhow::Result<Self> {
Expand Down
200 changes: 200 additions & 0 deletions sdk/src/account/text.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
// Copyright (C) 2019-2023 Aleo Systems Inc.
// This file is part of the Aleo SDK library.

// The Aleo SDK library is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// The Aleo SDK library 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 General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with the Aleo SDK library. If not, see <https://www.gnu.org/licenses/>.

use crate::{
types::{CiphertextNative, LiteralNative, PlaintextNative},
Address, Field, Group, Identifier, Literal, Scalar, ViewKey,
};
use std::ops::Deref;

use once_cell::sync::OnceCell;
use pyo3::{exceptions::PyTypeError, prelude::*};

use std::{collections::HashMap, str::FromStr};

/// The Aleo ciphertext type.
#[pyclass(frozen)]
pub struct Ciphertext(CiphertextNative);

#[pymethods]
impl Ciphertext {
/// Reads in the ciphertext string.
#[staticmethod]
fn from_string(s: &str) -> anyhow::Result<Self> {
CiphertextNative::from_str(s).map(Self)
}

/// Decrypts self into plaintext using the given account view key & nonce.
pub fn decrypt(&self, view_key: ViewKey, nonce: Group) -> anyhow::Result<Plaintext> {
self.0
.decrypt(view_key.into(), nonce.into())
.map(Into::into)
}

/// Decrypts self into plaintext using the given plaintext view key.
pub fn decrypt_symmetric(&self, plaintext_view_key: Field) -> anyhow::Result<Plaintext> {
self.0
.decrypt_symmetric(plaintext_view_key.into())
.map(Into::into)
}

/// Returns the ciphertext as a string.
fn __str__(&self) -> String {
self.0.to_string()
}

fn __eq__(&self, other: &Self) -> bool {
self.0 == other.0
}
}

impl Deref for Ciphertext {
type Target = CiphertextNative;

fn deref(&self) -> &Self::Target {
&self.0
}
}

impl From<CiphertextNative> for Ciphertext {
fn from(value: CiphertextNative) -> Self {
Self(value)
}
}

/// The Aleo plaintext type.
#[pyclass(frozen)]
#[derive(Clone)]
pub struct Plaintext(PlaintextNative);

#[pymethods]
impl Plaintext {
/// Returns a plaintext from a string literal.
#[staticmethod]
fn from_string(s: &str) -> anyhow::Result<Self> {
PlaintextNative::from_str(s).map(Self)
}

/// Returns a new Plaintext from a Literal.
#[staticmethod]
fn new_literal(literal: Literal) -> Self {
PlaintextNative::from(LiteralNative::from(literal)).into()
}

/// Returns a new Plaintext::Struct from a list of (key, value).
#[staticmethod]
fn new_struct(kv: Vec<(Identifier, Plaintext)>) -> Self {
let kv: Vec<_> = kv.into_iter().map(|(k, v)| (k.into(), v.into())).collect();
PlaintextNative::Struct(indexmap::IndexMap::from_iter(kv), OnceCell::new()).into()
}

/// Returns a new Plaintext::Array from a list of values.
#[staticmethod]
fn new_array(values: Vec<Plaintext>) -> Self {
let values: Vec<_> = values.into_iter().map(Into::into).collect();
PlaintextNative::Array(values, OnceCell::new()).into()
}

/// Encrypts self to the given address under the given randomizer.
fn encrypt(&self, address: Address, randomizer: Scalar) -> anyhow::Result<Ciphertext> {
self.0.encrypt(&address, randomizer.into()).map(Into::into)
}

/// Encrypts self under the given plaintext view key.
fn encrypt_symmetric(&self, plaintext_view_key: Field) -> anyhow::Result<Ciphertext> {
self.0
.encrypt_symmetric(plaintext_view_key.into())
.map(Into::into)
}

/// Returns true if self if Plaintext::Literal.
fn is_literal(&self) -> bool {
matches!(self.0, PlaintextNative::Literal(..))
}

/// Returns true if self if Plaintext::Struct.
fn is_struct(&self) -> bool {
matches!(self.0, PlaintextNative::Struct(..))
}

/// Returns true if self if Plaintext::Array
fn is_array(&self) -> bool {
matches!(self.0, PlaintextNative::Array(..))
}

/// Unboxes the underlying Plaintext::Literal.
fn as_literal(&self) -> PyResult<Literal> {
match &self.0 {
PlaintextNative::Literal(literal, _) => Ok(literal.clone().into()),
_ => Err(PyTypeError::new_err("Plaintext is not a literal")),
}
}

/// Unboxes the underlying Plaintext::Struct.
fn as_struct(&self) -> PyResult<HashMap<Identifier, Plaintext>> {
match &self.0 {
PlaintextNative::Struct(s, _) => {
let res: HashMap<Identifier, Plaintext> = s
.clone()
.into_iter()
.map(|(k, v)| (k.into(), v.into()))
.collect();
Ok(res)
}
_ => Err(PyTypeError::new_err("Plaintext is not a struct")),
}
}

/// Unboxes the underlying Plaintext::Array.
fn as_array(&self) -> PyResult<Vec<Plaintext>> {
match &self.0 {
PlaintextNative::Array(s, _) => {
let res: Vec<Plaintext> = s.clone().into_iter().map(|v| v.into()).collect();
Ok(res)
}
_ => Err(PyTypeError::new_err("Plaintext is not an array")),
}
}

/// Returns the plaintext as a string.
fn __str__(&self) -> String {
self.0.to_string()
}

fn __eq__(&self, other: &Self) -> bool {
self.0 == other.0
}
}

impl Deref for Plaintext {
type Target = PlaintextNative;

fn deref(&self) -> &Self::Target {
&self.0
}
}

impl From<PlaintextNative> for Plaintext {
fn from(value: PlaintextNative) -> Self {
Self(value)
}
}

impl From<Plaintext> for PlaintextNative {
fn from(value: Plaintext) -> Self {
value.0
}
}
6 changes: 6 additions & 0 deletions sdk/src/account/view_key.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,3 +82,9 @@ impl From<ViewKeyNative> for ViewKey {
Self(value)
}
}

impl From<ViewKey> for ViewKeyNative {
fn from(value: ViewKey) -> Self {
value.0
}
}
29 changes: 28 additions & 1 deletion sdk/src/algebra/field.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@

use crate::types::FieldNative;

use pyo3::prelude::*;
use pyo3::{exceptions::PyZeroDivisionError, prelude::*};
use rand::{distributions::Standard, prelude::*};
use snarkvm::prelude::Zero;

use std::{
Expand All @@ -39,6 +40,20 @@ impl Field {
FieldNative::from_str(s).map(Self)
}

/// Generates a new field using a cryptographically secure random number generator
#[staticmethod]
fn random() -> Self {
StdRng::from_entropy()
.sample::<FieldNative, _>(Standard)
.into()
}

/// Initializes a new field as a domain separator.
#[staticmethod]
fn domain_separator(domain: &str) -> Self {
Self(FieldNative::new_domain_separator(domain))
}

/// Initializes a new field from a `u128`.
#[staticmethod]
fn from_u128(value: u128) -> Self {
Expand All @@ -56,6 +71,18 @@ impl Field {
self.0.to_string()
}

fn __mul__(&self, other: Self) -> Self {
Self(self.0 * other.0)
}

fn __truediv__(&self, other: Self) -> PyResult<Self> {
if other.is_zero() {
Err(PyZeroDivisionError::new_err("division by zero"))
} else {
Ok(Self(self.0 / other.0))
}
}

fn __eq__(&self, other: &Self) -> bool {
self.0 == other.0
}
Expand Down
Loading

0 comments on commit 90ec87e

Please sign in to comment.