sdk/crypto: Extend MerkleNode with incrementalmerkletree traits.

This commit is contained in:
Luther Blissett
2022-10-16 14:57:44 +02:00
parent acdca1e747
commit 7da7d2e662
7 changed files with 93 additions and 15 deletions

View File

@@ -17,9 +17,7 @@ overflow-checks = true
[dependencies]
darkfi-sdk = { path = "../../src/sdk" }
# TODO: serial/crypto feature that enables all this stuff
darkfi = { path = "../../", features = ["serial", "pasta_curves", "incrementalmerkletree"] }
incrementalmerkletree = "0.3.0"
darkfi-serial = { path = "../../src/serial", features = ["crypto"] }
# TODO: Dummy getrandomndom = { version = "0.2.7", features = ["custom"] }
# Dummy getrandom
getrandom = { version = "0.2.7", features = ["custom"] }

View File

@@ -1,10 +1,10 @@
use darkfi::serial::{deserialize, SerialDecodable, SerialEncodable};
use darkfi_sdk::{
crypto::{MerkleNode, Nullifier},
entrypoint,
error::ContractResult,
incrementalmerkletree::bridgetree::BridgeTree,
};
use incrementalmerkletree::bridgetree::BridgeTree;
use darkfi_serial::{deserialize, SerialDecodable, SerialEncodable};
/// Available functions for this contract.
/// We identify them with the first byte passed in through the payload.

View File

@@ -4,8 +4,9 @@ use darkfi_sdk::{
MerkleNode,
},
error::{ContractError, ContractResult},
incrementalmerkletree::Tree,
msg,
pasta::pallas,
pasta::{group::Group, pallas},
state::Verification,
};
@@ -68,6 +69,8 @@ pub fn exec(state: &mut State, tx: Transaction) -> ContractResult {
state.tree.append(&MerkleNode::from(output.coin.inner()));
state.merkle_roots.push(state.tree.root(0).unwrap());
}
Ok(())
}
// `Verification` could be a generic trait we implement for doing

View File

@@ -27,6 +27,10 @@ pasta_curves = "0.4.0"
incrementalmerkletree = "0.3.0"
halo2_gadgets = "0.2.0"
# Misc
lazy_static = "1.4.0"
subtle = "2.4.1"
[dev-dependencies]
halo2_proofs = "0.2.0"
rand = "0.8.5"

View File

@@ -1,8 +1,34 @@
use core::str::FromStr;
use std::io;
use std::{io, iter};
use darkfi_serial::{SerialDecodable, SerialEncodable};
use pasta_curves::{group::ff::PrimeField, pallas};
use halo2_gadgets::sinsemilla::primitives::HashDomain;
use incrementalmerkletree::{Altitude, Hashable};
use lazy_static::lazy_static;
use pasta_curves::{
group::ff::{PrimeField, PrimeFieldBits},
pallas,
};
use subtle::{Choice, ConditionallySelectable};
use crate::crypto::constants::{
sinsemilla::{i2lebsp_k, L_ORCHARD_MERKLE, MERKLE_CRH_PERSONALIZATION},
MERKLE_DEPTH_ORCHARD,
};
lazy_static! {
static ref UNCOMMITTED_ORCHARD: pallas::Base = pallas::Base::from(2);
static ref EMPTY_ROOTS: Vec<MerkleNode> = {
iter::empty()
.chain(Some(MerkleNode::empty_leaf()))
.chain((0..MERKLE_DEPTH_ORCHARD).scan(MerkleNode::empty_leaf(), |state, l| {
let l = l as u8;
*state = MerkleNode::combine(l.into(), state, state);
Some(*state)
}))
.collect()
};
}
/// The `MerkleNode` is represented as a base field element.
#[repr(C)]
@@ -58,3 +84,46 @@ impl FromStr for MerkleNode {
Err(io::Error::new(io::ErrorKind::Other, "Invalid bytes for MerkleNode"))
}
}
impl ConditionallySelectable for MerkleNode {
fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
Self(pallas::Base::conditional_select(&a.0, &b.0, choice))
}
}
impl Hashable for MerkleNode {
fn empty_leaf() -> Self {
Self(*UNCOMMITTED_ORCHARD)
}
/// Implements `MerkleCRH^Orchard` as defined in
/// <https://zips.z.cash/protocol/protocol.pdf#orchardmerklecrh>
///
/// The layer with 2^n nodes is called "layer n":
/// - leaves are at layer MERKLE_DEPTH_ORCHARD = 32;
/// - the root is at layer 0.
/// `l` is MERKLE_DEPTH_ORCHARD - layer - 1.
/// - when hashing two leaves, we produce a node on the layer
/// above the the leaves, i.e. layer = 31, l = 0
/// - when hashing to the final root, we produce the anchor
/// with layer = 0, l = 31.
fn combine(altitude: Altitude, left: &Self, right: &Self) -> Self {
// MerkleCRH Sinsemilla hash domain.
let domain = HashDomain::new(MERKLE_CRH_PERSONALIZATION);
Self(
domain
.hash(
iter::empty()
.chain(i2lebsp_k(altitude.into()).iter().copied())
.chain(left.inner().to_le_bits().iter().by_vals().take(L_ORCHARD_MERKLE))
.chain(right.inner().to_le_bits().iter().by_vals().take(L_ORCHARD_MERKLE)),
)
.unwrap_or(pallas::Base::zero()),
)
}
fn empty_root(altitude: Altitude) -> Self {
EMPTY_ROOTS[<usize>::from(altitude)]
}
}

View File

@@ -6,7 +6,7 @@ use pasta_curves::{group::ff::PrimeField, pallas};
/// The `Nullifier` is represented as a base field element.
#[repr(C)]
#[derive(Debug, Clone, Copy, SerialEncodable, SerialDecodable)]
#[derive(Debug, Clone, Copy, Eq, PartialEq, SerialEncodable, SerialDecodable)]
pub struct Nullifier(pallas::Base);
impl Nullifier {

View File

@@ -14,9 +14,9 @@ macro_rules! entrypoint {
/// # Safety
#[no_mangle]
pub unsafe extern "C" fn entrypoint(input: *mut u8) -> u64 {
let instruction_data = $crate::entrypoint::deserialize(input);
let (state, instruction_data) = $crate::entrypoint::deserialize(input);
match $process_instruction(&instruction_data) {
match $process_instruction(&state, &instruction_data) {
Ok(()) => $crate::entrypoint::SUCCESS,
Err(e) => e.into(),
}
@@ -26,15 +26,19 @@ macro_rules! entrypoint {
/// Deserialize a given payload in `entrypoint`
/// # Safety
pub unsafe fn deserialize<'a>(input: *mut u8) -> &'a [u8] {
pub unsafe fn deserialize<'a>(input: *mut u8) -> (&'a [u8], &'a [u8]) {
let mut offset: usize = 0;
let state_data_len = *(input.add(offset) as *const u64) as usize;
offset += size_of::<u64>();
let state_data = { from_raw_parts(input.add(offset), state_data_len) };
offset += state_data_len;
let instruction_data_len = *(input.add(offset) as *const u64) as usize;
offset += size_of::<u64>();
let instruction_data = { from_raw_parts(input.add(offset), instruction_data_len) };
instruction_data
(state_data, instruction_data)
}
/// Allocate a piece of memory in the wasm VM