Explorar el Código

sdk/crypto: Extend MerkleNode with incrementalmerkletree traits.

Luther Blissett hace 3 años
padre
commit
7da7d2e662

+ 2 - 4
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"] }

+ 2 - 2
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.

+ 4 - 1
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

+ 4 - 0
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"

+ 71 - 2
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<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)]
+    }
+}

+ 1 - 1
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 {

+ 9 - 5
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 instruction_data_len = *(input.add(offset) as *const u64) as usize;
+    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