Jelajahi Sumber

contract/money: Add stub for minting arbitrary tokens.

parazyd 3 tahun lalu
induk
melakukan
607dde60d5

+ 2 - 2
src/contract/money/proof/mint_v1.zk

@@ -21,9 +21,9 @@ contract "Mint_V1" {
 	Base spend_hook,
 	# Data passed from this coin to the invoked contract
 	Base user_data,
-	# Random blinding factor for value commitment
+	# Random blinding factor for the value commitment
 	Scalar value_blind,
-	# Random blinding facfor for the token ID
+	# Random blinding factor for the token ID
 	Scalar token_blind,
 }
 

+ 74 - 0
src/contract/money/proof/token_mint_v1.zk

@@ -0,0 +1,74 @@
+constant "TokenMint_V1" {
+	EcFixedPointShort VALUE_COMMIT_VALUE,
+	EcFixedPoint VALUE_COMMIT_RANDOM,
+	EcFixedPointBase NULLIFIER_K,
+}
+
+contract "TokenMint_V1" {
+	# Token mint authority secret
+	Base mint_authority,
+	# Token supply
+	Base supply,
+	# Fixed supply
+	Base fixed_supply,
+	# Recipient's public key x coordinate
+	Base rcpt_x,
+	# Recipient's public key y coordinate
+	Base rcpt_y,
+	# Unique serial number corresponding to this coin
+	Base serial,
+	# Random blinding factor for coin
+	Base coin_blind,
+	# Allows composing this ZK proof to invoke other contracts
+	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
+	Scalar token_blind,
+}
+
+circuit "TokenMint_V1" {
+	# 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(mint_x, mint_y);
+	constrain_instance(token_id);
+
+	# Constrain whether this token has a fixed supply or not.
+	# In case it is, subsequent mints will not be allowed.
+	bool_check(fixed_supply);
+	constrain_instance(fixed_supply);
+
+	# Poseidon hash of the coin
+	C = poseidon_hash(
+		rcpt_x,
+		rcpt_y,
+		supply,
+		token_id,
+		spend_hook,
+		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));
+
+	# Pedersen commitment for the token ID
+	tcv = ec_mul_base(token_id, NULLIFIER_K);
+	tcr = ec_mul(token_blind, VALUE_COMMIT_RANDOM);
+	token_commit = ec_add(tcv, tcr);
+	constrain_instance(ec_get_x(token_commit));
+	constrain_instance(ec_get_y(token_commit));
+}

+ 20 - 0
src/contract/money/src/lib.rs

@@ -47,6 +47,7 @@ pub enum MoneyFunction {
     OtcSwap = 0x01,
     Stake = 0x02,
     Unstake = 0x03,
+    Mint = 0x04,
 }
 
 impl TryFrom<u8> for MoneyFunction {
@@ -58,6 +59,7 @@ impl TryFrom<u8> for MoneyFunction {
             0x01 => Ok(Self::OtcSwap),
             0x02 => Ok(Self::Stake),
             0x03 => Ok(Self::Unstake),
+            0x04 => Ok(Self::Mint),
             _ => Err(ContractError::InvalidFunction),
         }
     }
@@ -84,6 +86,7 @@ darkfi_sdk::define_contract!(
 // These are the different sled trees that will be created
 pub const MONEY_CONTRACT_COIN_ROOTS_TREE: &str = "coin_roots";
 pub const MONEY_CONTRACT_NULLIFIERS_TREE: &str = "nullifiers";
+pub const MONEY_CONTRACT_FIXED_SUPPLY_TREE: &str = "fixed_supply_tokens";
 pub const MONEY_CONTRACT_INFO_TREE: &str = "info";
 
 // This is a key inside the info tree
@@ -94,6 +97,8 @@ pub const MONEY_CONTRACT_FAUCET_PUBKEYS: &str = "faucet_pubkeys";
 pub const MONEY_CONTRACT_ZKAS_MINT_NS_V1: &str = "Mint_V1";
 /// zkas burn contract namespace
 pub const MONEY_CONTRACT_ZKAS_BURN_NS_V1: &str = "Burn_V1";
+/// zkas token mint contract namespace
+pub const MONEY_CONTRACT_ZKAS_TOKEN_MINT_NS_V1: &str = "TokenMint_V1";
 
 /// This function runs when the contract is (re)deployed and initialized.
 #[cfg(not(feature = "no-entrypoint"))]
@@ -111,6 +116,7 @@ fn init_contract(cid: ContractId, ix: &[u8]) -> ContractResult {
     };
     let mint_v1_bincode = include_bytes!("../proof/mint_v1.zk.bin");
     let burn_v1_bincode = include_bytes!("../proof/burn_v1.zk.bin");
+    let token_mint_v1_bincode = include_bytes!("../proof/token_mint_v1.zk.bin");
 
     /* TODO: Do I really want to make zkas a dependency? Yeah, in the future.
        For now we take anything.
@@ -125,6 +131,7 @@ fn init_contract(cid: ContractId, ix: &[u8]) -> ContractResult {
     */
     db_set(zkas_db, &serialize(&MONEY_CONTRACT_ZKAS_MINT_NS_V1), &mint_v1_bincode[..])?;
     db_set(zkas_db, &serialize(&MONEY_CONTRACT_ZKAS_BURN_NS_V1), &burn_v1_bincode[..])?;
+    db_set(zkas_db, &serialize(&MONEY_CONTRACT_ZKAS_TOKEN_MINT_NS_V1), &token_mint_v1_bincode[..])?;
 
     // Set up a database tree to hold Merkle roots
     let _ = match db_lookup(cid, MONEY_CONTRACT_COIN_ROOTS_TREE) {
@@ -138,6 +145,12 @@ fn init_contract(cid: ContractId, ix: &[u8]) -> ContractResult {
         Err(_) => db_init(cid, MONEY_CONTRACT_NULLIFIERS_TREE)?,
     };
 
+    // Set up a database tree to hold the set of fixed-supply tokens
+    let _ = match db_lookup(cid, MONEY_CONTRACT_FIXED_SUPPLY_TREE) {
+        Ok(v) => v,
+        Err(_) => db_init(cid, MONEY_CONTRACT_FIXED_SUPPLY_TREE)?,
+    };
+
     // Set up a database tree for arbitrary data
     let info_db = match db_lookup(cid, MONEY_CONTRACT_INFO_TREE) {
         Ok(v) => v,
@@ -231,6 +244,7 @@ fn get_metadata(_cid: ContractId, ix: &[u8]) -> ContractResult {
 
         MoneyFunction::Stake => unimplemented!(),
         MoneyFunction::Unstake => unimplemented!(),
+        MoneyFunction::Mint => unimplemented!(),
     };
 
     Ok(())
@@ -444,6 +458,11 @@ fn process_instruction(cid: ContractId, ix: &[u8]) -> ContractResult {
             msg!("[Unstake] Entered match arm");
             unimplemented!();
         }
+
+        MoneyFunction::Mint => {
+            msg!("[Mint] Entered match arm");
+            unimplemented!();
+        }
     }
 }
 
@@ -475,5 +494,6 @@ fn process_update(cid: ContractId, update_data: &[u8]) -> ContractResult {
 
         MoneyFunction::Stake => unimplemented!(),
         MoneyFunction::Unstake => unimplemented!(),
+        MoneyFunction::Mint => unimplemented!(),
     }
 }

+ 12 - 0
src/sdk/src/crypto/token_id.rs

@@ -17,11 +17,23 @@
  */
 
 use darkfi_serial::{SerialDecodable, SerialEncodable};
+use lazy_static::lazy_static;
 use pasta_curves::{group::ff::PrimeField, pallas};
 
 use super::{poseidon_hash, PublicKey, SecretKey};
 use crate::error::ContractError;
 
+lazy_static! {
+    // The idea here is that 0 is not a valid x coordinate for any pallas point,
+    // therefore a signature cannot be produced for such IDs. This allows us to
+    // avoid hardcoding contract IDs for arbitrary contract deployments, because
+    // the contracts with 0 as their x coordinate can never have a valid signature.
+
+    /// Native DARK token ID
+    pub static ref DARK_TOKEN_ID: TokenId =
+        TokenId::from(poseidon_hash([pallas::Base::zero(), pallas::Base::from(42)]));
+}
+
 /// TokenId represents an on-chain identifier for a certain token.
 #[derive(Copy, Clone, Debug, Eq, PartialEq, SerialEncodable, SerialDecodable)]
 pub struct TokenId(pallas::Base);