From 7da7d2e662800a6b76d742e4892f51c4d2e0cecb Mon Sep 17 00:00:00 2001 From: Luther Blissett Date: Sun, 16 Oct 2022 14:57:44 +0200 Subject: [PATCH] sdk/crypto: Extend MerkleNode with incrementalmerkletree traits. --- contract/money/Cargo.toml | 6 +-- contract/money/src/lib.rs | 4 +- contract/money/src/transfer.rs | 5 ++- src/sdk/Cargo.toml | 4 ++ src/sdk/src/crypto/merkle_node.rs | 73 ++++++++++++++++++++++++++++++- src/sdk/src/crypto/nullifier.rs | 2 +- src/sdk/src/entrypoint.rs | 14 +++--- 7 files changed, 93 insertions(+), 15 deletions(-) diff --git a/contract/money/Cargo.toml b/contract/money/Cargo.toml index 4a799d695..2687c503f 100644 --- a/contract/money/Cargo.toml +++ b/contract/money/Cargo.toml @@ -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"] } diff --git a/contract/money/src/lib.rs b/contract/money/src/lib.rs index e56ec10c3..310c3f3ac 100644 --- a/contract/money/src/lib.rs +++ b/contract/money/src/lib.rs @@ -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. diff --git a/contract/money/src/transfer.rs b/contract/money/src/transfer.rs index a38629203..3882bdabc 100644 --- a/contract/money/src/transfer.rs +++ b/contract/money/src/transfer.rs @@ -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 diff --git a/src/sdk/Cargo.toml b/src/sdk/Cargo.toml index 20b994163..337114986 100644 --- a/src/sdk/Cargo.toml +++ b/src/sdk/Cargo.toml @@ -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" diff --git a/src/sdk/src/crypto/merkle_node.rs b/src/sdk/src/crypto/merkle_node.rs index 3c548a985..9f4bdbad8 100644 --- a/src/sdk/src/crypto/merkle_node.rs +++ b/src/sdk/src/crypto/merkle_node.rs @@ -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 = { + 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 + /// + /// + /// 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[::from(altitude)] + } +} diff --git a/src/sdk/src/crypto/nullifier.rs b/src/sdk/src/crypto/nullifier.rs index 7c430c7db..c0e749809 100644 --- a/src/sdk/src/crypto/nullifier.rs +++ b/src/sdk/src/crypto/nullifier.rs @@ -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 { diff --git a/src/sdk/src/entrypoint.rs b/src/sdk/src/entrypoint.rs index d8b5b4221..397631d1b 100644 --- a/src/sdk/src/entrypoint.rs +++ b/src/sdk/src/entrypoint.rs @@ -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::(); + 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::(); - 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