Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions Cargo.lock

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

6 changes: 6 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,13 @@ crate-type = ["staticlib"]

[dependencies]
libc = "0.2"
pairing = "0.14.1"
lazy_static = "1"

[dependencies.sapling-crypto]
git = "https://github.com/zcash-hackworks/sapling-crypto"
rev = "e554b473dd10885d232f42237c13282f5b6fee43"

[profile.release]
lto = true
panic = 'abort'
23 changes: 23 additions & 0 deletions include/librustzcash.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,29 @@

extern "C" {
uint64_t librustzcash_xor(uint64_t a, uint64_t b);

/// Writes the "uncommitted" note value for empty leaves
/// of the merkle tree. `result` must be a valid pointer
/// to 32 bytes which will be written.
void librustzcash_tree_uncommitted(
unsigned char *result
);

/// Computes a merkle tree hash for a given depth.
/// The `depth` parameter should not be larger than
/// 62.
///
/// `a` and `b` each must be of length 32, and must each
/// be scalars of BLS12-381.
///
/// The result of the merkle tree hash is placed in
/// `result`, which must also be of length 32.
void librustzcash_merkle_hash(
size_t depth,
const unsigned char *a,
const unsigned char *b,
unsigned char *result
);
}

#endif // LIBRUSTZCASH_INCLUDE_H_
92 changes: 91 additions & 1 deletion src/rustzcash.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,97 @@
extern crate libc;
extern crate sapling_crypto;
extern crate pairing;

use libc::uint64_t;
#[macro_use]
extern crate lazy_static;

use pairing::{
BitIterator,
PrimeFieldRepr,
PrimeField,
bls12_381::{
Bls12,
Fr,
FrRepr
}
};

use sapling_crypto::{
jubjub::JubjubBls12,
pedersen_hash::{
pedersen_hash,
Personalization
}
};

use libc::{uint64_t, size_t, c_uchar};

lazy_static! {
static ref JUBJUB: JubjubBls12 = {
JubjubBls12::new()
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just to confirm, this is only the Jubjub parameters? The Sapling and Sprout CRSs will be loaded/unloaded via pointers? Or will the Sapling CRS be similarly statically-loaded?

Copy link
Copy Markdown
Contributor

@str4d str4d Apr 15, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ebfull and I discussed this. The Sapling and Sprout CRSs will be loaded via an API through which zcashd passes the paths to the params. Both verification keys along with the Sapling proving key will be loaded into memory immediately, while the Sprout proving key will be loaded and unloaded on-demand.

};
}

#[no_mangle]
pub extern "system" fn librustzcash_tree_uncommitted(
result: *mut [c_uchar; 32]
)
{
let tmp = sapling_crypto::primitives::Note::<Bls12>::uncommitted().into_repr();

// Should be okay, caller is responsible for ensuring the pointer
// is a valid pointer to 32 bytes that can be mutated.
let result = unsafe { &mut *result };

tmp.write_be(&mut result[..]).unwrap();
}

#[no_mangle]
pub extern "system" fn librustzcash_merkle_hash(
depth: size_t,
a: *const [c_uchar; 32],
b: *const [c_uchar; 32],
result: *mut [c_uchar; 32],
)
{
let mut a_repr = FrRepr::default();
let mut b_repr = FrRepr::default();

// Should be okay, because caller is responsible for ensuring
// the pointer is a valid pointer to 32 bytes, and that is the
// size of the representation
a_repr.read_be(unsafe { &(&*a)[..] }).unwrap();

// Should be okay, because caller is responsible for ensuring
// the pointer is a valid pointer to 32 bytes, and that is the
// size of the representation
b_repr.read_be(unsafe { &(&*b)[..] }).unwrap();

let mut lhs = [false; 256];
let mut rhs = [false; 256];

for (a, b) in lhs.iter_mut().rev().zip(BitIterator::new(a_repr)) {
*a = b;
}

for (a, b) in rhs.iter_mut().rev().zip(BitIterator::new(b_repr)) {
*a = b;
}

let tmp = pedersen_hash::<Bls12, _>(
Personalization::MerkleTree(depth),
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happens if a depth larger than 62 is passed in? Relatedly, how do panics unroll into the C++ code - as segfaults?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If a depth larger than 62 is passed in, undefined behavior. Probably a segfault.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we add a panic!() if the provided depth is larger than 62?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code this calls will panic if the depth is larger then 62.

lhs.iter().map(|&x| x)
.take(Fr::NUM_BITS as usize)
.chain(rhs.iter().map(|&x| x).take(Fr::NUM_BITS as usize)),
&JUBJUB
).into_xy().0.into_repr();

// Should be okay, caller is responsible for ensuring the pointer
// is a valid pointer to 32 bytes that can be mutated.
let result = unsafe { &mut *result };

tmp.write_be(&mut result[..]).unwrap();
}

/// XOR two uint64_t values and return the result, used
/// as a temporary mechanism for introducing Rust into
Expand Down