diff --git a/doc/src/SUMMARY.md b/doc/src/SUMMARY.md
index 427256c90..fa9d1cfb7 100644
--- a/doc/src/SUMMARY.md
+++ b/doc/src/SUMMARY.md
@@ -67,7 +67,7 @@
- [DEP 0001: Version Message Info (accepted)](dep/0001.md)
- [DEP 0002: Smart Contract Composability (deprecated)](dep/0002.md)
-- [DEP 0003: Token Mint Authorization (draft)](dep/0003.md)
+- [DEP 0003: Token Mint Authorization (accepted)](dep/0003.md)
# Specs
diff --git a/doc/src/dep/0003.md b/doc/src/dep/0003.md
index 84660bf54..b665815ce 100644
--- a/doc/src/dep/0003.md
+++ b/doc/src/dep/0003.md
@@ -1,7 +1,7 @@
# DEP 0003: Token Mint Authorization
```
-status: draft
+status: accepted
```
## Current Situation
@@ -43,8 +43,6 @@ For each coin $Cᵢ$, let there be corresponding proofs $πᵢ$ such that
**Token ID integrity** $T$ is calculated correctly committing to
`auth_parent`.
-**User data commitment** $U = \t{PoseidonHash}(u, bᵤ)$
-
**Coin commitment integrity** $Cᵢ = \t{PoseidonHash}(…, T, …)$
Additionally the contract checks that `auth_parent` is the function ID of
@@ -69,8 +67,6 @@ The contract performs the following checks:
* Constructs a pedersen commit $V$ to the value in the coin, along with a proof.
This allows auditing the supply since all commitments are linked publicly with
the token ID.
-* Unwrap the `user_data` exported from `Money::token_mint_v1()` which should
- be a commitment to the public key. Prove ownership of the public key.
### `Money::auth_mint_freeze_v1()`
diff --git a/src/contract/dao/src/client/auth_xfer.rs b/src/contract/dao/src/client/auth_xfer.rs
index 9910bb4de..2bbdbe677 100644
--- a/src/contract/dao/src/client/auth_xfer.rs
+++ b/src/contract/dao/src/client/auth_xfer.rs
@@ -97,8 +97,7 @@ impl DaoAuthMoneyTransferCall {
let circuit = ZkCircuit::new(prover_witnesses, auth_xfer_enc_coin_zkbin);
let proof =
- Proof::create(auth_xfer_enc_coin_pk, &[circuit], &public_inputs, &mut OsRng)
- .expect("DAO::exec() proving error!)");
+ Proof::create(auth_xfer_enc_coin_pk, &[circuit], &public_inputs, &mut OsRng)?;
proofs.push(proof);
enc_attrs.push(enc_note);
@@ -169,8 +168,7 @@ impl DaoAuthMoneyTransferCall {
];
let circuit = ZkCircuit::new(prover_witnesses, auth_xfer_zkbin);
- let proof = Proof::create(auth_xfer_pk, &[circuit], &public_inputs, &mut OsRng)
- .expect("DAO::exec() proving error!)");
+ let proof = Proof::create(auth_xfer_pk, &[circuit], &public_inputs, &mut OsRng)?;
proofs.push(proof);
Ok((params, proofs))
diff --git a/src/contract/dao/src/client/exec.rs b/src/contract/dao/src/client/exec.rs
index 78d25314d..3b983f003 100644
--- a/src/contract/dao/src/client/exec.rs
+++ b/src/contract/dao/src/client/exec.rs
@@ -109,8 +109,7 @@ impl DaoExecCall {
//export_witness_json("witness.json", &prover_witnesses, &public_inputs);
let circuit = ZkCircuit::new(prover_witnesses, exec_zkbin);
- let input_proof = Proof::create(exec_pk, &[circuit], &public_inputs, &mut OsRng)
- .expect("DAO::exec() proving error!)");
+ let input_proof = Proof::create(exec_pk, &[circuit], &public_inputs, &mut OsRng)?;
proofs.push(input_proof);
let params = DaoExecParams {
diff --git a/src/contract/dao/src/client/propose.rs b/src/contract/dao/src/client/propose.rs
index 3696b518a..3a448843d 100644
--- a/src/contract/dao/src/client/propose.rs
+++ b/src/contract/dao/src/client/propose.rs
@@ -143,8 +143,7 @@ impl DaoProposeCall {
let circuit = ZkCircuit::new(prover_witnesses, burn_zkbin);
let proving_key = &burn_pk;
- let input_proof = Proof::create(proving_key, &[circuit], &public_inputs, &mut OsRng)
- .expect("DAO::propose() proving error!");
+ let input_proof = Proof::create(proving_key, &[circuit], &public_inputs, &mut OsRng)?;
proofs.push(input_proof);
let input =
@@ -203,8 +202,7 @@ impl DaoProposeCall {
];
let circuit = ZkCircuit::new(prover_witnesses, main_zkbin);
- let main_proof = Proof::create(main_pk, &[circuit], &public_inputs, &mut OsRng)
- .expect("DAO::propose() proving error!");
+ let main_proof = Proof::create(main_pk, &[circuit], &public_inputs, &mut OsRng)?;
proofs.push(main_proof);
let enc_note =
diff --git a/src/contract/dao/src/client/vote.rs b/src/contract/dao/src/client/vote.rs
index 5917a0961..beb051657 100644
--- a/src/contract/dao/src/client/vote.rs
+++ b/src/contract/dao/src/client/vote.rs
@@ -166,8 +166,7 @@ impl DaoVoteCall {
let circuit = ZkCircuit::new(prover_witnesses, burn_zkbin);
debug!(target: "dao", "input_proof Proof::create()");
- let input_proof = Proof::create(burn_pk, &[circuit], &public_inputs, &mut OsRng)
- .expect("DAO::vote() proving error!");
+ let input_proof = Proof::create(burn_pk, &[circuit], &public_inputs, &mut OsRng)?;
proofs.push(input_proof);
let input = DaoVoteParamsInput {
@@ -270,8 +269,7 @@ impl DaoVoteCall {
let circuit = ZkCircuit::new(prover_witnesses, main_zkbin);
debug!(target: "dao", "main_proof = Proof::create()");
- let main_proof = Proof::create(main_pk, &[circuit], &public_inputs, &mut OsRng)
- .expect("DAO::vote() proving error!");
+ let main_proof = Proof::create(main_pk, &[circuit], &public_inputs, &mut OsRng)?;
proofs.push(main_proof);
let params =
diff --git a/src/contract/money/proof/auth_token_mint_v1.zk b/src/contract/money/proof/auth_token_mint_v1.zk
new file mode 100644
index 000000000..cf769831b
--- /dev/null
+++ b/src/contract/money/proof/auth_token_mint_v1.zk
@@ -0,0 +1,64 @@
+# Circuit used to mint arbitrary coins given a mint authority secret.
+k = 13;
+field = "pallas";
+
+constant "AuthTokenMint_V1" {
+ EcFixedPointShort VALUE_COMMIT_VALUE,
+ EcFixedPoint VALUE_COMMIT_RANDOM,
+ EcFixedPointBase NULLIFIER_K,
+}
+
+witness "AuthTokenMint_V1" {
+ # CoinAttributes {
+ Base coin_public_x,
+ Base coin_public_y,
+ Base coin_value,
+ Base coin_spend_hook,
+ Base coin_user_data,
+ Base coin_blind,
+ # }
+
+ # TokenAttributes {
+ Base token_auth_parent,
+ Base token_blind,
+ # }
+
+ # Secret key used by mint
+ Base mint_secret,
+
+ # Random blinding factor for the value commitment
+ Scalar value_commit_blind,
+}
+
+circuit "AuthTokenMint_V1" {
+ # Derive public key for the mint authority
+ mint_public = ec_mul_base(mint_secret, NULLIFIER_K);
+ mint_x = ec_get_x(mint_public);
+ mint_y = ec_get_y(mint_public);
+ constrain_instance(mint_x);
+ constrain_instance(mint_y);
+
+ # Derive the token ID
+ token_user_data = poseidon_hash(mint_x, mint_y);
+ token_id = poseidon_hash(token_auth_parent, token_user_data, token_blind);
+ constrain_instance(token_id);
+
+ # Poseidon hash of the minted coin
+ coin = poseidon_hash(
+ coin_public_x,
+ coin_public_y,
+ coin_value,
+ token_id,
+ coin_spend_hook,
+ coin_user_data,
+ coin_blind
+ );
+ constrain_instance(coin);
+
+ # Pedersen commitment for the coin's value
+ vcv = ec_mul_short(coin_value, VALUE_COMMIT_VALUE);
+ vcr = ec_mul(value_commit_blind, VALUE_COMMIT_RANDOM);
+ value_commit = ec_add(vcv, vcr);
+ constrain_instance(ec_get_x(value_commit));
+ constrain_instance(ec_get_y(value_commit));
+}
diff --git a/src/contract/money/proof/token_freeze_v1.zk b/src/contract/money/proof/token_freeze_v1.zk
index 223d8582a..5daa71220 100644
--- a/src/contract/money/proof/token_freeze_v1.zk
+++ b/src/contract/money/proof/token_freeze_v1.zk
@@ -6,22 +6,26 @@ constant "TokenFreeze_V1" {
}
witness "TokenFreeze_V1" {
- # Token mint authority secret
- Base mint_authority,
+ # TokenAttributes {
+ Base token_auth_parent,
+ Base token_blind,
+ # }
+
+ # Secret key used by mint
+ Base mint_secret,
}
circuit "TokenFreeze_V1" {
- # TokenID derivation path (See darkfi_sdk::crypto::ContractId)
- derivation_path = witness_base(69);
-
# Derive public key for the mint authority
- mint_public = ec_mul_base(mint_authority, NULLIFIER_K);
+ mint_public = ec_mul_base(mint_secret, NULLIFIER_K);
mint_x = ec_get_x(mint_public);
mint_y = ec_get_y(mint_public);
constrain_instance(mint_x);
constrain_instance(mint_y);
# Derive the token ID
- token_id = poseidon_hash(derivation_path, mint_x, mint_y);
+ token_user_data = poseidon_hash(mint_x, mint_y);
+ token_id = poseidon_hash(token_auth_parent, token_user_data, token_blind);
constrain_instance(token_id);
}
+
diff --git a/src/contract/money/proof/token_mint_v1.zk b/src/contract/money/proof/token_mint_v1.zk
index f2445a31b..c9013334f 100644
--- a/src/contract/money/proof/token_mint_v1.zk
+++ b/src/contract/money/proof/token_mint_v1.zk
@@ -9,61 +9,36 @@ constant "TokenMint_V1" {
}
witness "TokenMint_V1" {
- # Token mint authority secret
- Base mint_authority,
- # Token supply
- Base supply,
- # Recipient's public key x coordinate
- Base rcpt_x,
- # Recipient's public key y coordinate
- Base rcpt_y,
- # Unique serial number for the minted coin
- Base serial,
- # Allows composing this ZK proof to invoke other contracts
+ # CoinAttributes {
+ Base public_x,
+ Base public_y,
+ Base value,
Base spend_hook,
- # Data passed from this coin to the invoked contract
- Base user_data,
- # Random blinding factor for the value commitment
- Scalar value_blind,
- # Random blinding factor for the token ID
+ Base coin_user_data,
+ Base coin_blind,
+ # }
+
+ # TokenAttributes {
+ Base auth_parent,
+ Base token_user_data,
Base token_blind,
+ # }
}
circuit "TokenMint_V1" {
- # TokenID derivation path (See darkfi_sdk::crypto::TokenId)
- derivation_path = witness_base(69);
-
- # Derive public key for the mint authority
- 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);
-
# Derive the token ID
- token_id = poseidon_hash(derivation_path, mint_x, mint_y);
- constrain_instance(token_id);
+ token_id = poseidon_hash(auth_parent, token_user_data, token_blind);
+ constrain_instance(auth_parent);
- # Poseidon hash of the minted coin
- C = poseidon_hash(
- rcpt_x,
- rcpt_y,
- supply,
+ # Then show the coin contains the token ID
+ coin = poseidon_hash(
+ public_x,
+ public_y,
+ value,
token_id,
- serial,
spend_hook,
- user_data,
+ coin_user_data,
+ coin_blind,
);
- constrain_instance(C);
-
- # Pedersen commitment for the coin's value
- vcv = ec_mul_short(supply, VALUE_COMMIT_VALUE);
- vcr = ec_mul(value_blind, VALUE_COMMIT_RANDOM);
- value_commit = ec_add(vcv, vcr);
- constrain_instance(ec_get_x(value_commit));
- constrain_instance(ec_get_y(value_commit));
-
- # Commitment for the coin's token ID
- token_commit = poseidon_hash(token_id, token_blind);
- constrain_instance(token_commit);
+ constrain_instance(coin);
}
diff --git a/src/contract/money/src/client/auth_token_mint_v1.rs b/src/contract/money/src/client/auth_token_mint_v1.rs
new file mode 100644
index 000000000..b31e225c6
--- /dev/null
+++ b/src/contract/money/src/client/auth_token_mint_v1.rs
@@ -0,0 +1,122 @@
+/* This file is part of DarkFi (https://dark.fi)
+ *
+ * Copyright (C) 2020-2024 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 .
+ */
+
+use darkfi::{
+ zk::{halo2::Value, Proof, ProvingKey, Witness, ZkCircuit},
+ zkas::ZkBinary,
+ Result,
+};
+use darkfi_sdk::{
+ crypto::{note::AeadEncryptedNote, pasta_prelude::*, pedersen_commitment_u64, Keypair},
+ pasta::pallas,
+};
+use log::info;
+use rand::rngs::OsRng;
+
+use crate::{
+ client::MoneyNote,
+ model::{CoinAttributes, MoneyAuthTokenMintParamsV1, TokenAttributes},
+};
+
+pub struct AuthTokenMintCallDebris {
+ pub params: MoneyAuthTokenMintParamsV1,
+ pub proofs: Vec,
+}
+
+/// Struct holding necessary information to build a `Money::AuthTokenMintV1` contract call.
+pub struct AuthTokenMintCallBuilder {
+ pub coin_attrs: CoinAttributes,
+ pub token_attrs: TokenAttributes,
+
+ /// Mint authority keypair
+ pub mint_keypair: Keypair,
+
+ /// `AuthTokenMint_V1` zkas circuit ZkBinary
+ pub auth_mint_zkbin: ZkBinary,
+ /// Proving key for the `AuthTokenMint_V1` zk circuit,
+ pub auth_mint_pk: ProvingKey,
+}
+
+impl AuthTokenMintCallBuilder {
+ pub fn build(&self) -> Result {
+ info!("Building Money::AuthTokenMintV1 contract call");
+
+ let value_blind = pallas::Scalar::random(&mut OsRng);
+ let value_commit = pedersen_commitment_u64(self.coin_attrs.value, value_blind);
+
+ // Create the proof
+
+ let (public_x, public_y) = self.coin_attrs.public_key.xy();
+
+ let prover_witnesses = vec![
+ // Coin attributes
+ Witness::Base(Value::known(public_x)),
+ Witness::Base(Value::known(public_y)),
+ Witness::Base(Value::known(pallas::Base::from(self.coin_attrs.value))),
+ Witness::Base(Value::known(self.coin_attrs.spend_hook)),
+ Witness::Base(Value::known(self.coin_attrs.user_data)),
+ Witness::Base(Value::known(self.coin_attrs.blind)),
+ // Token attributes
+ Witness::Base(Value::known(self.token_attrs.auth_parent.inner())),
+ Witness::Base(Value::known(self.token_attrs.blind)),
+ // Secret key used by mint
+ Witness::Base(Value::known(self.mint_keypair.secret.inner())),
+ // Random blinding factor for the value commitment
+ Witness::Scalar(Value::known(value_blind)),
+ ];
+
+ let mint_pubkey = self.mint_keypair.public;
+ let value_coords = value_commit.to_affine().coordinates().unwrap();
+
+ let public_inputs = vec![
+ mint_pubkey.x(),
+ mint_pubkey.y(),
+ self.token_attrs.to_token_id().inner(),
+ self.coin_attrs.to_coin().inner(),
+ *value_coords.x(),
+ *value_coords.y(),
+ ];
+
+ let circuit = ZkCircuit::new(prover_witnesses, &self.auth_mint_zkbin);
+ let proof = Proof::create(&self.auth_mint_pk, &[circuit], &public_inputs, &mut OsRng)?;
+
+ // Create the note
+
+ let note = MoneyNote {
+ value: self.coin_attrs.value,
+ token_id: self.coin_attrs.token_id,
+ spend_hook: self.coin_attrs.spend_hook,
+ user_data: self.coin_attrs.user_data,
+ coin_blind: self.coin_attrs.blind,
+ value_blind,
+ token_blind: pallas::Base::ZERO,
+ memo: vec![],
+ };
+
+ let enc_note = AeadEncryptedNote::encrypt(¬e, &self.coin_attrs.public_key, &mut OsRng)?;
+
+ let params = MoneyAuthTokenMintParamsV1 {
+ token_id: self.token_attrs.to_token_id(),
+ value_commit,
+ enc_note,
+ mint_pubkey,
+ };
+ let debris = AuthTokenMintCallDebris { params, proofs: vec![proof] };
+ Ok(debris)
+ }
+}
diff --git a/src/contract/money/src/client/genesis_mint_v1.rs b/src/contract/money/src/client/genesis_mint_v1.rs
index 3f4e6e11b..222eb44d4 100644
--- a/src/contract/money/src/client/genesis_mint_v1.rs
+++ b/src/contract/money/src/client/genesis_mint_v1.rs
@@ -35,11 +35,11 @@ use crate::{
},
MoneyNote,
},
- model::{ClearInput, Coin, MoneyTokenMintParamsV1, Output},
+ model::{ClearInput, Coin, MoneyGenesisMintParamsV1, Output},
};
pub struct GenesisMintCallDebris {
- pub params: MoneyTokenMintParamsV1,
+ pub params: MoneyGenesisMintParamsV1,
pub proofs: Vec,
}
@@ -147,7 +147,7 @@ impl GenesisMintCallBuilder {
note: encrypted_note,
};
- let params = MoneyTokenMintParamsV1 { input: c_input, output: c_output };
+ let params = MoneyGenesisMintParamsV1 { input: c_input, output: c_output };
let debris = GenesisMintCallDebris { params, proofs: vec![proof] };
Ok(debris)
}
diff --git a/src/contract/money/src/client/mod.rs b/src/contract/money/src/client/mod.rs
index 320efbf2b..1f7dd4b4f 100644
--- a/src/contract/money/src/client/mod.rs
+++ b/src/contract/money/src/client/mod.rs
@@ -56,6 +56,48 @@ pub mod token_freeze_v1;
/// `Money::PoWRewardV1` API
pub mod pow_reward_v1;
+/// `Money::AuthTokenMintV1` API
+pub mod auth_token_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.
+pub const MONEY_INFO_TABLE: &str = "money_info";
+pub const MONEY_INFO_COL_LAST_SCANNED_SLOT: &str = "last_scanned_slot";
+
+pub const MONEY_TREE_TABLE: &str = "money_tree";
+pub const MONEY_TREE_COL_TREE: &str = "tree";
+
+pub const MONEY_KEYS_TABLE: &str = "money_keys";
+pub const MONEY_KEYS_COL_KEY_ID: &str = "key_id";
+pub const MONEY_KEYS_COL_IS_DEFAULT: &str = "is_default";
+pub const MONEY_KEYS_COL_PUBLIC: &str = "public";
+pub const MONEY_KEYS_COL_SECRET: &str = "secret";
+
+pub const MONEY_COINS_TABLE: &str = "money_coins";
+pub const MONEY_COINS_COL_COIN: &str = "coin";
+pub const MONEY_COINS_COL_IS_SPENT: &str = "is_spent";
+pub const MONEY_COINS_COL_SERIAL: &str = "serial";
+pub const MONEY_COINS_COL_VALUE: &str = "value";
+pub const MONEY_COINS_COL_TOKEN_ID: &str = "token_id";
+pub const MONEY_COINS_COL_SPEND_HOOK: &str = "spend_hook";
+pub const MONEY_COINS_COL_USER_DATA: &str = "user_data";
+pub const MONEY_COINS_COL_VALUE_BLIND: &str = "value_blind";
+pub const MONEY_COINS_COL_TOKEN_BLIND: &str = "token_blind";
+pub const MONEY_COINS_COL_SECRET: &str = "secret";
+pub const MONEY_COINS_COL_NULLIFIER: &str = "nullifier";
+pub const MONEY_COINS_COL_LEAF_POSITION: &str = "leaf_position";
+pub const MONEY_COINS_COL_MEMO: &str = "memo";
+
+pub const MONEY_TOKENS_TABLE: &str = "money_tokens";
+pub const MONEY_TOKENS_COL_MINT_AUTHORITY: &str = "mint_authority";
+pub const MONEY_TOKENS_COL_TOKEN_ID: &str = "token_id";
+pub const MONEY_TOKENS_COL_IS_FROZEN: &str = "is_frozen";
+
+pub const MONEY_ALIASES_TABLE: &str = "money_aliases";
+pub const MONEY_ALIASES_COL_ALIAS: &str = "alias";
+pub const MONEY_ALIASES_COL_TOKEN_ID: &str = "token_id";
+
/// `MoneyNote` holds the inner attributes of a `Coin`
/// It does not store the public key since it's encrypted for that key,
/// and so is not needed to infer the coin attributes.
diff --git a/src/contract/money/src/client/token_freeze_v1.rs b/src/contract/money/src/client/token_freeze_v1.rs
index 28b3529a5..686ad3fbd 100644
--- a/src/contract/money/src/client/token_freeze_v1.rs
+++ b/src/contract/money/src/client/token_freeze_v1.rs
@@ -21,40 +21,26 @@ use darkfi::{
zkas::ZkBinary,
Result,
};
-use darkfi_sdk::{
- crypto::{Keypair, PublicKey, TokenId},
- pasta::pallas,
-};
-use log::{debug, info};
+use darkfi_sdk::crypto::Keypair;
+use log::info;
use rand::rngs::OsRng;
-use crate::model::MoneyTokenFreezeParamsV1;
+use crate::model::{MoneyTokenFreezeParamsV1, TokenAttributes};
pub struct TokenFreezeCallDebris {
pub params: MoneyTokenFreezeParamsV1,
pub proofs: Vec,
}
-pub struct TokenFreezeRevealed {
- pub signature_public: PublicKey,
- pub token_id: TokenId,
-}
-
-impl TokenFreezeRevealed {
- pub fn to_vec(&self) -> Vec {
- let (sig_x, sig_y) = self.signature_public.xy();
- vec![sig_x, sig_y, self.token_id.inner()]
- }
-}
-
/// Struct holding necessary information to build a `Money::TokenFreezeV1` contract call.
pub struct TokenFreezeCallBuilder {
/// Mint authority keypair
- pub mint_authority: Keypair,
+ pub mint_keypair: Keypair,
+ pub token_attrs: TokenAttributes,
/// `TokenFreeze_V1` zkas circuit ZkBinary
- pub token_freeze_zkbin: ZkBinary,
+ pub freeze_zkbin: ZkBinary,
/// Proving key for the `TokenFreeze_V1` zk circuit,
- pub token_freeze_pk: ProvingKey,
+ pub freeze_pk: ProvingKey,
}
impl TokenFreezeCallBuilder {
@@ -63,32 +49,25 @@ impl TokenFreezeCallBuilder {
// For the TokenFreeze call, we just need to produce a valid signature,
// and enforce the correct derivation inside ZK.
- debug!("Creating token freeze ZK proof");
- let (proof, _public_inputs) = create_token_freeze_proof(
- &self.token_freeze_zkbin,
- &self.token_freeze_pk,
- &self.mint_authority,
- )?;
+ let prover_witnesses = vec![
+ // Token attributes
+ Witness::Base(Value::known(self.token_attrs.auth_parent.inner())),
+ Witness::Base(Value::known(self.token_attrs.blind)),
+ // Secret key used by mint
+ Witness::Base(Value::known(self.mint_keypair.secret.inner())),
+ ];
- let params = MoneyTokenFreezeParamsV1 { signature_public: self.mint_authority.public };
+ let mint_pubkey = self.mint_keypair.public;
+ let token_id = self.token_attrs.to_token_id();
+
+ let public_inputs = vec![mint_pubkey.x(), mint_pubkey.y(), token_id.inner()];
+ darkfi::zk::export_witness_json("witness.json", &prover_witnesses, &public_inputs);
+
+ let circuit = ZkCircuit::new(prover_witnesses, &self.freeze_zkbin);
+ let proof = Proof::create(&self.freeze_pk, &[circuit], &public_inputs, &mut OsRng)?;
+
+ let params = MoneyTokenFreezeParamsV1 { mint_public: self.mint_keypair.public, token_id };
let debris = TokenFreezeCallDebris { params, proofs: vec![proof] };
Ok(debris)
}
}
-
-pub(crate) fn create_token_freeze_proof(
- zkbin: &ZkBinary,
- pk: &ProvingKey,
- mint_authority: &Keypair,
-) -> Result<(Proof, TokenFreezeRevealed)> {
- let token_id = TokenId::derive(mint_authority.secret);
-
- let public_inputs = TokenFreezeRevealed { signature_public: mint_authority.public, token_id };
-
- let prover_witnesses = vec![Witness::Base(Value::known(mint_authority.secret.inner()))];
-
- let circuit = ZkCircuit::new(prover_witnesses, zkbin);
- let proof = Proof::create(pk, &[circuit], &public_inputs.to_vec(), &mut OsRng)?;
-
- Ok((proof, public_inputs))
-}
diff --git a/src/contract/money/src/client/token_mint_v1.rs b/src/contract/money/src/client/token_mint_v1.rs
index 0507a481c..228680daf 100644
--- a/src/contract/money/src/client/token_mint_v1.rs
+++ b/src/contract/money/src/client/token_mint_v1.rs
@@ -21,205 +21,56 @@ use darkfi::{
zkas::ZkBinary,
Result,
};
-use darkfi_sdk::{
- crypto::{
- note::AeadEncryptedNote, pasta_prelude::*, pedersen_commitment_u64, poseidon_hash, Keypair,
- PublicKey, TokenId,
- },
- pasta::pallas,
-};
+use darkfi_sdk::pasta::pallas;
use log::info;
use rand::rngs::OsRng;
-use crate::{
- client::{
- transfer_v1::{TransferCallClearInput, TransferCallOutput},
- MoneyNote,
- },
- model::{ClearInput, Coin, MoneyTokenMintParamsV1, Output},
-};
+use crate::model::{CoinAttributes, MoneyTokenMintParamsV1, TokenAttributes};
pub struct TokenMintCallDebris {
pub params: MoneyTokenMintParamsV1,
pub proofs: Vec,
}
-pub struct TokenMintRevealed {
- pub signature_public: PublicKey,
- pub token_id: TokenId,
- pub coin: Coin,
- pub value_commit: pallas::Point,
- pub token_commit: pallas::Base,
-}
-
-impl TokenMintRevealed {
- pub fn to_vec(&self) -> Vec {
- let (sig_x, sig_y) = self.signature_public.xy();
- let valcom_coords = self.value_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![
- sig_x,
- sig_y,
- self.token_id.inner(),
- self.coin.inner(),
- *valcom_coords.x(),
- *valcom_coords.y(),
- self.token_commit,
- ]
- }
-}
-
/// Struct holding necessary information to build a `Money::TokenMintV1` contract call.
pub struct TokenMintCallBuilder {
- /// 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,
+ pub coin_attrs: CoinAttributes,
+ pub token_attrs: TokenAttributes,
+
/// `TokenMint_V1` zkas circuit ZkBinary
- pub token_mint_zkbin: ZkBinary,
+ pub mint_zkbin: ZkBinary,
/// Proving key for the `TokenMint_V1` zk circuit,
- pub token_mint_pk: ProvingKey,
+ pub mint_pk: ProvingKey,
}
impl TokenMintCallBuilder {
pub fn build(&self) -> Result {
info!("Building Money::TokenMintV1 contract call");
- assert!(self.amount != 0);
+ let (public_x, public_y) = self.coin_attrs.public_key.xy();
- // 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 token_id = TokenId::derive(self.mint_authority.secret);
+ let prover_witnesses = vec![
+ // Coin attributes
+ Witness::Base(Value::known(public_x)),
+ Witness::Base(Value::known(public_y)),
+ Witness::Base(Value::known(pallas::Base::from(self.coin_attrs.value))),
+ Witness::Base(Value::known(self.coin_attrs.spend_hook)),
+ Witness::Base(Value::known(self.coin_attrs.user_data)),
+ Witness::Base(Value::known(self.coin_attrs.blind)),
+ // Token attributes
+ Witness::Base(Value::known(self.token_attrs.auth_parent.inner())),
+ Witness::Base(Value::known(self.token_attrs.user_data)),
+ Witness::Base(Value::known(self.token_attrs.blind)),
+ ];
- let input = TransferCallClearInput {
- value: self.amount,
- token_id,
- signature_secret: self.mint_authority.secret,
- };
+ let coin = self.coin_attrs.to_coin();
- let output = TransferCallOutput {
- public_key: self.recipient,
- value: self.amount,
- token_id,
- spend_hook: pallas::Base::ZERO,
- user_data: pallas::Base::ZERO,
- blind: pallas::Base::random(&mut OsRng),
- };
+ let public_inputs = vec![self.token_attrs.auth_parent.inner(), coin.inner()];
- // 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::Base::random(&mut OsRng);
+ let circuit = ZkCircuit::new(prover_witnesses, &self.mint_zkbin);
+ let proof = Proof::create(&self.mint_pk, &[circuit], &public_inputs, &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 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,
- self.spend_hook,
- self.user_data,
- coin_blind,
- )?;
-
- let note = MoneyNote {
- 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(¬e, &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 = MoneyTokenMintParamsV1 { input: c_input, output: c_output };
+ let params = MoneyTokenMintParamsV1 { coin };
let debris = TokenMintCallDebris { params, proofs: vec![proof] };
Ok(debris)
}
}
-
-#[allow(clippy::too_many_arguments)]
-pub fn create_token_mint_proof(
- zkbin: &ZkBinary,
- pk: &ProvingKey,
- output: &TransferCallOutput,
- mint_authority: &Keypair,
- value_blind: pallas::Scalar,
- token_blind: pallas::Base,
- spend_hook: pallas::Base,
- user_data: pallas::Base,
- coin_blind: pallas::Base,
-) -> Result<(Proof, TokenMintRevealed)> {
- let token_id = TokenId::derive(mint_authority.secret);
-
- let value_commit = pedersen_commitment_u64(output.value, value_blind);
- let token_commit = poseidon_hash([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(),
- spend_hook,
- user_data,
- coin_blind,
- ]));
-
- let public_inputs = TokenMintRevealed {
- signature_public: mint_authority.public,
- 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(spend_hook)),
- Witness::Base(Value::known(user_data)),
- Witness::Base(Value::known(coin_blind)),
- Witness::Scalar(Value::known(value_blind)),
- Witness::Base(Value::known(token_blind)),
- ];
-
- let circuit = ZkCircuit::new(prover_witnesses, zkbin);
- let proof = Proof::create(pk, &[circuit], &public_inputs.to_vec(), &mut OsRng)?;
-
- Ok((proof, public_inputs))
-}
diff --git a/src/contract/money/src/entrypoint.rs b/src/contract/money/src/entrypoint.rs
index 753193de1..c644d0a6b 100644
--- a/src/contract/money/src/entrypoint.rs
+++ b/src/contract/money/src/entrypoint.rs
@@ -29,8 +29,9 @@ use darkfi_serial::{deserialize, serialize, Encodable, WriteExt};
use crate::{
model::{
- MoneyFeeUpdateV1, MoneyGenesisMintUpdateV1, MoneyPoWRewardUpdateV1,
- MoneyTokenFreezeUpdateV1, MoneyTokenMintUpdateV1, MoneyTransferUpdateV1,
+ MoneyAuthTokenMintUpdateV1, MoneyFeeUpdateV1, MoneyGenesisMintUpdateV1,
+ MoneyPoWRewardUpdateV1, MoneyTokenFreezeUpdateV1, MoneyTokenMintUpdateV1,
+ MoneyTransferUpdateV1,
},
MoneyFunction, MONEY_CONTRACT_COINS_TREE, MONEY_CONTRACT_COIN_MERKLE_TREE,
MONEY_CONTRACT_COIN_ROOTS_TREE, MONEY_CONTRACT_DB_VERSION, MONEY_CONTRACT_FAUCET_PUBKEYS,
@@ -86,6 +87,13 @@ use pow_reward_v1::{
money_pow_reward_process_update_v1,
};
+/// `Money::AuthTokenMint` functions
+mod auth_token_mint_v1;
+use auth_token_mint_v1::{
+ money_auth_token_mint_get_metadata_v1, money_auth_token_mint_process_instruction_v1,
+ money_auth_token_mint_process_update_v1,
+};
+
darkfi_sdk::define_contract!(
init: init_contract,
exec: process_instruction,
@@ -196,6 +204,9 @@ fn get_metadata(cid: ContractId, ix: &[u8]) -> ContractResult {
MoneyFunction::TokenMintV1 => money_token_mint_get_metadata_v1(cid, call_idx, calls)?,
MoneyFunction::TokenFreezeV1 => money_token_freeze_get_metadata_v1(cid, call_idx, calls)?,
MoneyFunction::PoWRewardV1 => money_pow_reward_get_metadata_v1(cid, call_idx, calls)?,
+ MoneyFunction::AuthTokenMintV1 => {
+ money_auth_token_mint_get_metadata_v1(cid, call_idx, calls)?
+ }
};
set_return_data(&metadata)
@@ -232,6 +243,9 @@ fn process_instruction(cid: ContractId, ix: &[u8]) -> ContractResult {
MoneyFunction::PoWRewardV1 => {
money_pow_reward_process_instruction_v1(cid, call_idx, calls)?
}
+ MoneyFunction::AuthTokenMintV1 => {
+ money_auth_token_mint_process_instruction_v1(cid, call_idx, calls)?
+ }
};
set_return_data(&update_data)
@@ -279,5 +293,10 @@ fn process_update(cid: ContractId, update_data: &[u8]) -> ContractResult {
let update: MoneyPoWRewardUpdateV1 = deserialize(&update_data[1..])?;
Ok(money_pow_reward_process_update_v1(cid, update)?)
}
+
+ MoneyFunction::AuthTokenMintV1 => {
+ let update: MoneyAuthTokenMintUpdateV1 = deserialize(&update_data[1..])?;
+ Ok(money_auth_token_mint_process_update_v1(cid, update)?)
+ }
}
}
diff --git a/src/contract/money/src/entrypoint/auth_token_mint_v1.rs b/src/contract/money/src/entrypoint/auth_token_mint_v1.rs
new file mode 100644
index 000000000..e926644b1
--- /dev/null
+++ b/src/contract/money/src/entrypoint/auth_token_mint_v1.rs
@@ -0,0 +1,112 @@
+/* This file is part of DarkFi (https://dark.fi)
+ *
+ * Copyright (C) 2020-2024 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 .
+ */
+
+use darkfi_sdk::{
+ crypto::{pasta_prelude::*, ContractId, PublicKey},
+ dark_tree::DarkLeaf,
+ db::{db_contains_key, db_lookup},
+ error::{ContractError, ContractResult},
+ msg,
+ pasta::pallas,
+ ContractCall,
+};
+use darkfi_serial::{deserialize, serialize, Encodable, WriteExt};
+
+use crate::{
+ error::MoneyError,
+ model::{MoneyAuthTokenMintParamsV1, MoneyAuthTokenMintUpdateV1, MoneyTokenMintParamsV1},
+ MoneyFunction, MONEY_CONTRACT_TOKEN_FREEZE_TREE, MONEY_CONTRACT_ZKAS_AUTH_TOKEN_MINT_NS_V1,
+};
+
+/// `get_metadata` function for `Money::AuthTokenMintV1`
+pub(crate) fn money_auth_token_mint_get_metadata_v1(
+ _cid: ContractId,
+ call_idx: u32,
+ calls: Vec>,
+) -> Result, ContractError> {
+ let self_node = &calls[call_idx as usize];
+ let self_data = &self_node.data;
+ let self_params: MoneyAuthTokenMintParamsV1 = deserialize(&self_data.data[1..])?;
+
+ assert_eq!(self_node.children_indexes.len(), 1);
+ let child_idx = self_node.children_indexes[0];
+ let child_node = &calls[child_idx];
+ let child_data = &child_node.data;
+ let child_params: MoneyTokenMintParamsV1 = deserialize(&child_data.data[1..])?;
+
+ // Public inputs for the ZK proofs we have to verify
+ let mut zk_public_inputs: Vec<(String, Vec)> = vec![];
+ // Public keys for the transaction signatures we have to verify.
+ let signature_pubkeys: Vec = vec![self_params.mint_pubkey];
+
+ let value_commit = self_params.value_commit.to_affine().coordinates().unwrap();
+ zk_public_inputs.push((
+ MONEY_CONTRACT_ZKAS_AUTH_TOKEN_MINT_NS_V1.to_string(),
+ vec![
+ self_params.mint_pubkey.x(),
+ self_params.mint_pubkey.y(),
+ self_params.token_id.inner(),
+ child_params.coin.inner(),
+ *value_commit.x(),
+ *value_commit.y(),
+ ],
+ ));
+
+ // Serialize everything gathered and return it
+ let mut metadata = vec![];
+ zk_public_inputs.encode(&mut metadata)?;
+ signature_pubkeys.encode(&mut metadata)?;
+
+ Ok(metadata)
+}
+
+/// `process_instruction` function for `Money::AuthTokenMintV1`
+pub(crate) fn money_auth_token_mint_process_instruction_v1(
+ cid: ContractId,
+ call_idx: u32,
+ calls: Vec>,
+) -> Result, ContractError> {
+ let self_ = &calls[call_idx as usize].data;
+ let params: MoneyAuthTokenMintParamsV1 = deserialize(&self_.data[1..])?;
+
+ // We have to check if the token mint is frozen.
+ let token_freeze_db = db_lookup(cid, MONEY_CONTRACT_TOKEN_FREEZE_TREE)?;
+
+ // Check that the mint is not frozen
+ if db_contains_key(token_freeze_db, &serialize(¶ms.token_id))? {
+ msg!("[MintV1] Error: Token mint for {} is frozen", params.token_id);
+ return Err(MoneyError::TokenMintFrozen.into())
+ }
+
+ // Create a state update.
+ let update = MoneyAuthTokenMintUpdateV1 {};
+ let mut update_data = vec![];
+ update_data.write_u8(MoneyFunction::AuthTokenMintV1 as u8)?;
+ update.encode(&mut update_data)?;
+
+ Ok(update_data)
+}
+
+/// `process_update` function for `Money::AuthTokenMintV1`
+pub(crate) fn money_auth_token_mint_process_update_v1(
+ _cid: ContractId,
+ _update: MoneyAuthTokenMintUpdateV1,
+) -> ContractResult {
+ // Do nothing... Coin is added with token_mint() call instead.
+ Ok(())
+}
diff --git a/src/contract/money/src/entrypoint/token_freeze_v1.rs b/src/contract/money/src/entrypoint/token_freeze_v1.rs
index 79af6891b..7dc26f8a0 100644
--- a/src/contract/money/src/entrypoint/token_freeze_v1.rs
+++ b/src/contract/money/src/entrypoint/token_freeze_v1.rs
@@ -17,7 +17,7 @@
*/
use darkfi_sdk::{
- crypto::{ContractId, PublicKey, TokenId},
+ crypto::{ContractId, PublicKey},
dark_tree::DarkLeaf,
db::{db_contains_key, db_lookup, db_set},
error::{ContractError, ContractResult},
@@ -45,16 +45,15 @@ pub(crate) fn money_token_freeze_get_metadata_v1(
// Public inputs for the ZK proofs we have to verify
let mut zk_public_inputs: Vec<(String, Vec)> = vec![];
// Public keys for the transaction signatures we have to verify
- let signature_pubkeys: Vec = vec![params.signature_public];
+ let signature_pubkeys: Vec = vec![params.mint_public];
// Derive the TokenId from the public key
- let (sig_x, sig_y) = params.signature_public.xy();
- let token_id = TokenId::derive_public(params.signature_public);
+ let (mint_x, mint_y) = params.mint_public.xy();
// In ZK we just verify that the token ID is properly derived from the authority.
zk_public_inputs.push((
MONEY_CONTRACT_ZKAS_TOKEN_FRZ_NS_V1.to_string(),
- vec![sig_x, sig_y, token_id.inner()],
+ vec![mint_x, mint_y, params.token_id.inner()],
));
// Serialize everything gathered and return it
@@ -76,16 +75,15 @@ pub(crate) fn money_token_freeze_process_instruction_v1(
// We just check if the mint was already frozen beforehand
let token_freeze_db = db_lookup(cid, MONEY_CONTRACT_TOKEN_FREEZE_TREE)?;
- let token_id = TokenId::derive_public(params.signature_public);
// Check that the mint is not frozen
- if db_contains_key(token_freeze_db, &serialize(&token_id))? {
- msg!("[MintV1] Error: Token mint for {} is frozen", token_id);
+ if db_contains_key(token_freeze_db, &serialize(¶ms.token_id))? {
+ msg!("[MintV1] Error: Token mint for {} is frozen", params.token_id);
return Err(MoneyError::TokenMintFrozen.into())
}
// Create a state update. We only need the new coin.
- let update = MoneyTokenFreezeUpdateV1 { signature_public: params.signature_public };
+ let update = MoneyTokenFreezeUpdateV1 { token_id: params.token_id };
let mut update_data = vec![];
update_data.write_u8(MoneyFunction::TokenFreezeV1 as u8)?;
update.encode(&mut update_data)?;
@@ -99,9 +97,8 @@ pub(crate) fn money_token_freeze_process_update_v1(
update: MoneyTokenFreezeUpdateV1,
) -> ContractResult {
let token_freeze_db = db_lookup(cid, MONEY_CONTRACT_TOKEN_FREEZE_TREE)?;
- let token_id = TokenId::derive_public(update.signature_public);
- msg!("[MintV1] Freezing mint for token {}", token_id);
- db_set(token_freeze_db, &serialize(&token_id), &[])?;
+ msg!("[MintV1] Freezing mint for token {}", update.token_id);
+ db_set(token_freeze_db, &serialize(&update.token_id), &[])?;
Ok(())
}
diff --git a/src/contract/money/src/entrypoint/token_mint_v1.rs b/src/contract/money/src/entrypoint/token_mint_v1.rs
index 46fb67dad..e6e2efc6b 100644
--- a/src/contract/money/src/entrypoint/token_mint_v1.rs
+++ b/src/contract/money/src/entrypoint/token_mint_v1.rs
@@ -17,9 +17,7 @@
*/
use darkfi_sdk::{
- crypto::{
- pasta_prelude::*, pedersen_commitment_u64, poseidon_hash, ContractId, MerkleNode, TokenId,
- },
+ crypto::{ContractId, FuncRef, MerkleNode, PublicKey},
dark_tree::DarkLeaf,
db::{db_contains_key, db_lookup, db_set},
error::{ContractError, ContractResult},
@@ -34,7 +32,7 @@ use crate::{
model::{MoneyTokenMintParamsV1, MoneyTokenMintUpdateV1},
MoneyFunction, MONEY_CONTRACT_COINS_TREE, MONEY_CONTRACT_COIN_MERKLE_TREE,
MONEY_CONTRACT_COIN_ROOTS_TREE, MONEY_CONTRACT_INFO_TREE, MONEY_CONTRACT_LATEST_COIN_ROOT,
- MONEY_CONTRACT_TOKEN_FREEZE_TREE, MONEY_CONTRACT_ZKAS_TOKEN_MINT_NS_V1,
+ MONEY_CONTRACT_ZKAS_TOKEN_MINT_NS_V1,
};
/// `get_metadata` function for `Money::TokenMintV1`
@@ -46,31 +44,22 @@ pub(crate) fn money_token_mint_get_metadata_v1(
let self_ = &calls[call_idx as usize].data;
let params: MoneyTokenMintParamsV1 = deserialize(&self_.data[1..])?;
+ let parent_idx = calls[call_idx as usize].parent_index.unwrap();
+ let parent_call = &calls[parent_idx].data;
+ let parent_contract_id = parent_call.contract_id;
+ let parent_func_code = parent_call.data[0];
+
// Public inputs for the ZK proofs we have to verify
let mut zk_public_inputs: Vec<(String, Vec)> = vec![];
// Public keys for the transaction signatures we have to verify.
- // The minting transaction creates 1 clear input and 1 anonymous output.
- // We check the signature from the clear input, which is supposed to be
- // signed by the mint authority.
- let signature_pubkeys = vec![params.input.signature_public];
+ let signature_pubkeys: Vec = vec![];
- // Derive the TokenId from the public key
- let (sig_x, sig_y) = params.input.signature_public.xy();
- let token_id = TokenId::derive_public(params.input.signature_public);
-
- let value_coords = params.output.value_commit.to_affine().coordinates().unwrap();
+ let parent_func_id =
+ FuncRef { contract_id: parent_contract_id, func_code: parent_func_code }.to_func_id();
zk_public_inputs.push((
MONEY_CONTRACT_ZKAS_TOKEN_MINT_NS_V1.to_string(),
- vec![
- sig_x,
- sig_y,
- token_id.inner(),
- params.output.coin.inner(),
- *value_coords.x(),
- *value_coords.y(),
- params.output.token_commit,
- ],
+ vec![parent_func_id.inner(), params.coin.inner()],
));
// Serialize everything gathered and return it
@@ -93,46 +82,15 @@ pub(crate) fn money_token_mint_process_instruction_v1(
// We have to check if the token mint is frozen, and if by some chance
// the minted coin has existed already.
let coins_db = db_lookup(cid, MONEY_CONTRACT_COINS_TREE)?;
- let token_freeze_db = db_lookup(cid, MONEY_CONTRACT_TOKEN_FREEZE_TREE)?;
-
- // Check that the signature public key is actually the token ID
- let token_id = TokenId::derive_public(params.input.signature_public);
- if token_id != params.input.token_id {
- msg!("[MintV1] Error: Token ID does not derive from mint authority");
- return Err(MoneyError::TokenIdDoesNotDeriveFromMint.into())
- }
-
- // Check that the mint is not frozen
- if db_contains_key(token_freeze_db, &serialize(&token_id))? {
- msg!("[MintV1] Error: Token mint for {} is frozen", token_id);
- return Err(MoneyError::TokenMintFrozen.into())
- }
// Check that the coin from the output hasn't existed before
- if db_contains_key(coins_db, &serialize(¶ms.output.coin))? {
+ if db_contains_key(coins_db, &serialize(¶ms.coin))? {
msg!("[MintV1] Error: Duplicate coin in output");
return Err(MoneyError::DuplicateCoin.into())
}
- // Verify that the value and token commitments match. In here we just
- // confirm that the clear input and the anon output have the same
- // commitments.
- if pedersen_commitment_u64(params.input.value, params.input.value_blind) !=
- params.output.value_commit
- {
- msg!("[MintV1] Error: Value commitment mismatch");
- return Err(MoneyError::ValueMismatch.into())
- }
-
- if poseidon_hash([params.input.token_id.inner(), params.input.token_blind]) !=
- params.output.token_commit
- {
- msg!("[MintV1] Error: Token commitment mismatch");
- return Err(MoneyError::TokenMismatch.into())
- }
-
// Create a state update. We only need the new coin.
- let update = MoneyTokenMintUpdateV1 { coin: params.output.coin };
+ let update = MoneyTokenMintUpdateV1 { coin: params.coin };
let mut update_data = vec![];
update_data.write_u8(MoneyFunction::TokenMintV1 as u8)?;
update.encode(&mut update_data)?;
diff --git a/src/contract/money/src/lib.rs b/src/contract/money/src/lib.rs
index 9bc28be15..a62b3b922 100644
--- a/src/contract/money/src/lib.rs
+++ b/src/contract/money/src/lib.rs
@@ -32,6 +32,7 @@ pub enum MoneyFunction {
TokenMintV1 = 0x04,
TokenFreezeV1 = 0x05,
PoWRewardV1 = 0x06,
+ AuthTokenMintV1 = 0x07,
}
// ANCHOR_END: money-function
@@ -47,6 +48,7 @@ impl TryFrom for MoneyFunction {
0x04 => Ok(Self::TokenMintV1),
0x05 => Ok(Self::TokenFreezeV1),
0x06 => Ok(Self::PoWRewardV1),
+ 0x07 => Ok(Self::AuthTokenMintV1),
_ => Err(ContractError::InvalidFunction),
}
}
@@ -90,3 +92,5 @@ pub const MONEY_CONTRACT_ZKAS_BURN_NS_V1: &str = "Burn_V1";
pub const MONEY_CONTRACT_ZKAS_TOKEN_MINT_NS_V1: &str = "TokenMint_V1";
/// zkas token freeze circuit namespace
pub const MONEY_CONTRACT_ZKAS_TOKEN_FRZ_NS_V1: &str = "TokenFreeze_V1";
+/// zkas token auth mint circuit namespace
+pub const MONEY_CONTRACT_ZKAS_AUTH_TOKEN_MINT_NS_V1: &str = "AuthTokenMint_V1";
diff --git a/src/contract/money/src/model.rs b/src/contract/money/src/model.rs
index 04765a7e7..366781784 100644
--- a/src/contract/money/src/model.rs
+++ b/src/contract/money/src/model.rs
@@ -18,7 +18,7 @@
use darkfi_sdk::{
crypto::{
- ecvrf::VrfProof, note::AeadEncryptedNote, pasta_prelude::PrimeField, poseidon_hash,
+ ecvrf::VrfProof, note::AeadEncryptedNote, pasta_prelude::PrimeField, poseidon_hash, FuncId,
MerkleNode, Nullifier, PublicKey, SecretKey, TokenId,
},
error::ContractError,
@@ -90,6 +90,20 @@ impl CoinAttributes {
}
}
+#[derive(Debug, Clone, SerialEncodable, SerialDecodable)]
+pub struct TokenAttributes {
+ pub auth_parent: FuncId,
+ pub user_data: pallas::Base,
+ pub blind: pallas::Base,
+}
+
+impl TokenAttributes {
+ pub fn to_token_id(&self) -> TokenId {
+ let token_id = poseidon_hash([self.auth_parent.inner(), self.user_data, self.blind]);
+ TokenId::from(token_id)
+ }
+}
+
#[derive(Debug, Clone, SerialEncodable, SerialDecodable)]
pub struct NullifierAttributes {
/// Secret key for the public key in the coin.
@@ -228,10 +242,8 @@ pub struct MoneyGenesisMintUpdateV1 {
/// Parameters for `Money::TokenMint`
#[derive(Clone, Debug, SerialEncodable, SerialDecodable)]
pub struct MoneyTokenMintParamsV1 {
- /// Clear input
- pub input: ClearInput,
- /// Anonymous output
- pub output: Output,
+ /// The newly minted coin
+ pub coin: Coin,
}
/// State update for `Money::TokenMint`
@@ -241,20 +253,33 @@ pub struct MoneyTokenMintUpdateV1 {
pub coin: Coin,
}
+/// Parameters for `Money::auth_token_mint()`
+#[derive(Clone, Debug, SerialEncodable, SerialDecodable)]
+pub struct MoneyAuthTokenMintParamsV1 {
+ pub token_id: TokenId,
+ pub value_commit: pallas::Point,
+ pub enc_note: AeadEncryptedNote,
+ pub mint_pubkey: PublicKey,
+}
+
+/// State update for `Money::auth_token_mint()`
+#[derive(Clone, Debug, SerialEncodable, SerialDecodable)]
+pub struct MoneyAuthTokenMintUpdateV1 {}
+
/// Parameters for `Money::TokenFreeze`
#[derive(Clone, Debug, SerialEncodable, SerialDecodable)]
pub struct MoneyTokenFreezeParamsV1 {
/// Mint authority public key
///
/// We use this to derive the token ID and verify the signature.
- pub signature_public: PublicKey,
+ pub mint_public: PublicKey,
+ pub token_id: TokenId,
}
/// State update for `Money::TokenFreeze`
#[derive(Clone, Debug, SerialEncodable, SerialDecodable)]
pub struct MoneyTokenFreezeUpdateV1 {
- /// Mint authority public key
- pub signature_public: PublicKey,
+ pub token_id: TokenId,
}
/// Parameters for `Money::PoWReward`
diff --git a/src/contract/money/tests/genesis_mint.rs b/src/contract/money/tests/genesis_mint.rs
index 9bf50b2ce..1909fb385 100644
--- a/src/contract/money/tests/genesis_mint.rs
+++ b/src/contract/money/tests/genesis_mint.rs
@@ -106,7 +106,8 @@ fn genesis_mint() -> Result<()> {
th.assert_trees(&HOLDERS);
// Alice gathers her new owncoin
- let alice_oc = th.gather_owncoin(&Holder::Alice, &genesis_mint_params.output, None)?;
+ let alice_oc =
+ th.gather_owncoin_from_output(&Holder::Alice, &genesis_mint_params.output, None)?;
alice_owncoins.push(alice_oc);
info!(target: "money", "[Bob] ========================");
@@ -130,7 +131,8 @@ fn genesis_mint() -> Result<()> {
th.assert_trees(&HOLDERS);
// Bob gathers his new owncoin
- let bob_oc = th.gather_owncoin(&Holder::Bob, &genesis_mint_params.output, None)?;
+ let bob_oc =
+ th.gather_owncoin_from_output(&Holder::Bob, &genesis_mint_params.output, None)?;
bob_owncoins.push(bob_oc);
// Now Alice can send a little bit of funds to Bob
@@ -164,11 +166,13 @@ fn genesis_mint() -> Result<()> {
th.assert_trees(&HOLDERS);
// Bob should have his old OwnCoin, and this new one.
- let bob_oc = th.gather_owncoin(&Holder::Bob, &transfer_params.outputs[0], None)?;
+ let bob_oc =
+ th.gather_owncoin_from_output(&Holder::Bob, &transfer_params.outputs[0], None)?;
bob_owncoins.push(bob_oc);
// Alice should now have one OwnCoin with the change from the above transaction.
- let alice_oc = th.gather_owncoin(&Holder::Alice, &transfer_params.outputs[1], None)?;
+ let alice_oc =
+ th.gather_owncoin_from_output(&Holder::Alice, &transfer_params.outputs[1], None)?;
alice_owncoins.push(alice_oc);
assert!(alice_owncoins.len() == 1);
@@ -205,11 +209,13 @@ fn genesis_mint() -> Result<()> {
th.assert_trees(&HOLDERS);
// Alice should now have two OwnCoins
- let alice_oc = th.gather_owncoin(&Holder::Alice, &transfer_params.outputs[0], None)?;
+ let alice_oc =
+ th.gather_owncoin_from_output(&Holder::Alice, &transfer_params.outputs[0], None)?;
alice_owncoins.push(alice_oc);
// Bob should have two with the change from the above tx
- let bob_oc = th.gather_owncoin(&Holder::Bob, &transfer_params.outputs[1], None)?;
+ let bob_oc =
+ th.gather_owncoin_from_output(&Holder::Bob, &transfer_params.outputs[1], None)?;
bob_owncoins.push(bob_oc);
// Validating transaction outcomes
diff --git a/src/contract/money/tests/integration.rs b/src/contract/money/tests/integration.rs
index b18dbe42e..2c276c1a6 100644
--- a/src/contract/money/tests/integration.rs
+++ b/src/contract/money/tests/integration.rs
@@ -52,7 +52,7 @@ fn money_integration() -> Result<()> {
}
let alice_owncoin =
- th.gather_owncoin(&Holder::Alice, &alice_proposal_params.output, None)?;
+ th.gather_owncoin_from_output(&Holder::Alice, &alice_proposal_params.output, None)?;
assert!(alice_owncoin.note.value == expected_reward(current_block_height));
th.assert_trees(&HOLDERS);
@@ -73,7 +73,7 @@ fn money_integration() -> Result<()> {
.await?;
}
- let _ = th.gather_owncoin(&Holder::Bob, &bob_proposal_params.output, None)?;
+ let _ = th.gather_owncoin_from_output(&Holder::Bob, &bob_proposal_params.output, None)?;
th.assert_trees(&HOLDERS);
diff --git a/src/contract/money/tests/mint_pay_swap.rs b/src/contract/money/tests/mint_pay_swap.rs
index 1178255f6..d36743bfe 100644
--- a/src/contract/money/tests/mint_pay_swap.rs
+++ b/src/contract/money/tests/mint_pay_swap.rs
@@ -62,40 +62,42 @@ fn mint_pay_swap() -> Result<()> {
info!(target: "money", "[Alice] ================================");
info!(target: "money", "[Alice] Building token mint tx for Alice");
info!(target: "money", "[Alice] ================================");
- let (mint_tx, params) =
+ let (mint_tx, mint_params, mint_auth_params) =
th.token_mint(ALICE_INITIAL, &Holder::Alice, &Holder::Alice, None, None)?;
for holder in &HOLDERS {
info!(target: "money", "[{holder:?}] ==============================");
info!(target: "money", "[{holder:?}] Executing Alice token mint tx");
info!(target: "money", "[{holder:?}] ==============================");
- th.execute_token_mint_tx(holder, &mint_tx, ¶ms, current_block_height).await?;
+ th.execute_token_mint_tx(holder, &mint_tx, &mint_params, current_block_height).await?;
}
th.assert_trees(&HOLDERS);
// Alice gathers her new owncoin
- let alice_oc = th.gather_owncoin(&Holder::Alice, ¶ms.output, None)?;
+ let alice_oc =
+ th.gather_owncoin(&Holder::Alice, &mint_params.coin, &mint_auth_params.enc_note, None)?;
let alice_token_id = alice_oc.note.token_id;
alice_owncoins.push(alice_oc);
info!(target: "money", "[Bob] ==============================");
info!(target: "money", "[Bob] Building token mint tx for Bob");
info!(target: "money", "[Bob] ==============================");
- let (mint_tx, params) =
+ let (mint_tx, mint_params, mint_auth_params) =
th.token_mint(BOB_INITIAL, &Holder::Bob, &Holder::Bob, None, None)?;
for holder in &HOLDERS {
info!(target: "money", "[{holder:?}] ===========================");
info!(target: "money", "[{holder:?}] Executing Bob token mint tx");
info!(target: "money", "[{holder:?}] ===========================");
- th.execute_token_mint_tx(holder, &mint_tx, ¶ms, current_block_height).await?;
+ th.execute_token_mint_tx(holder, &mint_tx, &mint_params, current_block_height).await?;
}
th.assert_trees(&HOLDERS);
// Bob gathers hist new owncoin
- let bob_oc = th.gather_owncoin(&Holder::Bob, ¶ms.output, None)?;
+ let bob_oc =
+ th.gather_owncoin(&Holder::Bob, &mint_params.coin, &mint_auth_params.enc_note, None)?;
let bob_token_id = bob_oc.note.token_id;
bob_owncoins.push(bob_oc);
@@ -282,7 +284,7 @@ fn mint_pay_swap() -> Result<()> {
th.assert_trees(&HOLDERS);
// Alice should now have a single OwnCoin with her initial airdrop
- let alice_oc = th.gather_owncoin(&Holder::Alice, ¶ms.outputs[0], None)?;
+ let alice_oc = th.gather_owncoin_from_output(&Holder::Alice, ¶ms.outputs[0], None)?;
alice_owncoins.push(alice_oc);
assert!(alice_owncoins.len() == 1);
@@ -313,7 +315,7 @@ fn mint_pay_swap() -> Result<()> {
th.assert_trees(&HOLDERS);
// Bob should now have a single OwnCoin with his initial airdrop
- let bob_oc = th.gather_owncoin(&Holder::Bob, ¶ms.outputs[0], None)?;
+ let bob_oc = th.gather_owncoin_from_output(&Holder::Bob, ¶ms.outputs[0], None)?;
bob_owncoins.push(bob_oc);
assert!(bob_owncoins.len() == 1);
diff --git a/src/contract/money/tests/pow_reward.rs b/src/contract/money/tests/pow_reward.rs
index ab3089ae4..e36e73434 100644
--- a/src/contract/money/tests/pow_reward.rs
+++ b/src/contract/money/tests/pow_reward.rs
@@ -105,7 +105,8 @@ fn pow_reward() -> Result<()> {
th.assert_trees(&HOLDERS);
// Alice gathers her new owncoin
- let alice_oc = th.gather_owncoin(&Holder::Alice, &pow_reward_params.output, None)?;
+ let alice_oc =
+ th.gather_owncoin_from_output(&Holder::Alice, &pow_reward_params.output, None)?;
alice_owncoins.push(alice_oc);
// Now Alice can send a little bit of funds to Bob
@@ -140,11 +141,13 @@ fn pow_reward() -> Result<()> {
th.assert_trees(&HOLDERS);
// Bob should have this new OwnCoin.
- let bob_oc = th.gather_owncoin(&Holder::Bob, &transfer_params.outputs[0], None)?;
+ let bob_oc =
+ th.gather_owncoin_from_output(&Holder::Bob, &transfer_params.outputs[0], None)?;
bob_owncoins.push(bob_oc);
// Alice should now have one OwnCoin with the change from the above transaction.
- let alice_oc = th.gather_owncoin(&Holder::Alice, &transfer_params.outputs[1], None)?;
+ let alice_oc =
+ th.gather_owncoin_from_output(&Holder::Alice, &transfer_params.outputs[1], None)?;
alice_owncoins.push(alice_oc);
// Alice can also send her PoW reward directly to bob
@@ -170,7 +173,8 @@ fn pow_reward() -> Result<()> {
th.assert_trees(&HOLDERS);
// Bob gathers his new owncoin
- let bob_oc = th.gather_owncoin(&Holder::Bob, &pow_reward_params.output, None)?;
+ let bob_oc =
+ th.gather_owncoin_from_output(&Holder::Bob, &pow_reward_params.output, None)?;
bob_owncoins.push(bob_oc);
// Validating transaction outcomes
diff --git a/src/contract/money/tests/token_mint.rs b/src/contract/money/tests/token_mint.rs
index e0ce6e375..79f852475 100644
--- a/src/contract/money/tests/token_mint.rs
+++ b/src/contract/money/tests/token_mint.rs
@@ -38,7 +38,7 @@ fn token_mint() -> Result<()> {
let mut th = TestHarness::new(&["money".to_string()], false).await?;
info!("[Bob] Building BOB token mint tx");
- let (token_mint_tx, token_mint_params) =
+ let (token_mint_tx, token_mint_params, token_auth_mint_params) =
th.token_mint(BOB_SUPPLY, &Holder::Bob, &Holder::Bob, None, None)?;
for holder in &HOLDERS {
@@ -55,7 +55,12 @@ fn token_mint() -> Result<()> {
th.assert_trees(&HOLDERS);
// Bob gathers his new coin
- th.gather_owncoin(&Holder::Bob, &token_mint_params.output, None)?;
+ th.gather_owncoin(
+ &Holder::Bob,
+ &token_mint_params.coin,
+ &token_auth_mint_params.enc_note,
+ None,
+ )?;
info!("[Bob] Building BOB token freeze tx");
let (token_frz_tx, token_frz_params) = th.token_freeze(&Holder::Bob)?;
diff --git a/src/contract/money/tests/txs_verification.rs b/src/contract/money/tests/txs_verification.rs
index 047bb3e60..c3ee75e3a 100644
--- a/src/contract/money/tests/txs_verification.rs
+++ b/src/contract/money/tests/txs_verification.rs
@@ -55,7 +55,7 @@ fn txs_verification() -> Result<()> {
info!(target: "money", "[Alice] ================================");
info!(target: "money", "[Alice] Building token mint tx for Alice");
info!(target: "money", "[Alice] ================================");
- let (token_mint_tx, token_mint_params) =
+ let (token_mint_tx, token_mint_params, token_auth_mint_params) =
th.token_mint(ALICE_INITIAL, &Holder::Alice, &Holder::Alice, None, None)?;
for holder in &HOLDERS {
@@ -74,7 +74,12 @@ fn txs_verification() -> Result<()> {
th.assert_trees(&HOLDERS);
// Alice gathers her new owncoin
- let alice_oc = th.gather_owncoin(&Holder::Alice, &token_mint_params.output, None)?;
+ let alice_oc = th.gather_owncoin(
+ &Holder::Alice,
+ &token_mint_params.coin,
+ &token_auth_mint_params.enc_note,
+ None,
+ )?;
let alice_token_id = alice_oc.note.token_id;
alice_owncoins.push(alice_oc);
@@ -144,11 +149,13 @@ fn txs_verification() -> Result<()> {
th.assert_trees(&HOLDERS);
// Bob should now have the new OwnCoin.
- let bob_oc = th.gather_owncoin(&Holder::Bob, &txs_params[0].outputs[0], None)?;
+ let bob_oc =
+ th.gather_owncoin_from_output(&Holder::Bob, &txs_params[0].outputs[0], None)?;
bob_owncoins.push(bob_oc);
// Alice should now have one OwnCoin with the change from the above transaction.
- let alice_oc = th.gather_owncoin(&Holder::Alice, &txs_params[0].outputs[1], None)?;
+ let alice_oc =
+ th.gather_owncoin_from_output(&Holder::Alice, &txs_params[0].outputs[1], None)?;
alice_owncoins.push(alice_oc);
assert!(alice_owncoins.len() == 1);
diff --git a/src/contract/money/tests/verification_bench.rs b/src/contract/money/tests/verification_bench.rs
index 7ec30f741..506f28870 100644
--- a/src/contract/money/tests/verification_bench.rs
+++ b/src/contract/money/tests/verification_bench.rs
@@ -75,7 +75,8 @@ fn alice2alice_random_amounts() -> Result<()> {
// Gather new owncoins
let mut owncoins = vec![];
- let owncoin = th.gather_owncoin(&Holder::Alice, &airdrop_params.outputs[0], None)?;
+ let owncoin =
+ th.gather_owncoin_from_output(&Holder::Alice, &airdrop_params.outputs[0], None)?;
let token_id = owncoin.note.token_id;
owncoins.push(owncoin);
@@ -175,7 +176,8 @@ fn alice2alice_multiplecoins_random_amounts() -> Result<()> {
th.assert_trees(&HOLDERS);
// Gather new owncoins
- let owncoin = th.gather_owncoin(&Holder::Alice, &mint_params.output, None)?;
+ let owncoin =
+ th.gather_owncoin_from_output(&Holder::Alice, &mint_params.output, None)?;
let token_id = owncoin.note.token_id;
owncoins.push(vec![owncoin]);
minted_amounts.push(amount);
diff --git a/src/contract/test-harness/src/lib.rs b/src/contract/test-harness/src/lib.rs
index 281dfdb4f..f75126e66 100644
--- a/src/contract/test-harness/src/lib.rs
+++ b/src/contract/test-harness/src/lib.rs
@@ -33,13 +33,13 @@ use darkfi::{
use darkfi_dao_contract::model::{DaoBulla, DaoProposalBulla};
use darkfi_money_contract::{
client::{MoneyNote, OwnCoin},
- model::Output,
+ model::{Coin, Output},
};
use darkfi_sdk::{
bridgetree,
crypto::{
- pasta_prelude::Field, poseidon_hash, ContractId, Keypair, MerkleNode, MerkleTree,
- Nullifier, PublicKey, SecretKey, TokenId,
+ note::AeadEncryptedNote, pasta_prelude::Field, poseidon_hash, ContractId, Keypair,
+ MerkleNode, MerkleTree, Nullifier, PublicKey, SecretKey,
},
pasta::pallas,
};
@@ -115,6 +115,7 @@ pub enum TxAction {
pub struct Wallet {
pub keypair: Keypair,
pub token_mint_authority: Keypair,
+ pub token_blind: pallas::Base,
pub contract_deploy_authority: Keypair,
pub validator: ValidatorPtr,
pub money_merkle_tree: MerkleTree,
@@ -168,11 +169,13 @@ impl Wallet {
let spent_money_coins = vec![];
let token_mint_authority = Keypair::random(&mut OsRng);
+ let token_blind = pallas::Base::random(&mut OsRng);
let contract_deploy_authority = Keypair::random(&mut OsRng);
Ok(Self {
keypair,
token_mint_authority,
+ token_blind,
contract_deploy_authority,
validator,
money_merkle_tree,
@@ -288,7 +291,8 @@ impl TestHarness {
pub fn gather_owncoin(
&mut self,
holder: &Holder,
- output: &Output,
+ coin: &Coin,
+ note: &AeadEncryptedNote,
secret_key: Option,
) -> Result {
let wallet = self.holders.get_mut(holder).unwrap();
@@ -298,14 +302,14 @@ impl TestHarness {
None => wallet.keypair.secret,
};
- let note: MoneyNote = output.note.decrypt(&secret_key)?;
+ let note: MoneyNote = note.decrypt(&secret_key)?;
let oc = OwnCoin {
- coin: output.coin,
+ coin: coin.clone(),
note: note.clone(),
secret: secret_key,
nullifier: Nullifier::from(poseidon_hash([
wallet.keypair.secret.inner(),
- output.coin.inner(),
+ coin.inner(),
])),
leaf_position,
};
@@ -315,6 +319,15 @@ impl TestHarness {
Ok(oc)
}
+ pub fn gather_owncoin_from_output(
+ &mut self,
+ holder: &Holder,
+ output: &Output,
+ secret_key: Option,
+ ) -> Result {
+ self.gather_owncoin(holder, &output.coin, &output.note, secret_key)
+ }
+
/// This should be used after transfer call, so we can mark the merkle tree
/// before each output coin. Assumes using wallet secret key.
pub fn gather_multiple_owncoins(
@@ -397,11 +410,6 @@ impl TestHarness {
}
}
- pub fn token_id(&self, holder: &Holder) -> TokenId {
- let holder = self.holders.get(holder).unwrap();
- TokenId::derive_public(holder.token_mint_authority.public)
- }
-
pub fn contract_id(&self, holder: &Holder) -> ContractId {
let holder = self.holders.get(holder).unwrap();
ContractId::derive_public(holder.contract_deploy_authority.public)
diff --git a/src/contract/test-harness/src/money_airdrop.rs b/src/contract/test-harness/src/money_airdrop.rs
index eed5bbefa..f739f4d8f 100644
--- a/src/contract/test-harness/src/money_airdrop.rs
+++ b/src/contract/test-harness/src/money_airdrop.rs
@@ -147,7 +147,7 @@ impl TestHarness {
self.assert_trees(holders);
// Gather new owncoin
- let oc = self.gather_owncoin(holder, &airdrop_params.outputs[0], None)?;
+ let oc = self.gather_owncoin_from_output(holder, &airdrop_params.outputs[0], None)?;
Ok(oc)
}
diff --git a/src/contract/test-harness/src/money_genesis_mint.rs b/src/contract/test-harness/src/money_genesis_mint.rs
index 7ef9da494..f7d73bbd0 100644
--- a/src/contract/test-harness/src/money_genesis_mint.rs
+++ b/src/contract/test-harness/src/money_genesis_mint.rs
@@ -23,8 +23,8 @@ use darkfi::{
Result,
};
use darkfi_money_contract::{
- client::genesis_mint_v1::GenesisMintCallBuilder, model::MoneyTokenMintParamsV1, MoneyFunction,
- MONEY_CONTRACT_ZKAS_MINT_NS_V1,
+ client::genesis_mint_v1::GenesisMintCallBuilder, model::MoneyGenesisMintParamsV1,
+ MoneyFunction, MONEY_CONTRACT_ZKAS_MINT_NS_V1,
};
use darkfi_sdk::{
crypto::{MerkleNode, MONEY_CONTRACT_ID},
@@ -41,7 +41,7 @@ impl TestHarness {
&mut self,
holder: &Holder,
amount: u64,
- ) -> Result<(Transaction, MoneyTokenMintParamsV1)> {
+ ) -> Result<(Transaction, MoneyGenesisMintParamsV1)> {
let wallet = self.holders.get(holder).unwrap();
let (mint_pk, mint_zkbin) =
@@ -92,7 +92,7 @@ impl TestHarness {
&mut self,
holder: &Holder,
tx: &Transaction,
- params: &MoneyTokenMintParamsV1,
+ params: &MoneyGenesisMintParamsV1,
block_height: u64,
) -> Result<()> {
let wallet = self.holders.get_mut(holder).unwrap();
diff --git a/src/contract/test-harness/src/money_token.rs b/src/contract/test-harness/src/money_token.rs
index 9cbfb41de..881738cbe 100644
--- a/src/contract/test-harness/src/money_token.rs
+++ b/src/contract/test-harness/src/money_token.rs
@@ -24,12 +24,20 @@ use darkfi::{
Result,
};
use darkfi_money_contract::{
- client::{token_freeze_v1::TokenFreezeCallBuilder, token_mint_v1::TokenMintCallBuilder},
- model::{MoneyTokenFreezeParamsV1, MoneyTokenMintParamsV1},
- MoneyFunction, MONEY_CONTRACT_ZKAS_TOKEN_FRZ_NS_V1, MONEY_CONTRACT_ZKAS_TOKEN_MINT_NS_V1,
+ client::{
+ auth_token_mint_v1::AuthTokenMintCallBuilder, token_freeze_v1::TokenFreezeCallBuilder,
+ token_mint_v1::TokenMintCallBuilder,
+ },
+ model::{
+ CoinAttributes, MoneyAuthTokenMintParamsV1, MoneyTokenFreezeParamsV1,
+ MoneyTokenMintParamsV1, TokenAttributes,
+ },
+ MoneyFunction, MONEY_CONTRACT_ZKAS_AUTH_TOKEN_MINT_NS_V1, MONEY_CONTRACT_ZKAS_TOKEN_FRZ_NS_V1,
+ MONEY_CONTRACT_ZKAS_TOKEN_MINT_NS_V1,
};
use darkfi_sdk::{
- crypto::{MerkleNode, MONEY_CONTRACT_ID},
+ crypto::{poseidon_hash, FuncRef, MerkleNode, MONEY_CONTRACT_ID},
+ dark_tree::DarkLeaf,
pasta::pallas,
ContractCall,
};
@@ -46,40 +54,85 @@ impl TestHarness {
recipient: &Holder,
spend_hook: Option,
user_data: Option,
- ) -> Result<(Transaction, MoneyTokenMintParamsV1)> {
+ ) -> Result<(Transaction, MoneyTokenMintParamsV1, MoneyAuthTokenMintParamsV1)> {
let wallet = self.holders.get(holder).unwrap();
let mint_authority = wallet.token_mint_authority;
+ let token_blind = wallet.token_blind;
let rcpt = self.holders.get(recipient).unwrap().keypair.public;
- let (mint_pk, mint_zkbin) =
- self.proving_keys.get(&MONEY_CONTRACT_ZKAS_TOKEN_MINT_NS_V1.to_string()).unwrap();
+ let (mint_pk, mint_zkbin) = self
+ .proving_keys
+ .get(&MONEY_CONTRACT_ZKAS_TOKEN_MINT_NS_V1.to_string())
+ .unwrap()
+ .clone();
+ let (auth_mint_pk, auth_mint_zkbin) = self
+ .proving_keys
+ .get(&MONEY_CONTRACT_ZKAS_AUTH_TOKEN_MINT_NS_V1.to_string())
+ .unwrap()
+ .clone();
let tx_action_benchmark =
self.tx_action_benchmarks.get_mut(&TxAction::MoneyTokenMint).unwrap();
let timer = Instant::now();
- let builder = TokenMintCallBuilder {
- mint_authority,
- recipient: rcpt,
- amount,
+ let auth_func_id = FuncRef {
+ contract_id: *MONEY_CONTRACT_ID,
+ func_code: MoneyFunction::AuthTokenMintV1 as u8,
+ }
+ .to_func_id();
+
+ let token_attrs = TokenAttributes {
+ auth_parent: auth_func_id,
+ user_data: poseidon_hash([mint_authority.public.x(), mint_authority.public.y()]),
+ blind: token_blind,
+ };
+ let token_id = token_attrs.to_token_id();
+
+ let coin_attrs = CoinAttributes {
+ public_key: rcpt,
+ value: amount,
+ token_id,
spend_hook: spend_hook.unwrap_or(pallas::Base::ZERO),
user_data: user_data.unwrap_or(pallas::Base::ZERO),
- token_mint_zkbin: mint_zkbin.clone(),
- token_mint_pk: mint_pk.clone(),
+ blind: pallas::Base::random(&mut OsRng),
};
- let debris = builder.build()?;
-
+ let builder = TokenMintCallBuilder {
+ coin_attrs: coin_attrs.clone(),
+ token_attrs: token_attrs.clone(),
+ mint_zkbin,
+ mint_pk,
+ };
+ let mint_debris = builder.build()?;
let mut data = vec![MoneyFunction::TokenMintV1 as u8];
- debris.params.encode(&mut data)?;
- let call = ContractCall { contract_id: *MONEY_CONTRACT_ID, data };
- let mut tx_builder =
- TransactionBuilder::new(ContractCallLeaf { call, proofs: debris.proofs }, vec![])?;
- let mut tx = tx_builder.build()?;
- let sigs = tx.create_sigs(&mut OsRng, &[mint_authority.secret])?;
- tx.signatures = vec![sigs];
+ mint_debris.params.encode(&mut data)?;
+ let mint_call = ContractCall { contract_id: *MONEY_CONTRACT_ID, data };
+
+ let builder = AuthTokenMintCallBuilder {
+ coin_attrs,
+ token_attrs,
+ mint_keypair: mint_authority,
+ auth_mint_zkbin,
+ auth_mint_pk,
+ };
+ let auth_debris = builder.build()?;
+ let mut data = vec![MoneyFunction::AuthTokenMintV1 as u8];
+ auth_debris.params.encode(&mut data)?;
+ let auth_call = ContractCall { contract_id: *MONEY_CONTRACT_ID, data };
+
+ let mut tx = Transaction {
+ calls: vec![
+ DarkLeaf { data: mint_call, parent_index: Some(1), children_indexes: vec![] },
+ DarkLeaf { data: auth_call, parent_index: None, children_indexes: vec![0] },
+ ],
+ proofs: vec![mint_debris.proofs, auth_debris.proofs],
+ signatures: vec![],
+ };
+ let mint_sigs = tx.create_sigs(&mut OsRng, &[])?;
+ let auth_sigs = tx.create_sigs(&mut OsRng, &[mint_authority.secret])?;
+ tx.signatures = vec![mint_sigs, auth_sigs];
tx_action_benchmark.creation_times.push(timer.elapsed());
// Calculate transaction sizes
@@ -90,7 +143,7 @@ impl TestHarness {
let size = std::mem::size_of_val(&*base58);
tx_action_benchmark.broadcasted_sizes.push(size);
- Ok((tx, debris.params))
+ Ok((tx, mint_debris.params, auth_debris.params))
}
pub async fn execute_token_mint_tx(
@@ -106,7 +159,8 @@ impl TestHarness {
let timer = Instant::now();
wallet.validator.add_transactions(&[tx.clone()], block_height, true).await?;
- wallet.money_merkle_tree.append(MerkleNode::from(params.output.coin.inner()));
+ wallet.money_merkle_tree.append(MerkleNode::from(params.coin.inner()));
+
tx_action_benchmark.verify_times.push(timer.elapsed());
Ok(())
@@ -117,7 +171,8 @@ impl TestHarness {
holder: &Holder,
) -> Result<(Transaction, MoneyTokenFreezeParamsV1)> {
let wallet = self.holders.get(holder).unwrap();
- let mint_authority = wallet.token_mint_authority;
+ let mint_keypair = wallet.token_mint_authority;
+ let token_blind = wallet.token_blind;
let (frz_pk, frz_zkbin) =
self.proving_keys.get(&MONEY_CONTRACT_ZKAS_TOKEN_FRZ_NS_V1.to_string()).unwrap();
@@ -127,10 +182,23 @@ impl TestHarness {
let timer = Instant::now();
+ let auth_func_id = FuncRef {
+ contract_id: *MONEY_CONTRACT_ID,
+ func_code: MoneyFunction::AuthTokenMintV1 as u8,
+ }
+ .to_func_id();
+
+ let token_attrs = TokenAttributes {
+ auth_parent: auth_func_id,
+ user_data: poseidon_hash([mint_keypair.public.x(), mint_keypair.public.y()]),
+ blind: token_blind,
+ };
+
let builder = TokenFreezeCallBuilder {
- mint_authority,
- token_freeze_zkbin: frz_zkbin.clone(),
- token_freeze_pk: frz_pk.clone(),
+ mint_keypair,
+ token_attrs,
+ freeze_zkbin: frz_zkbin.clone(),
+ freeze_pk: frz_pk.clone(),
};
let debris = builder.build()?;
@@ -141,7 +209,7 @@ impl TestHarness {
let mut tx_builder =
TransactionBuilder::new(ContractCallLeaf { call, proofs: debris.proofs }, vec![])?;
let mut tx = tx_builder.build()?;
- let sigs = tx.create_sigs(&mut OsRng, &[mint_authority.secret])?;
+ let sigs = tx.create_sigs(&mut OsRng, &[mint_keypair.secret])?;
tx.signatures = vec![sigs];
tx_action_benchmark.creation_times.push(timer.elapsed());
diff --git a/src/contract/test-harness/src/vks.rs b/src/contract/test-harness/src/vks.rs
index 0b59bae9f..c6cf2294b 100644
--- a/src/contract/test-harness/src/vks.rs
+++ b/src/contract/test-harness/src/vks.rs
@@ -38,7 +38,8 @@ use darkfi_dao_contract::{
};
use darkfi_deployooor_contract::DEPLOY_CONTRACT_ZKAS_DERIVE_NS_V1;
use darkfi_money_contract::{
- MONEY_CONTRACT_ZKAS_BURN_NS_V1, MONEY_CONTRACT_ZKAS_FEE_NS_V1, MONEY_CONTRACT_ZKAS_MINT_NS_V1,
+ MONEY_CONTRACT_ZKAS_AUTH_TOKEN_MINT_NS_V1, MONEY_CONTRACT_ZKAS_BURN_NS_V1,
+ MONEY_CONTRACT_ZKAS_FEE_NS_V1, MONEY_CONTRACT_ZKAS_MINT_NS_V1,
MONEY_CONTRACT_ZKAS_TOKEN_FRZ_NS_V1, MONEY_CONTRACT_ZKAS_TOKEN_MINT_NS_V1,
};
use darkfi_sdk::crypto::{contract_id::DEPLOYOOOR_CONTRACT_ID, DAO_CONTRACT_ID, MONEY_CONTRACT_ID};
@@ -46,8 +47,8 @@ use darkfi_serial::{deserialize, serialize};
use log::debug;
/// Update this if any circuits are changed
-const VKS_HASH: &str = "8d491e5f127c14ddaa4eb9ac0de25fa3971c5ce7c794a62807c1c7283bcdaeae";
-const PKS_HASH: &str = "a9e4e440db9d467bbd61fb9ddc900c9bd155bbbd02f7c73e9012b558daf4af00";
+const VKS_HASH: &str = "e775e5a40a07a5048819bf137040c644a881624f3cf0785487f7a2253400d105";
+const PKS_HASH: &str = "a3307265fb6fbf8620080634a6ae10d82ceaf0394faeaad2a4bdf00c92bb8ade";
fn pks_path(typ: &str) -> Result {
let output = Command::new("git").arg("rev-parse").arg("--show-toplevel").output()?.stdout;
@@ -119,6 +120,7 @@ pub fn read_or_gen_vks_and_pks() -> Result<(Pks, Vks)> {
&include_bytes!("../../money/proof/burn_v1.zk.bin")[..],
&include_bytes!("../../money/proof/token_mint_v1.zk.bin")[..],
&include_bytes!("../../money/proof/token_freeze_v1.zk.bin")[..],
+ &include_bytes!("../../money/proof/auth_token_mint_v1.zk.bin")[..],
// DAO
&include_bytes!("../../dao/proof/dao-mint.zk.bin")[..],
&include_bytes!("../../dao/proof/dao-propose-input.zk.bin")[..],
@@ -187,7 +189,8 @@ pub fn inject(sled_db: &sled::Db, vks: &Vks) -> Result<()> {
MONEY_CONTRACT_ZKAS_MINT_NS_V1 |
MONEY_CONTRACT_ZKAS_BURN_NS_V1 |
MONEY_CONTRACT_ZKAS_TOKEN_MINT_NS_V1 |
- MONEY_CONTRACT_ZKAS_TOKEN_FRZ_NS_V1 => {
+ MONEY_CONTRACT_ZKAS_TOKEN_FRZ_NS_V1 |
+ MONEY_CONTRACT_ZKAS_AUTH_TOKEN_MINT_NS_V1 => {
let key = serialize(&namespace.as_str());
let value = serialize(&(bincode.clone(), vk.clone()));
money_zkas_tree.insert(key, value)?;
diff --git a/src/sdk/src/crypto/func_ref.rs b/src/sdk/src/crypto/func_ref.rs
new file mode 100644
index 000000000..8cbd59e46
--- /dev/null
+++ b/src/sdk/src/crypto/func_ref.rs
@@ -0,0 +1,48 @@
+/* This file is part of DarkFi (https://dark.fi)
+ *
+ * Copyright (C) 2020-2024 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 .
+ */
+#[cfg(feature = "async")]
+use darkfi_serial::async_trait;
+use darkfi_serial::{SerialDecodable, SerialEncodable};
+use pasta_curves::pallas;
+
+use super::{poseidon_hash, ContractId};
+
+pub type FunctionCode = u8;
+
+#[derive(Copy, Clone, Debug, Eq, PartialEq, SerialEncodable, SerialDecodable)]
+pub struct FuncRef {
+ pub contract_id: ContractId,
+ pub func_code: FunctionCode,
+}
+
+impl FuncRef {
+ pub fn to_func_id(&self) -> FuncId {
+ let func_id =
+ poseidon_hash([self.contract_id.inner(), pallas::Base::from(self.func_code as u64)]);
+ FuncId(func_id)
+ }
+}
+
+#[derive(Copy, Clone, Debug, Eq, PartialEq, SerialEncodable, SerialDecodable)]
+pub struct FuncId(pallas::Base);
+
+impl FuncId {
+ pub fn inner(&self) -> pallas::Base {
+ self.0
+ }
+}
diff --git a/src/sdk/src/crypto/mod.rs b/src/sdk/src/crypto/mod.rs
index a0c386864..ec6aa3ba5 100644
--- a/src/sdk/src/crypto/mod.rs
+++ b/src/sdk/src/crypto/mod.rs
@@ -34,6 +34,10 @@ pub use keypair::{Keypair, PublicKey, SecretKey};
pub mod contract_id;
pub use contract_id::{ContractId, DAO_CONTRACT_ID, DEPLOYOOOR_CONTRACT_ID, MONEY_CONTRACT_ID};
+/// Function ID definitions and methods
+pub mod func_ref;
+pub use func_ref::{FuncId, FuncRef};
+
/// Token ID definitions and methods
pub mod token_id;
pub use token_id::{TokenId, DARK_TOKEN_ID};
diff --git a/src/sdk/src/crypto/token_id.rs b/src/sdk/src/crypto/token_id.rs
index 5f95ad31d..b7dcccfea 100644
--- a/src/sdk/src/crypto/token_id.rs
+++ b/src/sdk/src/crypto/token_id.rs
@@ -45,6 +45,7 @@ pub struct TokenId(pallas::Base);
impl TokenId {
/// Derives a `TokenId` from a `SecretKey` (mint authority)
+ #[deprecated]
pub fn derive(mint_authority: SecretKey) -> Self {
let public_key = PublicKey::from_secret(mint_authority);
let (x, y) = public_key.xy();
@@ -53,12 +54,22 @@ impl TokenId {
}
/// Derives a `TokenId` from a `PublicKey`
+ #[deprecated]
pub fn derive_public(public_key: PublicKey) -> Self {
let (x, y) = public_key.xy();
let hash = poseidon_hash([*TOKEN_ID_PREFIX, x, y]);
Self(hash)
}
+ pub fn derive_from(
+ func_id: pallas::Base,
+ user_data: pallas::Base,
+ blind: pallas::Base,
+ ) -> Self {
+ let token_id = poseidon_hash([func_id, user_data, blind]);
+ Self(token_id)
+ }
+
/// Get the inner `pallas::Base` element.
pub fn inner(&self) -> pallas::Base {
self.0