Selaa lähdekoodia

contract/money: Mint V1 API

parazyd 3 vuotta sitten
vanhempi
sitoutus
41cdbbbe13

+ 3 - 2
src/contract/money/proof/token_mint_v1.zk

@@ -31,8 +31,9 @@ circuit "TokenMint_V1" {
 	mint_public = ec_mul_base(mint_authority, NULLIFIER_K);
 	mint_x = ec_get_x(mint_public);
 	mint_y = ec_get_y(mint_public);
-	constrain_instance(mint_x);
-	constrain_instance(mint_y);
+	# I don't think we need to have these two as public inputs
+	#constrain_instance(mint_x);
+	#constrain_instance(mint_y);
 
 	# Derive the token ID
 	token_id = poseidon_hash(mint_x, mint_y);

+ 220 - 0
src/contract/money/src/client/mint_v1.rs

@@ -0,0 +1,220 @@
+/* This file is part of DarkFi (https://dark.fi)
+ *
+ * Copyright (C) 2020-2023 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::{
+    zk::{halo2::Value, Proof, ProvingKey, Witness, ZkCircuit},
+    zkas::ZkBinary,
+    Result,
+};
+use darkfi_sdk::{
+    crypto::{
+        note::AeadEncryptedNote, pasta_prelude::*, pedersen_commitment_base,
+        pedersen_commitment_u64, poseidon_hash, Coin, Keypair, PublicKey, TokenId,
+    },
+    pasta::pallas,
+};
+use log::{debug, info};
+use rand::rngs::OsRng;
+
+use crate::{
+    client::{
+        transfer_v1::{TransactionBuilderClearInputInfo, TransactionBuilderOutputInfo},
+        MoneyNote,
+    },
+    model::{ClearInput, MoneyMintParamsV1, Output},
+};
+
+pub struct MintCallDebris {
+    pub params: MoneyMintParamsV1,
+    pub proofs: Vec<Proof>,
+}
+
+pub struct TokenMintRevealed {
+    pub token_id: TokenId,
+    pub coin: Coin,
+    pub value_commit: pallas::Point,
+    pub token_commit: pallas::Point,
+}
+
+impl TokenMintRevealed {
+    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 these in the same order
+        // as the `constrain_instance` calls in the zkas code.
+        vec![
+            self.token_id.inner(),
+            self.coin.inner(),
+            *valcom_coords.x(),
+            *valcom_coords.y(),
+            *tokcom_coords.x(),
+            *tokcom_coords.y(),
+        ]
+    }
+}
+
+/// Struct holding necessary information to build a `Money::MintV1` contract call.
+pub struct MintCallBuilder {
+    /// Mint authority keypair
+    pub mint_authority: Keypair,
+    /// Recipient of the minted tokens
+    pub recipient: PublicKey,
+    /// Amount of tokens we want to mint
+    pub amount: u64,
+    /// Spend hook for the output
+    pub spend_hook: pallas::Base,
+    /// User data for the output
+    pub user_data: pallas::Base,
+    /// `TokenMint_V1` zkas circuit ZkBinary
+    pub token_mint_zkbin: ZkBinary,
+    /// Proving key for the `TokenMint_V1` zk circuit,
+    pub token_mint_pk: ProvingKey,
+}
+
+impl MintCallBuilder {
+    pub fn build(&self) -> Result<MintCallDebris> {
+        debug!("Building Money::MintV1 contract call");
+        assert!(self.amount != 0);
+
+        // In this call, we will build one clear input and one anonymous output.
+        // The mint authority pubkey is used to derive the token ID.
+        let (mint_x, mint_y) = self.mint_authority.public.xy();
+        let token_id = TokenId::from(poseidon_hash([mint_x, mint_y]));
+
+        let input = TransactionBuilderClearInputInfo {
+            value: self.amount,
+            token_id,
+            signature_secret: self.mint_authority.secret,
+        };
+
+        let output = TransactionBuilderOutputInfo {
+            value: self.amount,
+            token_id,
+            public_key: self.recipient,
+        };
+
+        // We just create the pedersen commitment blinds here. We simply
+        // enforce that the clear input and the anon output have the same
+        // commitments. Not sure if this can be avoided, but also is it
+        // really necessary to avoid?
+        let value_blind = pallas::Scalar::random(&mut OsRng);
+        let token_blind = pallas::Scalar::random(&mut OsRng);
+
+        let c_input = ClearInput {
+            value: input.value,
+            token_id: input.token_id,
+            value_blind,
+            token_blind,
+            signature_public: PublicKey::from_secret(input.signature_secret),
+        };
+
+        let serial = pallas::Base::random(&mut OsRng);
+        let coin_blind = pallas::Base::random(&mut OsRng);
+
+        info!("Creating token mint proof for output");
+        let (proof, public_inputs) = create_token_mint_proof(
+            &self.token_mint_zkbin,
+            &self.token_mint_pk,
+            &output,
+            &self.mint_authority,
+            value_blind,
+            token_blind,
+            serial,
+            self.spend_hook,
+            self.user_data,
+            coin_blind,
+        )?;
+
+        let note = MoneyNote {
+            serial,
+            value: output.value,
+            token_id: output.token_id,
+            spend_hook: self.spend_hook,
+            user_data: self.user_data,
+            coin_blind,
+            value_blind,
+            token_blind,
+            memo: vec![],
+        };
+
+        let encrypted_note = AeadEncryptedNote::encrypt(&note, &output.public_key, &mut OsRng)?;
+
+        let c_output = Output {
+            value_commit: public_inputs.value_commit,
+            token_commit: public_inputs.token_commit,
+            coin: public_inputs.coin,
+            note: encrypted_note,
+        };
+
+        let params = MoneyMintParamsV1 { input: c_input, output: c_output };
+        let debris = MintCallDebris { params, proofs: vec![proof] };
+        Ok(debris)
+    }
+}
+
+pub(crate) fn create_token_mint_proof(
+    zkbin: &ZkBinary,
+    pk: &ProvingKey,
+    output: &TransactionBuilderOutputInfo,
+    mint_authority: &Keypair,
+    value_blind: pallas::Scalar,
+    token_blind: pallas::Scalar,
+    serial: pallas::Base,
+    spend_hook: pallas::Base,
+    user_data: pallas::Base,
+    coin_blind: pallas::Base,
+) -> Result<(Proof, TokenMintRevealed)> {
+    let (mint_x, mint_y) = mint_authority.public.xy();
+    let token_id = TokenId::from(poseidon_hash([mint_x, mint_y]));
+
+    let value_commit = pedersen_commitment_u64(output.value, value_blind);
+    let token_commit = pedersen_commitment_base(token_id.inner(), token_blind);
+
+    let (rcpt_x, rcpt_y) = output.public_key.xy();
+
+    let coin = Coin::from(poseidon_hash([
+        rcpt_x,
+        rcpt_y,
+        pallas::Base::from(output.value),
+        token_id.inner(),
+        serial,
+        spend_hook,
+        user_data,
+        coin_blind,
+    ]));
+
+    let public_inputs = TokenMintRevealed { token_id, coin, value_commit, token_commit };
+
+    let prover_witnesses = vec![
+        Witness::Base(Value::known(mint_authority.secret.inner())),
+        Witness::Base(Value::known(pallas::Base::from(output.value))),
+        Witness::Base(Value::known(rcpt_x)),
+        Witness::Base(Value::known(rcpt_y)),
+        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], &public_inputs.to_vec(), &mut OsRng)?;
+
+    Ok((proof, public_inputs))
+}

+ 3 - 0
src/contract/money/src/client/mod.rs

@@ -38,6 +38,9 @@ pub mod transfer_v1;
 /// `Money::OtcSwapV1` API
 pub mod swap_v1;
 
+/// `Money::MintV1` API
+pub mod mint_v1;
+
 // Wallet SQL table constant names. These have to represent the `wallet.sql`
 // SQL schema.
 // TODO: They should also be prefixed with the contract ID to avoid collisions.

+ 0 - 2
src/contract/money/src/entrypoint/mint_v1.rs

@@ -67,8 +67,6 @@ pub(crate) fn money_mint_get_metadata_v1(
     zk_public_inputs.push((
         MONEY_CONTRACT_ZKAS_TOKEN_MINT_NS_V1.to_string(),
         vec![
-            sig_x,
-            sig_y,
             token_id,
             params.output.coin.inner(),
             *value_coords.x(),