mirror of
https://github.com/darkrenaissance/darkfi.git
synced 2026-04-28 03:00:18 -04:00
145 lines
3.5 KiB
Rust
145 lines
3.5 KiB
Rust
use bitvec::{order::Lsb0, view::AsBits};
|
|
use ff::PrimeField;
|
|
use group::Curve;
|
|
use lazy_static::lazy_static;
|
|
use std::io;
|
|
|
|
use super::{coin::Coin, merkle::Hashable};
|
|
use crate::{
|
|
error::Result,
|
|
serial::{Decodable, Encodable},
|
|
};
|
|
|
|
pub const SAPLING_COMMITMENT_TREE_DEPTH: usize = 6;
|
|
|
|
/// Compute a parent node in the Sapling commitment tree given its two children.
|
|
pub fn merkle_hash(depth: usize, lhs: &[u8; 32], rhs: &[u8; 32]) -> bls12_381::Scalar {
|
|
// This thing is nasty lol
|
|
let lhs = {
|
|
let mut tmp = [false; 256];
|
|
for (a, b) in tmp.iter_mut().zip(lhs.as_bits::<Lsb0>()) {
|
|
*a = *b;
|
|
}
|
|
tmp
|
|
};
|
|
|
|
let rhs = {
|
|
let mut tmp = [false; 256];
|
|
for (a, b) in tmp.iter_mut().zip(rhs.as_bits::<Lsb0>()) {
|
|
*a = *b;
|
|
}
|
|
tmp
|
|
};
|
|
|
|
jubjub::ExtendedPoint::from(zcash_primitives::pedersen_hash::pedersen_hash(
|
|
zcash_primitives::pedersen_hash::Personalization::MerkleTree(depth),
|
|
lhs.iter()
|
|
.copied()
|
|
.take(bls12_381::Scalar::NUM_BITS as usize)
|
|
.chain(
|
|
rhs.iter()
|
|
.copied()
|
|
.take(bls12_381::Scalar::NUM_BITS as usize),
|
|
),
|
|
))
|
|
.to_affine()
|
|
.get_u()
|
|
}
|
|
|
|
pub fn hash_coin(coin: &[u8; 32]) -> bls12_381::Scalar {
|
|
let rhs = {
|
|
let mut tmp = [false; 256];
|
|
for (a, b) in tmp.iter_mut().zip(coin.as_bits::<Lsb0>()) {
|
|
*a = *b;
|
|
}
|
|
tmp
|
|
};
|
|
|
|
jubjub::ExtendedPoint::from(zcash_primitives::pedersen_hash::pedersen_hash(
|
|
zcash_primitives::pedersen_hash::Personalization::NoteCommitment,
|
|
rhs.iter().copied(),
|
|
))
|
|
.to_affine()
|
|
.get_u()
|
|
}
|
|
|
|
/// A node within the Sapling commitment tree.
|
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
|
pub struct Node {
|
|
pub repr: [u8; 32],
|
|
}
|
|
|
|
impl Node {
|
|
pub fn new(repr: [u8; 32]) -> Self {
|
|
Self { repr }
|
|
}
|
|
|
|
pub fn from_coin(coin: &Coin) -> Self {
|
|
Self {
|
|
repr: hash_coin(&coin.repr).to_repr(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Hashable for Node {
|
|
fn read<R: io::Read>(mut reader: R) -> io::Result<Self> {
|
|
let mut repr = [0u8; 32];
|
|
reader.read_exact(&mut repr)?;
|
|
Ok(Self::new(repr))
|
|
}
|
|
|
|
fn write<W: io::Write>(&self, mut writer: W) -> io::Result<()> {
|
|
writer.write_all(self.repr.as_ref())
|
|
}
|
|
|
|
fn combine(depth: usize, lhs: &Self, rhs: &Self) -> Self {
|
|
Self {
|
|
repr: merkle_hash(depth, &lhs.repr, &rhs.repr).to_repr(),
|
|
}
|
|
}
|
|
|
|
fn blank() -> Self {
|
|
// The smallest u-coordinate that is not on the curve
|
|
// is one.
|
|
let uncommitted_note = bls12_381::Scalar::one();
|
|
Self {
|
|
repr: uncommitted_note.to_repr(),
|
|
}
|
|
}
|
|
|
|
fn empty_root(depth: usize) -> Self {
|
|
EMPTY_ROOTS[depth]
|
|
}
|
|
}
|
|
|
|
impl From<Node> for bls12_381::Scalar {
|
|
fn from(node: Node) -> Self {
|
|
bls12_381::Scalar::from_repr(node.repr).expect("Tree nodes should be in the prime field")
|
|
}
|
|
}
|
|
|
|
impl Encodable for Node {
|
|
fn encode<S: io::Write>(&self, mut s: S) -> Result<usize> {
|
|
Ok(self.repr.encode(s)?)
|
|
}
|
|
}
|
|
|
|
impl Decodable for Node {
|
|
fn decode<D: io::Read>(mut d: D) -> Result<Self> {
|
|
Ok(Self {
|
|
repr: Decodable::decode(d)?,
|
|
})
|
|
}
|
|
}
|
|
|
|
lazy_static! {
|
|
static ref EMPTY_ROOTS: Vec<Node> = {
|
|
let mut v = vec![Node::blank()];
|
|
for d in 0..SAPLING_COMMITMENT_TREE_DEPTH {
|
|
let next = Node::combine(d, &v[d], &v[d]);
|
|
v.push(next);
|
|
}
|
|
v
|
|
};
|
|
}
|