From 1a540c67e0ab49692f211e24c7893cdebc02471e Mon Sep 17 00:00:00 2001 From: parazyd Date: Tue, 30 May 2023 09:31:00 +0200 Subject: [PATCH] Derive TokenID with a derivation prefix. --- bin/drk/src/wallet_money.rs | 7 +------ src/contract/money/proof/token_freeze_v1.zk | 10 ++++++---- src/contract/money/proof/token_mint_v1.zk | 10 ++++++---- .../money/src/client/token_freeze_v1.rs | 11 +++++----- .../money/src/client/token_mint_v1.rs | 18 ++++++++++++----- .../money/src/entrypoint/token_freeze_v1.rs | 20 +++++++++---------- .../money/src/entrypoint/token_mint_v1.rs | 20 +++++++++---------- src/sdk/src/crypto/token_id.rs | 16 ++++++++++++--- 8 files changed, 65 insertions(+), 47 deletions(-) diff --git a/bin/drk/src/wallet_money.rs b/bin/drk/src/wallet_money.rs index 90d6247d8..b9422ef37 100644 --- a/bin/drk/src/wallet_money.rs +++ b/bin/drk/src/wallet_money.rs @@ -521,19 +521,14 @@ impl Drk { if call.contract_id == cid && call.data[0] == MoneyFunction::TokenMintV1 as u8 { eprintln!("Found Money::MintV1 in call {}", i); let params: MoneyTokenMintParamsV1 = deserialize(&call.data[1..])?; - outputs.push(params.output); - continue } if call.contract_id == cid && call.data[0] == MoneyFunction::TokenFreezeV1 as u8 { eprintln!("Found Money::FreezeV1 in call {}", i); let params: MoneyTokenFreezeParamsV1 = deserialize(&call.data[1..])?; - - let (mint_x, mint_y) = params.signature_public.xy(); - let token_id = TokenId::from(poseidon_hash([mint_x, mint_y])); - + let token_id = TokenId::derive_public(params.signature_public); freezes.push(token_id); } } diff --git a/src/contract/money/proof/token_freeze_v1.zk b/src/contract/money/proof/token_freeze_v1.zk index bfa5910fb..e4c74aeaa 100644 --- a/src/contract/money/proof/token_freeze_v1.zk +++ b/src/contract/money/proof/token_freeze_v1.zk @@ -8,15 +8,17 @@ witness "TokenFreeze_V1" { } 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_x = ec_get_x(mint_public); mint_y = ec_get_y(mint_public); - # I don't think we need to enforce these two as public inputs - #constrain_instance(mint_x); - #constrain_instance(mint_y); + constrain_instance(mint_x); + constrain_instance(mint_y); # Derive the token ID - token_id = poseidon_hash(mint_x, mint_y); + token_id = poseidon_hash(derivation_path, mint_x, mint_y); 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 71214e497..93660279c 100644 --- a/src/contract/money/proof/token_mint_v1.zk +++ b/src/contract/money/proof/token_mint_v1.zk @@ -31,16 +31,18 @@ witness "TokenMint_V1" { circuit "TokenMint_V1" { # TODO: verify if supply must be > 0 and add corresponding opcode + # 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); - # I don't think we need to have these two as public inputs - #constrain_instance(mint_x); - #constrain_instance(mint_y); + constrain_instance(mint_x); + constrain_instance(mint_y); # Derive the token ID - token_id = poseidon_hash(mint_x, mint_y); + token_id = poseidon_hash(derivation_path, mint_x, mint_y); constrain_instance(token_id); # Poseidon hash of the minted coin diff --git a/src/contract/money/src/client/token_freeze_v1.rs b/src/contract/money/src/client/token_freeze_v1.rs index d5366b8ae..218811894 100644 --- a/src/contract/money/src/client/token_freeze_v1.rs +++ b/src/contract/money/src/client/token_freeze_v1.rs @@ -22,7 +22,7 @@ use darkfi::{ Result, }; use darkfi_sdk::{ - crypto::{poseidon_hash, Keypair, TokenId}, + crypto::{Keypair, PublicKey, TokenId}, pasta::pallas, }; use log::{debug, info}; @@ -36,12 +36,14 @@ pub struct TokenFreezeCallDebris { } pub struct TokenFreezeRevealed { + pub signature_public: PublicKey, pub token_id: TokenId, } impl TokenFreezeRevealed { pub fn to_vec(&self) -> Vec { - vec![self.token_id.inner()] + let (sig_x, sig_y) = self.signature_public.xy(); + vec![sig_x, sig_y, self.token_id.inner()] } } @@ -79,10 +81,9 @@ pub(crate) fn create_token_freeze_proof( pk: &ProvingKey, mint_authority: &Keypair, ) -> Result<(Proof, TokenFreezeRevealed)> { - let (mint_x, mint_y) = mint_authority.public.xy(); - let token_id = TokenId::from(poseidon_hash([mint_x, mint_y])); + let token_id = TokenId::derive(mint_authority.secret); - let public_inputs = TokenFreezeRevealed { token_id }; + let public_inputs = TokenFreezeRevealed { signature_public: mint_authority.public, token_id }; let prover_witnesses = vec![Witness::Base(Value::known(mint_authority.secret.inner()))]; diff --git a/src/contract/money/src/client/token_mint_v1.rs b/src/contract/money/src/client/token_mint_v1.rs index 363da32c9..d84dcfcc2 100644 --- a/src/contract/money/src/client/token_mint_v1.rs +++ b/src/contract/money/src/client/token_mint_v1.rs @@ -45,6 +45,7 @@ pub struct TokenMintCallDebris { } pub struct TokenMintRevealed { + pub signature_public: PublicKey, pub token_id: TokenId, pub coin: Coin, pub value_commit: pallas::Point, @@ -53,12 +54,15 @@ pub struct TokenMintRevealed { 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(); 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![ + sig_x, + sig_y, self.token_id.inner(), self.coin.inner(), *valcom_coords.x(), @@ -94,8 +98,7 @@ impl TokenMintCallBuilder { // 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 token_id = TokenId::derive(self.mint_authority.secret); let input = TransactionBuilderClearInputInfo { value: self.amount, @@ -181,8 +184,7 @@ pub fn create_token_mint_proof( 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 token_id = TokenId::derive(mint_authority.secret); let value_commit = pedersen_commitment_u64(output.value, value_blind); let token_commit = pedersen_commitment_base(token_id.inner(), token_blind); @@ -200,7 +202,13 @@ pub fn create_token_mint_proof( coin_blind, ])); - let public_inputs = TokenMintRevealed { token_id, coin, value_commit, token_commit }; + 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())), diff --git a/src/contract/money/src/entrypoint/token_freeze_v1.rs b/src/contract/money/src/entrypoint/token_freeze_v1.rs index 25d8d9791..eb471df58 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::{poseidon_hash, ContractId, PublicKey, TokenId}, + crypto::{ContractId, PublicKey, TokenId}, db::{db_contains_key, db_lookup, db_set}, error::{ContractError, ContractResult}, msg, @@ -46,11 +46,15 @@ pub(crate) fn money_token_freeze_get_metadata_v1( // Public keys for the transaction signatures we have to verify let signature_pubkeys: Vec = vec![params.signature_public]; - let (mint_x, mint_y) = params.signature_public.xy(); - let token_id = poseidon_hash([mint_x, mint_y]); + // 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); // 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![token_id])); + zk_public_inputs.push(( + MONEY_CONTRACT_ZKAS_TOKEN_FRZ_NS_V1.to_string(), + vec![sig_x, sig_y, token_id.inner()], + )); // Serialize everything gathered and return it let mut metadata = vec![]; @@ -72,8 +76,7 @@ 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 (mint_x, mint_y) = params.signature_public.xy(); - let token_id = TokenId::from(poseidon_hash([mint_x, mint_y])); + 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))? { @@ -96,10 +99,7 @@ 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 (mint_x, mint_y) = update.signature_public.xy(); - let token_id = TokenId::from(poseidon_hash([mint_x, mint_y])); - + 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), &[])?; diff --git a/src/contract/money/src/entrypoint/token_mint_v1.rs b/src/contract/money/src/entrypoint/token_mint_v1.rs index 38d68e492..3ef1b1971 100644 --- a/src/contract/money/src/entrypoint/token_mint_v1.rs +++ b/src/contract/money/src/entrypoint/token_mint_v1.rs @@ -18,8 +18,8 @@ use darkfi_sdk::{ crypto::{ - pasta_prelude::*, pedersen_commitment_base, pedersen_commitment_u64, poseidon_hash, - ContractId, MerkleNode, TokenId, + pasta_prelude::*, pedersen_commitment_base, pedersen_commitment_u64, ContractId, + MerkleNode, TokenId, }, db::{db_contains_key, db_lookup, db_set}, error::{ContractError, ContractResult}, @@ -54,18 +54,19 @@ pub(crate) fn money_token_mint_get_metadata_v1( // signed by the mint authority. let signature_pubkeys = vec![params.input.signature_public]; + // 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 token_coords = params.output.token_commit.to_affine().coordinates().unwrap(); - // Since we expect a signature from the mint authority, we use those coordinates - // as public inputs for the ZK proof: - let (sig_x, sig_y) = params.input.signature_public.xy(); - let token_id = poseidon_hash([sig_x, sig_y]); - zk_public_inputs.push(( MONEY_CONTRACT_ZKAS_TOKEN_MINT_NS_V1.to_string(), vec![ - token_id, + sig_x, + sig_y, + token_id.inner(), params.output.coin.inner(), *value_coords.x(), *value_coords.y(), @@ -97,8 +98,7 @@ pub(crate) fn money_token_mint_process_instruction_v1( let token_freeze_db = db_lookup(cid, MONEY_CONTRACT_TOKEN_FREEZE_TREE)?; // Check that the signature public key is actually the token ID - let (mint_x, mint_y) = params.input.signature_public.xy(); - let token_id = TokenId::from(poseidon_hash([mint_x, mint_y])); + 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()) diff --git a/src/sdk/src/crypto/token_id.rs b/src/sdk/src/crypto/token_id.rs index 6daf766cd..d9cb9daf0 100644 --- a/src/sdk/src/crypto/token_id.rs +++ b/src/sdk/src/crypto/token_id.rs @@ -29,9 +29,12 @@ lazy_static! { // avoid hardcoding contract IDs for arbitrary contract deployments, because // the contracts with 0 as their x coordinate can never have a valid signature. + /// Derivation prefix for `TokenId` + pub static ref TOKEN_ID_PREFIX: pallas::Base = pallas::Base::from(69); + /// Native DARK token ID pub static ref DARK_TOKEN_ID: TokenId = - TokenId::from(poseidon_hash([pallas::Base::zero(), pallas::Base::from(42)])); + TokenId::from(poseidon_hash([*TOKEN_ID_PREFIX, pallas::Base::zero(), pallas::Base::from(42)])); } /// TokenId represents an on-chain identifier for a certain token. @@ -39,11 +42,18 @@ lazy_static! { pub struct TokenId(pallas::Base); impl TokenId { - /// Derives a `TokenId` given a `SecretKey` (mint authority) + /// Derives a `TokenId` from a `SecretKey` (mint authority) pub fn derive(mint_authority: SecretKey) -> Self { let public_key = PublicKey::from_secret(mint_authority); let (x, y) = public_key.xy(); - let hash = poseidon_hash::<2>([x, y]); + let hash = poseidon_hash([*TOKEN_ID_PREFIX, x, y]); + Self(hash) + } + + /// Derives a `TokenId` from a `PublicKey` + 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) }