Просмотр исходного кода

contract/money: Implement transaction builder.

parazyd 3 лет назад
Родитель
Сommit
fec2304dd8

+ 2 - 5
src/consensus/metadata.rs

@@ -26,10 +26,7 @@ use rand::rngs::OsRng;
 
 use super::leadcoin::LeadCoin;
 use crate::{
-    crypto::{
-        proof::{Proof, ProvingKey, VerifyingKey},
-        types::*,
-    },
+    crypto::proof::{Proof, ProvingKey, VerifyingKey},
     Result,
 };
 
@@ -89,7 +86,7 @@ impl LeadProof {
         Self { proof }
     }
 
-    pub fn verify(&self, vk: &VerifyingKey, public_inputs: &[DrkCircuitField]) -> Result<()> {
+    pub fn verify(&self, vk: &VerifyingKey, public_inputs: &[pallas::Base]) -> Result<()> {
         if let Err(e) = self.proof.verify(vk, public_inputs) {
             error!("Verification of consensus lead proof failed: {}", e);
             return Err(e.into())

+ 10 - 0
src/contract/money/Cargo.toml

@@ -12,6 +12,15 @@ crate-type = ["cdylib", "rlib"]
 darkfi-sdk = { path = "../../sdk" }
 darkfi-serial = { path = "../../serial", features = ["derive", "crypto"] }
 
+# The following dependencies are used for the client API and
+# probably shouldn't be in WASM
+chacha20poly1305 = { version = "0.10.1", optional = true }
+darkfi = { path = "../../../", features = ["crypto"], optional = true }
+halo2_proofs = { version = "0.2.0", optional = true }
+log = { version = "0.4.17", optional = true }
+rand = { version = "0.8.5", optional = true }
+
+
 # We need to disable random using "custom" which makes the crate a noop
 # so the wasm32-unknown-unknown target is enabled.
 [target.'cfg(target_arch = "wasm32")'.dependencies]
@@ -20,3 +29,4 @@ getrandom = { version = "0.2.8", features = ["custom"] }
 [features]
 default = []
 no-entrypoint = []
+client = ["darkfi", "rand", "chacha20poly1305", "log", "halo2_proofs"]

+ 10 - 10
src/contract/money/proof/burn.zk

@@ -5,26 +5,26 @@ constant "Burn" {
 }
 
 contract "Burn" {
-	# Secret key used to derive nullifier and coin's public key
-	Base secret,
-	# Unique serial number corresponding to this coin
-	Base serial,
 	# The value of this coin
 	Base value,
 	# The token ID
 	Base token,
-	# Random blinding factor for coin
-	Base coin_blind,
+	# Random blinding factor for value commitment
+	Scalar value_blind,
+	# Random blinding factor for the token ID
+	Scalar token_blind,
+	# Unique serial number corresponding to this coin
+	Base serial,
 	# Allows composing this ZK proof to invoke other contracts
 	Base spend_hook,
 	# Data passed from this coin to the invoked contract
 	Base user_data,
 	# Blinding factor for the encrypted user_data
 	Base user_data_blind,
-	# Random blinding factor for value commitment
-	Scalar value_blind,
-	# Random blinding factor for the token ID
-	Scalar token_blind,
+	# Random blinding factor for coin
+	Base coin_blind,
+	# Secret key used to derive nullifier and coin's public key
+	Base secret,
 	# Leaf position of the coin in the Merkle tree of coins
 	Uint32 leaf_pos,
 	# Merkle path to the coin

+ 712 - 0
src/contract/money/src/client.rs

@@ -0,0 +1,712 @@
+/* This file is part of DarkFi (https://dark.fi)
+ *
+ * Copyright (C) 2020-2022 Dyne.org foundation
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+
+//! This module implements the client-side of this contract's interaction.
+//! What we basically do here is implement an API that creates the necessary
+//! structures and is able to export them to create a DarkFi Transaction
+//! object that can be broadcasted to the network when we want to make a
+//! payment with some coins in our wallet.
+//! Note that this API doesn't involve any wallet interaction, but only
+//! takes the necessary objects provided by the caller. This is so we can
+//! abstract away the wallet interface to client implementations.
+
+use chacha20poly1305::{AeadInPlace, ChaCha20Poly1305, KeyInit};
+use darkfi::{
+    crypto::proof::{Proof, ProvingKey},
+    zk::{vm::ZkCircuit, vm_stack::Witness},
+    zkas::ZkBinary,
+    ClientFailed, Error, Result,
+};
+use darkfi_sdk::{
+    crypto::{
+        constants::MERKLE_DEPTH,
+        diffie_hellman::{kdf_sapling, sapling_ka_agree},
+        pedersen::{pedersen_commitment_base, pedersen_commitment_u64, ValueBlind, ValueCommit},
+        poseidon_hash, Keypair, MerkleNode, Nullifier, PublicKey, SecretKey, TokenId,
+    },
+    incrementalmerkletree,
+    incrementalmerkletree::{bridgetree::BridgeTree, Hashable, Tree},
+    pasta::{
+        arithmetic::CurveAffine,
+        group::{ff::PrimeField, Curve},
+        pallas,
+    },
+};
+use darkfi_serial::{Decodable, Encodable, SerialDecodable, SerialEncodable};
+use halo2_proofs::{arithmetic::Field, circuit::Value};
+use log::{debug, error};
+use rand::rngs::OsRng;
+
+use crate::state::{ClearInput, Input, MoneyTransferParams, Output};
+
+/// Byte length of the AEAD tag of the chacha20 cipher used for note encryption
+pub const AEAD_TAG_SIZE: usize = 16;
+
+/// The `Coin` is represented as a base field element.
+#[derive(Debug, Clone, Copy, Eq, PartialEq, SerialEncodable, SerialDecodable)]
+pub struct Coin(pallas::Base);
+
+impl Coin {
+    /// Reference the raw inner base field element
+    pub fn inner(&self) -> pallas::Base {
+        self.0
+    }
+
+    /// Try to create a `Coin` type from the given 32 bytes.
+    /// Returns an error if the bytes don't fit in the base field.
+    pub fn from_bytes(bytes: [u8; 32]) -> Result<Self> {
+        match pallas::Base::from_repr(bytes).into() {
+            Some(v) => Ok(Self(v)),
+            None => Err(Error::CoinFromBytes),
+        }
+    }
+}
+
+impl From<pallas::Base> for Coin {
+    fn from(x: pallas::Base) -> Self {
+        Self(x)
+    }
+}
+
+/// The `OwnCoin` is a representation of `Coin` with its respective metadata.
+#[derive(Debug, Clone, Eq, PartialEq, SerialEncodable, SerialDecodable)]
+pub struct OwnCoin {
+    /// The coin hash
+    pub coin: Coin,
+    /// The attached Note
+    pub note: Note,
+    /// Coin's secret key
+    pub secret: SecretKey,
+    /// Coin's nullifier,
+    pub nullifier: Nullifier,
+    /// Coin's leaf position in the Merkle tree of coins
+    pub leaf_position: incrementalmerkletree::Position,
+}
+
+/// The `Note` holds the inner attributes of a `Coin`
+#[derive(Debug, Clone, Eq, PartialEq, SerialEncodable, SerialDecodable)]
+pub struct Note {
+    /// Serial number of the coin, used for the nullifier
+    pub serial: pallas::Base,
+    /// Value of the coin
+    pub value: u64,
+    /// Token ID of the coin
+    pub token_id: TokenId,
+    /// Blinding factor for the coin bulla
+    pub coin_blind: pallas::Base,
+    /// Blinding factor for the value pedersen commitment
+    pub value_blind: ValueBlind,
+    /// Blinding factor for the token ID pedersen commitment
+    pub token_blind: ValueBlind,
+    /// Attached memo (arbitrary data)
+    pub memo: Vec<u8>,
+}
+
+impl Note {
+    /// Encrypt the note to some given `PublicKey` using an AEAD cipher.
+    pub fn encrypt(&self, public_key: &PublicKey) -> Result<EncryptedNote> {
+        let ephem_keypair = Keypair::random(&mut OsRng);
+        let shared_secret = sapling_ka_agree(&ephem_keypair.secret, public_key);
+        let key = kdf_sapling(&shared_secret, &ephem_keypair.public);
+
+        let mut input = vec![];
+        self.encode(&mut input)?;
+        let input_len = input.len();
+
+        let mut ciphertext = vec![0_u8; input_len + AEAD_TAG_SIZE];
+        ciphertext[..input_len].copy_from_slice(&input);
+
+        ChaCha20Poly1305::new(key.as_ref().into())
+            .encrypt_in_place([0u8; 12][..].into(), &[], &mut ciphertext)
+            .unwrap();
+
+        Ok(EncryptedNote { ciphertext, ephem_public: ephem_keypair.public })
+    }
+}
+
+/// The `EncryptedNote` represents a structure holding the ciphertext (which is
+/// an encryption of the `Note` object, and the ephemeral `PublicKey` created at
+/// the time when the encryption was done
+#[derive(Debug, Clone, Eq, PartialEq, SerialEncodable, SerialDecodable)]
+pub struct EncryptedNote {
+    /// Ciphertext of the encrypted `Note`
+    ciphertext: Vec<u8>,
+    /// Ephemeral public key created at the time of encrypting the note
+    ephem_public: PublicKey,
+}
+
+impl EncryptedNote {
+    pub fn decrypt(&self, secret: &SecretKey) -> Result<Note> {
+        let shared_secret = sapling_ka_agree(secret, &self.ephem_public);
+        let key = kdf_sapling(&shared_secret, &self.ephem_public);
+
+        let ciphertext_len = self.ciphertext.len();
+        let mut plaintext = vec![0_u8; ciphertext_len];
+        plaintext.copy_from_slice(&self.ciphertext);
+
+        match ChaCha20Poly1305::new(key.as_ref().into()).decrypt_in_place(
+            [0u8; 12][..].into(),
+            &[],
+            &mut plaintext,
+        ) {
+            Ok(()) => Ok(Note::decode(&plaintext[..ciphertext_len - AEAD_TAG_SIZE])?),
+            Err(e) => Err(Error::NoteDecryptionFailed(e.to_string())),
+        }
+    }
+}
+
+struct TransactionBuilderClearInputInfo {
+    pub value: u64,
+    pub token_id: TokenId,
+    pub signature_secret: SecretKey,
+}
+
+struct TransactionBuilderInputInfo {
+    pub leaf_position: incrementalmerkletree::Position,
+    pub merkle_path: Vec<MerkleNode>,
+    pub secret: SecretKey,
+    pub note: Note,
+}
+
+struct TransactionBuilderOutputInfo {
+    pub value: u64,
+    pub token_id: TokenId,
+    pub public_key: PublicKey,
+}
+
+struct TransferBurnRevealed {
+    pub value_commit: ValueCommit,
+    pub token_commit: ValueCommit,
+    pub nullifier: Nullifier,
+    pub merkle_root: MerkleNode,
+    pub spend_hook: pallas::Base,
+    pub user_data_enc: pallas::Base,
+    pub signature_public: PublicKey,
+}
+
+impl TransferBurnRevealed {
+    pub fn compute(
+        value: u64,
+        token_id: TokenId,
+        value_blind: ValueBlind,
+        token_blind: ValueBlind,
+        serial: pallas::Base,
+        spend_hook: pallas::Base,
+        user_data: pallas::Base,
+        user_data_blind: pallas::Base,
+        coin_blind: pallas::Base,
+        secret_key: SecretKey,
+        leaf_position: incrementalmerkletree::Position,
+        merkle_path: Vec<MerkleNode>,
+        signature_secret: SecretKey,
+    ) -> Self {
+        let nullifier = Nullifier::from(poseidon_hash([secret_key.inner(), serial]));
+
+        let public_key = PublicKey::from_secret(secret_key);
+        let (pub_x, pub_y) = public_key.xy();
+
+        let coin = poseidon_hash([
+            pub_x,
+            pub_y,
+            pallas::Base::from(value),
+            token_id.inner(),
+            serial,
+            spend_hook,
+            user_data,
+            coin_blind,
+        ]);
+
+        let merkle_root = {
+            let position: u64 = leaf_position.into();
+            let mut current = MerkleNode::from(coin);
+            for (level, sibling) in merkle_path.iter().enumerate() {
+                let level = level as u8;
+                current = if position & (1 << level) == 0 {
+                    MerkleNode::combine(level.into(), &current, sibling)
+                } else {
+                    MerkleNode::combine(level.into(), sibling, &current)
+                };
+            }
+            current
+        };
+
+        let user_data_enc = poseidon_hash([user_data, user_data_blind]);
+
+        let value_commit = pedersen_commitment_u64(value, value_blind);
+        let token_commit = pedersen_commitment_base(token_id.inner(), token_blind);
+
+        Self {
+            value_commit,
+            token_commit,
+            nullifier,
+            merkle_root,
+            spend_hook,
+            user_data_enc,
+            signature_public: PublicKey::from_secret(signature_secret),
+        }
+    }
+
+    pub fn to_vec(&self) -> Vec<pallas::Base> {
+        let valcom_coords = self.value_commit.to_affine().coordinates().unwrap();
+        let tokcom_coords = self.token_commit.to_affine().coordinates().unwrap();
+        let sigpub_coords = self.signature_public.inner().to_affine().coordinates().unwrap();
+
+        // NOTE: It's important to keep this order the same as the `constrain_instance`
+        //       calls in the zkas code.
+        vec![
+            self.nullifier.inner(),
+            *valcom_coords.x(),
+            *valcom_coords.y(),
+            *tokcom_coords.x(),
+            *tokcom_coords.y(),
+            self.merkle_root.inner(),
+            self.user_data_enc,
+            *sigpub_coords.x(),
+            *sigpub_coords.y(),
+            // TODO: Why is spend_hook in the struct but not here?
+        ]
+    }
+}
+
+struct TransferMintRevealed {
+    pub coin: Coin,
+    pub value_commit: ValueCommit,
+    pub token_commit: ValueCommit,
+}
+
+impl TransferMintRevealed {
+    pub fn compute(
+        value: u64,
+        token_id: TokenId,
+        value_blind: ValueBlind,
+        token_blind: ValueBlind,
+        serial: pallas::Base,
+        spend_hook: pallas::Base,
+        user_data: pallas::Base,
+        coin_blind: pallas::Base,
+        public_key: PublicKey,
+    ) -> Self {
+        let value_commit = pedersen_commitment_u64(value, value_blind);
+        let token_commit = pedersen_commitment_base(token_id.inner(), token_blind);
+
+        let (pub_x, pub_y) = public_key.xy();
+
+        let coin = Coin::from(poseidon_hash([
+            pub_x,
+            pub_y,
+            pallas::Base::from(value),
+            token_id.inner(),
+            serial,
+            spend_hook,
+            user_data,
+            coin_blind,
+        ]));
+
+        Self { coin, value_commit, token_commit }
+    }
+
+    pub fn to_vec(&self) -> Vec<pallas::Base> {
+        let valcom_coords = self.value_commit.to_affine().coordinates().unwrap();
+        let tokcom_coords = self.token_commit.to_affine().coordinates().unwrap();
+
+        // NOTE: It's important to keep this order the same as the `constrain_instance`
+        //       calls in the zkas code.
+        vec![
+            self.coin.inner(),
+            *valcom_coords.x() * tokcom_coords.y() * valcom_coords.x() * tokcom_coords.y(),
+        ]
+    }
+}
+
+fn create_transfer_mint_proof(
+    zkbin: &ZkBinary,
+    pk: &ProvingKey,
+    value: u64,
+    token_id: TokenId,
+    value_blind: ValueBlind,
+    token_blind: ValueBlind,
+    serial: pallas::Base,
+    spend_hook: pallas::Base,
+    user_data: pallas::Base,
+    coin_blind: pallas::Base,
+    public_key: PublicKey,
+) -> Result<(Proof, TransferMintRevealed)> {
+    let revealed = TransferMintRevealed::compute(
+        value,
+        token_id,
+        value_blind,
+        token_blind,
+        serial,
+        spend_hook,
+        user_data,
+        coin_blind,
+        public_key,
+    );
+
+    let (pub_x, pub_y) = public_key.xy();
+
+    // NOTE: It's important to keep these in the same order as the zkas code.
+    let prover_witnesses = vec![
+        Witness::Base(Value::known(pub_x)),
+        Witness::Base(Value::known(pub_y)),
+        Witness::Base(Value::known(pallas::Base::from(value))),
+        Witness::Base(Value::known(token_id.inner())),
+        Witness::Base(Value::known(serial)),
+        Witness::Base(Value::known(coin_blind)),
+        Witness::Base(Value::known(spend_hook)),
+        Witness::Base(Value::known(user_data)),
+        Witness::Scalar(Value::known(value_blind)),
+        Witness::Scalar(Value::known(token_blind)),
+    ];
+
+    let circuit = ZkCircuit::new(prover_witnesses, zkbin.clone());
+    let proof = Proof::create(pk, &[circuit], &revealed.to_vec(), &mut OsRng)?;
+
+    Ok((proof, revealed))
+}
+
+fn create_transfer_burn_proof(
+    zkbin: &ZkBinary,
+    pk: &ProvingKey,
+    value: u64,
+    token_id: TokenId,
+    value_blind: ValueBlind,
+    token_blind: ValueBlind,
+    serial: pallas::Base,
+    spend_hook: pallas::Base,
+    user_data: pallas::Base,
+    user_data_blind: pallas::Base,
+    coin_blind: pallas::Base,
+    secret_key: SecretKey,
+    leaf_position: incrementalmerkletree::Position,
+    merkle_path: Vec<MerkleNode>,
+    signature_secret: SecretKey,
+) -> Result<(Proof, TransferBurnRevealed)> {
+    let revealed = TransferBurnRevealed::compute(
+        value,
+        token_id,
+        value_blind,
+        token_blind,
+        serial,
+        spend_hook,
+        user_data,
+        user_data_blind,
+        coin_blind,
+        secret_key,
+        leaf_position,
+        merkle_path.clone(),
+        signature_secret,
+    );
+
+    // NOTE: It's important to keep these in the same order as the zkas code.
+    let prover_witnesses = vec![
+        Witness::Base(Value::known(pallas::Base::from(value))),
+        Witness::Base(Value::known(token_id.inner())),
+        Witness::Scalar(Value::known(value_blind)),
+        Witness::Scalar(Value::known(token_blind)),
+        Witness::Base(Value::known(serial)),
+        Witness::Base(Value::known(spend_hook)),
+        Witness::Base(Value::known(user_data)),
+        Witness::Base(Value::known(user_data_blind)),
+        Witness::Base(Value::known(coin_blind)),
+        Witness::Base(Value::known(secret_key.inner())),
+        Witness::Uint32(Value::known(u64::from(leaf_position).try_into().unwrap())),
+        Witness::MerklePath(Value::known(merkle_path.try_into().unwrap())),
+        Witness::Base(Value::known(signature_secret.inner())),
+    ];
+
+    let circuit = ZkCircuit::new(prover_witnesses, zkbin.clone());
+    let proof = Proof::create(pk, &[circuit], &revealed.to_vec(), &mut OsRng)?;
+
+    Ok((proof, revealed))
+}
+
+/// Build money contract transfer transaction parameters with the given data:
+/// * `keypair` - Caller's keypair
+/// * `pubkey` - Public key of the recipient
+/// * `value` - Value of the transfer
+/// * `coins` - Set of coins we're able to spend
+/// * `tree` - Current Merkle tree of coins
+/// * `mint_pk` - Proving key for the ZK mint proof
+/// * `burn_pk` - Proving key for the ZK burn proof
+/// * `clear_input` - Marks if we're creating clear or anonymous inputs
+pub fn build_transfer_tx(
+    keypair: &Keypair,
+    pubkey: &PublicKey,
+    value: u64,
+    coins: Vec<OwnCoin>,
+    tree: BridgeTree<MerkleNode, MERKLE_DEPTH>,
+    mint_zkbin: &ZkBinary,
+    mint_pk: &ProvingKey,
+    burn_zkbin: &ZkBinary,
+    burn_pk: &ProvingKey,
+    clear_input: bool,
+) -> Result<(MoneyTransferParams, Vec<Proof>, Vec<SecretKey>)> {
+    debug!("Building money contract transaction");
+    assert!(value != 0);
+    assert!(!coins.is_empty());
+    // Ensure the coins given to us are all of the same token_id.
+    // The money contract base transfer doesn't allow conversions.
+    let token_id = coins[0].note.token_id;
+    for coin in coins.iter() {
+        assert_eq!(token_id, coin.note.token_id);
+    }
+
+    let mut clear_inputs = vec![];
+    let mut inputs = vec![];
+    let mut outputs = vec![];
+    let mut spent_coins = vec![];
+
+    if clear_input {
+        debug!("Money::build_transfer_tx(): Building clear input");
+        let input =
+            TransactionBuilderClearInputInfo { value, token_id, signature_secret: keypair.secret };
+        clear_inputs.push(input);
+    } else {
+        debug!("Money::build_transfer_tx(): Building anonymous inputs");
+        let mut inputs_value = 0;
+        for coin in coins.iter() {
+            if inputs_value >= value {
+                debug!("inputs_value >= value");
+                break
+            }
+
+            let leaf_position = coin.leaf_position;
+            let root = tree.root(0).unwrap();
+            let merkle_path = tree.authentication_path(leaf_position, &root).unwrap();
+            inputs_value += coin.note.value;
+
+            let input = TransactionBuilderInputInfo {
+                leaf_position,
+                merkle_path,
+                secret: coin.secret,
+                note: coin.note.clone(),
+            };
+
+            inputs.push(input);
+            spent_coins.push(coin);
+        }
+
+        if inputs_value < value {
+            error!("Money::build_transfer_tx(): Not enough value to build tx inputs");
+            return Err(ClientFailed::NotEnoughValue(inputs_value).into())
+        }
+
+        if inputs_value > value {
+            let return_value = inputs_value - value;
+            outputs.push(TransactionBuilderOutputInfo {
+                value: return_value,
+                token_id,
+                public_key: keypair.public,
+            });
+        }
+
+        debug!("Money::build_transfer_tx(): Finished building inputs");
+    }
+
+    outputs.push(TransactionBuilderOutputInfo { value, token_id, public_key: *pubkey });
+    assert!(clear_inputs.len() + inputs.len() > 0);
+
+    // We now fill this with necessary stuff
+    let mut params = MoneyTransferParams { clear_inputs: vec![], inputs: vec![], outputs: vec![] };
+
+    // I assumed this vec will contain a secret key for each clear input and anonymous input.
+    let mut signature_secrets = vec![];
+
+    let token_blind = ValueBlind::random(&mut OsRng);
+    for input in clear_inputs {
+        // TODO: FIXME: What to do with this signature secret?
+        let signature_public = PublicKey::from_secret(input.signature_secret);
+        signature_secrets.push(input.signature_secret);
+        let value_blind = ValueBlind::random(&mut OsRng);
+
+        params.clear_inputs.push(ClearInput {
+            value: input.value,
+            token_id: input.token_id,
+            value_blind,
+            token_blind,
+            signature_public,
+        });
+    }
+
+    let mut input_blinds = vec![];
+    let mut output_blinds = vec![];
+    let mut zk_proofs = vec![];
+
+    for input in inputs {
+        let value_blind = ValueBlind::random(&mut OsRng);
+        input_blinds.push(value_blind);
+
+        let signature_secret = SecretKey::random(&mut OsRng);
+        signature_secrets.push(signature_secret);
+
+        // Disable composability for this old obsolete API
+        let spend_hook = pallas::Base::zero();
+        let user_data = pallas::Base::zero();
+        let user_data_blind = pallas::Base::random(&mut OsRng);
+
+        let (proof, revealed) = create_transfer_burn_proof(
+            burn_zkbin,
+            burn_pk,
+            input.note.value,
+            input.note.token_id,
+            value_blind,
+            token_blind,
+            input.note.serial,
+            spend_hook,
+            user_data,
+            user_data_blind,
+            input.note.coin_blind,
+            input.secret,
+            input.leaf_position,
+            input.merkle_path,
+            signature_secret,
+        )?;
+
+        params.inputs.push(Input {
+            value_commit: revealed.value_commit,
+            token_commit: revealed.token_commit,
+            nullifier: revealed.nullifier,
+            merkle_root: revealed.merkle_root,
+            spend_hook: revealed.spend_hook,
+            user_data_enc: revealed.user_data_enc,
+            signature_public: revealed.signature_public,
+        });
+
+        zk_proofs.push(proof);
+    }
+
+    // This value_blind calc assumes there will always be at least a single output
+    assert!(!outputs.is_empty());
+
+    for (i, output) in outputs.iter().enumerate() {
+        let value_blind = if i == outputs.len() - 1 {
+            compute_remainder_blind(&params.clear_inputs, &input_blinds, &output_blinds)
+        } else {
+            ValueBlind::random(&mut OsRng)
+        };
+
+        output_blinds.push(value_blind);
+
+        let serial = pallas::Base::random(&mut OsRng);
+        let coin_blind = pallas::Base::random(&mut OsRng);
+
+        // Disable composability for this old obsolete API
+        let spend_hook = pallas::Base::zero();
+        let user_data = pallas::Base::zero();
+
+        let (proof, revealed) = create_transfer_mint_proof(
+            mint_zkbin,
+            mint_pk,
+            output.value,
+            output.token_id,
+            value_blind,
+            token_blind,
+            serial,
+            spend_hook,
+            user_data,
+            coin_blind,
+            output.public_key,
+        )?;
+
+        zk_proofs.push(proof);
+
+        // Encrypted note
+        let note = Note {
+            serial,
+            value: output.value,
+            token_id: output.token_id,
+            coin_blind,
+            value_blind,
+            token_blind,
+            // NOTE: Perhaps pass in memos to this entire function with
+            //       VecDeque and then pop front to add here.
+            memo: vec![],
+        };
+
+        let encrypted_note = note.encrypt(&output.public_key)?;
+
+        params.outputs.push(Output {
+            value_commit: revealed.value_commit,
+            token_commit: revealed.token_commit,
+            coin: revealed.coin.inner(),
+            ciphertext: encrypted_note.ciphertext,
+            ephem_public: encrypted_note.ephem_public,
+        })
+    }
+
+    // Now we should have all the params, zk proofs, and signature secrets.
+    // We return it all and let the caller deal with it.
+
+    Ok((params, zk_proofs, signature_secrets))
+}
+
+fn compute_remainder_blind(
+    clear_inputs: &[ClearInput],
+    input_blinds: &[ValueBlind],
+    output_blinds: &[ValueBlind],
+) -> ValueBlind {
+    let mut total = ValueBlind::zero();
+
+    for input in clear_inputs {
+        total += input.value_blind;
+    }
+
+    for input_blind in input_blinds {
+        total += input_blind
+    }
+
+    for output_blind in output_blinds {
+        total -= output_blind;
+    }
+
+    total
+}
+
+#[cfg(test)]
+mod tests {
+    use darkfi_sdk::pasta::group::ff::Field;
+
+    use super::*;
+
+    #[test]
+    fn test_note_encdec() {
+        let note = Note {
+            serial: pallas::Base::random(&mut OsRng),
+            value: 100,
+            token_id: TokenId::from(pallas::Base::random(&mut OsRng)),
+            coin_blind: pallas::Base::random(&mut OsRng),
+            value_blind: pallas::Scalar::random(&mut OsRng),
+            token_blind: pallas::Scalar::random(&mut OsRng),
+            memo: vec![32, 223, 231, 3, 1, 1],
+        };
+
+        let keypair = Keypair::random(&mut OsRng);
+
+        let encrypted_note = note.encrypt(&keypair.public).unwrap();
+        let note2 = encrypted_note.decrypt(&keypair.secret).unwrap();
+        assert_eq!(note.serial, note2.serial);
+        assert_eq!(note.value, note2.value);
+        assert_eq!(note.token_id, note2.token_id);
+        assert_eq!(note.coin_blind, note2.coin_blind);
+        assert_eq!(note.value_blind, note2.value_blind);
+        assert_eq!(note.token_blind, note2.token_blind);
+        assert_eq!(note.memo, note2.memo);
+        assert_eq!(note, note2);
+    }
+}

+ 13 - 6
src/contract/money/src/lib.rs

@@ -17,9 +17,8 @@
  */
 
 use darkfi_sdk::{
-    crypto::{ContractId, MerkleNode, MerkleTree, PublicKey},
+    crypto::{Coin, ContractId, MerkleNode, MerkleTree, PublicKey},
     db::{db_contains_key, db_get, db_init, db_lookup, db_set},
-    define_contract,
     error::{ContractError, ContractResult},
     merkle::merkle_add,
     msg,
@@ -48,8 +47,12 @@ impl From<u8> for MoneyFunction {
 pub mod state;
 use state::{MoneyTransferParams, MoneyTransferUpdate};
 
+#[cfg(feature = "client")]
+/// Transaction building API for clients interacting with this contract.
+pub mod client;
+
 #[cfg(not(feature = "no-entrypoint"))]
-define_contract!(
+darkfi_sdk::define_contract!(
     init: init_contract,
     exec: process_instruction,
     apply: process_update,
@@ -185,7 +188,8 @@ fn get_metadata(_cid: ContractId, ix: &[u8]) -> ContractResult {
                 zk_public_values.push((
                     ZKAS_MINT_NS.to_string(),
                     vec![
-                        output.coin.inner(),
+                        //output.coin.inner(),
+                        output.coin,
                         *value_coords.x(),
                         *value_coords.y(),
                         *token_coords.x(),
@@ -266,13 +270,16 @@ fn process_instruction(cid: ContractId, ix: &[u8]) -> ContractResult {
             }
 
             // Newly created coins for this transaction are in the outputs.
-            let new_coins = Vec::with_capacity(params.outputs.len());
+            let mut new_coins = Vec::with_capacity(params.outputs.len());
             for (i, output) in params.outputs.iter().enumerate() {
                 // TODO: Should we have coins in a sled tree too to check dupes?
-                if new_coins.contains(&output.coin) {
+                if new_coins.contains(&Coin::from(output.coin)) {
                     msg!("[Transfer] Error: Duplicate coin found in output {}", i);
                     return Err(ContractError::Custom(23))
                 }
+
+                // FIXME: Needs some work on types and their place within all these libraries
+                new_coins.push(Coin::from(output.coin))
             }
 
             // Create a state update

+ 2 - 1
src/contract/money/src/state.rs

@@ -87,7 +87,8 @@ pub struct Output {
     /// Pedersen commitment for the output's token ID
     pub token_commit: ValueCommit,
     /// Minted coin
-    pub coin: Coin,
+    pub coin: pallas::Base,
+    //pub coin: Coin,
     /// The encrypted note ciphertext
     pub ciphertext: Vec<u8>,
     /// The ephemeral public key

+ 0 - 208
src/crypto/burn_proof.rs

@@ -1,208 +0,0 @@
-/* This file is part of DarkFi (https://dark.fi)
- *
- * Copyright (C) 2020-2022 Dyne.org foundation
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <https://www.gnu.org/licenses/>.
- */
-
-use std::time::Instant;
-
-use darkfi_sdk::{
-    crypto::{
-        pedersen::{pedersen_commitment_base, pedersen_commitment_u64},
-        poseidon_hash, MerkleNode, Nullifier, PublicKey, SecretKey, TokenId,
-    },
-    incrementalmerkletree::Hashable,
-    pasta::{arithmetic::CurveAffine, group::Curve},
-};
-use darkfi_serial::{SerialDecodable, SerialEncodable};
-use halo2_proofs::circuit::Value;
-use log::debug;
-use rand::rngs::OsRng;
-
-use super::proof::{Proof, ProvingKey, VerifyingKey};
-use crate::{
-    crypto::types::{
-        DrkCircuitField, DrkCoinBlind, DrkSerial, DrkSpendHook, DrkUserData, DrkUserDataBlind,
-        DrkUserDataEnc, DrkValue, DrkValueBlind, DrkValueCommit,
-    },
-    zk::circuit::burn_contract::BurnContract,
-    Result,
-};
-
-#[derive(Debug, Clone, PartialEq, Eq, SerialEncodable, SerialDecodable)]
-pub struct BurnRevealedValues {
-    pub value_commit: DrkValueCommit,
-    pub token_commit: DrkValueCommit,
-    pub nullifier: Nullifier,
-    pub merkle_root: MerkleNode,
-    pub spend_hook: DrkSpendHook,
-    pub user_data_enc: DrkUserDataEnc,
-    pub signature_public: PublicKey,
-}
-
-impl BurnRevealedValues {
-    #[allow(clippy::too_many_arguments)]
-    pub fn compute(
-        value: u64,
-        token_id: TokenId,
-        value_blind: DrkValueBlind,
-        token_blind: DrkValueBlind,
-        serial: DrkSerial,
-        coin_blind: DrkCoinBlind,
-        secret: SecretKey,
-        leaf_position: incrementalmerkletree::Position,
-        merkle_path: Vec<MerkleNode>,
-        spend_hook: DrkSpendHook,
-        user_data: DrkUserData,
-        user_data_blind: DrkUserDataBlind,
-        signature_secret: SecretKey,
-    ) -> Self {
-        let nullifier = Nullifier::from(poseidon_hash::<2>([secret.inner(), serial]));
-
-        let public_key = PublicKey::from_secret(secret);
-        let (pub_x, pub_y) = public_key.xy();
-
-        let coin = poseidon_hash::<8>([
-            pub_x,
-            pub_y,
-            DrkValue::from(value),
-            token_id.inner(),
-            serial,
-            spend_hook,
-            user_data,
-            coin_blind,
-        ]);
-
-        let merkle_root = {
-            let position: u64 = leaf_position.into();
-            let mut current = MerkleNode::from(coin);
-            for (level, sibling) in merkle_path.iter().enumerate() {
-                let level = level as u8;
-                current = if position & (1 << level) == 0 {
-                    MerkleNode::combine(level.into(), &current, sibling)
-                } else {
-                    MerkleNode::combine(level.into(), sibling, &current)
-                };
-            }
-            current
-        };
-
-        let user_data_enc = poseidon_hash::<2>([user_data, user_data_blind]);
-
-        let value_commit = pedersen_commitment_u64(value, value_blind);
-        let token_commit = pedersen_commitment_base(token_id.inner(), token_blind);
-
-        BurnRevealedValues {
-            value_commit,
-            token_commit,
-            nullifier,
-            merkle_root,
-            spend_hook,
-            user_data_enc,
-            signature_public: PublicKey::from_secret(signature_secret),
-        }
-    }
-
-    pub fn make_outputs(&self) -> Vec<DrkCircuitField> {
-        let value_coords = self.value_commit.to_affine().coordinates().unwrap();
-        let token_coords = self.token_commit.to_affine().coordinates().unwrap();
-        let merkle_root = self.merkle_root.inner();
-        let user_data_enc = self.user_data_enc;
-        let (sig_x, sig_y) = self.signature_public.xy();
-
-        vec![
-            self.nullifier.inner(),
-            *value_coords.x(),
-            *value_coords.y(),
-            *token_coords.x(),
-            *token_coords.y(),
-            merkle_root,
-            user_data_enc,
-            sig_x,
-            sig_y,
-        ]
-    }
-}
-
-#[allow(clippy::too_many_arguments)]
-pub fn create_burn_proof(
-    pk: &ProvingKey,
-    value: u64,
-    token_id: TokenId,
-    value_blind: DrkValueBlind,
-    token_blind: DrkValueBlind,
-    serial: DrkSerial,
-    spend_hook: DrkSpendHook,
-    user_data: DrkUserData,
-    user_data_blind: DrkUserDataBlind,
-    coin_blind: DrkCoinBlind,
-    secret: SecretKey,
-    leaf_position: incrementalmerkletree::Position,
-    merkle_path: Vec<MerkleNode>,
-    signature_secret: SecretKey,
-) -> Result<(Proof, BurnRevealedValues)> {
-    let revealed = BurnRevealedValues::compute(
-        value,
-        token_id,
-        value_blind,
-        token_blind,
-        serial,
-        coin_blind,
-        secret,
-        leaf_position,
-        merkle_path.clone(),
-        spend_hook,
-        user_data,
-        user_data_blind,
-        signature_secret,
-    );
-
-    let leaf_position: u64 = leaf_position.into();
-
-    let c = BurnContract {
-        secret_key: Value::known(secret.inner()),
-        serial: Value::known(serial),
-        value: Value::known(DrkValue::from(value)),
-        token: Value::known(token_id.inner()),
-        coin_blind: Value::known(coin_blind),
-        value_blind: Value::known(value_blind),
-        token_blind: Value::known(token_blind),
-        leaf_pos: Value::known(leaf_position as u32),
-        merkle_path: Value::known(merkle_path.try_into().unwrap()),
-        spend_hook: Value::known(spend_hook),
-        user_data: Value::known(user_data),
-        user_data_blind: Value::known(user_data_blind),
-        sig_secret: Value::known(signature_secret.inner()),
-    };
-
-    let start = Instant::now();
-    let public_inputs = revealed.make_outputs();
-    let proof = Proof::create(pk, &[c], &public_inputs, &mut OsRng)?;
-    debug!("Prove burn: [{:?}]", start.elapsed());
-
-    Ok((proof, revealed))
-}
-
-pub fn verify_burn_proof(
-    vk: &VerifyingKey,
-    proof: &Proof,
-    revealed: &BurnRevealedValues,
-) -> Result<()> {
-    let start = Instant::now();
-    let public_inputs = revealed.make_outputs();
-    proof.verify(vk, &public_inputs)?;
-    debug!("Verify burn: [{:?}]", start.elapsed());
-    Ok(())
-}

+ 0 - 47
src/crypto/coin.rs

@@ -1,47 +0,0 @@
-/* This file is part of DarkFi (https://dark.fi)
- *
- * Copyright (C) 2020-2022 Dyne.org foundation
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <https://www.gnu.org/licenses/>.
- */
-
-use darkfi_sdk::{
-    crypto::{Nullifier, SecretKey},
-    pasta::{group::ff::PrimeField, pallas},
-};
-use darkfi_serial::{SerialDecodable, SerialEncodable};
-
-use super::note::Note;
-
-#[derive(Clone, Copy, PartialEq, Eq, Debug, SerialEncodable, SerialDecodable)]
-pub struct Coin(pub pallas::Base);
-
-impl Coin {
-    pub fn from_bytes(bytes: [u8; 32]) -> Self {
-        pallas::Base::from_repr(bytes).map(Coin).unwrap()
-    }
-
-    pub fn to_bytes(self) -> [u8; 32] {
-        self.0.to_repr()
-    }
-}
-
-#[derive(Clone, Debug, PartialEq, Eq, SerialEncodable, SerialDecodable)]
-pub struct OwnCoin {
-    pub coin: Coin,
-    pub note: Note,
-    pub secret: SecretKey,
-    pub nullifier: Nullifier,
-    pub leaf_position: incrementalmerkletree::Position,
-}

+ 0 - 157
src/crypto/mint_proof.rs

@@ -1,157 +0,0 @@
-/* This file is part of DarkFi (https://dark.fi)
- *
- * Copyright (C) 2020-2022 Dyne.org foundation
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <https://www.gnu.org/licenses/>.
- */
-
-use std::time::Instant;
-
-use darkfi_sdk::{
-    crypto::{
-        pedersen::{pedersen_commitment_base, pedersen_commitment_u64},
-        poseidon_hash, PublicKey, TokenId,
-    },
-    pasta::{arithmetic::CurveAffine, group::Curve},
-};
-use darkfi_serial::{SerialDecodable, SerialEncodable};
-use halo2_proofs::circuit::Value;
-use log::debug;
-use rand::rngs::OsRng;
-
-use crate::{
-    crypto::{
-        coin::Coin,
-        proof::{Proof, ProvingKey, VerifyingKey},
-        types::{
-            DrkCircuitField, DrkCoinBlind, DrkSerial, DrkSpendHook, DrkUserData, DrkValue,
-            DrkValueBlind, DrkValueCommit,
-        },
-    },
-    zk::circuit::mint_contract::MintContract,
-    Result,
-};
-
-#[derive(Debug, Clone, PartialEq, Eq, SerialEncodable, SerialDecodable)]
-pub struct MintRevealedValues {
-    pub value_commit: DrkValueCommit,
-    pub token_commit: DrkValueCommit,
-    pub coin: Coin,
-}
-
-impl MintRevealedValues {
-    #[allow(clippy::too_many_arguments)]
-    pub fn compute(
-        value: u64,
-        token_id: TokenId,
-        value_blind: DrkValueBlind,
-        token_blind: DrkValueBlind,
-        serial: DrkSerial,
-        spend_hook: DrkSpendHook,
-        user_data: DrkUserData,
-        coin_blind: DrkCoinBlind,
-        public_key: PublicKey,
-    ) -> Self {
-        let value_commit = pedersen_commitment_u64(value, value_blind);
-        let token_commit = pedersen_commitment_base(token_id.inner(), token_blind);
-
-        let (pub_x, pub_y) = public_key.xy();
-
-        let coin = poseidon_hash::<8>([
-            pub_x,
-            pub_y,
-            DrkValue::from(value),
-            token_id.inner(),
-            serial,
-            spend_hook,
-            user_data,
-            coin_blind,
-        ]);
-
-        MintRevealedValues { value_commit, token_commit, coin: Coin(coin) }
-    }
-
-    pub fn make_outputs(&self) -> Vec<DrkCircuitField> {
-        let value_coords = self.value_commit.to_affine().coordinates().unwrap();
-        let token_coords = self.token_commit.to_affine().coordinates().unwrap();
-
-        vec![
-            self.coin.0,
-            *value_coords.x(),
-            *value_coords.y(),
-            *token_coords.x(),
-            *token_coords.y(),
-        ]
-    }
-}
-
-#[allow(clippy::too_many_arguments)]
-pub fn create_mint_proof(
-    pk: &ProvingKey,
-    value: u64,
-    token_id: TokenId,
-    value_blind: DrkValueBlind,
-    token_blind: DrkValueBlind,
-    serial: DrkSerial,
-    spend_hook: DrkSpendHook,
-    user_data: DrkUserData,
-    coin_blind: DrkCoinBlind,
-    public_key: PublicKey,
-) -> Result<(Proof, MintRevealedValues)> {
-    let revealed = MintRevealedValues::compute(
-        value,
-        token_id,
-        value_blind,
-        token_blind,
-        serial,
-        spend_hook,
-        user_data,
-        coin_blind,
-        public_key,
-    );
-
-    let (pub_x, pub_y) = public_key.xy();
-
-    let c = MintContract {
-        pub_x: Value::known(pub_x),
-        pub_y: Value::known(pub_y),
-        value: Value::known(DrkValue::from(value)),
-        token: Value::known(token_id.inner()),
-        serial: Value::known(serial),
-        coin_blind: Value::known(coin_blind),
-        spend_hook: Value::known(spend_hook),
-        user_data: Value::known(user_data),
-        value_blind: Value::known(value_blind),
-        token_blind: Value::known(token_blind),
-    };
-
-    let start = Instant::now();
-    let public_inputs = revealed.make_outputs();
-    let proof = Proof::create(pk, &[c], &public_inputs, &mut OsRng)?;
-    debug!("Prove mint: [{:?}]", start.elapsed());
-
-    Ok((proof, revealed))
-}
-
-pub fn verify_mint_proof(
-    vk: &VerifyingKey,
-    proof: &Proof,
-    revealed: &MintRevealedValues,
-) -> Result<()> {
-    let start = Instant::now();
-    let public_inputs = revealed.make_outputs();
-    proof.verify(vk, &public_inputs)?;
-    debug!("Verify mint: [{:?}]", start.elapsed());
-    Ok(())
-}

+ 0 - 10
src/crypto/mod.rs

@@ -16,19 +16,9 @@
  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
  */
 
-pub mod burn_proof;
-pub mod coin;
-pub mod diffie_hellman;
-pub mod mint_proof;
-pub mod note;
-pub mod types;
-
 /// VDF (Verifiable Delay Function) using MiMC
 pub mod mimc_vdf;
 
 /// Halo2 proof API abstractions
 pub mod proof;
 pub use proof::Proof;
-
-pub use burn_proof::BurnRevealedValues;
-pub use mint_proof::MintRevealedValues;

+ 0 - 122
src/crypto/note.rs

@@ -1,122 +0,0 @@
-/* This file is part of DarkFi (https://dark.fi)
- *
- * Copyright (C) 2020-2022 Dyne.org foundation
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <https://www.gnu.org/licenses/>.
- */
-
-use chacha20poly1305::{AeadInPlace, ChaCha20Poly1305, KeyInit};
-use darkfi_sdk::crypto::{PublicKey, SecretKey, TokenId};
-use darkfi_serial::{Decodable, Encodable, SerialDecodable, SerialEncodable};
-use rand::rngs::OsRng;
-
-use crate::{
-    crypto::{
-        diffie_hellman::{kdf_sapling, sapling_ka_agree},
-        types::{DrkCoinBlind, DrkSerial, DrkValueBlind},
-    },
-    Error, Result,
-};
-
-pub const AEAD_TAG_SIZE: usize = 16;
-
-#[derive(Clone, Debug, PartialEq, Eq, SerialEncodable, SerialDecodable)]
-pub struct Note {
-    pub serial: DrkSerial,
-    pub value: u64,
-    pub token_id: TokenId,
-    pub coin_blind: DrkCoinBlind,
-    pub value_blind: DrkValueBlind,
-    pub token_blind: DrkValueBlind,
-    pub memo: Vec<u8>,
-}
-
-impl Note {
-    pub fn encrypt(&self, public: &PublicKey) -> Result<EncryptedNote> {
-        let ephem_secret = SecretKey::random(&mut OsRng);
-        let ephem_public = PublicKey::from_secret(ephem_secret);
-        let shared_secret = sapling_ka_agree(&ephem_secret, public);
-        let key = kdf_sapling(&shared_secret, &ephem_public);
-
-        let mut input = Vec::new();
-        self.encode(&mut input)?;
-        let input_len = input.len();
-
-        let mut ciphertext = vec![0_u8; input_len + AEAD_TAG_SIZE];
-        ciphertext[..input_len].copy_from_slice(&input);
-
-        ChaCha20Poly1305::new(key.as_ref().into())
-            .encrypt_in_place([0u8; 12][..].into(), &[], &mut ciphertext)
-            .unwrap();
-
-        Ok(EncryptedNote { ciphertext, ephem_public })
-    }
-}
-
-#[derive(Debug, Clone, PartialEq, Eq, SerialEncodable, SerialDecodable)]
-pub struct EncryptedNote {
-    ciphertext: Vec<u8>,
-    ephem_public: PublicKey,
-}
-
-impl EncryptedNote {
-    pub fn decrypt(&self, secret: &SecretKey) -> Result<Note> {
-        let shared_secret = sapling_ka_agree(secret, &self.ephem_public);
-        let key = kdf_sapling(&shared_secret, &self.ephem_public);
-
-        let ciphertext_len = self.ciphertext.len();
-        let mut plaintext = vec![0_u8; ciphertext_len];
-        plaintext.copy_from_slice(&self.ciphertext);
-
-        match ChaCha20Poly1305::new(key.as_ref().into()).decrypt_in_place(
-            [0u8; 12][..].into(),
-            &[],
-            &mut plaintext,
-        ) {
-            Ok(()) => Ok(Note::decode(&plaintext[..ciphertext_len - AEAD_TAG_SIZE])?),
-            Err(e) => Err(Error::NoteDecryptionFailed(e.to_string())),
-        }
-    }
-}
-
-#[cfg(test)]
-mod tests {
-    use super::*;
-    use darkfi_sdk::{
-        crypto::{Keypair, TokenId},
-        pasta::{group::ff::Field, pallas},
-    };
-
-    #[test]
-    fn test_note_encdec() {
-        let note = Note {
-            serial: DrkSerial::random(&mut OsRng),
-            value: 110,
-            token_id: TokenId::from(pallas::Base::random(&mut OsRng)),
-            coin_blind: DrkCoinBlind::random(&mut OsRng),
-            value_blind: DrkValueBlind::random(&mut OsRng),
-            token_blind: DrkValueBlind::random(&mut OsRng),
-            memo: vec![32, 223, 231, 3, 1, 1],
-        };
-
-        let keypair = Keypair::random(&mut OsRng);
-
-        let encrypted_note = note.encrypt(&keypair.public).unwrap();
-        let note2 = encrypted_note.decrypt(&keypair.secret).unwrap();
-        assert_eq!(note.value, note2.value);
-        assert_eq!(note.token_id, note2.token_id);
-        assert_eq!(note.token_blind, note2.token_blind);
-        assert_eq!(note.memo, note2.memo);
-    }
-}

+ 13 - 22
src/crypto/proof.rs

@@ -23,11 +23,9 @@ use halo2_proofs::{
     poly::commitment::Params,
     transcript::{Blake2bRead, Blake2bWrite},
 };
-use pasta_curves::vesta;
+use pasta_curves::{pallas, vesta};
 use rand::RngCore;
 
-use crate::crypto::types::DrkCircuitField;
-
 // TODO: this API needs rework. It's not very good.
 // keygen_pk() takes a VerifyingKey by value,
 // yet ProvingKey also provides get_vk() -> &VerifyingKey
@@ -42,7 +40,7 @@ pub struct VerifyingKey {
 }
 
 impl VerifyingKey {
-    pub fn build(k: u32, c: &impl Circuit<DrkCircuitField>) -> Self {
+    pub fn build(k: u32, c: &impl Circuit<pallas::Base>) -> Self {
         let params = Params::new(k);
         let vk = plonk::keygen_vk(&params, c).unwrap();
         VerifyingKey { params, vk }
@@ -56,7 +54,7 @@ pub struct ProvingKey {
 }
 
 impl ProvingKey {
-    pub fn build(k: u32, c: &impl Circuit<DrkCircuitField>) -> Self {
+    pub fn build(k: u32, c: &impl Circuit<pallas::Base>) -> Self {
         let params = Params::new(k);
         let vk = plonk::keygen_vk(&params, c).unwrap();
         let pk = plonk::keygen_pk(&params, vk, c).unwrap();
@@ -75,8 +73,8 @@ impl AsRef<[u8]> for Proof {
 impl Proof {
     pub fn create(
         pk: &ProvingKey,
-        circuits: &[impl Circuit<DrkCircuitField>],
-        instances: &[DrkCircuitField],
+        circuits: &[impl Circuit<pallas::Base>],
+        instances: &[pallas::Base],
         mut rng: impl RngCore,
     ) -> std::result::Result<Self, plonk::Error> {
         let mut transcript = Blake2bWrite::<_, vesta::Affine, _>::init(vec![]);
@@ -95,7 +93,7 @@ impl Proof {
     pub fn verify(
         &self,
         vk: &VerifyingKey,
-        instances: &[DrkCircuitField],
+        instances: &[pallas::Base],
     ) -> std::result::Result<(), plonk::Error> {
         let strategy = SingleVerifier::new(&vk.params);
         let mut transcript = Blake2bRead::init(&self.0[..]);
@@ -111,14 +109,7 @@ impl Proof {
 #[cfg(test)]
 mod tests {
     use super::*;
-    use crate::{
-        crypto::{
-            mint_proof::create_mint_proof,
-            types::{DrkCoinBlind, DrkSerial, DrkSpendHook, DrkUserData, DrkValueBlind},
-        },
-        zk::circuit::MintContract,
-        Result,
-    };
+    use crate::{crypto::mint_proof::create_mint_proof, zk::circuit::MintContract, Result};
     use darkfi_sdk::{
         crypto::{PublicKey, SecretKey, TokenId},
         pasta::{group::ff::Field, pallas},
@@ -130,12 +121,12 @@ mod tests {
     fn test_proof_serialization() -> Result<()> {
         let value = 110_u64;
         let token_id = TokenId::from(pallas::Base::random(&mut OsRng));
-        let value_blind = DrkValueBlind::random(&mut OsRng);
-        let token_blind = DrkValueBlind::random(&mut OsRng);
-        let serial = DrkSerial::random(&mut OsRng);
-        let spend_hook = DrkSpendHook::random(&mut OsRng);
-        let user_data = DrkUserData::random(&mut OsRng);
-        let coin_blind = DrkCoinBlind::random(&mut OsRng);
+        let value_blind = ValueBlind::random(&mut OsRng);
+        let token_blind = ValueBlind::random(&mut OsRng);
+        let serial = pallas::Base::random(&mut OsRng);
+        let spend_hook = pallas::Base::random(&mut OsRng);
+        let user_data = pallas::Base::random(&mut OsRng);
+        let coin_blind = pallas::Base::random(&mut OsRng);
         let public_key = PublicKey::from_secret(SecretKey::random(&mut OsRng));
 
         let pk = ProvingKey::build(11, &MintContract::default());

+ 0 - 35
src/crypto/types.rs

@@ -1,35 +0,0 @@
-/* This file is part of DarkFi (https://dark.fi)
- *
- * Copyright (C) 2020-2022 Dyne.org foundation
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <https://www.gnu.org/licenses/>.
- */
-
-//! Type aliases used in the codebase.
-// Helpful for changing the curve and crypto we're using.
-use pasta_curves::pallas;
-
-pub type DrkCircuitField = pallas::Base;
-
-pub type DrkValue = pallas::Base;
-pub type DrkSerial = pallas::Base;
-
-pub type DrkSpendHook = pallas::Base;
-pub type DrkUserData = pallas::Base;
-pub type DrkUserDataBlind = pallas::Base;
-pub type DrkUserDataEnc = pallas::Base;
-
-pub type DrkCoinBlind = pallas::Base;
-pub type DrkValueBlind = pallas::Scalar;
-pub type DrkValueCommit = pallas::Point;

+ 3 - 0
src/error.rs

@@ -173,6 +173,9 @@ pub enum Error {
     #[error("Failed converting bytes to PublicKey")]
     PublicKeyFromBytes,
 
+    #[error("Failed converting bytes to Coin")]
+    CoinFromBytes,
+
     #[error("Failed converting bytes to SecretKey")]
     SecretKeyFromBytes,
 

+ 0 - 6
src/lib.rs

@@ -39,9 +39,6 @@ pub mod dht;
 #[cfg(feature = "net")]
 pub mod net;
 
-//#[cfg(feature = "node")]
-//pub mod node;
-
 #[cfg(feature = "raft")]
 pub mod raft;
 
@@ -51,9 +48,6 @@ pub mod rpc;
 #[cfg(feature = "system")]
 pub mod system;
 
-#[cfg(feature = "tx")]
-pub mod tx;
-
 #[cfg(feature = "tx")]
 pub mod tx2;
 

+ 2 - 1
src/runtime/vm_runtime.rs

@@ -320,7 +320,8 @@ impl Runtime {
     /// The permissions for this are handled by the `ContractId` in the sled db API so we
     /// assume that the contract is only able to do write operations on its own sled trees.
     pub fn deploy(&mut self, payload: &[u8]) -> Result<()> {
-        debug!("deploy: {:?}", payload);
+        info!("[wasm-runtime] Running deploy");
+        debug!("[wasm-runtime] payload: {:?}", payload);
         let _ = self.call(ContractSection::Deploy, payload)?;
 
         // If the above didn't fail, we write the batches.

+ 3 - 16
src/crypto/diffie_hellman.rs → src/sdk/src/crypto/diffie_hellman.rs

@@ -17,34 +17,21 @@
  */
 
 use blake2b_simd::{Hash as Blake2bHash, Params as Blake2bParams};
-use darkfi_sdk::crypto::{util::mod_r_p, PublicKey, SecretKey};
 use pasta_curves::group::{cofactor::CofactorGroup, GroupEncoding, Wnaf};
 
-pub const KDF_SAPLING_PERSONALIZATION: &[u8; 16] = b"DarkFiSaplingKDF";
+use super::{util::mod_r_p, PublicKey, SecretKey};
 
-/// Functions used for encrypting the note in transaction outputs.
+pub const KDF_SAPLING_PERSONALIZATION: &[u8; 16] = b"DarkFiSaplingKDF";
 
 /// Sapling key agreement for note encryption.
-///
-/// Implements section 5.4.4.3 of the Zcash Protocol Specification.
+/// Implements section 5.4.4.3 of the Zcash Protocol Specification
 pub fn sapling_ka_agree(esk: &SecretKey, pk_d: &PublicKey) -> PublicKey {
-    // [8 esk] pk_d
-    // <ExtendedPoint as CofactorGroup>::clear_cofactor is implemented using
-    // ExtendedPoint::mul_by_cofactor in the jubjub crate.
-
-    // ExtendedPoint::multiply currently just implements double-and-add,
-    // so using wNAF is a concrete speed improvement (as it operates over a window
-    // of bits instead of individual bits).
-    // We want that to be fast because it's in the hot path for trial decryption of
-    // notes on chain.
     let esk_s = mod_r_p(esk.inner());
     let mut wnaf = Wnaf::new();
     PublicKey::from(wnaf.scalar(&esk_s).base(pk_d.inner()).clear_cofactor())
 }
 
 /// Sapling KDF for note encryption.
-///
-/// Implements section 5.4.4.4 of the Zcash Protocol Specification.
 pub fn kdf_sapling(dhsecret: &PublicKey, epk: &PublicKey) -> Blake2bHash {
     Blake2bParams::new()
         .hash_length(32)

+ 3 - 0
src/sdk/src/crypto/mod.rs

@@ -30,6 +30,9 @@
 /// Cryptographic constants
 pub mod constants;
 
+/// Diffie-Hellman techniques
+pub mod diffie_hellman;
+
 /// Miscellaneous utilities
 pub mod util;
 pub use util::poseidon_hash;

+ 4 - 4
src/sdk/src/db.rs

@@ -128,10 +128,10 @@ pub fn db_contains_key(db_handle: DbHandle, key: &[u8]) -> GenericResult<bool> {
     let ret = unsafe { db_contains_key_(buf.as_ptr(), len as u32) };
 
     match ret {
-        CALLER_ACCESS_DENIED => return Err(ContractError::CallerAccessDenied),
-        DB_CONTAINS_KEY_FAILED => return Err(ContractError::DbContainsKeyFailed),
-        0 => return Ok(false),
-        1 => return Ok(true),
+        CALLER_ACCESS_DENIED => Err(ContractError::CallerAccessDenied),
+        DB_CONTAINS_KEY_FAILED => Err(ContractError::DbContainsKeyFailed),
+        0 => Ok(false),
+        1 => Ok(true),
         _ => unimplemented!(),
     }
 }