Skip to content

Commit

Permalink
crypto: create new crate, scratch work
Browse files Browse the repository at this point in the history
Adds asset identifiers and value commitment generator computation.
Adds a basic test of value commitments with different bases.

This commit changes the workspace to use a Penumbra fork of some of the
arkworks crates.  The current change is in the Display impl for Fp* instances.
The fork's `ours` branch is based off of `v0.3.0`, in order to not pick up any
unreleased breaking changes.
  • Loading branch information
hdevalence committed Oct 4, 2021
1 parent 9f6ac72 commit 4c32464
Show file tree
Hide file tree
Showing 8 changed files with 274 additions and 3 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/notes.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ jobs:
# - published crates are excluded
# Doing this in one go is useful because the JSON file with search
# indexes is overwritten on each cargo doc invocation.
cargo doc --no-deps -p tendermint -p ark-sponge -p decaf377 -p poseidon377 -p penumbra-proto -p penumbra
cargo doc --no-deps -p tendermint -p ark-sponge -p decaf377 -p poseidon377 -p penumbra-crypto -p penumbra-proto -p penumbra
- name: Move API docs to subdirectory
run: |
if [ -d "firebase-tmp" ]; then rm -rf firebase-tmp; fi
Expand Down
5 changes: 5 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,14 @@

members = [
"proto",
"crypto",
"penumbra",
]

[patch.crates-io]
tracing = { git = "https://github.com/tokio-rs/tracing/", branch = "v0.1.x" }
tracing-subscriber = { git = "https://github.com/tokio-rs/tracing/", branch = "v0.1.x" }

# The "ours" branch is based off of v0.3.0
ark-ff = { git = "https://github.com/penumbra-zone/algebra", branch = "ours" }
ark-serialize = { git = "https://github.com/penumbra-zone/algebra", branch = "ours" }
16 changes: 16 additions & 0 deletions crypto/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
[package]
name = "penumbra-crypto"
version = "0.1.0"
edition = "2018"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
decaf377 = { git = "https://github.com/penumbra-zone/decaf377" }
poseidon377 = { git = "https://github.com/penumbra-zone/poseidon377" }
hex = "0.4"
blake2b_simd = "0.5"
ark-ff = "0.3"
once_cell = "1.8"
# only needed because ark-ff doesn't display correctly
num-bigint = "0.4"
87 changes: 87 additions & 0 deletions crypto/src/asset.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
//! Asset types and identifiers.

use ark_ff::fields::PrimeField;
use once_cell::sync::Lazy;

use crate::Fq;

/// An identifier for an IBC asset type.
///
/// This is similar to, but different from, the design in [ADR001]. As in
/// ADR001, a denomination trace is hashed to a fixed-size identifier, but
/// unlike ADR001, we hash to a field element rather than a byte string.
///
/// A denomination trace looks like
///
/// - `denom` (native chain A asset)
/// - `transfer/channelToA/denom` (chain B representation of chain A asset)
/// - `transfer/channelToB/transfer/channelToA/denom` (chain C representation of chain B representation of chain A asset)
///
/// ADR001 defines the IBC asset ID as the SHA-256 hash of the denomination
/// trace. Instead, Penumbra hashes to a field element, so that asset IDs can
/// be more easily used inside of a circuit.
///
/// [ADR001]:
/// https://github.com/cosmos/ibc-go/blob/main/docs/architecture/adr-001-coin-source-tracing.md
#[derive(Copy, Clone, PartialEq, Eq)]
pub struct Id(pub Fq);

impl std::fmt::Debug for Id {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
use ark_ff::BigInteger;
let bytes = self.0.into_repr().to_bytes_le();
f.write_fmt(format_args!("asset::Id({})", hex::encode(&bytes)))
}
}

// XXX define a DenomTrace structure ?

impl From<&[u8]> for Id {
fn from(slice: &[u8]) -> Id {
// Convert an asset name to an asset ID by hashing to a scalar
Id(Fq::from_le_bytes_mod_order(
// XXX choice of hash function?
blake2b_simd::Params::default()
.personal(b"penumbra.asset")
.hash(slice)
.as_bytes(),
))
}
}

/// The domain separator used to hash asset ids to value generators.
static VALUE_GENERATOR_DOMAIN_SEP: Lazy<Fq> = Lazy::new(|| {
Fq::from_le_bytes_mod_order(blake2b_simd::blake2b(b"penumbra.value.generator").as_bytes())
});

impl Id {
/// Compute the value commitment generator for this asset.
pub fn value_generator(&self) -> decaf377::Element {
use crate::poseidon_hash::hash_1;
let hash = hash_1(&VALUE_GENERATOR_DOMAIN_SEP, self.0);
decaf377::Element::map_to_group_cdh(&hash)
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn make_up_some_fake_asset_ids() {
// marked for future deletion
// not really a test, just a way to exercise the code

let pen_trace = b"pen";
let atom_trace = b"HubPort/HubChannel/atom";

let pen_id = Id::from(&pen_trace[..]);
let atom_id = Id::from(&atom_trace[..]);

dbg!(pen_id);
dbg!(atom_id);

dbg!(pen_id.value_generator());
dbg!(atom_id.value_generator());
}
}
6 changes: 6 additions & 0 deletions crypto/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
pub use decaf377::{Fq, Fr};

pub mod asset;
pub mod value;

mod poseidon_hash;
37 changes: 37 additions & 0 deletions crypto/src/poseidon_hash.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// XXX move into poseidon377 crate?

use crate::Fq;

use poseidon377::ark_sponge::{
poseidon::PoseidonSponge, CryptographicSponge, FieldBasedCryptographicSponge,
};

pub fn hash_1(domain_separator: &Fq, value: Fq) -> Fq {
// we want to set the capacity to domain_separator and the rate to value,
// then run the sponge and extract the rate. it's a bit hard to do this
// using the ark-sponge api, which is trying to do a higher-level duplex
// construction and doesn't allow access to the underlying sponge

let mut sponge = PoseidonSponge::new(&poseidon377::params::rate_1());

// arkworks sponge api doesn't let us call permute
//
// best we can do now is to look in the source to see how the rate and
// capacity are arranged and try to plumb the functionality we want through
// the higher-level API
//
// arkworks uses (rate || capacity) instead of (capacity || rate)
//
// this also gives incompatible outputs, but let's deal with that later

// set the capacity
assert_eq!(sponge.state.len(), 2);
sponge.state[1] = *domain_separator;

// now use absorb to set the rate (hopefully)
sponge.absorb(&value);
// and squeeze an element
let out_vec = sponge.squeeze_native_field_elements(1);

out_vec.into_iter().next().unwrap()
}
122 changes: 122 additions & 0 deletions crypto/src/value.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
//! Values (?)

use std::ops::Deref;

use ark_ff::PrimeField;
use once_cell::sync::Lazy;

use crate::{asset, Fq, Fr};

#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct Value {
pub amount: u64,
pub asset_id: asset::Id,
}

#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct Commitment(pub decaf377::Element);

static VALUE_BLINDING_GENERATOR: Lazy<decaf377::Element> = Lazy::new(|| {
let s = Fq::from_le_bytes_mod_order(blake2b_simd::blake2b(b"penumbra.val.blinding").as_bytes());
decaf377::Element::map_to_group_cdh(&s)
});

impl Value {
#[allow(non_snake_case)]
pub fn commit(&self, blinding: Fr) -> Commitment {
let G_v = self.asset_id.value_generator();
let H = VALUE_BLINDING_GENERATOR.deref();

let v = Fr::from(self.amount);

// XXX would be more convenient for decaf377 not to
// have a custom scalar type ?
let v = decaf377::Scalar::from(v);
let blinding = decaf377::Scalar::from(blinding);

let C = v * G_v + blinding * H;

Commitment(C)
}
}

impl std::ops::Add<Commitment> for Commitment {
type Output = Commitment;
fn add(self, rhs: Commitment) -> Self::Output {
Commitment(self.0 + rhs.0)
}
}

impl std::ops::Sub<Commitment> for Commitment {
type Output = Commitment;
fn sub(self, rhs: Commitment) -> Self::Output {
Commitment(self.0 - rhs.0)
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn sum_value_commitments() {
use ark_ff::Field;

let pen_trace = b"pen";
let atom_trace = b"HubPort/HubChannel/atom";

let pen_id = asset::Id::from(&pen_trace[..]);
let atom_id = asset::Id::from(&atom_trace[..]);

// some values of different types
let v1 = Value {
amount: 10,
asset_id: pen_id,
};
let v2 = Value {
amount: 8,
asset_id: pen_id,
};
let v3 = Value {
amount: 2,
asset_id: pen_id,
};
let v4 = Value {
amount: 13,
asset_id: atom_id,
};
let v5 = Value {
amount: 17,
asset_id: atom_id,
};
let v6 = Value {
amount: 30,
asset_id: atom_id,
};

// some random-looking blinding factors
let b1 = Fr::from(-129).inverse().unwrap();
let b2 = Fr::from(-199).inverse().unwrap();
let b3 = Fr::from(-121).inverse().unwrap();
let b4 = Fr::from(-179).inverse().unwrap();
let b5 = Fr::from(-379).inverse().unwrap();
let b6 = Fr::from(-879).inverse().unwrap();

// form commitments
let c1 = v1.commit(b1);
let c2 = v2.commit(b2);
let c3 = v3.commit(b3);
let c4 = v4.commit(b4);
let c5 = v5.commit(b5);
let c6 = v6.commit(b6);

// values sum to 0, so this is a commitment to 0...
let c0 = c1 - c2 - c3 + c4 + c5 - c6;
// with the following synthetic blinding factor:
let b0 = b1 - b2 - b3 + b4 + b5 - b6;

// so c0 = 0 * G_v1 + 0 * G_v2 + b0 * H
let b0 = decaf377::Scalar::from(b0);
assert_eq!(c0.0, b0 * VALUE_BLINDING_GENERATOR.deref());
}
}
2 changes: 0 additions & 2 deletions penumbra/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,6 @@ publish = false
# Workspace dependencies
penumbra-proto = { path = "../proto" }
# Penumbra dependencies
decaf377 = { git = "https://github.com/penumbra-zone/decaf377" }
poseidon377 = { git = "https://github.com/penumbra-zone/poseidon377" }
tower-abci = { git = "https://github.com/penumbra-zone/tower-abci/" }
tendermint-proto = { git = "https://github.com/penumbra-zone/tendermint-rs.git", branch = "abci-domain-types" }
tendermint = { git = "https://github.com/penumbra-zone/tendermint-rs.git", branch = "abci-domain-types" }
Expand Down

0 comments on commit 4c32464

Please sign in to comment.