use darkfi::{
tx::{ContractCallLeaf, Transaction, TransactionBuilder},
zk::halo2::Field,
Result,
};
use darkfi_money_contract::{
client::{
auth_token_freeze_v1::AuthTokenFreezeCallBuilder,
auth_token_mint_v1::AuthTokenMintCallBuilder, token_mint_v1::TokenMintCallBuilder,
MoneyNote, OwnCoin,
},
model::{
CoinAttributes, MoneyAuthTokenFreezeParamsV1, MoneyAuthTokenMintParamsV1, MoneyFeeParamsV1,
MoneyTokenMintParamsV1, TokenAttributes,
},
MoneyFunction, MONEY_CONTRACT_ZKAS_AUTH_TOKEN_MINT_NS_V1, MONEY_CONTRACT_ZKAS_TOKEN_MINT_NS_V1,
};
use darkfi_sdk::{
crypto::{poseidon_hash, BaseBlind, Blind, FuncId, FuncRef, MerkleNode, MONEY_CONTRACT_ID},
dark_tree::DarkTree,
pasta::pallas,
ContractCall,
};
use darkfi_serial::AsyncEncodable;
use log::debug;
use rand::rngs::OsRng;
use super::{Holder, TestHarness};
impl TestHarness {
#[allow(clippy::too_many_arguments)]
pub async fn token_mint(
&mut self,
amount: u64,
holder: &Holder,
recipient: &Holder,
token_blind: BaseBlind,
spend_hook: Option<FuncId>,
user_data: Option<pallas::Base>,
block_height: u32,
) -> Result<(
Transaction,
MoneyTokenMintParamsV1,
MoneyAuthTokenMintParamsV1,
Option<MoneyFeeParamsV1>,
)> {
let wallet = self.holders.get(holder).unwrap();
let mint_authority = wallet.token_mint_authority;
let rcpt = self.holders.get(recipient).unwrap().keypair.public;
let (token_mint_pk, token_mint_zkbin) =
self.proving_keys.get(MONEY_CONTRACT_ZKAS_TOKEN_MINT_NS_V1).unwrap();
let (auth_mint_pk, auth_mint_zkbin) =
self.proving_keys.get(MONEY_CONTRACT_ZKAS_AUTH_TOKEN_MINT_NS_V1).unwrap();
let auth_func_id = FuncRef {
contract_id: *MONEY_CONTRACT_ID,
func_code: MoneyFunction::AuthTokenMintV1 as u8,
}
.to_func_id();
let (mint_auth_x, mint_auth_y) = mint_authority.public.xy();
let token_attrs = TokenAttributes {
auth_parent: auth_func_id,
user_data: poseidon_hash([mint_auth_x, mint_auth_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(FuncId::none()),
user_data: user_data.unwrap_or(pallas::Base::ZERO),
blind: Blind::random(&mut OsRng),
};
let builder = AuthTokenMintCallBuilder {
coin_attrs: coin_attrs.clone(),
token_attrs: token_attrs.clone(),
mint_keypair: mint_authority,
auth_mint_zkbin: auth_mint_zkbin.clone(),
auth_mint_pk: auth_mint_pk.clone(),
};
let auth_debris = builder.build()?;
let mut data = vec![MoneyFunction::AuthTokenMintV1 as u8];
auth_debris.params.encode_async(&mut data).await?;
let auth_call = ContractCall { contract_id: *MONEY_CONTRACT_ID, data };
let builder = TokenMintCallBuilder {
coin_attrs,
token_attrs,
mint_zkbin: token_mint_zkbin.clone(),
mint_pk: token_mint_pk.clone(),
};
let mint_debris = builder.build()?;
let mut data = vec![MoneyFunction::TokenMintV1 as u8];
mint_debris.params.encode_async(&mut data).await?;
let mint_call = ContractCall { contract_id: *MONEY_CONTRACT_ID, data };
let mut tx_builder = TransactionBuilder::new(
ContractCallLeaf { call: mint_call, proofs: mint_debris.proofs },
vec![DarkTree::new(
ContractCallLeaf { call: auth_call, proofs: auth_debris.proofs },
vec![],
None,
None,
)],
)?;
let mut fee_params = None;
let mut fee_signature_secrets = None;
if self.verify_fees {
let mut tx = tx_builder.build()?;
let auth_sigs = tx.create_sigs(&[mint_authority.secret])?;
let mint_sigs = tx.create_sigs(&[])?;
tx.signatures = vec![auth_sigs, mint_sigs];
let (fee_call, fee_proofs, fee_secrets, _spent_fee_coins, fee_call_params) =
self.append_fee_call(holder, tx, block_height, &[]).await?;
tx_builder.append(ContractCallLeaf { call: fee_call, proofs: fee_proofs }, vec![])?;
fee_signature_secrets = Some(fee_secrets);
fee_params = Some(fee_call_params);
}
let mut tx = tx_builder.build()?;
let auth_sigs = tx.create_sigs(&[mint_authority.secret])?;
let mint_sigs = tx.create_sigs(&[])?;
tx.signatures = vec![auth_sigs, mint_sigs];
if let Some(fee_signature_secrets) = fee_signature_secrets {
let sigs = tx.create_sigs(&fee_signature_secrets)?;
tx.signatures.push(sigs);
}
Ok((tx, mint_debris.params, auth_debris.params, fee_params))
}
#[allow(clippy::too_many_arguments)]
pub async fn execute_token_mint_tx(
&mut self,
holder: &Holder,
tx: Transaction,
mint_params: &MoneyTokenMintParamsV1,
auth_params: &MoneyAuthTokenMintParamsV1,
fee_params: &Option<MoneyFeeParamsV1>,
block_height: u32,
append: bool,
) -> Result<Vec<OwnCoin>> {
let wallet = self.holders.get_mut(holder).unwrap();
wallet.add_transaction("money::token_mint", tx, block_height).await?;
if let Some(ref fee_params) = fee_params {
if append {
if let Some(spent_coin) = wallet
.unspent_money_coins
.iter()
.find(|x| x.nullifier() == fee_params.input.nullifier)
.cloned()
{
debug!("Found spent OwnCoin({}) for {:?}", spent_coin.coin, holder);
wallet
.unspent_money_coins
.retain(|x| x.nullifier() != fee_params.input.nullifier);
wallet.spent_money_coins.push(spent_coin.clone());
}
}
}
let mut found_owncoins = vec![];
if append {
wallet.money_merkle_tree.append(MerkleNode::from(mint_params.coin.inner()));
if let Ok(note) = auth_params.enc_note.decrypt::<MoneyNote>(&wallet.keypair.secret) {
let owncoin = OwnCoin {
coin: mint_params.coin,
note: note.clone(),
secret: wallet.keypair.secret,
leaf_position: wallet.money_merkle_tree.mark().unwrap(),
};
debug!("Found new OwnCoin({}) for {:?}", owncoin.coin, holder);
wallet.unspent_money_coins.push(owncoin.clone());
found_owncoins.push(owncoin);
};
if let Some(ref fee_params) = fee_params {
wallet.money_merkle_tree.append(MerkleNode::from(fee_params.output.coin.inner()));
if let Ok(note) =
fee_params.output.note.decrypt::<MoneyNote>(&wallet.keypair.secret)
{
let owncoin = OwnCoin {
coin: fee_params.output.coin,
note: note.clone(),
secret: wallet.keypair.secret,
leaf_position: wallet.money_merkle_tree.mark().unwrap(),
};
debug!("Found new OwnCoin({}) for {:?}", owncoin.coin, holder);
wallet.unspent_money_coins.push(owncoin.clone());
found_owncoins.push(owncoin);
}
}
}
Ok(found_owncoins)
}
pub async fn token_freeze(
&mut self,
holder: &Holder,
block_height: u32,
) -> Result<(Transaction, MoneyAuthTokenFreezeParamsV1, Option<MoneyFeeParamsV1>)> {
let wallet = self.holders.get(holder).unwrap();
let mint_authority = wallet.token_mint_authority;
let (auth_mint_pk, auth_mint_zkbin) =
self.proving_keys.get(MONEY_CONTRACT_ZKAS_AUTH_TOKEN_MINT_NS_V1).unwrap();
let auth_func_id = FuncRef {
contract_id: *MONEY_CONTRACT_ID,
func_code: MoneyFunction::AuthTokenMintV1 as u8,
}
.to_func_id();
let (mint_auth_x, mint_auth_y) = mint_authority.public.xy();
let token_blind = BaseBlind::random(&mut OsRng);
let token_attrs = TokenAttributes {
auth_parent: auth_func_id,
user_data: poseidon_hash([mint_auth_x, mint_auth_y]),
blind: token_blind,
};
let builder = AuthTokenFreezeCallBuilder {
mint_keypair: mint_authority,
token_attrs,
auth_mint_pk: auth_mint_pk.clone(),
auth_mint_zkbin: auth_mint_zkbin.clone(),
};
let freeze_debris = builder.build()?;
let mut data = vec![MoneyFunction::AuthTokenFreezeV1 as u8];
freeze_debris.params.encode_async(&mut data).await?;
let freeze_call = ContractCall { contract_id: *MONEY_CONTRACT_ID, data };
let mut tx_builder = TransactionBuilder::new(
ContractCallLeaf { call: freeze_call, proofs: freeze_debris.proofs },
vec![],
)?;
let mut fee_params = None;
let mut fee_signature_secrets = None;
if self.verify_fees {
let mut tx = tx_builder.build()?;
let freeze_sigs = tx.create_sigs(&[mint_authority.secret])?;
tx.signatures = vec![freeze_sigs];
let (fee_call, fee_proofs, fee_secrets, _spent_fee_coins, fee_call_params) =
self.append_fee_call(holder, tx, block_height, &[]).await?;
tx_builder.append(ContractCallLeaf { call: fee_call, proofs: fee_proofs }, vec![])?;
fee_signature_secrets = Some(fee_secrets);
fee_params = Some(fee_call_params);
}
let mut tx = tx_builder.build()?;
let freeze_sigs = tx.create_sigs(&[mint_authority.secret])?;
tx.signatures = vec![freeze_sigs];
if let Some(fee_signature_secrets) = fee_signature_secrets {
let sigs = tx.create_sigs(&fee_signature_secrets)?;
tx.signatures.push(sigs);
}
Ok((tx, freeze_debris.params, fee_params))
}
pub async fn execute_token_freeze_tx(
&mut self,
holder: &Holder,
tx: Transaction,
_freeze_params: &MoneyAuthTokenFreezeParamsV1,
fee_params: &Option<MoneyFeeParamsV1>,
block_height: u32,
append: bool,
) -> Result<Vec<OwnCoin>> {
let wallet = self.holders.get_mut(holder).unwrap();
wallet.add_transaction("money::token_freeze", tx, block_height).await?;
let mut found_owncoins = vec![];
if let Some(ref fee_params) = fee_params {
if append {
let nullifier = fee_params.input.nullifier.inner();
wallet
.money_null_smt
.insert_batch(vec![(nullifier, nullifier)])
.expect("smt.insert_batch()");
if let Some(spent_coin) = wallet
.unspent_money_coins
.iter()
.find(|x| x.nullifier() == fee_params.input.nullifier)
.cloned()
{
debug!("Found spent OwnCoin({}) for {:?}", spent_coin.coin, holder);
wallet
.unspent_money_coins
.retain(|x| x.nullifier() != fee_params.input.nullifier);
wallet.spent_money_coins.push(spent_coin.clone());
}
wallet.money_merkle_tree.append(MerkleNode::from(fee_params.output.coin.inner()));
if let Ok(note) =
fee_params.output.note.decrypt::<MoneyNote>(&wallet.keypair.secret)
{
let owncoin = OwnCoin {
coin: fee_params.output.coin,
note: note.clone(),
secret: wallet.keypair.secret,
leaf_position: wallet.money_merkle_tree.mark().unwrap(),
};
debug!("Found new OwnCoin({}) for {:?}", owncoin.coin, holder);
wallet.unspent_money_coins.push(owncoin.clone());
found_owncoins.push(owncoin);
}
}
}
Ok(found_owncoins)
}
}