mirror of
https://github.com/darkrenaissance/darkfi.git
synced 2026-04-28 03:00:18 -04:00
contract/money: Rework test harness, fix TokenMint_V1 proof.
This commit is contained in:
@@ -14,6 +14,8 @@ contract "TokenMint_V1" {
|
||||
Base rcpt_x,
|
||||
# Recipient's public key y coordinate
|
||||
Base rcpt_y,
|
||||
# Unique serial number for the minted coin
|
||||
Base serial,
|
||||
# Random blinding factor for the minted coin
|
||||
Base coin_blind,
|
||||
# Allows composing this ZK proof to invoke other contracts
|
||||
@@ -45,6 +47,7 @@ circuit "TokenMint_V1" {
|
||||
rcpt_y,
|
||||
supply,
|
||||
token_id,
|
||||
serial,
|
||||
spend_hook,
|
||||
user_data,
|
||||
coin_blind,
|
||||
|
||||
@@ -206,6 +206,7 @@ pub(crate) fn create_token_mint_proof(
|
||||
Witness::Base(Value::known(pallas::Base::from(output.value))),
|
||||
Witness::Base(Value::known(rcpt_x)),
|
||||
Witness::Base(Value::known(rcpt_y)),
|
||||
Witness::Base(Value::known(serial)),
|
||||
Witness::Base(Value::known(coin_blind)),
|
||||
Witness::Base(Value::known(spend_hook)),
|
||||
Witness::Base(Value::known(user_data)),
|
||||
|
||||
@@ -57,7 +57,7 @@ pub(crate) fn money_mint_get_metadata_v1(
|
||||
signature_pubkeys.push(params.input.signature_public);
|
||||
|
||||
let value_coords = params.output.value_commit.to_affine().coordinates().unwrap();
|
||||
let token_coords = params.output.value_commit.to_affine().coordinates().unwrap();
|
||||
let token_coords = params.output.token_commit.to_affine().coordinates().unwrap();
|
||||
|
||||
// Since we expect a signature from the mint authority, we use those coordinates
|
||||
// as public inputs for the ZK proof:
|
||||
|
||||
@@ -23,218 +23,140 @@ use darkfi::{
|
||||
TESTNET_GENESIS_TIMESTAMP, TESTNET_INITIAL_DISTRIBUTION,
|
||||
},
|
||||
tx::Transaction,
|
||||
wallet::WalletDb,
|
||||
zk::{empty_witnesses, ProvingKey, ZkCircuit},
|
||||
wallet::{WalletDb, WalletPtr},
|
||||
zk::{empty_witnesses, halo2::Field, ProvingKey, ZkCircuit},
|
||||
zkas::ZkBinary,
|
||||
Result,
|
||||
};
|
||||
use darkfi_money_contract::client::OwnCoin;
|
||||
use darkfi_sdk::{
|
||||
crypto::{
|
||||
pasta_prelude::*, ContractId, Keypair, MerkleTree, PublicKey, DARK_TOKEN_ID,
|
||||
MONEY_CONTRACT_ID,
|
||||
},
|
||||
crypto::{Keypair, MerkleTree, PublicKey, DARK_TOKEN_ID, MONEY_CONTRACT_ID},
|
||||
db::SMART_CONTRACT_ZKAS_DB_NAME,
|
||||
pasta::pallas,
|
||||
ContractCall,
|
||||
};
|
||||
use darkfi_serial::{serialize, Encodable};
|
||||
use log::{info, warn};
|
||||
use rand::rngs::OsRng;
|
||||
|
||||
use darkfi_money_contract::{
|
||||
client::transfer_v1::TransferCallBuilder, model::MoneyTransferParamsV1 as MoneyTransferParams,
|
||||
MoneyFunction::TransferV1 as MoneyTransfer, MONEY_CONTRACT_ZKAS_BURN_NS_V1,
|
||||
MONEY_CONTRACT_ZKAS_MINT_NS_V1, MONEY_CONTRACT_ZKAS_TOKEN_MINT_NS_V1,
|
||||
client::{mint_v1::MintCallBuilder, transfer_v1::TransferCallBuilder},
|
||||
model::{MoneyMintParamsV1, MoneyTransferParamsV1},
|
||||
MoneyFunction, MONEY_CONTRACT_ZKAS_BURN_NS_V1, MONEY_CONTRACT_ZKAS_MINT_NS_V1,
|
||||
MONEY_CONTRACT_ZKAS_TOKEN_FRZ_NS_V1, MONEY_CONTRACT_ZKAS_TOKEN_MINT_NS_V1,
|
||||
};
|
||||
|
||||
pub fn init_logger() -> Result<()> {
|
||||
pub fn init_logger() {
|
||||
let mut cfg = simplelog::ConfigBuilder::new();
|
||||
cfg.add_filter_ignore("sled".to_string());
|
||||
if let Err(_) = simplelog::TermLogger::init(
|
||||
//simplelog::LevelFilter::Info,
|
||||
cfg.add_filter_ignore("blockchain::contractstore".to_string());
|
||||
let _ = simplelog::TermLogger::init(
|
||||
simplelog::LevelFilter::Debug,
|
||||
//simplelog::LevelFilter::Trace,
|
||||
cfg.build(),
|
||||
simplelog::TerminalMode::Mixed,
|
||||
simplelog::ColorChoice::Auto,
|
||||
) {
|
||||
warn!(target: "money_harness", "Logger already initialized");
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
pub struct Wallet {
|
||||
pub keypair: Keypair,
|
||||
pub state: ValidatorStatePtr,
|
||||
pub merkle_tree: MerkleTree,
|
||||
pub wallet: WalletPtr,
|
||||
pub coins: Vec<OwnCoin>,
|
||||
pub spent_coins: Vec<OwnCoin>,
|
||||
}
|
||||
|
||||
impl Wallet {
|
||||
async fn new(keypair: Keypair, faucet_pubkeys: &[PublicKey]) -> Result<Self> {
|
||||
let wallet = WalletDb::new("sqlite::memory:", "foo").await?;
|
||||
let sled_db = sled::Config::new().temporary(true).open()?;
|
||||
|
||||
let state = ValidatorState::new(
|
||||
&sled_db,
|
||||
*TESTNET_BOOTSTRAP_TIMESTAMP,
|
||||
*TESTNET_GENESIS_TIMESTAMP,
|
||||
*TESTNET_GENESIS_HASH_BYTES,
|
||||
*TESTNET_INITIAL_DISTRIBUTION,
|
||||
wallet.clone(),
|
||||
faucet_pubkeys.to_vec(),
|
||||
false,
|
||||
false,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let merkle_tree = MerkleTree::new(100);
|
||||
|
||||
let coins = vec![];
|
||||
let spent_coins = vec![];
|
||||
|
||||
Ok(Self { keypair, state, merkle_tree, wallet, coins, spent_coins })
|
||||
}
|
||||
}
|
||||
|
||||
pub struct MoneyTestHarness {
|
||||
pub faucet_kp: Keypair,
|
||||
pub alice_kp: Keypair,
|
||||
pub bob_kp: Keypair,
|
||||
pub charlie_kp: Keypair,
|
||||
pub faucet_pubkeys: Vec<PublicKey>,
|
||||
pub faucet_state: ValidatorStatePtr,
|
||||
pub alice_state: ValidatorStatePtr,
|
||||
pub bob_state: ValidatorStatePtr,
|
||||
pub charlie_state: ValidatorStatePtr,
|
||||
pub money_contract_id: ContractId,
|
||||
pub proving_keys: HashMap<[u8; 32], Vec<(&'static str, ProvingKey)>>,
|
||||
pub mint_zkbin: ZkBinary,
|
||||
pub burn_zkbin: ZkBinary,
|
||||
pub token_mint_zkbin: ZkBinary,
|
||||
pub mint_pk: ProvingKey,
|
||||
pub burn_pk: ProvingKey,
|
||||
pub token_mint_pk: ProvingKey,
|
||||
pub faucet_merkle_tree: MerkleTree,
|
||||
pub alice_merkle_tree: MerkleTree,
|
||||
pub bob_merkle_tree: MerkleTree,
|
||||
pub charlie_merkle_tree: MerkleTree,
|
||||
pub faucet: Wallet,
|
||||
pub alice: Wallet,
|
||||
pub bob: Wallet,
|
||||
pub charlie: Wallet,
|
||||
pub proving_keys: HashMap<&'static str, (ProvingKey, ZkBinary)>,
|
||||
}
|
||||
|
||||
impl MoneyTestHarness {
|
||||
pub async fn new() -> Result<Self> {
|
||||
let faucet_kp = Keypair::random(&mut OsRng);
|
||||
let alice_kp = Keypair::random(&mut OsRng);
|
||||
let bob_kp = Keypair::random(&mut OsRng);
|
||||
let charlie_kp = Keypair::random(&mut OsRng);
|
||||
let faucet_pubkeys = vec![faucet_kp.public];
|
||||
let faucet = Wallet::new(faucet_kp, &faucet_pubkeys).await?;
|
||||
|
||||
let faucet_wallet = WalletDb::new("sqlite::memory:", "foo").await?;
|
||||
let alice_wallet = WalletDb::new("sqlite::memory:", "foo").await?;
|
||||
let bob_wallet = WalletDb::new("sqlite::memory:", "foo").await?;
|
||||
let charlie_wallet = WalletDb::new("sqlite::memory:", "foo").await?;
|
||||
let alice_kp = Keypair::random(&mut OsRng);
|
||||
let alice = Wallet::new(alice_kp, &faucet_pubkeys).await?;
|
||||
|
||||
let faucet_sled_db = sled::Config::new().temporary(true).open()?;
|
||||
let alice_sled_db = sled::Config::new().temporary(true).open()?;
|
||||
let bob_sled_db = sled::Config::new().temporary(true).open()?;
|
||||
let charlie_sled_db = sled::Config::new().temporary(true).open()?;
|
||||
let bob_kp = Keypair::random(&mut OsRng);
|
||||
let bob = Wallet::new(bob_kp, &faucet_pubkeys).await?;
|
||||
|
||||
let faucet_state = ValidatorState::new(
|
||||
&faucet_sled_db,
|
||||
*TESTNET_BOOTSTRAP_TIMESTAMP,
|
||||
*TESTNET_GENESIS_TIMESTAMP,
|
||||
*TESTNET_GENESIS_HASH_BYTES,
|
||||
*TESTNET_INITIAL_DISTRIBUTION,
|
||||
faucet_wallet,
|
||||
faucet_pubkeys.clone(),
|
||||
false,
|
||||
false,
|
||||
)
|
||||
.await?;
|
||||
let charlie_kp = Keypair::random(&mut OsRng);
|
||||
let charlie = Wallet::new(charlie_kp, &faucet_pubkeys).await?;
|
||||
|
||||
let alice_state = ValidatorState::new(
|
||||
&alice_sled_db,
|
||||
*TESTNET_BOOTSTRAP_TIMESTAMP,
|
||||
*TESTNET_GENESIS_TIMESTAMP,
|
||||
*TESTNET_GENESIS_HASH_BYTES,
|
||||
*TESTNET_INITIAL_DISTRIBUTION,
|
||||
alice_wallet,
|
||||
faucet_pubkeys.clone(),
|
||||
false,
|
||||
false,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let bob_state = ValidatorState::new(
|
||||
&bob_sled_db,
|
||||
*TESTNET_BOOTSTRAP_TIMESTAMP,
|
||||
*TESTNET_GENESIS_TIMESTAMP,
|
||||
*TESTNET_GENESIS_HASH_BYTES,
|
||||
*TESTNET_INITIAL_DISTRIBUTION,
|
||||
bob_wallet,
|
||||
faucet_pubkeys.clone(),
|
||||
false,
|
||||
false,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let charlie_state = ValidatorState::new(
|
||||
&charlie_sled_db,
|
||||
*TESTNET_BOOTSTRAP_TIMESTAMP,
|
||||
*TESTNET_GENESIS_TIMESTAMP,
|
||||
*TESTNET_GENESIS_HASH_BYTES,
|
||||
*TESTNET_INITIAL_DISTRIBUTION,
|
||||
charlie_wallet,
|
||||
faucet_pubkeys.clone(),
|
||||
false,
|
||||
false,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let money_contract_id = *MONEY_CONTRACT_ID;
|
||||
|
||||
let alice_sled = alice_state.read().await.blockchain.sled_db.clone();
|
||||
let db_handle = alice_state.read().await.blockchain.contracts.lookup(
|
||||
// Get the zkas circuits and build proving keys
|
||||
let mut proving_keys = HashMap::new();
|
||||
let alice_sled = alice.state.read().await.blockchain.sled_db.clone();
|
||||
let db_handle = alice.state.read().await.blockchain.contracts.lookup(
|
||||
&alice_sled,
|
||||
&money_contract_id,
|
||||
&MONEY_CONTRACT_ID,
|
||||
SMART_CONTRACT_ZKAS_DB_NAME,
|
||||
)?;
|
||||
|
||||
let mint_zkbin = db_handle.get(&serialize(&MONEY_CONTRACT_ZKAS_MINT_NS_V1))?.unwrap();
|
||||
let burn_zkbin = db_handle.get(&serialize(&MONEY_CONTRACT_ZKAS_BURN_NS_V1))?.unwrap();
|
||||
let token_mint_zkbin =
|
||||
db_handle.get(&serialize(&MONEY_CONTRACT_ZKAS_TOKEN_MINT_NS_V1))?.unwrap();
|
||||
info!(target: "money_harness", "Decoding bincode");
|
||||
let mint_zkbin = ZkBinary::decode(&mint_zkbin)?;
|
||||
let burn_zkbin = ZkBinary::decode(&burn_zkbin)?;
|
||||
let token_mint_zkbin = ZkBinary::decode(&token_mint_zkbin)?;
|
||||
let mint_witnesses = empty_witnesses(&mint_zkbin);
|
||||
let burn_witnesses = empty_witnesses(&burn_zkbin);
|
||||
let token_mint_witnesses = empty_witnesses(&token_mint_zkbin);
|
||||
let mint_circuit = ZkCircuit::new(mint_witnesses, mint_zkbin.clone());
|
||||
let burn_circuit = ZkCircuit::new(burn_witnesses, burn_zkbin.clone());
|
||||
let token_mint_circuit = ZkCircuit::new(token_mint_witnesses, token_mint_zkbin.clone());
|
||||
macro_rules! mkpk {
|
||||
($ns:expr) => {
|
||||
let zkbin = db_handle.get(&serialize(&$ns))?.unwrap();
|
||||
let zkbin = ZkBinary::decode(&zkbin)?;
|
||||
let witnesses = empty_witnesses(&zkbin);
|
||||
let circuit = ZkCircuit::new(witnesses, zkbin.clone());
|
||||
let pk = ProvingKey::build(13, &circuit);
|
||||
proving_keys.insert($ns, (pk, zkbin));
|
||||
};
|
||||
}
|
||||
|
||||
info!(target: "money_harness", "Creating zk proving keys");
|
||||
let k = 13;
|
||||
let mut proving_keys = HashMap::<[u8; 32], Vec<(&str, ProvingKey)>>::new();
|
||||
let mint_pk = ProvingKey::build(k, &mint_circuit);
|
||||
let burn_pk = ProvingKey::build(k, &burn_circuit);
|
||||
let token_mint_pk = ProvingKey::build(k, &token_mint_circuit);
|
||||
let pks = vec![
|
||||
(MONEY_CONTRACT_ZKAS_MINT_NS_V1, mint_pk.clone()),
|
||||
(MONEY_CONTRACT_ZKAS_BURN_NS_V1, burn_pk.clone()),
|
||||
(MONEY_CONTRACT_ZKAS_TOKEN_MINT_NS_V1, mint_pk.clone()),
|
||||
];
|
||||
proving_keys.insert(money_contract_id.inner().to_repr(), pks);
|
||||
mkpk!(MONEY_CONTRACT_ZKAS_MINT_NS_V1);
|
||||
mkpk!(MONEY_CONTRACT_ZKAS_BURN_NS_V1);
|
||||
mkpk!(MONEY_CONTRACT_ZKAS_TOKEN_MINT_NS_V1);
|
||||
mkpk!(MONEY_CONTRACT_ZKAS_TOKEN_FRZ_NS_V1);
|
||||
|
||||
let faucet_merkle_tree = MerkleTree::new(100);
|
||||
let alice_merkle_tree = MerkleTree::new(100);
|
||||
let bob_merkle_tree = MerkleTree::new(100);
|
||||
let charlie_merkle_tree = MerkleTree::new(100);
|
||||
|
||||
Ok(Self {
|
||||
faucet_kp,
|
||||
alice_kp,
|
||||
bob_kp,
|
||||
charlie_kp,
|
||||
faucet_pubkeys,
|
||||
faucet_state,
|
||||
alice_state,
|
||||
bob_state,
|
||||
charlie_state,
|
||||
money_contract_id,
|
||||
proving_keys,
|
||||
mint_pk,
|
||||
burn_pk,
|
||||
token_mint_pk,
|
||||
mint_zkbin,
|
||||
burn_zkbin,
|
||||
token_mint_zkbin,
|
||||
faucet_merkle_tree,
|
||||
alice_merkle_tree,
|
||||
bob_merkle_tree,
|
||||
charlie_merkle_tree,
|
||||
})
|
||||
Ok(Self { faucet, alice, bob, charlie, proving_keys })
|
||||
}
|
||||
|
||||
pub fn airdrop(
|
||||
pub fn airdrop_native(
|
||||
&self,
|
||||
amount: u64,
|
||||
rcpt: PublicKey,
|
||||
) -> Result<(Transaction, MoneyTransferParams)> {
|
||||
// TODO: verify change usage is correct
|
||||
let call_debris = TransferCallBuilder {
|
||||
keypair: self.faucet_kp,
|
||||
recipient: rcpt,
|
||||
value: amount,
|
||||
value: u64,
|
||||
recipient: PublicKey,
|
||||
) -> Result<(Transaction, MoneyTransferParamsV1)> {
|
||||
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();
|
||||
|
||||
let builder = TransferCallBuilder {
|
||||
keypair: self.faucet.keypair,
|
||||
recipient,
|
||||
value,
|
||||
token_id: *DARK_TOKEN_ID,
|
||||
rcpt_spend_hook: pallas::Base::zero(),
|
||||
rcpt_user_data: pallas::Base::zero(),
|
||||
@@ -243,25 +165,56 @@ impl MoneyTestHarness {
|
||||
change_user_data: pallas::Base::zero(),
|
||||
change_user_data_blind: pallas::Base::random(&mut OsRng),
|
||||
coins: vec![],
|
||||
tree: self.faucet_merkle_tree.clone(),
|
||||
mint_zkbin: self.mint_zkbin.clone(),
|
||||
mint_pk: self.mint_pk.clone(),
|
||||
burn_zkbin: self.burn_zkbin.clone(),
|
||||
burn_pk: self.burn_pk.clone(),
|
||||
tree: self.faucet.merkle_tree.clone(),
|
||||
mint_zkbin: mint_zkbin.clone(),
|
||||
mint_pk: mint_pk.clone(),
|
||||
burn_zkbin: burn_zkbin.clone(),
|
||||
burn_pk: burn_pk.clone(),
|
||||
clear_input: true,
|
||||
}
|
||||
.build()?;
|
||||
};
|
||||
|
||||
let contract_id = *MONEY_CONTRACT_ID;
|
||||
let debris = builder.build()?;
|
||||
|
||||
let mut data = vec![MoneyTransfer as u8];
|
||||
call_debris.params.encode(&mut data)?;
|
||||
let calls = vec![ContractCall { contract_id, data }];
|
||||
let proofs = vec![call_debris.proofs];
|
||||
let mut data = vec![MoneyFunction::TransferV1 as u8];
|
||||
debris.params.encode(&mut data)?;
|
||||
let calls = vec![ContractCall { contract_id: *MONEY_CONTRACT_ID, data }];
|
||||
let proofs = vec![debris.proofs];
|
||||
let mut tx = Transaction { calls, proofs, signatures: vec![] };
|
||||
let sigs = tx.create_sigs(&mut OsRng, &call_debris.signature_secrets)?;
|
||||
let sigs = tx.create_sigs(&mut OsRng, &debris.signature_secrets)?;
|
||||
tx.signatures = vec![sigs];
|
||||
|
||||
Ok((tx, call_debris.params))
|
||||
Ok((tx, debris.params))
|
||||
}
|
||||
|
||||
pub fn mint_token(
|
||||
&self,
|
||||
mint_authority: Keypair,
|
||||
amount: u64,
|
||||
recipient: PublicKey,
|
||||
) -> Result<(Transaction, MoneyMintParamsV1)> {
|
||||
let (token_mint_pk, token_mint_zkbin) =
|
||||
self.proving_keys.get(&MONEY_CONTRACT_ZKAS_TOKEN_MINT_NS_V1).unwrap();
|
||||
|
||||
let builder = MintCallBuilder {
|
||||
mint_authority,
|
||||
recipient,
|
||||
amount,
|
||||
spend_hook: pallas::Base::zero(),
|
||||
user_data: pallas::Base::zero(),
|
||||
token_mint_zkbin: token_mint_zkbin.clone(),
|
||||
token_mint_pk: token_mint_pk.clone(),
|
||||
};
|
||||
|
||||
let debris = builder.build()?;
|
||||
|
||||
let mut data = vec![MoneyFunction::MintV1 as u8];
|
||||
debris.params.encode(&mut data)?;
|
||||
let calls = vec![ContractCall { contract_id: *MONEY_CONTRACT_ID, data }];
|
||||
let proofs = vec![debris.proofs];
|
||||
let mut tx = Transaction { calls, proofs, signatures: vec![] };
|
||||
let sigs = tx.create_sigs(&mut OsRng, &[mint_authority.secret])?;
|
||||
tx.signatures = vec![sigs];
|
||||
|
||||
Ok((tx, debris.params))
|
||||
}
|
||||
}
|
||||
|
||||
167
src/contract/money/tests/integration.rs
Normal file
167
src/contract/money/tests/integration.rs
Normal file
@@ -0,0 +1,167 @@
|
||||
/* This file is part of DarkFi (https://dark.fi)
|
||||
*
|
||||
* Copyright (C) 2020-2023 Dyne.org foundation
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
//! Integration test for functionalities of the money smart contract:
|
||||
//!
|
||||
//! * Airdrops of the native token from the faucet
|
||||
//! * Arbitrary token minting
|
||||
//! * Transfers/Payments
|
||||
//! * Atomic swaps
|
||||
//! * Token mint freezing
|
||||
//!
|
||||
//! With this test we want to confirm the money contract state transitions
|
||||
//! work between multiple parties and are able to be verified.
|
||||
//!
|
||||
//! TODO: Malicious cases
|
||||
|
||||
use darkfi::{tx::Transaction, Result};
|
||||
use darkfi_sdk::{
|
||||
crypto::{poseidon_hash, Keypair, MerkleNode, Nullifier, MONEY_CONTRACT_ID},
|
||||
incrementalmerkletree::Tree,
|
||||
ContractCall,
|
||||
};
|
||||
use darkfi_serial::Encodable;
|
||||
use log::info;
|
||||
use rand::rngs::OsRng;
|
||||
|
||||
use darkfi_money_contract::{
|
||||
client::{freeze_v1::FreezeCallBuilder, MoneyNote, OwnCoin},
|
||||
MoneyFunction, MONEY_CONTRACT_ZKAS_TOKEN_FRZ_NS_V1,
|
||||
};
|
||||
|
||||
mod harness;
|
||||
use harness::{init_logger, MoneyTestHarness};
|
||||
|
||||
#[async_std::test]
|
||||
async fn money_integration() -> Result<()> {
|
||||
init_logger();
|
||||
|
||||
let mut th = MoneyTestHarness::new().await?;
|
||||
|
||||
// Let's first airdrop some tokens to Alice.
|
||||
let (alice_airdrop_tx, alice_airdrop_params) =
|
||||
th.airdrop_native(200, th.alice.keypair.public)?;
|
||||
|
||||
info!("[Faucet] Executing Alice airdrop tx");
|
||||
th.faucet.state.read().await.verify_transactions(&[alice_airdrop_tx.clone()], true).await?;
|
||||
th.faucet.merkle_tree.append(&MerkleNode::from(alice_airdrop_params.outputs[0].coin.inner()));
|
||||
|
||||
info!("[Alice] Executing Alice airdrop tx");
|
||||
th.alice.state.read().await.verify_transactions(&[alice_airdrop_tx.clone()], true).await?;
|
||||
th.alice.merkle_tree.append(&MerkleNode::from(alice_airdrop_params.outputs[0].coin.inner()));
|
||||
// Alice has to witness this coin because it's hers.
|
||||
let leaf_position = th.alice.merkle_tree.witness().unwrap();
|
||||
|
||||
info!("[Bob] Executing Alice airdrop tx");
|
||||
th.bob.state.read().await.verify_transactions(&[alice_airdrop_tx.clone()], true).await?;
|
||||
th.bob.merkle_tree.append(&MerkleNode::from(alice_airdrop_params.outputs[0].coin.inner()));
|
||||
|
||||
info!("[Charlie] Executing Alice airdrop tx");
|
||||
th.charlie.state.read().await.verify_transactions(&[alice_airdrop_tx.clone()], true).await?;
|
||||
th.charlie.merkle_tree.append(&MerkleNode::from(alice_airdrop_params.outputs[0].coin.inner()));
|
||||
|
||||
assert_eq!(th.alice.merkle_tree.root(0).unwrap(), th.bob.merkle_tree.root(0).unwrap());
|
||||
assert_eq!(th.bob.merkle_tree.root(0).unwrap(), th.charlie.merkle_tree.root(0).unwrap());
|
||||
assert_eq!(th.faucet.merkle_tree.root(0).unwrap(), th.charlie.merkle_tree.root(0).unwrap());
|
||||
|
||||
// Alice builds an `OwnCoin` from her airdrop.
|
||||
let note: MoneyNote = alice_airdrop_params.outputs[0].note.decrypt(&th.alice.keypair.secret)?;
|
||||
let owncoin = OwnCoin {
|
||||
coin: alice_airdrop_params.outputs[0].coin,
|
||||
note: note.clone(),
|
||||
secret: th.alice.keypair.secret,
|
||||
nullifier: Nullifier::from(poseidon_hash([th.alice.keypair.secret.inner(), note.serial])),
|
||||
leaf_position,
|
||||
};
|
||||
th.alice.coins.push(owncoin);
|
||||
|
||||
// Bob creates a new mint authority keypair and mints some tokens for Charlie.
|
||||
let bob_token_authority = Keypair::random(&mut OsRng);
|
||||
let (bob_charlie_mint_tx, bob_charlie_mint_params) =
|
||||
th.mint_token(bob_token_authority, 500, th.charlie.keypair.public)?;
|
||||
|
||||
info!("[Faucet] Executing BOBTOKEN mint to Charlie");
|
||||
th.faucet.state.read().await.verify_transactions(&[bob_charlie_mint_tx.clone()], true).await?;
|
||||
th.faucet.merkle_tree.append(&MerkleNode::from(bob_charlie_mint_params.output.coin.inner()));
|
||||
|
||||
info!("[Alice] Executing BOBTOKEN mint to Charlie");
|
||||
th.alice.state.read().await.verify_transactions(&[bob_charlie_mint_tx.clone()], true).await?;
|
||||
th.alice.merkle_tree.append(&MerkleNode::from(bob_charlie_mint_params.output.coin.inner()));
|
||||
|
||||
info!("[Bob] Executing BOBTOKEN mint to Charlie");
|
||||
th.bob.state.read().await.verify_transactions(&[bob_charlie_mint_tx.clone()], true).await?;
|
||||
th.bob.merkle_tree.append(&MerkleNode::from(bob_charlie_mint_params.output.coin.inner()));
|
||||
|
||||
info!("[Charlie] Executing BOBTOKEN mint to Charlie");
|
||||
th.charlie.state.read().await.verify_transactions(&[bob_charlie_mint_tx.clone()], true).await?;
|
||||
th.charlie.merkle_tree.append(&MerkleNode::from(bob_charlie_mint_params.output.coin.inner()));
|
||||
// Charlie has to witness this coin because it's his.
|
||||
let leaf_position = th.charlie.merkle_tree.witness().unwrap();
|
||||
|
||||
assert_eq!(th.alice.merkle_tree.root(0).unwrap(), th.bob.merkle_tree.root(0).unwrap());
|
||||
assert_eq!(th.bob.merkle_tree.root(0).unwrap(), th.charlie.merkle_tree.root(0).unwrap());
|
||||
assert_eq!(th.faucet.merkle_tree.root(0).unwrap(), th.charlie.merkle_tree.root(0).unwrap());
|
||||
|
||||
// Charlie builds an `OwnCoin` from this mint.
|
||||
let note: MoneyNote =
|
||||
bob_charlie_mint_params.output.note.decrypt(&th.charlie.keypair.secret)?;
|
||||
|
||||
let owncoin = OwnCoin {
|
||||
coin: bob_charlie_mint_params.output.coin,
|
||||
note: note.clone(),
|
||||
secret: th.charlie.keypair.secret,
|
||||
nullifier: Nullifier::from(poseidon_hash([th.charlie.keypair.secret.inner(), note.serial])),
|
||||
leaf_position,
|
||||
};
|
||||
th.charlie.coins.push(owncoin);
|
||||
|
||||
// Let's attempt to freeze the BOBTOKEN mint,
|
||||
// and after that we shouldn't be able to mint anymore.
|
||||
let (token_freeze_pk, token_freeze_zkbin) =
|
||||
th.proving_keys.get(&MONEY_CONTRACT_ZKAS_TOKEN_FRZ_NS_V1).unwrap();
|
||||
|
||||
let bob_frz_builder = FreezeCallBuilder {
|
||||
mint_authority: bob_token_authority,
|
||||
token_freeze_zkbin: token_freeze_zkbin.clone(),
|
||||
token_freeze_pk: token_freeze_pk.clone(),
|
||||
};
|
||||
|
||||
let debris = bob_frz_builder.build()?;
|
||||
let mut data = vec![MoneyFunction::FreezeV1 as u8];
|
||||
debris.params.encode(&mut data)?;
|
||||
let calls = vec![ContractCall { contract_id: *MONEY_CONTRACT_ID, data }];
|
||||
let proofs = vec![debris.proofs];
|
||||
let mut bob_frz_tx = Transaction { calls, proofs, signatures: vec![] };
|
||||
let sigs = bob_frz_tx.create_sigs(&mut OsRng, &[bob_token_authority.secret])?;
|
||||
bob_frz_tx.signatures = vec![sigs];
|
||||
|
||||
info!("[Faucet] Executing BOBTOKEN freeze");
|
||||
th.faucet.state.read().await.verify_transactions(&[bob_frz_tx.clone()], true).await?;
|
||||
|
||||
info!("[Alice] Executing BOBTOKEN freeze");
|
||||
th.alice.state.read().await.verify_transactions(&[bob_frz_tx.clone()], true).await?;
|
||||
|
||||
info!("[Bob] Executing BOBTOKEN freeze");
|
||||
th.bob.state.read().await.verify_transactions(&[bob_frz_tx.clone()], true).await?;
|
||||
|
||||
info!("[Charlie] Executing BOBTOKEN freeze");
|
||||
th.charlie.state.read().await.verify_transactions(&[bob_frz_tx.clone()], true).await?;
|
||||
|
||||
// Thanks for reading
|
||||
Ok(())
|
||||
}
|
||||
Reference in New Issue
Block a user