mirror of
https://github.com/darkrenaissance/darkfi.git
synced 2026-04-28 03:00:18 -04:00
contract/test-harness: Deduplicate Nullifier and Coin handling code
This commit is contained in:
@@ -21,17 +21,11 @@ use darkfi::{
|
||||
Result,
|
||||
};
|
||||
use darkfi_deployooor_contract::{client::deploy_v1::DeployCallBuilder, DeployFunction};
|
||||
use darkfi_money_contract::{
|
||||
client::{MoneyNote, OwnCoin},
|
||||
model::MoneyFeeParamsV1,
|
||||
};
|
||||
use darkfi_money_contract::{client::OwnCoin, model::MoneyFeeParamsV1};
|
||||
use darkfi_sdk::{
|
||||
crypto::{contract_id::DEPLOYOOOR_CONTRACT_ID, MerkleNode},
|
||||
deploy::DeployParamsV1,
|
||||
ContractCall,
|
||||
crypto::contract_id::DEPLOYOOOR_CONTRACT_ID, deploy::DeployParamsV1, ContractCall,
|
||||
};
|
||||
use darkfi_serial::AsyncEncodable;
|
||||
use tracing::debug;
|
||||
use darkfi_serial::Encodable;
|
||||
|
||||
use super::{Holder, TestHarness};
|
||||
|
||||
@@ -45,7 +39,7 @@ impl TestHarness {
|
||||
wasm_bincode: Vec<u8>,
|
||||
block_height: u32,
|
||||
) -> Result<(Transaction, DeployParamsV1, Option<MoneyFeeParamsV1>)> {
|
||||
let wallet = self.holders.get(holder).unwrap();
|
||||
let wallet = self.wallet(holder);
|
||||
let deploy_keypair = wallet.contract_deploy_authority;
|
||||
|
||||
// Build the contract call
|
||||
@@ -54,7 +48,7 @@ impl TestHarness {
|
||||
|
||||
// Encode the call
|
||||
let mut data = vec![DeployFunction::DeployV1 as u8];
|
||||
debris.params.encode_async(&mut data).await?;
|
||||
debris.params.encode(&mut data)?;
|
||||
let call = ContractCall { contract_id: *DEPLOYOOOR_CONTRACT_ID, data };
|
||||
let mut tx_builder =
|
||||
TransactionBuilder::new(ContractCallLeaf { call, proofs: vec![] }, vec![])?;
|
||||
@@ -102,50 +96,12 @@ impl TestHarness {
|
||||
) -> Result<Vec<OwnCoin>> {
|
||||
let wallet = self.holders.get_mut(holder).unwrap();
|
||||
|
||||
// Execute the transaction
|
||||
wallet.add_transaction("deploy::deploy", tx, block_height).await?;
|
||||
|
||||
if !append {
|
||||
return Ok(vec![])
|
||||
return Ok(vec![]);
|
||||
}
|
||||
|
||||
if let Some(ref fee_params) = fee_params {
|
||||
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()));
|
||||
|
||||
let Ok(note) = fee_params.output.note.decrypt::<MoneyNote>(&wallet.keypair.secret)
|
||||
else {
|
||||
return Ok(vec![])
|
||||
};
|
||||
|
||||
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());
|
||||
return Ok(vec![owncoin])
|
||||
}
|
||||
|
||||
Ok(vec![])
|
||||
Ok(wallet.process_fee(fee_params, holder))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,17 +23,9 @@ use darkfi::{
|
||||
use darkfi_deployooor_contract::{
|
||||
client::lock_v1::LockCallBuilder, model::LockParamsV1, DeployFunction,
|
||||
};
|
||||
use darkfi_money_contract::{
|
||||
client::{MoneyNote, OwnCoin},
|
||||
model::MoneyFeeParamsV1,
|
||||
};
|
||||
use darkfi_sdk::{
|
||||
crypto::{contract_id::DEPLOYOOOR_CONTRACT_ID, MerkleNode},
|
||||
//deploy::LockParamsV1,
|
||||
ContractCall,
|
||||
};
|
||||
use darkfi_serial::AsyncEncodable;
|
||||
use tracing::debug;
|
||||
use darkfi_money_contract::{client::OwnCoin, model::MoneyFeeParamsV1};
|
||||
use darkfi_sdk::{crypto::contract_id::DEPLOYOOOR_CONTRACT_ID, ContractCall};
|
||||
use darkfi_serial::Encodable;
|
||||
|
||||
use super::{Holder, TestHarness};
|
||||
|
||||
@@ -46,7 +38,7 @@ impl TestHarness {
|
||||
holder: &Holder,
|
||||
block_height: u32,
|
||||
) -> Result<(Transaction, LockParamsV1, Option<MoneyFeeParamsV1>)> {
|
||||
let wallet = self.holders.get(holder).unwrap();
|
||||
let wallet = self.wallet(holder);
|
||||
let deploy_keypair = wallet.contract_deploy_authority;
|
||||
|
||||
// Build the contract call
|
||||
@@ -55,7 +47,7 @@ impl TestHarness {
|
||||
|
||||
// Encode the call
|
||||
let mut data = vec![DeployFunction::LockV1 as u8];
|
||||
debris.params.encode_async(&mut data).await?;
|
||||
debris.params.encode(&mut data)?;
|
||||
let call = ContractCall { contract_id: *DEPLOYOOOR_CONTRACT_ID, data };
|
||||
let mut tx_builder =
|
||||
TransactionBuilder::new(ContractCallLeaf { call, proofs: vec![] }, vec![])?;
|
||||
@@ -101,52 +93,14 @@ impl TestHarness {
|
||||
block_height: u32,
|
||||
append: bool,
|
||||
) -> Result<Vec<OwnCoin>> {
|
||||
let wallet = self.holders.get_mut(holder).unwrap();
|
||||
let wallet = self.wallet_mut(holder);
|
||||
|
||||
// Execute the transaction
|
||||
wallet.add_transaction("deploy::lock", tx, block_height).await?;
|
||||
|
||||
if !append {
|
||||
return Ok(vec![])
|
||||
return Ok(vec![]);
|
||||
}
|
||||
|
||||
if let Some(ref fee_params) = fee_params {
|
||||
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()));
|
||||
|
||||
let Ok(note) = fee_params.output.note.decrypt::<MoneyNote>(&wallet.keypair.secret)
|
||||
else {
|
||||
return Ok(vec![])
|
||||
};
|
||||
|
||||
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());
|
||||
return Ok(vec![owncoin])
|
||||
}
|
||||
|
||||
Ok(vec![])
|
||||
Ok(wallet.process_fee(fee_params, holder))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,21 +29,20 @@ use darkfi_dao_contract::{
|
||||
DAO_CONTRACT_ZKAS_EXEC_NS,
|
||||
};
|
||||
use darkfi_money_contract::{
|
||||
client::{transfer_v1 as xfer, MoneyNote, OwnCoin},
|
||||
client::{transfer_v1 as xfer, OwnCoin},
|
||||
model::{CoinAttributes, MoneyFeeParamsV1, MoneyTransferParamsV1},
|
||||
MoneyFunction, MONEY_CONTRACT_ZKAS_BURN_NS_V1, MONEY_CONTRACT_ZKAS_MINT_NS_V1,
|
||||
};
|
||||
use darkfi_sdk::{
|
||||
crypto::{
|
||||
contract_id::{DAO_CONTRACT_ID, MONEY_CONTRACT_ID},
|
||||
pedersen_commitment_u64, Blind, FuncRef, MerkleNode, ScalarBlind, SecretKey,
|
||||
pedersen_commitment_u64, Blind, FuncRef, ScalarBlind, SecretKey,
|
||||
},
|
||||
dark_tree::DarkTree,
|
||||
ContractCall,
|
||||
};
|
||||
use darkfi_serial::AsyncEncodable;
|
||||
use darkfi_serial::Encodable;
|
||||
use rand::rngs::OsRng;
|
||||
use tracing::debug;
|
||||
|
||||
use super::{Holder, TestHarness};
|
||||
|
||||
@@ -64,7 +63,7 @@ impl TestHarness {
|
||||
all_vote_blind: ScalarBlind,
|
||||
block_height: u32,
|
||||
) -> Result<(Transaction, MoneyTransferParamsV1, Option<MoneyFeeParamsV1>)> {
|
||||
let dao_wallet = self.holders.get(&Holder::Dao).unwrap();
|
||||
let dao_wallet = self.wallet(&Holder::Dao);
|
||||
|
||||
let (mint_pk, mint_zkbin) = self.proving_keys.get(MONEY_CONTRACT_ZKAS_MINT_NS_V1).unwrap();
|
||||
let (burn_pk, burn_zkbin) = self.proving_keys.get(MONEY_CONTRACT_ZKAS_BURN_NS_V1).unwrap();
|
||||
@@ -136,7 +135,7 @@ impl TestHarness {
|
||||
|
||||
let (xfer_params, xfer_secrets) = xfer_builder.build()?;
|
||||
let mut data = vec![MoneyFunction::TransferV1 as u8];
|
||||
xfer_params.encode_async(&mut data).await?;
|
||||
xfer_params.encode(&mut data)?;
|
||||
let xfer_call = ContractCall { contract_id: *MONEY_CONTRACT_ID, data };
|
||||
|
||||
// We need to extract stuff from the inputs and outputs that we'll also
|
||||
@@ -172,7 +171,7 @@ impl TestHarness {
|
||||
dao_exec_pk,
|
||||
)?;
|
||||
let mut data = vec![DaoFunction::Exec as u8];
|
||||
exec_params.encode_async(&mut data).await?;
|
||||
exec_params.encode(&mut data)?;
|
||||
let exec_call = ContractCall { contract_id: *DAO_CONTRACT_ID, data };
|
||||
|
||||
// Auth module
|
||||
@@ -190,7 +189,7 @@ impl TestHarness {
|
||||
dao_auth_xfer_enc_coin_pk,
|
||||
)?;
|
||||
let mut data = vec![DaoFunction::AuthMoneyTransfer as u8];
|
||||
auth_xfer_params.encode_async(&mut data).await?;
|
||||
auth_xfer_params.encode(&mut data)?;
|
||||
let auth_xfer_call = ContractCall { contract_id: *DAO_CONTRACT_ID, data };
|
||||
|
||||
// We need to construct this tree, where exec is the parent:
|
||||
@@ -267,7 +266,7 @@ impl TestHarness {
|
||||
all_vote_blind: ScalarBlind,
|
||||
block_height: u32,
|
||||
) -> Result<(Transaction, Option<MoneyFeeParamsV1>)> {
|
||||
let wallet = self.holders.get_mut(holder).unwrap();
|
||||
let wallet = self.wallet(holder);
|
||||
|
||||
let (dao_exec_pk, dao_exec_zkbin) = match dao_early_exec_secret_key {
|
||||
Some(_) => self.proving_keys.get(DAO_CONTRACT_ZKAS_EARLY_EXEC_NS).unwrap(),
|
||||
@@ -297,7 +296,7 @@ impl TestHarness {
|
||||
|
||||
// Encode the call
|
||||
let mut data = vec![DaoFunction::Exec as u8];
|
||||
exec_params.encode_async(&mut data).await?;
|
||||
exec_params.encode(&mut data)?;
|
||||
let exec_call = ContractCall { contract_id: *DAO_CONTRACT_ID, data };
|
||||
|
||||
// Create the TransactionBuilder containing the `DAO::Exec` call
|
||||
@@ -348,61 +347,27 @@ impl TestHarness {
|
||||
block_height: u32,
|
||||
append: bool,
|
||||
) -> Result<Vec<OwnCoin>> {
|
||||
let wallet = self.holders.get_mut(holder).unwrap();
|
||||
let wallet = self.wallet_mut(holder);
|
||||
|
||||
// Execute the transaction
|
||||
wallet.add_transaction("dao::exec", tx, block_height).await?;
|
||||
|
||||
if !append {
|
||||
return Ok(vec![])
|
||||
return Ok(vec![]);
|
||||
}
|
||||
|
||||
let (mut inputs, mut outputs) = match xfer_params {
|
||||
Some(params) => (params.inputs.to_vec(), params.outputs.to_vec()),
|
||||
None => (vec![], vec![]),
|
||||
};
|
||||
|
||||
if let Some(ref fee_params) = fee_params {
|
||||
inputs.push(fee_params.input.clone());
|
||||
outputs.push(fee_params.output.clone());
|
||||
// Combine transfer and fee inputs/outputs, then process uniformly
|
||||
let mut inputs = vec![];
|
||||
let mut outputs = vec![];
|
||||
if let Some(params) = xfer_params {
|
||||
inputs.extend_from_slice(¶ms.inputs);
|
||||
outputs.extend_from_slice(¶ms.outputs);
|
||||
}
|
||||
if let Some(ref fp) = fee_params {
|
||||
inputs.push(fp.input.clone());
|
||||
outputs.push(fp.output.clone());
|
||||
}
|
||||
|
||||
let nullifiers = inputs.iter().map(|i| i.nullifier.inner()).map(|l| (l, l)).collect();
|
||||
wallet.money_null_smt.insert_batch(nullifiers).expect("smt.insert_batch()");
|
||||
|
||||
for input in inputs {
|
||||
if let Some(spent_coin) = wallet
|
||||
.unspent_money_coins
|
||||
.iter()
|
||||
.find(|x| x.nullifier() == input.nullifier)
|
||||
.cloned()
|
||||
{
|
||||
debug!("Found spent OwnCoin({}) for {:?}", spent_coin.coin, holder);
|
||||
wallet.unspent_money_coins.retain(|x| x.nullifier() != input.nullifier);
|
||||
wallet.spent_money_coins.push(spent_coin.clone());
|
||||
}
|
||||
}
|
||||
|
||||
let mut found_owncoins = vec![];
|
||||
for output in outputs {
|
||||
wallet.money_merkle_tree.append(MerkleNode::from(output.coin.inner()));
|
||||
|
||||
let Ok(note) = output.note.decrypt::<MoneyNote>(&wallet.keypair.secret) else {
|
||||
continue
|
||||
};
|
||||
|
||||
let owncoin = OwnCoin {
|
||||
coin: 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)
|
||||
wallet.process_inputs(&inputs, holder);
|
||||
Ok(wallet.process_outputs(&outputs, holder))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,16 +25,12 @@ use darkfi_dao_contract::{
|
||||
model::{Dao, DaoMintParams},
|
||||
DaoFunction, DAO_CONTRACT_ZKAS_MINT_NS,
|
||||
};
|
||||
use darkfi_money_contract::{
|
||||
client::{MoneyNote, OwnCoin},
|
||||
model::MoneyFeeParamsV1,
|
||||
};
|
||||
use darkfi_money_contract::{client::OwnCoin, model::MoneyFeeParamsV1};
|
||||
use darkfi_sdk::{
|
||||
crypto::{contract_id::DAO_CONTRACT_ID, MerkleNode, SecretKey},
|
||||
ContractCall,
|
||||
};
|
||||
use darkfi_serial::AsyncEncodable;
|
||||
use tracing::debug;
|
||||
use darkfi_serial::Encodable;
|
||||
|
||||
use super::{Holder, TestHarness};
|
||||
|
||||
@@ -74,7 +70,7 @@ impl TestHarness {
|
||||
|
||||
// Encode the call
|
||||
let mut data = vec![DaoFunction::Mint as u8];
|
||||
params.encode_async(&mut data).await?;
|
||||
params.encode(&mut data)?;
|
||||
let call = ContractCall { contract_id: *DAO_CONTRACT_ID, data };
|
||||
let mut tx_builder = TransactionBuilder::new(ContractCallLeaf { call, proofs }, vec![])?;
|
||||
|
||||
@@ -119,56 +115,19 @@ impl TestHarness {
|
||||
block_height: u32,
|
||||
append: bool,
|
||||
) -> Result<Vec<OwnCoin>> {
|
||||
let wallet = self.holders.get_mut(holder).unwrap();
|
||||
let wallet = self.wallet_mut(holder);
|
||||
|
||||
// Execute the transaction
|
||||
wallet.add_transaction("dao::mint", tx, block_height).await?;
|
||||
|
||||
if !append {
|
||||
return Ok(vec![])
|
||||
return Ok(vec![]);
|
||||
}
|
||||
|
||||
// Track the DAO bulla in the DAO Merkle tree
|
||||
wallet.dao_merkle_tree.append(MerkleNode::from(params.dao_bulla.inner()));
|
||||
let leaf_pos = wallet.dao_merkle_tree.mark().unwrap();
|
||||
wallet.dao_leafs.insert(params.dao_bulla, leaf_pos);
|
||||
|
||||
if let Some(ref fee_params) = fee_params {
|
||||
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()));
|
||||
|
||||
let Ok(note) = fee_params.output.note.decrypt::<MoneyNote>(&wallet.keypair.secret)
|
||||
else {
|
||||
return Ok(vec![])
|
||||
};
|
||||
|
||||
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());
|
||||
return Ok(vec![owncoin])
|
||||
}
|
||||
|
||||
Ok(vec![])
|
||||
Ok(wallet.process_fee(fee_params, holder))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ use darkfi_dao_contract::{
|
||||
DaoFunction, DAO_CONTRACT_ZKAS_PROPOSE_INPUT_NS, DAO_CONTRACT_ZKAS_PROPOSE_MAIN_NS,
|
||||
};
|
||||
use darkfi_money_contract::{
|
||||
client::{MoneyNote, OwnCoin},
|
||||
client::OwnCoin,
|
||||
model::{CoinAttributes, MoneyFeeParamsV1},
|
||||
MoneyFunction,
|
||||
};
|
||||
@@ -39,9 +39,8 @@ use darkfi_sdk::{
|
||||
pasta::pallas,
|
||||
ContractCall,
|
||||
};
|
||||
use darkfi_serial::AsyncEncodable;
|
||||
use darkfi_serial::Encodable;
|
||||
use rand::rngs::OsRng;
|
||||
use tracing::debug;
|
||||
|
||||
use super::{Holder, TestHarness};
|
||||
|
||||
@@ -58,7 +57,7 @@ impl TestHarness {
|
||||
block_height: u32,
|
||||
duration_blockwindows: u64,
|
||||
) -> Result<(Transaction, DaoProposeParams, Option<MoneyFeeParamsV1>, DaoProposal)> {
|
||||
let wallet = self.holders.get(proposer).unwrap();
|
||||
let wallet = self.wallet(proposer);
|
||||
|
||||
let (dao_propose_burn_pk, dao_propose_burn_zkbin) =
|
||||
self.proving_keys.get(DAO_CONTRACT_ZKAS_PROPOSE_INPUT_NS).unwrap();
|
||||
@@ -103,7 +102,7 @@ impl TestHarness {
|
||||
proposal_coins.push(coin_params.to_coin());
|
||||
}
|
||||
let mut proposal_data = vec![];
|
||||
proposal_coins.encode_async(&mut proposal_data).await?;
|
||||
proposal_coins.encode(&mut proposal_data)?;
|
||||
|
||||
// Create Auth calls
|
||||
let auth_calls = vec![
|
||||
@@ -155,7 +154,7 @@ impl TestHarness {
|
||||
|
||||
// Encode the call
|
||||
let mut data = vec![DaoFunction::Propose as u8];
|
||||
params.encode_async(&mut data).await?;
|
||||
params.encode(&mut data)?;
|
||||
let call = ContractCall { contract_id: *DAO_CONTRACT_ID, data };
|
||||
let mut tx_builder = TransactionBuilder::new(ContractCallLeaf { call, proofs }, vec![])?;
|
||||
|
||||
@@ -198,7 +197,7 @@ impl TestHarness {
|
||||
block_height: u32,
|
||||
duration_blockwindows: u64,
|
||||
) -> Result<(Transaction, DaoProposeParams, Option<MoneyFeeParamsV1>, DaoProposal)> {
|
||||
let wallet = self.holders.get(proposer).unwrap();
|
||||
let wallet = self.wallet(proposer);
|
||||
|
||||
let (dao_propose_burn_pk, dao_propose_burn_zkbin) =
|
||||
self.proving_keys.get(DAO_CONTRACT_ZKAS_PROPOSE_INPUT_NS).unwrap();
|
||||
@@ -213,20 +212,6 @@ impl TestHarness {
|
||||
.unwrap()
|
||||
.clone();
|
||||
|
||||
// Useful code snippet to dump a sled contract DB
|
||||
/*{
|
||||
let blockchain = &wallet.validator.read().await.blockchain;
|
||||
let contracts = &blockchain.contracts;
|
||||
let tree = contracts
|
||||
.lookup(&blockchain.sled_db, &MONEY_CONTRACT_ID, "nullifier_roots")
|
||||
.unwrap();
|
||||
for kv in tree.iter() {
|
||||
let (key, value) = kv.unwrap();
|
||||
debug!("STATE {:?}", key);
|
||||
debug!(" => {:?}", value);
|
||||
}
|
||||
}*/
|
||||
|
||||
let input = DaoProposeStakeInput {
|
||||
secret: wallet.keypair.secret,
|
||||
note: propose_owncoin.note.clone(),
|
||||
@@ -273,7 +258,7 @@ impl TestHarness {
|
||||
|
||||
// Encode the call
|
||||
let mut data = vec![DaoFunction::Propose as u8];
|
||||
params.encode_async(&mut data).await?;
|
||||
params.encode(&mut data)?;
|
||||
let call = ContractCall { contract_id: *DAO_CONTRACT_ID, data };
|
||||
let mut tx_builder = TransactionBuilder::new(ContractCallLeaf { call, proofs }, vec![])?;
|
||||
|
||||
@@ -318,59 +303,22 @@ impl TestHarness {
|
||||
block_height: u32,
|
||||
append: bool,
|
||||
) -> Result<Vec<OwnCoin>> {
|
||||
let wallet = self.holders.get_mut(holder).unwrap();
|
||||
let wallet = self.wallet_mut(holder);
|
||||
|
||||
// Execute the transaction
|
||||
wallet.add_transaction("dao::propose", tx, block_height).await?;
|
||||
|
||||
wallet.money_null_smt_snapshot = Some(wallet.money_null_smt.clone());
|
||||
|
||||
if !append {
|
||||
return Ok(vec![])
|
||||
return Ok(vec![]);
|
||||
}
|
||||
|
||||
// Track proposal in the proposals tree
|
||||
wallet.dao_proposals_tree.append(MerkleNode::from(params.proposal_bulla.inner()));
|
||||
let prop_leaf_pos = wallet.dao_proposals_tree.mark().unwrap();
|
||||
let prop_money_snapshot = wallet.money_merkle_tree.clone();
|
||||
wallet.dao_prop_leafs.insert(params.proposal_bulla, (prop_leaf_pos, prop_money_snapshot));
|
||||
|
||||
if let Some(ref fee_params) = fee_params {
|
||||
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()));
|
||||
|
||||
let Ok(note) = fee_params.output.note.decrypt::<MoneyNote>(&wallet.keypair.secret)
|
||||
else {
|
||||
return Ok(vec![])
|
||||
};
|
||||
|
||||
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());
|
||||
return Ok(vec![owncoin])
|
||||
}
|
||||
|
||||
Ok(vec![])
|
||||
Ok(wallet.process_fee(fee_params, holder))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
*
|
||||
* Copyright (C) 2020-2026 Dyne.org foundation
|
||||
*
|
||||
* This program is free software: you can redistributemoney it and/or modify
|
||||
* 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.
|
||||
@@ -26,16 +26,9 @@ use darkfi_dao_contract::{
|
||||
model::{Dao, DaoProposal, DaoVoteParams},
|
||||
DaoFunction, DAO_CONTRACT_ZKAS_VOTE_INPUT_NS, DAO_CONTRACT_ZKAS_VOTE_MAIN_NS,
|
||||
};
|
||||
use darkfi_money_contract::{
|
||||
client::{MoneyNote, OwnCoin},
|
||||
model::MoneyFeeParamsV1,
|
||||
};
|
||||
use darkfi_sdk::{
|
||||
crypto::{contract_id::DAO_CONTRACT_ID, MerkleNode},
|
||||
ContractCall,
|
||||
};
|
||||
use darkfi_serial::AsyncEncodable;
|
||||
use tracing::debug;
|
||||
use darkfi_money_contract::{client::OwnCoin, model::MoneyFeeParamsV1};
|
||||
use darkfi_sdk::{crypto::contract_id::DAO_CONTRACT_ID, ContractCall};
|
||||
use darkfi_serial::Encodable;
|
||||
|
||||
use super::{Holder, TestHarness};
|
||||
|
||||
@@ -49,7 +42,7 @@ impl TestHarness {
|
||||
proposal: &DaoProposal,
|
||||
block_height: u32,
|
||||
) -> Result<(Transaction, DaoVoteParams, Option<MoneyFeeParamsV1>)> {
|
||||
let wallet = self.holders.get(voter).unwrap();
|
||||
let wallet = self.wallet(voter);
|
||||
|
||||
let (dao_vote_burn_pk, dao_vote_burn_zkbin) =
|
||||
self.proving_keys.get(DAO_CONTRACT_ZKAS_VOTE_INPUT_NS).unwrap();
|
||||
@@ -94,7 +87,7 @@ impl TestHarness {
|
||||
|
||||
// Encode the call
|
||||
let mut data = vec![DaoFunction::Vote as u8];
|
||||
params.encode_async(&mut data).await?;
|
||||
params.encode(&mut data)?;
|
||||
let call = ContractCall { contract_id: *DAO_CONTRACT_ID, data };
|
||||
let mut tx_builder = TransactionBuilder::new(ContractCallLeaf { call, proofs }, vec![])?;
|
||||
|
||||
@@ -138,52 +131,14 @@ impl TestHarness {
|
||||
block_height: u32,
|
||||
append: bool,
|
||||
) -> Result<Vec<OwnCoin>> {
|
||||
let wallet = self.holders.get_mut(holder).unwrap();
|
||||
let wallet = self.wallet_mut(holder);
|
||||
|
||||
// Execute the transaction
|
||||
wallet.add_transaction("dao::vote", tx, block_height).await?;
|
||||
|
||||
if !append {
|
||||
return Ok(vec![])
|
||||
return Ok(vec![]);
|
||||
}
|
||||
|
||||
if let Some(ref fee_params) = fee_params {
|
||||
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()));
|
||||
|
||||
let Ok(note) = fee_params.output.note.decrypt::<MoneyNote>(&wallet.keypair.secret)
|
||||
else {
|
||||
return Ok(vec![])
|
||||
};
|
||||
|
||||
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());
|
||||
return Ok(vec![owncoin])
|
||||
}
|
||||
|
||||
Ok(vec![])
|
||||
Ok(wallet.process_fee(fee_params, holder))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,8 +18,10 @@
|
||||
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
fs::OpenOptions,
|
||||
io::{Cursor, Write},
|
||||
slice,
|
||||
time::Instant,
|
||||
};
|
||||
|
||||
use darkfi::{
|
||||
@@ -36,64 +38,68 @@ use darkfi::{
|
||||
zkas::ZkBinary,
|
||||
Result,
|
||||
};
|
||||
use darkfi_dao_contract::model::{DaoBulla, DaoProposalBulla};
|
||||
use darkfi_money_contract::client::OwnCoin;
|
||||
use darkfi_dao_contract::model::{Dao, DaoBulla, DaoProposal, DaoProposalBulla, DaoVoteParams};
|
||||
use darkfi_money_contract::{
|
||||
client::{MoneyNote, OwnCoin},
|
||||
model::{
|
||||
CoinAttributes, Input, MoneyFeeParamsV1, MoneyGenesisMintParamsV1, Nullifier, Output,
|
||||
TokenAttributes, TokenId,
|
||||
},
|
||||
MoneyFunction,
|
||||
};
|
||||
use darkfi_sdk::{
|
||||
bridgetree,
|
||||
crypto::{
|
||||
contract_id::MONEY_CONTRACT_ID,
|
||||
poseidon_hash,
|
||||
smt::{MemoryStorageFp, PoseidonFp, SmtMemoryFp, EMPTY_NODES_FP},
|
||||
Keypair, MerkleNode, MerkleTree,
|
||||
BaseBlind, FuncRef, Keypair, MerkleNode, MerkleTree, ScalarBlind, SecretKey,
|
||||
},
|
||||
pasta::pallas,
|
||||
};
|
||||
use darkfi_serial::{serialize, Encodable};
|
||||
use num_bigint::BigUint;
|
||||
use rand::rngs::OsRng;
|
||||
use sled_overlay::sled;
|
||||
use tracing::warn;
|
||||
use tracing::{debug, warn};
|
||||
|
||||
/// Utility module for caching ZK proof PKs and VKs
|
||||
pub mod vks;
|
||||
|
||||
/// `Money::PoWReward` functionality
|
||||
mod money_pow_reward;
|
||||
|
||||
/// `Money::Fee` functionality
|
||||
mod money_fee;
|
||||
|
||||
/// `Money::GenesisMint` functionality
|
||||
mod money_genesis_mint;
|
||||
|
||||
/// `Money::OtcSwap` functionality
|
||||
mod money_otc_swap;
|
||||
/// `Money::PoWReward` functionality
|
||||
mod money_pow_reward;
|
||||
/// `Money::TokenMint` functionality
|
||||
mod money_token;
|
||||
/// `Money::Transfer` functionality
|
||||
mod money_transfer;
|
||||
|
||||
/// `Money::TokenMint` functionality
|
||||
mod money_token;
|
||||
|
||||
/// `Money::OtcSwap` functionality
|
||||
mod money_otc_swap;
|
||||
|
||||
/// `Deployooor::Deploy` functionality
|
||||
mod contract_deploy;
|
||||
|
||||
/// `Deployooor::Lock` functionality
|
||||
mod contract_lock;
|
||||
|
||||
/// `Dao::Exec` functionality
|
||||
mod dao_exec;
|
||||
/// `Dao::Mint` functionality
|
||||
mod dao_mint;
|
||||
|
||||
/// `Dao::Propose` functionality
|
||||
mod dao_propose;
|
||||
|
||||
/// `Dao::Vote` functionality
|
||||
mod dao_vote;
|
||||
|
||||
/// `Dao::Exec` functionality
|
||||
mod dao_exec;
|
||||
/// PoW target
|
||||
const POW_TARGET: u32 = 120;
|
||||
|
||||
/// Initialize the logging mechanism
|
||||
pub fn init_logger() {
|
||||
// We check this error so we can execute same file tests in parallel,
|
||||
// otherwise second one fails to init logger here.
|
||||
// We check this error so we can execute same-file tests in parallel.
|
||||
// Otherwise subsequent calls fail to init the logger here.
|
||||
if setup_test_logger(
|
||||
&["sled"],
|
||||
false,
|
||||
@@ -104,7 +110,7 @@ pub fn init_logger() {
|
||||
)
|
||||
.is_err()
|
||||
{
|
||||
warn!(target: "test_harness", "Logger already initialized");
|
||||
warn!(target: "test-harness", "Logger already initialized");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -130,13 +136,16 @@ pub struct Wallet {
|
||||
pub validator: ValidatorPtr,
|
||||
/// Holder's instance of the Merkle tree for the `Money` contract
|
||||
pub money_merkle_tree: MerkleTree,
|
||||
/// Holder's instance of the SMT tree for the `Money` contract
|
||||
/// Holder's instance of the nullifiers SMT tree for the `Money` contract
|
||||
pub money_null_smt: SmtMemoryFp,
|
||||
/// Holder's instance of the SMT tree for the `Money` contract (snapshotted for DAO::propose())
|
||||
/// Holder's instance of the nullifiers SMT tree for the `Money` contract
|
||||
/// snapshotted for `DAO::Propose`
|
||||
pub money_null_smt_snapshot: Option<SmtMemoryFp>,
|
||||
/// Holder's instance of the Merkle tree for the `DAO` contract (holding DAO bullas)
|
||||
/// Holder's instance of the Merkle tree for the `DAO` contract
|
||||
/// holding DAO bullas
|
||||
pub dao_merkle_tree: MerkleTree,
|
||||
/// Holder's instance of the Merkle tree for the `DAO` contract (holding DAO proposals)
|
||||
/// Holder's instance of the Merkle tree for the `DAO` contract
|
||||
/// holding DAO proposals
|
||||
pub dao_proposals_tree: MerkleTree,
|
||||
/// Holder's set of unspent [`OwnCoin`]s from the `Money` contract
|
||||
pub unspent_money_coins: Vec<OwnCoin>,
|
||||
@@ -166,8 +175,8 @@ impl Wallet {
|
||||
// Inject the cached VKs into the database
|
||||
let overlay = BlockchainOverlay::new(&Blockchain::new(&sled_db)?)?;
|
||||
vks::inject(&overlay, vks)?;
|
||||
let pow_target = 120;
|
||||
deploy_native_contracts(&overlay, pow_target).await?;
|
||||
|
||||
deploy_native_contracts(&overlay, POW_TARGET).await?;
|
||||
let diff = overlay.lock().unwrap().overlay.lock().unwrap().diff(&[])?;
|
||||
overlay.lock().unwrap().contracts.update_state_monotree(&diff)?;
|
||||
overlay.lock().unwrap().overlay.lock().unwrap().apply()?;
|
||||
@@ -175,15 +184,15 @@ impl Wallet {
|
||||
// Create the `Validator` instance
|
||||
let validator_config = ValidatorConfig {
|
||||
confirmation_threshold: 3,
|
||||
pow_target,
|
||||
pow_target: POW_TARGET,
|
||||
pow_fixed_difficulty: Some(BigUint::from(1_u8)),
|
||||
genesis_block,
|
||||
verify_fees,
|
||||
};
|
||||
let validator = Validator::new(&sled_db, &validator_config).await?;
|
||||
|
||||
// The Merkle tree for the `Money` contract is initialized with a "null"
|
||||
// leaf at position 0.
|
||||
// The Merkle tree for the Money contract is initialized with a
|
||||
// "null" leaf at position 0.
|
||||
let mut money_merkle_tree = MerkleTree::new(1);
|
||||
money_merkle_tree.append(MerkleNode::from(pallas::Base::ZERO));
|
||||
money_merkle_tree.mark().unwrap();
|
||||
@@ -217,7 +226,7 @@ impl Wallet {
|
||||
block_height: u32,
|
||||
) -> Result<()> {
|
||||
if self.bench_wasm {
|
||||
let _ = benchmark_wasm_calls(callname, &self.validator, &tx, block_height).await;
|
||||
let _ = benchmark_wasm_calls(callname, self.validator.clone(), &tx, block_height).await;
|
||||
}
|
||||
|
||||
let validator = self.validator.read().await;
|
||||
@@ -232,21 +241,85 @@ impl Wallet {
|
||||
.await?;
|
||||
|
||||
// Write the data
|
||||
{
|
||||
let blockchain = &validator.blockchain;
|
||||
let txs = &blockchain.transactions;
|
||||
txs.insert(slice::from_ref(&tx)).expect("insert tx");
|
||||
txs.insert_location(&[tx.hash()], block_height).expect("insert loc");
|
||||
}
|
||||
let blockchain = &validator.blockchain;
|
||||
let txs = &blockchain.transactions;
|
||||
txs.insert(slice::from_ref(&tx)).expect("insert tx");
|
||||
txs.insert_location(&[tx.hash()], block_height).expect("insert loc");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Mark a single nullifier as spent in the SMT and move any matching
|
||||
/// OwnCoin from `unspent_money_coins` to `spent_money_coins`
|
||||
pub fn mark_spent_nullifier(&mut self, nullifier: Nullifier, holder: &Holder) {
|
||||
let n = nullifier.inner();
|
||||
self.money_null_smt.insert_batch(vec![(n, n)]).expect("smt.insert_batch()");
|
||||
|
||||
if let Some(spent_coin) =
|
||||
self.unspent_money_coins.iter().find(|x| x.nullifier() == nullifier).cloned()
|
||||
{
|
||||
debug!("Found spent OwnCoin({}) for {:?}", spent_coin.coin, holder);
|
||||
self.unspent_money_coins.retain(|x| x.nullifier() != nullifier);
|
||||
self.spent_money_coins.push(spent_coin);
|
||||
}
|
||||
}
|
||||
|
||||
/// Process a set of [`Input`]s.
|
||||
/// Insert nullifiers into the SMT and move any matching OwnCoins from
|
||||
/// unspent to spent.
|
||||
pub fn process_inputs(&mut self, inputs: &[Input], holder: &Holder) {
|
||||
for input in inputs {
|
||||
self.mark_spent_nullifier(input.nullifier, holder);
|
||||
}
|
||||
}
|
||||
|
||||
/// Process a set of [`Output`]s.
|
||||
/// Append each coin to the Merkle tree and attempt to decrypt the note.
|
||||
/// Returns any new OwnCoins found.
|
||||
pub fn process_outputs(&mut self, outputs: &[Output], holder: &Holder) -> Vec<OwnCoin> {
|
||||
let mut found = vec![];
|
||||
|
||||
for output in outputs {
|
||||
self.money_merkle_tree.append(MerkleNode::from(output.coin.inner()));
|
||||
|
||||
let Ok(note) = output.note.decrypt::<MoneyNote>(&self.keypair.secret) else { continue };
|
||||
|
||||
let owncoin = OwnCoin {
|
||||
coin: output.coin,
|
||||
note: note.clone(),
|
||||
secret: self.keypair.secret,
|
||||
leaf_position: self.money_merkle_tree.mark().unwrap(),
|
||||
};
|
||||
|
||||
debug!("Found new OwnCoin({}) for {:?}", owncoin.coin, holder);
|
||||
self.unspent_money_coins.push(owncoin.clone());
|
||||
found.push(owncoin);
|
||||
}
|
||||
|
||||
found
|
||||
}
|
||||
|
||||
/// Process the fee component of a transaction (if present).
|
||||
/// Handles the fee input nullifier and fee change output.
|
||||
/// Returns any new OwnCoins found.
|
||||
pub fn process_fee(
|
||||
&mut self,
|
||||
fee_params: &Option<MoneyFeeParamsV1>,
|
||||
holder: &Holder,
|
||||
) -> Vec<OwnCoin> {
|
||||
let Some(ref fp) = fee_params else { return vec![] };
|
||||
|
||||
self.mark_spent_nullifier(fp.input.nullifier, holder);
|
||||
self.process_outputs(slice::from_ref(&fp.output), holder)
|
||||
}
|
||||
}
|
||||
|
||||
/// Native contract test harness instance
|
||||
pub struct TestHarness {
|
||||
/// Initialized [`Holder`]s for this instance
|
||||
pub holders: HashMap<Holder, Wallet>,
|
||||
/// Ordered list of all holder keys (for broadcast operations)
|
||||
pub holder_keys: Vec<Holder>,
|
||||
/// Cached [`ProvingKey`]s for native contract ZK proving
|
||||
pub proving_keys: HashMap<String, (ProvingKey, ZkBinary)>,
|
||||
/// The genesis block for this harness
|
||||
@@ -283,13 +356,15 @@ impl TestHarness {
|
||||
let sled_db = sled::Config::new().temporary(true).open()?;
|
||||
let overlay = BlockchainOverlay::new(&Blockchain::new(&sled_db)?)?;
|
||||
vks::inject(&overlay, &vks)?;
|
||||
deploy_native_contracts(&overlay, 90).await?;
|
||||
deploy_native_contracts(&overlay, POW_TARGET).await?;
|
||||
let diff = overlay.lock().unwrap().overlay.lock().unwrap().diff(&[])?;
|
||||
genesis_block.header.state_root =
|
||||
overlay.lock().unwrap().contracts.update_state_monotree(&diff)?;
|
||||
|
||||
// Create `Wallet` instances
|
||||
let mut holders_map = HashMap::new();
|
||||
let mut holder_keys = Vec::with_capacity(holders.len());
|
||||
|
||||
for holder in holders {
|
||||
let keypair = Keypair::random(&mut rng);
|
||||
let token_mint_authority = Keypair::random(&mut rng);
|
||||
@@ -306,35 +381,517 @@ impl TestHarness {
|
||||
.await?;
|
||||
|
||||
holders_map.insert(*holder, wallet);
|
||||
holder_keys.push(*holder);
|
||||
}
|
||||
|
||||
Ok(Self { holders: holders_map, proving_keys, genesis_block, verify_fees })
|
||||
Ok(Self { holders: holders_map, holder_keys, proving_keys, genesis_block, verify_fees })
|
||||
}
|
||||
|
||||
/// Assert that all holders' trees are the same
|
||||
/// Get a reference to a Holder's Wallet
|
||||
pub fn wallet(&self, holder: &Holder) -> &Wallet {
|
||||
self.holders.get(holder).unwrap()
|
||||
}
|
||||
|
||||
/// Get a mutable reference to a Holder's Wallet
|
||||
pub fn wallet_mut(&mut self, holder: &Holder) -> &mut Wallet {
|
||||
self.holders.get_mut(holder).unwrap()
|
||||
}
|
||||
|
||||
/// Get a Holder's unspent OwnCoins
|
||||
pub fn coins(&self, holder: &Holder) -> &[OwnCoin] {
|
||||
&self.wallet(holder).unspent_money_coins
|
||||
}
|
||||
|
||||
/// Get a Holder's unspent OwnCoins filtered by token ID
|
||||
pub fn coins_by_token(&self, holder: &Holder, token_id: TokenId) -> Vec<OwnCoin> {
|
||||
self.coins(holder).iter().filter(|c| c.note.token_id == token_id).cloned().collect()
|
||||
}
|
||||
|
||||
/// Get the total balance of a Holder for a given token
|
||||
pub fn balance(&self, holder: &Holder, token_id: TokenId) -> u64 {
|
||||
self.coins(holder)
|
||||
.iter()
|
||||
.filter(|c| c.note.token_id == token_id)
|
||||
.map(|c| c.note.value)
|
||||
.sum()
|
||||
}
|
||||
|
||||
/// Assert that all holders' Merkle trees are consistent
|
||||
pub fn assert_trees(&self, holders: &[Holder]) {
|
||||
assert!(holders.len() > 1);
|
||||
// Gather wallets
|
||||
assert!(!holders.is_empty());
|
||||
let mut wallets = vec![];
|
||||
for holder in holders {
|
||||
wallets.push(self.holders.get(holder).unwrap());
|
||||
}
|
||||
// Compare trees
|
||||
let wallet = wallets[0];
|
||||
let money_root = wallet.money_merkle_tree.root(0).unwrap();
|
||||
|
||||
let money_root = wallets[0].money_merkle_tree.root(0).unwrap();
|
||||
for wallet in &wallets[1..] {
|
||||
assert!(money_root == wallet.money_merkle_tree.root(0).unwrap());
|
||||
assert_eq!(money_root, wallet.money_merkle_tree.root(0).unwrap());
|
||||
}
|
||||
}
|
||||
|
||||
/// Assert all registered holders' Merkle trees are consistent
|
||||
pub fn assert_all_trees(&self) {
|
||||
if !self.holder_keys.is_empty() {
|
||||
self.assert_trees(&self.holder_keys);
|
||||
}
|
||||
}
|
||||
|
||||
/// Mint a token for `recipient` and execute the tx on all registered
|
||||
/// holders.
|
||||
/// Returns the minted `TokenId`
|
||||
pub async fn token_mint_to_all(
|
||||
&mut self,
|
||||
amount: u64,
|
||||
holder: &Holder,
|
||||
recipient: &Holder,
|
||||
block_height: u32,
|
||||
) -> Result<TokenId> {
|
||||
let token_blind = BaseBlind::random(&mut OsRng);
|
||||
let (tx, mint_params, auth_params, fee_params) = self
|
||||
.token_mint(amount, holder, recipient, token_blind, None, None, block_height)
|
||||
.await?;
|
||||
|
||||
// Derive the Token ID
|
||||
let token_id = self.derive_token_id(recipient, token_blind);
|
||||
|
||||
let holders = self.holder_keys.clone();
|
||||
for h in &holders {
|
||||
self.execute_token_mint_tx(
|
||||
h,
|
||||
tx.clone(),
|
||||
&mint_params,
|
||||
&auth_params,
|
||||
&fee_params,
|
||||
block_height,
|
||||
true,
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
|
||||
self.assert_all_trees();
|
||||
|
||||
Ok(token_id)
|
||||
}
|
||||
|
||||
/// Mint a token with a specific `token_blind` and execute on all
|
||||
/// registered holders. Returns the [`TokenId`].
|
||||
///
|
||||
/// Use this instead of `token_mint_to_all` when you need the same
|
||||
/// token blind across multiple mints (e.g. DAO governance tokens).
|
||||
pub async fn token_mint_with_blind_to_all(
|
||||
&mut self,
|
||||
amount: u64,
|
||||
holder: &Holder,
|
||||
recipient: &Holder,
|
||||
token_blind: BaseBlind,
|
||||
block_height: u32,
|
||||
) -> Result<TokenId> {
|
||||
let (tx, mint_params, auth_params, fee_params) = self
|
||||
.token_mint(amount, holder, recipient, token_blind, None, None, block_height)
|
||||
.await?;
|
||||
|
||||
let token_id = self.derive_token_id(recipient, token_blind);
|
||||
|
||||
let holders = self.holder_keys.clone();
|
||||
for h in &holders {
|
||||
self.execute_token_mint_tx(
|
||||
h,
|
||||
tx.clone(),
|
||||
&mint_params,
|
||||
&auth_params,
|
||||
&fee_params,
|
||||
block_height,
|
||||
true,
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
self.assert_all_trees();
|
||||
|
||||
Ok(token_id)
|
||||
}
|
||||
|
||||
/// Transfer `amount` of `token_id` from `sender` to `recipient` and
|
||||
/// execute the tx on all registered holders.
|
||||
pub async fn transfer_to_all(
|
||||
&mut self,
|
||||
amount: u64,
|
||||
sender: &Holder,
|
||||
recipient: &Holder,
|
||||
token_id: TokenId,
|
||||
block_height: u32,
|
||||
) -> Result<()> {
|
||||
let owncoins = self.coins_by_token(sender, token_id);
|
||||
let (tx, (params, fee_params), _spent) = self
|
||||
.transfer(amount, sender, recipient, &owncoins, token_id, block_height, false)
|
||||
.await?;
|
||||
|
||||
let holders = self.holder_keys.clone();
|
||||
for h in &holders {
|
||||
self.execute_transfer_tx(h, tx.clone(), ¶ms, &fee_params, block_height, true)
|
||||
.await?;
|
||||
}
|
||||
|
||||
self.assert_all_trees();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Build a genesis mint for `holder` and execute on all registered holders.
|
||||
/// Returns the found [`OwnCoin`]s.
|
||||
pub async fn genesis_mint_to_all(
|
||||
&mut self,
|
||||
holder: &Holder,
|
||||
amounts: &[u64],
|
||||
block_height: u32,
|
||||
) -> Result<Vec<OwnCoin>> {
|
||||
let (tx, params) = self.genesis_mint(holder, amounts, None, None).await?;
|
||||
self.genesis_mint_to_all_with(tx, ¶ms, block_height).await
|
||||
}
|
||||
|
||||
/// Execute a pre-built genesis mint transaction on all registered holders.
|
||||
/// Useful when you need to test the transaction before broadcasting
|
||||
/// (e.g. malicious block height checks).
|
||||
pub async fn genesis_mint_to_all_with(
|
||||
&mut self,
|
||||
tx: Transaction,
|
||||
params: &MoneyGenesisMintParamsV1,
|
||||
block_height: u32,
|
||||
) -> Result<Vec<OwnCoin>> {
|
||||
let holders = self.holder_keys.clone();
|
||||
let mut found = vec![];
|
||||
for h in &holders {
|
||||
found.extend(
|
||||
self.execute_genesis_mint_tx(h, tx.clone(), params, block_height, true).await?,
|
||||
);
|
||||
}
|
||||
self.assert_all_trees();
|
||||
Ok(found)
|
||||
}
|
||||
|
||||
/// Freeze a token authority for `holder` and execute on all registered
|
||||
/// holders.
|
||||
pub async fn token_freeze_to_all(&mut self, holder: &Holder, block_height: u32) -> Result<()> {
|
||||
let (tx, freeze_params, fee_params) = self.token_freeze(holder, block_height).await?;
|
||||
|
||||
let holders = self.holder_keys.clone();
|
||||
for h in &holders {
|
||||
self.execute_token_freeze_tx(
|
||||
h,
|
||||
tx.clone(),
|
||||
&freeze_params,
|
||||
&fee_params,
|
||||
block_height,
|
||||
true,
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
self.assert_all_trees();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Generate a new block mined by `miner` and broadcast to all registered
|
||||
/// holders. Convenience wrapper around `generate_block` that uses
|
||||
/// `holder_keys` instead of requiring the caller to pass holders.
|
||||
pub async fn generate_block_all(&mut self, miner: &Holder) -> Result<Vec<OwnCoin>> {
|
||||
let holders = self.holder_keys.clone();
|
||||
self.generate_block(miner, &holders).await
|
||||
}
|
||||
|
||||
/// Consolidate all coins of `token_id` owned by `holder` into a single
|
||||
/// coin by transferring to self, then execute on all registered holders.
|
||||
pub async fn consolidate_to_all(
|
||||
&mut self,
|
||||
holder: &Holder,
|
||||
token_id: TokenId,
|
||||
block_height: u32,
|
||||
) -> Result<()> {
|
||||
let owncoins = self.coins_by_token(holder, token_id);
|
||||
if owncoins.len() <= 1 {
|
||||
// Nothing to consolidate
|
||||
return Ok(())
|
||||
}
|
||||
|
||||
let total: u64 = owncoins.iter().map(|c| c.note.value).sum();
|
||||
let (tx, (params, fee_params), _spent) =
|
||||
self.transfer(total, holder, holder, &owncoins, token_id, block_height, false).await?;
|
||||
|
||||
let holders = self.holder_keys.clone();
|
||||
for h in &holders {
|
||||
self.execute_transfer_tx(h, tx.clone(), ¶ms, &fee_params, block_height, true)
|
||||
.await?;
|
||||
}
|
||||
|
||||
self.assert_all_trees();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Perform an OTC swap between two holders and execute on all registered
|
||||
/// holders.
|
||||
pub async fn otc_swap_to_all(
|
||||
&mut self,
|
||||
holder0: &Holder,
|
||||
coin0: &OwnCoin,
|
||||
holder1: &Holder,
|
||||
coin1: &OwnCoin,
|
||||
block_height: u32,
|
||||
) -> Result<()> {
|
||||
let (tx, params, fee_params) =
|
||||
self.otc_swap(holder0, coin0, holder1, coin1, block_height).await?;
|
||||
|
||||
let holders = self.holder_keys.clone();
|
||||
for h in &holders {
|
||||
self.execute_otc_swap_tx(h, tx.clone(), ¶ms, &fee_params, block_height, true)
|
||||
.await?;
|
||||
}
|
||||
|
||||
self.assert_all_trees();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Build a `Dao::Mint` transaction and execute on all registered holders.
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub async fn dao_mint_to_all(
|
||||
&mut self,
|
||||
holder: &Holder,
|
||||
dao: &Dao,
|
||||
dao_notes_secret_key: &SecretKey,
|
||||
dao_proposer_secret_key: &SecretKey,
|
||||
dao_proposals_secret_key: &SecretKey,
|
||||
dao_votes_secret_key: &SecretKey,
|
||||
dao_exec_secret_key: &SecretKey,
|
||||
dao_early_exec_secret_key: &SecretKey,
|
||||
block_height: u32,
|
||||
) -> Result<()> {
|
||||
let (tx, params, fee_params) = self
|
||||
.dao_mint(
|
||||
holder,
|
||||
dao,
|
||||
dao_notes_secret_key,
|
||||
dao_proposer_secret_key,
|
||||
dao_proposals_secret_key,
|
||||
dao_votes_secret_key,
|
||||
dao_exec_secret_key,
|
||||
dao_early_exec_secret_key,
|
||||
block_height,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let holders = self.holder_keys.clone();
|
||||
for h in &holders {
|
||||
self.execute_dao_mint_tx(h, tx.clone(), ¶ms, &fee_params, block_height, true)
|
||||
.await?;
|
||||
}
|
||||
self.assert_all_trees();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Build a `Dao::Propose` (transfer) transaction and execute on all
|
||||
/// registered holders. Returns the [`DaoProposal`] for subsequent
|
||||
/// voting/execution.
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub async fn dao_propose_transfer_to_all(
|
||||
&mut self,
|
||||
proposer: &Holder,
|
||||
proposal_coinattrs: &[CoinAttributes],
|
||||
user_data: pallas::Base,
|
||||
dao: &Dao,
|
||||
dao_proposer_secret_key: &SecretKey,
|
||||
block_height: u32,
|
||||
duration_blockwindows: u64,
|
||||
) -> Result<DaoProposal> {
|
||||
let (tx, params, fee_params, proposal_info) = self
|
||||
.dao_propose_transfer(
|
||||
proposer,
|
||||
proposal_coinattrs,
|
||||
user_data,
|
||||
dao,
|
||||
dao_proposer_secret_key,
|
||||
block_height,
|
||||
duration_blockwindows,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let holders = self.holder_keys.clone();
|
||||
for h in &holders {
|
||||
self.execute_dao_propose_tx(h, tx.clone(), ¶ms, &fee_params, block_height, true)
|
||||
.await?;
|
||||
}
|
||||
self.assert_all_trees();
|
||||
Ok(proposal_info)
|
||||
}
|
||||
|
||||
/// Build a `Dao::Propose` (generic) transaction and execute on all
|
||||
/// registered holders. Returns the [`DaoProposal`].
|
||||
pub async fn dao_propose_generic_to_all(
|
||||
&mut self,
|
||||
proposer: &Holder,
|
||||
user_data: pallas::Base,
|
||||
dao: &Dao,
|
||||
dao_proposer_secret_key: &SecretKey,
|
||||
block_height: u32,
|
||||
duration_blockwindows: u64,
|
||||
) -> Result<DaoProposal> {
|
||||
let (tx, params, fee_params, proposal_info) = self
|
||||
.dao_propose_generic(
|
||||
proposer,
|
||||
user_data,
|
||||
dao,
|
||||
dao_proposer_secret_key,
|
||||
block_height,
|
||||
duration_blockwindows,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let holders = self.holder_keys.clone();
|
||||
for h in &holders {
|
||||
self.execute_dao_propose_tx(h, tx.clone(), ¶ms, &fee_params, block_height, true)
|
||||
.await?;
|
||||
}
|
||||
self.assert_all_trees();
|
||||
Ok(proposal_info)
|
||||
}
|
||||
|
||||
/// Build and broadcast a single `Dao::Vote` transaction on all registered
|
||||
/// holders. Returns the [`DaoVoteParams`] (needed for vote counting).
|
||||
pub async fn dao_vote_to_all(
|
||||
&mut self,
|
||||
voter: &Holder,
|
||||
vote_option: bool,
|
||||
dao: &Dao,
|
||||
proposal: &DaoProposal,
|
||||
block_height: u32,
|
||||
) -> Result<DaoVoteParams> {
|
||||
let (tx, vote_params, fee_params) =
|
||||
self.dao_vote(voter, vote_option, dao, proposal, block_height).await?;
|
||||
|
||||
let holders = self.holder_keys.clone();
|
||||
for h in &holders {
|
||||
self.execute_dao_vote_tx(h, tx.clone(), &fee_params, block_height, true).await?;
|
||||
}
|
||||
self.assert_all_trees();
|
||||
Ok(vote_params)
|
||||
}
|
||||
|
||||
/// Build and broadcast a `Dao::Exec` (transfer) transaction on all
|
||||
/// registered holders.
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub async fn dao_exec_transfer_to_all(
|
||||
&mut self,
|
||||
executor: &Holder,
|
||||
dao: &Dao,
|
||||
dao_exec_secret_key: &SecretKey,
|
||||
dao_early_exec_secret_key: &Option<SecretKey>,
|
||||
proposal_info: &DaoProposal,
|
||||
proposal_coinattrs: Vec<CoinAttributes>,
|
||||
total_yes_vote_value: u64,
|
||||
total_all_vote_value: u64,
|
||||
total_yes_vote_blind: ScalarBlind,
|
||||
total_all_vote_blind: ScalarBlind,
|
||||
block_height: u32,
|
||||
) -> Result<()> {
|
||||
let (tx, xfer_params, fee_params) = self
|
||||
.dao_exec_transfer(
|
||||
executor,
|
||||
dao,
|
||||
dao_exec_secret_key,
|
||||
dao_early_exec_secret_key,
|
||||
proposal_info,
|
||||
proposal_coinattrs,
|
||||
total_yes_vote_value,
|
||||
total_all_vote_value,
|
||||
total_yes_vote_blind,
|
||||
total_all_vote_blind,
|
||||
block_height,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let holders = self.holder_keys.clone();
|
||||
for h in &holders {
|
||||
self.execute_dao_exec_tx(
|
||||
h,
|
||||
tx.clone(),
|
||||
Some(&xfer_params),
|
||||
&fee_params,
|
||||
block_height,
|
||||
true,
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
self.assert_all_trees();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Build and broadcast a `Dao::Exec` (generic) transaction on all
|
||||
/// registered holders.
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub async fn dao_exec_generic_to_all(
|
||||
&mut self,
|
||||
executor: &Holder,
|
||||
dao: &Dao,
|
||||
dao_exec_secret_key: &SecretKey,
|
||||
dao_early_exec_secret_key: &Option<SecretKey>,
|
||||
proposal_info: &DaoProposal,
|
||||
total_yes_vote_value: u64,
|
||||
total_all_vote_value: u64,
|
||||
total_yes_vote_blind: ScalarBlind,
|
||||
total_all_vote_blind: ScalarBlind,
|
||||
block_height: u32,
|
||||
) -> Result<()> {
|
||||
let (tx, fee_params) = self
|
||||
.dao_exec_generic(
|
||||
executor,
|
||||
dao,
|
||||
dao_exec_secret_key,
|
||||
dao_early_exec_secret_key,
|
||||
proposal_info,
|
||||
total_yes_vote_value,
|
||||
total_all_vote_value,
|
||||
total_yes_vote_blind,
|
||||
total_all_vote_blind,
|
||||
block_height,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let holders = self.holder_keys.clone();
|
||||
for h in &holders {
|
||||
self.execute_dao_exec_tx(h, tx.clone(), None, &fee_params, block_height, true).await?;
|
||||
}
|
||||
self.assert_all_trees();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Derive the [`TokenId`] that a given holder's `token_mint_authority`
|
||||
/// would produce with a given blind.
|
||||
pub fn derive_token_id(&self, holder: &Holder, token_blind: BaseBlind) -> TokenId {
|
||||
let wallet = self.wallet(holder);
|
||||
let mint_authority = wallet.token_mint_authority;
|
||||
|
||||
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,
|
||||
};
|
||||
|
||||
token_attrs.to_token_id()
|
||||
}
|
||||
}
|
||||
|
||||
async fn benchmark_wasm_calls(
|
||||
callname: &str,
|
||||
validator: &ValidatorPtr,
|
||||
validator: ValidatorPtr,
|
||||
tx: &Transaction,
|
||||
block_height: u32,
|
||||
) -> Result<()> {
|
||||
let mut file = std::fs::OpenOptions::new().create(true).append(true).open("bench.csv")?;
|
||||
let mut file = OpenOptions::new().create(true).append(true).open("bench.csv")?;
|
||||
|
||||
let validator = validator.read().await;
|
||||
for (idx, call) in tx.calls.iter().enumerate() {
|
||||
@@ -356,29 +913,29 @@ async fn benchmark_wasm_calls(
|
||||
tx.calls.encode(&mut payload)?;
|
||||
|
||||
let mut times = [0; 3];
|
||||
let now = std::time::Instant::now();
|
||||
let now = Instant::now();
|
||||
let _metadata = runtime.metadata(&payload)?;
|
||||
times[0] = now.elapsed().as_micros();
|
||||
|
||||
let now = std::time::Instant::now();
|
||||
let now = Instant::now();
|
||||
let mut update = vec![call.data.data[0]];
|
||||
update.append(&mut runtime.exec(&payload)?);
|
||||
times[1] = now.elapsed().as_micros();
|
||||
|
||||
let now = std::time::Instant::now();
|
||||
let now = Instant::now();
|
||||
runtime.apply(&update)?;
|
||||
times[2] = now.elapsed().as_micros();
|
||||
|
||||
writeln!(
|
||||
file,
|
||||
"{}, {}, {}, {}, {}, {}, {}",
|
||||
"{},{},{},{},{},{},{}",
|
||||
callname,
|
||||
tx.hash(),
|
||||
idx,
|
||||
times[0],
|
||||
times[1],
|
||||
times[2],
|
||||
serialize(tx).len()
|
||||
serialize(tx).len(),
|
||||
)?;
|
||||
}
|
||||
|
||||
|
||||
@@ -36,14 +36,14 @@ use darkfi_money_contract::{
|
||||
use darkfi_sdk::{
|
||||
crypto::{
|
||||
contract_id::MONEY_CONTRACT_ID, note::AeadEncryptedNote, BaseBlind, Blind, FuncId,
|
||||
MerkleNode, ScalarBlind, SecretKey,
|
||||
ScalarBlind, SecretKey,
|
||||
},
|
||||
pasta::pallas,
|
||||
ContractCall,
|
||||
};
|
||||
use darkfi_serial::AsyncEncodable;
|
||||
use darkfi_serial::Encodable;
|
||||
use rand::rngs::OsRng;
|
||||
use tracing::{debug, info};
|
||||
use tracing::info;
|
||||
|
||||
use super::{Holder, TestHarness};
|
||||
|
||||
@@ -55,7 +55,7 @@ impl TestHarness {
|
||||
&mut self,
|
||||
holder: &Holder,
|
||||
) -> Result<(Transaction, MoneyFeeParamsV1)> {
|
||||
let wallet = self.holders.get(holder).unwrap();
|
||||
let wallet = self.wallet(holder);
|
||||
|
||||
// Compute fee call required fee
|
||||
let required_fee = compute_fee(&FEE_CALL_GAS);
|
||||
@@ -92,7 +92,7 @@ impl TestHarness {
|
||||
// Generate an ephemeral signing key
|
||||
let signature_secret = SecretKey::random(&mut OsRng);
|
||||
|
||||
info!("Creting FeeV1 ZK proof");
|
||||
info!("Creating FeeV1 ZK proof");
|
||||
let (fee_pk, fee_zkbin) = self.proving_keys.get(MONEY_CONTRACT_ZKAS_FEE_NS_V1).unwrap();
|
||||
|
||||
let (proof, public_inputs) = create_fee_proof(
|
||||
@@ -143,8 +143,8 @@ impl TestHarness {
|
||||
};
|
||||
|
||||
let mut data = vec![MoneyFunction::FeeV1 as u8];
|
||||
required_fee.encode_async(&mut data).await?;
|
||||
params.encode_async(&mut data).await?;
|
||||
required_fee.encode(&mut data)?;
|
||||
params.encode(&mut data)?;
|
||||
let call = ContractCall { contract_id: *MONEY_CONTRACT_ID, data };
|
||||
let mut tx_builder =
|
||||
TransactionBuilder::new(ContractCallLeaf { call, proofs: vec![proof] }, vec![])?;
|
||||
@@ -155,7 +155,7 @@ impl TestHarness {
|
||||
Ok((tx, params))
|
||||
}
|
||||
|
||||
/// Execute the transaction created by `create_empty_fee_call()` for a given [`Holder`]
|
||||
/// Execute the transaction created by `create_empty_fee_call()` for a given [`Holder`].
|
||||
///
|
||||
/// Returns any found [`OwnCoin`]s.
|
||||
pub async fn execute_empty_fee_call_tx(
|
||||
@@ -165,44 +165,9 @@ impl TestHarness {
|
||||
params: &MoneyFeeParamsV1,
|
||||
block_height: u32,
|
||||
) -> Result<Vec<OwnCoin>> {
|
||||
let wallet = self.holders.get_mut(holder).unwrap();
|
||||
|
||||
let nullifier = params.input.nullifier.inner();
|
||||
wallet
|
||||
.money_null_smt
|
||||
.insert_batch(vec![(nullifier, nullifier)])
|
||||
.expect("smt.insert_batch()");
|
||||
|
||||
let wallet = self.wallet_mut(holder);
|
||||
wallet.add_transaction("money::fee", tx, block_height).await?;
|
||||
wallet.money_merkle_tree.append(MerkleNode::from(params.output.coin.inner()));
|
||||
|
||||
// Attempt to decrypt the output note to see if this is a coin for the holder
|
||||
let Ok(note) = params.output.note.decrypt::<MoneyNote>(&wallet.keypair.secret) else {
|
||||
return Ok(vec![])
|
||||
};
|
||||
|
||||
let owncoin = OwnCoin {
|
||||
coin: params.output.coin,
|
||||
note: note.clone(),
|
||||
secret: wallet.keypair.secret,
|
||||
leaf_position: wallet.money_merkle_tree.mark().unwrap(),
|
||||
};
|
||||
|
||||
let spent_coin = wallet
|
||||
.unspent_money_coins
|
||||
.iter()
|
||||
.find(|x| x.nullifier() == params.input.nullifier)
|
||||
.unwrap()
|
||||
.clone();
|
||||
|
||||
debug!("Found spent OwnCoin({}) for {:?}", spent_coin.coin, holder);
|
||||
debug!("Found new OwnCoin({}) for {:?}", owncoin.coin, holder);
|
||||
|
||||
wallet.unspent_money_coins.retain(|x| x.nullifier() != params.input.nullifier);
|
||||
wallet.spent_money_coins.push(spent_coin);
|
||||
wallet.unspent_money_coins.push(owncoin.clone());
|
||||
|
||||
Ok(vec![owncoin])
|
||||
Ok(wallet.process_fee(&Some(params.clone()), holder))
|
||||
}
|
||||
|
||||
/// Create and append a `Money::Fee` call to a given [`Transaction`] for
|
||||
@@ -218,9 +183,9 @@ impl TestHarness {
|
||||
block_height: u32,
|
||||
spent_coins: &[OwnCoin],
|
||||
) -> Result<(ContractCall, Vec<Proof>, Vec<SecretKey>, Vec<OwnCoin>, MoneyFeeParamsV1)> {
|
||||
// First we verify the fee-less transaction to see how much gas it uses for execution
|
||||
// and verification.
|
||||
let wallet = self.holders.get(holder).unwrap();
|
||||
// First we verify the fee-less transaction to see how much gas it
|
||||
// uses for execution and verification.
|
||||
let wallet = self.wallet(holder);
|
||||
let validator = wallet.validator.read().await;
|
||||
let gas_used = validator
|
||||
.add_test_transactions(
|
||||
@@ -236,8 +201,8 @@ impl TestHarness {
|
||||
// Compute the required fee
|
||||
let required_fee = compute_fee(&(gas_used + FEE_CALL_GAS));
|
||||
|
||||
// Knowing the total gas, we can now find an OwnCoin of enough value
|
||||
// so that we can create a valid Money::Fee call.
|
||||
// Knowing the total gas, we can now find an OwnCoin of enough
|
||||
// value so that we can create a valid Money::Fee call.
|
||||
let spent_coins: HashSet<&OwnCoin, RandomState> = HashSet::from_iter(spent_coins);
|
||||
let mut available_coins = wallet.unspent_money_coins.clone();
|
||||
available_coins
|
||||
@@ -325,8 +290,8 @@ impl TestHarness {
|
||||
|
||||
// Encode the contract call
|
||||
let mut data = vec![MoneyFunction::FeeV1 as u8];
|
||||
required_fee.encode_async(&mut data).await?;
|
||||
params.encode_async(&mut data).await?;
|
||||
required_fee.encode(&mut data)?;
|
||||
params.encode(&mut data)?;
|
||||
let call = ContractCall { contract_id: *MONEY_CONTRACT_ID, data };
|
||||
|
||||
Ok((call, vec![proof], vec![signature_secret], vec![coin.clone()], params))
|
||||
|
||||
@@ -21,17 +21,16 @@ use darkfi::{
|
||||
Result,
|
||||
};
|
||||
use darkfi_money_contract::{
|
||||
client::{genesis_mint_v1::GenesisMintCallBuilder, MoneyNote, OwnCoin},
|
||||
client::{genesis_mint_v1::GenesisMintCallBuilder, OwnCoin},
|
||||
model::MoneyGenesisMintParamsV1,
|
||||
MoneyFunction, MONEY_CONTRACT_ZKAS_MINT_NS_V1,
|
||||
};
|
||||
use darkfi_sdk::{
|
||||
crypto::{contract_id::MONEY_CONTRACT_ID, FuncId, MerkleNode},
|
||||
crypto::{contract_id::MONEY_CONTRACT_ID, FuncId},
|
||||
pasta::pallas,
|
||||
ContractCall,
|
||||
};
|
||||
use darkfi_serial::AsyncEncodable;
|
||||
use tracing::debug;
|
||||
use darkfi_serial::Encodable;
|
||||
|
||||
use super::{Holder, TestHarness};
|
||||
|
||||
@@ -46,7 +45,7 @@ impl TestHarness {
|
||||
spend_hook: Option<FuncId>,
|
||||
user_data: Option<pallas::Base>,
|
||||
) -> Result<(Transaction, MoneyGenesisMintParamsV1)> {
|
||||
let wallet = self.holders.get(holder).unwrap();
|
||||
let wallet = self.wallet(holder);
|
||||
|
||||
let (mint_pk, mint_zkbin) = self.proving_keys.get(MONEY_CONTRACT_ZKAS_MINT_NS_V1).unwrap();
|
||||
|
||||
@@ -65,7 +64,7 @@ impl TestHarness {
|
||||
|
||||
// Encode and build the transaction
|
||||
let mut data = vec![MoneyFunction::GenesisMintV1 as u8];
|
||||
debris.params.encode_async(&mut data).await?;
|
||||
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![])?;
|
||||
@@ -89,35 +88,12 @@ impl TestHarness {
|
||||
) -> Result<Vec<OwnCoin>> {
|
||||
let wallet = self.holders.get_mut(holder).unwrap();
|
||||
|
||||
// Execute the transaction
|
||||
wallet.add_transaction("money::genesis_mint", tx, block_height).await?;
|
||||
|
||||
if !append {
|
||||
return Ok(vec![])
|
||||
return Ok(vec![]);
|
||||
}
|
||||
|
||||
// Iterate over call outputs to find any new OwnCoins
|
||||
let mut found_owncoins = vec![];
|
||||
for output in ¶ms.outputs {
|
||||
wallet.money_merkle_tree.append(MerkleNode::from(output.coin.inner()));
|
||||
|
||||
// Attempt to decrypt the output note to see if this is a coin for the holder.
|
||||
let Ok(note) = output.note.decrypt::<MoneyNote>(&wallet.keypair.secret) else {
|
||||
continue
|
||||
};
|
||||
|
||||
let owncoin = OwnCoin {
|
||||
coin: 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)
|
||||
Ok(wallet.process_outputs(¶ms.outputs, holder))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,18 +24,17 @@ use darkfi::{
|
||||
Result,
|
||||
};
|
||||
use darkfi_money_contract::{
|
||||
client::{swap_v1::SwapCallBuilder, MoneyNote, OwnCoin},
|
||||
client::{swap_v1::SwapCallBuilder, OwnCoin},
|
||||
model::{MoneyFeeParamsV1, MoneyTransferParamsV1},
|
||||
MoneyFunction, MONEY_CONTRACT_ZKAS_BURN_NS_V1, MONEY_CONTRACT_ZKAS_MINT_NS_V1,
|
||||
};
|
||||
use darkfi_sdk::{
|
||||
crypto::{contract_id::MONEY_CONTRACT_ID, BaseBlind, Blind, FuncId, MerkleNode},
|
||||
crypto::{contract_id::MONEY_CONTRACT_ID, BaseBlind, Blind, FuncId},
|
||||
pasta::pallas,
|
||||
ContractCall,
|
||||
};
|
||||
use darkfi_serial::AsyncEncodable;
|
||||
use darkfi_serial::Encodable;
|
||||
use rand::rngs::OsRng;
|
||||
use tracing::debug;
|
||||
|
||||
use super::{Holder, TestHarness};
|
||||
|
||||
@@ -51,8 +50,8 @@ impl TestHarness {
|
||||
owncoin1: &OwnCoin,
|
||||
block_height: u32,
|
||||
) -> Result<(Transaction, MoneyTransferParamsV1, Option<MoneyFeeParamsV1>)> {
|
||||
let wallet0 = self.holders.get(holder0).unwrap();
|
||||
let wallet1 = self.holders.get(holder1).unwrap();
|
||||
let wallet0 = self.wallet(holder0);
|
||||
let wallet1 = self.wallet(holder1);
|
||||
|
||||
let (mint_pk, mint_zkbin) = self.proving_keys.get(MONEY_CONTRACT_ZKAS_MINT_NS_V1).unwrap();
|
||||
let (burn_pk, burn_zkbin) = self.proving_keys.get(MONEY_CONTRACT_ZKAS_BURN_NS_V1).unwrap();
|
||||
@@ -131,7 +130,7 @@ impl TestHarness {
|
||||
|
||||
// Encode the contract call
|
||||
let mut data = vec![MoneyFunction::OtcSwapV1 as u8];
|
||||
swap_full_params.encode_async(&mut data).await?;
|
||||
swap_full_params.encode(&mut data)?;
|
||||
let call = ContractCall { contract_id: *MONEY_CONTRACT_ID, data };
|
||||
let mut tx_builder =
|
||||
TransactionBuilder::new(ContractCallLeaf { call, proofs: swap_full_proofs }, vec![])?;
|
||||
@@ -185,60 +184,23 @@ impl TestHarness {
|
||||
block_height: u32,
|
||||
append: bool,
|
||||
) -> Result<Vec<OwnCoin>> {
|
||||
let wallet = self.holders.get_mut(holder).unwrap();
|
||||
let wallet = self.wallet_mut(holder);
|
||||
|
||||
// Execute the transaction
|
||||
wallet.add_transaction("money::otc_swap", tx, block_height).await?;
|
||||
|
||||
let mut found_owncoins = vec![];
|
||||
|
||||
if !append {
|
||||
return Ok(found_owncoins)
|
||||
return Ok(vec![]);
|
||||
}
|
||||
|
||||
// Combine swap inputs/outputs with fee inputs/outputs, then process
|
||||
let mut inputs = swap_params.inputs.to_vec();
|
||||
let mut outputs = swap_params.outputs.to_vec();
|
||||
if let Some(ref fee_params) = fee_params {
|
||||
inputs.push(fee_params.input.clone());
|
||||
outputs.push(fee_params.output.clone());
|
||||
if let Some(ref fp) = fee_params {
|
||||
inputs.push(fp.input.clone());
|
||||
outputs.push(fp.output.clone());
|
||||
}
|
||||
|
||||
let nullifiers = inputs.iter().map(|i| i.nullifier.inner()).map(|l| (l, l)).collect();
|
||||
wallet.money_null_smt.insert_batch(nullifiers).expect("smt.insert_batch()");
|
||||
|
||||
for input in inputs {
|
||||
if let Some(spent_coin) = wallet
|
||||
.unspent_money_coins
|
||||
.iter()
|
||||
.find(|x| x.nullifier() == input.nullifier)
|
||||
.cloned()
|
||||
{
|
||||
debug!("Found spent OwnCoin({}) for {:?}", spent_coin.coin, holder);
|
||||
wallet.unspent_money_coins.retain(|x| x.nullifier() != input.nullifier);
|
||||
wallet.spent_money_coins.push(spent_coin.clone());
|
||||
}
|
||||
}
|
||||
|
||||
for output in outputs {
|
||||
wallet.money_merkle_tree.append(MerkleNode::from(output.coin.inner()));
|
||||
|
||||
// Attempt to decrypt the encrypted note
|
||||
let Ok(note) = output.note.decrypt::<MoneyNote>(&wallet.keypair.secret) else {
|
||||
continue
|
||||
};
|
||||
|
||||
let owncoin = OwnCoin {
|
||||
coin: 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)
|
||||
wallet.process_inputs(&inputs, holder);
|
||||
Ok(wallet.process_outputs(&outputs, holder))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
use std::slice;
|
||||
|
||||
use darkfi::{
|
||||
blockchain::{BlockInfo, BlockchainOverlay, Header},
|
||||
tx::{ContractCallLeaf, Transaction, TransactionBuilder},
|
||||
@@ -23,15 +25,15 @@ use darkfi::{
|
||||
Result,
|
||||
};
|
||||
use darkfi_money_contract::{
|
||||
client::{pow_reward_v1::PoWRewardCallBuilder, MoneyNote, OwnCoin},
|
||||
client::{pow_reward_v1::PoWRewardCallBuilder, OwnCoin},
|
||||
model::MoneyPoWRewardParamsV1,
|
||||
MoneyFunction, MONEY_CONTRACT_ZKAS_MINT_NS_V1,
|
||||
};
|
||||
use darkfi_sdk::{
|
||||
crypto::{contract_id::MONEY_CONTRACT_ID, MerkleNode, MerkleTree},
|
||||
crypto::{contract_id::MONEY_CONTRACT_ID, MerkleTree},
|
||||
ContractCall,
|
||||
};
|
||||
use darkfi_serial::AsyncEncodable;
|
||||
use darkfi_serial::Encodable;
|
||||
use tracing::info;
|
||||
|
||||
use super::{Holder, TestHarness};
|
||||
@@ -39,7 +41,8 @@ use super::{Holder, TestHarness};
|
||||
impl TestHarness {
|
||||
/// Create a `Money::PoWReward` transaction for a given [`Holder`].
|
||||
///
|
||||
/// Optionally takes a specific reward recipient and a nonstandard reward value.
|
||||
/// Optionally takes a specific reward recipient and a nonstandard
|
||||
/// reward value.
|
||||
/// Returns the created [`Transaction`] and [`MoneyPoWRewardParamsV1`].
|
||||
async fn pow_reward(
|
||||
&mut self,
|
||||
@@ -48,7 +51,7 @@ impl TestHarness {
|
||||
reward: Option<u64>,
|
||||
fees: Option<u64>,
|
||||
) -> Result<(Transaction, MoneyPoWRewardParamsV1)> {
|
||||
let wallet = self.holders.get(holder).unwrap();
|
||||
let wallet = self.wallet(holder);
|
||||
|
||||
let (mint_pk, mint_zkbin) = self.proving_keys.get(MONEY_CONTRACT_ZKAS_MINT_NS_V1).unwrap();
|
||||
|
||||
@@ -56,11 +59,7 @@ impl TestHarness {
|
||||
let last_block = wallet.validator.read().await.blockchain.last_block()?;
|
||||
|
||||
// If there's a set reward recipient, use it, otherwise reward the holder
|
||||
let recipient = if let Some(holder) = recipient {
|
||||
Some(self.holders.get(holder).unwrap().keypair.public)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let recipient = recipient.map(|holder| self.wallet(holder).keypair.public);
|
||||
|
||||
// If there's fees paid, use them, otherwise set to zero
|
||||
let fees = fees.unwrap_or_default();
|
||||
@@ -84,7 +83,7 @@ impl TestHarness {
|
||||
|
||||
// Encode the transaction
|
||||
let mut data = vec![MoneyFunction::PoWRewardV1 as u8];
|
||||
debris.params.encode_async(&mut data).await?;
|
||||
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![])?;
|
||||
@@ -95,7 +94,7 @@ impl TestHarness {
|
||||
Ok((tx, debris.params))
|
||||
}
|
||||
|
||||
/// Generate and add an empty block to the given [`Holder`]s blockchains.
|
||||
/// Generate and add an empty block to the given [`Holder`]'s blockchains.
|
||||
/// The `miner` holder will produce the block and receive the reward.
|
||||
///
|
||||
/// Returns any found [`OwnCoin`]s.
|
||||
@@ -109,7 +108,7 @@ impl TestHarness {
|
||||
let (tx, params) = self.pow_reward(miner, None, None, None).await?;
|
||||
|
||||
// Fetch the last block in the blockchain
|
||||
let wallet = self.holders.get(miner).unwrap();
|
||||
let wallet = self.wallet(miner);
|
||||
let validator = wallet.validator.read().await;
|
||||
let previous = validator.blockchain.last_block()?;
|
||||
|
||||
@@ -141,33 +140,19 @@ impl TestHarness {
|
||||
)
|
||||
.await?;
|
||||
drop(validator);
|
||||
|
||||
let diff = overlay.lock().unwrap().overlay.lock().unwrap().diff(&[])?;
|
||||
block.header.state_root = overlay.lock().unwrap().contracts.update_state_monotree(&diff)?;
|
||||
|
||||
// Attach signature
|
||||
block.sign(&wallet.keypair.secret);
|
||||
|
||||
// For all holders, append the block
|
||||
// For all holders, append the block and process the reward output
|
||||
let mut found_owncoins = vec![];
|
||||
for holder in holders {
|
||||
let wallet = self.holders.get_mut(holder).unwrap();
|
||||
let wallet = self.wallet_mut(holder);
|
||||
wallet.validator.write().await.add_test_blocks(&[block.clone()]).await?;
|
||||
wallet.money_merkle_tree.append(MerkleNode::from(params.output.coin.inner()));
|
||||
|
||||
// Attempt to decrypt the note to see if this is a coin for the holder
|
||||
let Ok(note) = params.output.note.decrypt::<MoneyNote>(&wallet.keypair.secret) else {
|
||||
continue
|
||||
};
|
||||
|
||||
let owncoin = OwnCoin {
|
||||
coin: params.output.coin,
|
||||
note: note.clone(),
|
||||
secret: wallet.keypair.secret,
|
||||
leaf_position: wallet.money_merkle_tree.mark().unwrap(),
|
||||
};
|
||||
|
||||
wallet.unspent_money_coins.push(owncoin.clone());
|
||||
found_owncoins.push(owncoin);
|
||||
found_owncoins.extend(wallet.process_outputs(slice::from_ref(¶ms.output), holder));
|
||||
}
|
||||
|
||||
Ok(found_owncoins)
|
||||
|
||||
@@ -39,7 +39,7 @@ use darkfi_sdk::{
|
||||
pasta::pallas,
|
||||
ContractCall,
|
||||
};
|
||||
use darkfi_serial::AsyncEncodable;
|
||||
use darkfi_serial::Encodable;
|
||||
use rand::rngs::OsRng;
|
||||
use tracing::debug;
|
||||
|
||||
@@ -63,9 +63,9 @@ impl TestHarness {
|
||||
MoneyAuthTokenMintParamsV1,
|
||||
Option<MoneyFeeParamsV1>,
|
||||
)> {
|
||||
let wallet = self.holders.get(holder).unwrap();
|
||||
let wallet = self.wallet(holder);
|
||||
let mint_authority = wallet.token_mint_authority;
|
||||
let rcpt = self.holders.get(recipient).unwrap().keypair.public;
|
||||
let rcpt = self.wallet(recipient).keypair.public;
|
||||
|
||||
let (token_mint_pk, token_mint_zkbin) =
|
||||
self.proving_keys.get(MONEY_CONTRACT_ZKAS_TOKEN_MINT_NS_V1).unwrap();
|
||||
@@ -109,7 +109,7 @@ impl TestHarness {
|
||||
};
|
||||
let auth_debris = builder.build()?;
|
||||
let mut data = vec![MoneyFunction::AuthTokenMintV1 as u8];
|
||||
auth_debris.params.encode_async(&mut data).await?;
|
||||
auth_debris.params.encode(&mut data)?;
|
||||
let auth_call = ContractCall { contract_id: *MONEY_CONTRACT_ID, data };
|
||||
|
||||
// Create the minting call
|
||||
@@ -121,7 +121,7 @@ impl TestHarness {
|
||||
};
|
||||
let mint_debris = builder.build()?;
|
||||
let mut data = vec![MoneyFunction::TokenMintV1 as u8];
|
||||
mint_debris.params.encode_async(&mut data).await?;
|
||||
mint_debris.params.encode(&mut data)?;
|
||||
let mint_call = ContractCall { contract_id: *MONEY_CONTRACT_ID, data };
|
||||
|
||||
// Create the TransactionBuilder containing above calls
|
||||
@@ -180,35 +180,17 @@ impl TestHarness {
|
||||
block_height: u32,
|
||||
append: bool,
|
||||
) -> Result<Vec<OwnCoin>> {
|
||||
let wallet = self.holders.get_mut(holder).unwrap();
|
||||
let wallet = self.wallet_mut(holder);
|
||||
|
||||
// Execute the transaction
|
||||
wallet.add_transaction("money::token_mint", tx, block_height).await?;
|
||||
|
||||
// Iterate over all inputs to mark any spent coins
|
||||
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()));
|
||||
// Process the fee input (mark spent)
|
||||
found_owncoins.extend(wallet.process_fee(fee_params, holder));
|
||||
|
||||
// Attempt to decrypt the encrypted note of the minted token
|
||||
// Process the minted coin output
|
||||
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,
|
||||
@@ -216,30 +198,9 @@ impl TestHarness {
|
||||
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()));
|
||||
|
||||
// Attempt to decrypt the encrypted note in the fee output
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -252,7 +213,7 @@ impl TestHarness {
|
||||
holder: &Holder,
|
||||
block_height: u32,
|
||||
) -> Result<(Transaction, MoneyAuthTokenFreezeParamsV1, Option<MoneyFeeParamsV1>)> {
|
||||
let wallet = self.holders.get(holder).unwrap();
|
||||
let wallet = self.wallet(holder);
|
||||
let mint_authority = wallet.token_mint_authority;
|
||||
|
||||
let (auth_mint_pk, auth_mint_zkbin) =
|
||||
@@ -282,7 +243,7 @@ impl TestHarness {
|
||||
};
|
||||
let freeze_debris = builder.build()?;
|
||||
let mut data = vec![MoneyFunction::AuthTokenFreezeV1 as u8];
|
||||
freeze_debris.params.encode_async(&mut data).await?;
|
||||
freeze_debris.params.encode(&mut data)?;
|
||||
let freeze_call = ContractCall { contract_id: *MONEY_CONTRACT_ID, data };
|
||||
|
||||
// Create the TransactionBuilder containing the above call
|
||||
@@ -332,53 +293,14 @@ impl TestHarness {
|
||||
block_height: u32,
|
||||
append: bool,
|
||||
) -> Result<Vec<OwnCoin>> {
|
||||
let wallet = self.holders.get_mut(holder).unwrap();
|
||||
let wallet = self.wallet_mut(holder);
|
||||
|
||||
// Execute the transaction
|
||||
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()));
|
||||
|
||||
// Attempt to decrypt the encrypted note
|
||||
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);
|
||||
}
|
||||
}
|
||||
if !append {
|
||||
return Ok(vec![]);
|
||||
}
|
||||
|
||||
Ok(found_owncoins)
|
||||
Ok(wallet.process_fee(fee_params, holder))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,16 +21,12 @@ use darkfi::{
|
||||
Result,
|
||||
};
|
||||
use darkfi_money_contract::{
|
||||
client::{transfer_v1::make_transfer_call, MoneyNote, OwnCoin},
|
||||
client::{transfer_v1::make_transfer_call, OwnCoin},
|
||||
model::{MoneyFeeParamsV1, MoneyTransferParamsV1, TokenId},
|
||||
MoneyFunction, MONEY_CONTRACT_ZKAS_BURN_NS_V1, MONEY_CONTRACT_ZKAS_MINT_NS_V1,
|
||||
};
|
||||
use darkfi_sdk::{
|
||||
crypto::{contract_id::MONEY_CONTRACT_ID, MerkleNode},
|
||||
ContractCall,
|
||||
};
|
||||
use darkfi_serial::AsyncEncodable;
|
||||
use tracing::debug;
|
||||
use darkfi_sdk::{crypto::contract_id::MONEY_CONTRACT_ID, ContractCall};
|
||||
use darkfi_serial::Encodable;
|
||||
|
||||
use super::{Holder, TestHarness};
|
||||
|
||||
@@ -48,8 +44,8 @@ impl TestHarness {
|
||||
half_split: bool,
|
||||
) -> Result<(Transaction, (MoneyTransferParamsV1, Option<MoneyFeeParamsV1>), Vec<OwnCoin>)>
|
||||
{
|
||||
let wallet = self.holders.get(holder).unwrap();
|
||||
let rcpt = self.holders.get(recipient).unwrap().keypair.public;
|
||||
let wallet = self.wallet(holder);
|
||||
let rcpt = self.wallet(recipient).keypair.public;
|
||||
|
||||
let (mint_pk, mint_zkbin) = self.proving_keys.get(MONEY_CONTRACT_ZKAS_MINT_NS_V1).unwrap();
|
||||
let (burn_pk, burn_zkbin) = self.proving_keys.get(MONEY_CONTRACT_ZKAS_BURN_NS_V1).unwrap();
|
||||
@@ -73,20 +69,21 @@ impl TestHarness {
|
||||
|
||||
// Encode the call
|
||||
let mut data = vec![MoneyFunction::TransferV1 as u8];
|
||||
params.encode_async(&mut data).await?;
|
||||
params.encode(&mut data)?;
|
||||
let call = ContractCall { contract_id: *MONEY_CONTRACT_ID, data };
|
||||
|
||||
// Create the TransactionBuilder containing the `Transfer` call
|
||||
let mut tx_builder =
|
||||
TransactionBuilder::new(ContractCallLeaf { call, proofs: secrets.proofs }, vec![])?;
|
||||
|
||||
// If we have tx fees enabled, we first have to execute the fee-less tx to gather its
|
||||
// used gas, and then we feed it into the fee-creating function.
|
||||
// We also tell it about any spent coins so we don't accidentally reuse them in the
|
||||
// fee call.
|
||||
// TODO: We have to build a proper coin selection algorithm so that we can utilize
|
||||
// the Money::Transfer to merge any coins which would give us a coin with enough
|
||||
// value for paying the transaction fee.
|
||||
// If we have tx fees enabled, we first have to execute the fee-less
|
||||
// transaction to gather its used gas, and then we feed it into the
|
||||
// fee-creating function.
|
||||
// We also tell it about any spent coins so we don't accidentally
|
||||
// reuse them in the fee call.
|
||||
// TODO: We have to build a proper coin selection algorithm so that we
|
||||
// can utilize the Money::Transfer to merge any coins which would give
|
||||
// us a coin with enough value for paying the transaction fee.
|
||||
let mut fee_params = None;
|
||||
let mut fee_signature_secrets = None;
|
||||
if self.verify_fees {
|
||||
@@ -118,7 +115,7 @@ impl TestHarness {
|
||||
|
||||
/// Execute a `Money::Transfer` transaction for a given [`Holder`].
|
||||
///
|
||||
/// Returns any found [`OwnCoin`]s.
|
||||
/// Returns any found [`OwnCoin`]s
|
||||
pub async fn execute_transfer_tx(
|
||||
&mut self,
|
||||
holder: &Holder,
|
||||
@@ -128,95 +125,17 @@ impl TestHarness {
|
||||
block_height: u32,
|
||||
append: bool,
|
||||
) -> Result<Vec<OwnCoin>> {
|
||||
let wallet = self.holders.get_mut(holder).unwrap();
|
||||
let wallet = self.wallet_mut(holder);
|
||||
|
||||
// Execute the transaction
|
||||
wallet.add_transaction("money::transfer", tx, block_height).await?;
|
||||
|
||||
// Iterate over call inputs to mark any spent coins
|
||||
let nullifiers =
|
||||
call_params.inputs.iter().map(|i| i.nullifier.inner()).map(|l| (l, l)).collect();
|
||||
wallet.money_null_smt.insert_batch(nullifiers).expect("smt.insert_batch()");
|
||||
// Always insert nullifiers into SMT (needed for state consistency)
|
||||
wallet.process_inputs(&call_params.inputs, holder);
|
||||
|
||||
let mut found_owncoins = vec![];
|
||||
if append {
|
||||
for input in &call_params.inputs {
|
||||
if let Some(spent_coin) = wallet
|
||||
.unspent_money_coins
|
||||
.iter()
|
||||
.find(|x| x.nullifier() == input.nullifier)
|
||||
.cloned()
|
||||
{
|
||||
debug!("Found spent OwnCoin({}) for {:?}", spent_coin.coin, holder);
|
||||
wallet.unspent_money_coins.retain(|x| x.nullifier() != input.nullifier);
|
||||
wallet.spent_money_coins.push(spent_coin.clone());
|
||||
}
|
||||
}
|
||||
|
||||
// Iterate over call outputs to find any new OwnCoins
|
||||
for output in &call_params.outputs {
|
||||
wallet.money_merkle_tree.append(MerkleNode::from(output.coin.inner()));
|
||||
|
||||
// Attempt to decrypt the output note to see if this is a coin for the holder.
|
||||
let Ok(note) = output.note.decrypt::<MoneyNote>(&wallet.keypair.secret) else {
|
||||
continue
|
||||
};
|
||||
|
||||
let owncoin = OwnCoin {
|
||||
coin: 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);
|
||||
}
|
||||
}
|
||||
|
||||
// Handle fee call
|
||||
if let Some(ref fee_params) = fee_params {
|
||||
// Process call input to mark any spent coins
|
||||
let nullifier = fee_params.input.nullifier.inner();
|
||||
wallet
|
||||
.money_null_smt
|
||||
.insert_batch(vec![(nullifier, nullifier)])
|
||||
.expect("smt.insert_batch()");
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
// Process call output to find any new OwnCoins
|
||||
wallet.money_merkle_tree.append(MerkleNode::from(fee_params.output.coin.inner()));
|
||||
|
||||
// Attempt to decrypt the output note to see if this is a coin for the holder.
|
||||
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);
|
||||
};
|
||||
}
|
||||
found_owncoins.extend(wallet.process_outputs(&call_params.outputs, holder));
|
||||
found_owncoins.extend(wallet.process_fee(fee_params, holder));
|
||||
}
|
||||
|
||||
Ok(found_owncoins)
|
||||
|
||||
Reference in New Issue
Block a user