darkfi_contract_test_harness/
contract_deploy.rs

1/* This file is part of DarkFi (https://dark.fi)
2 *
3 * Copyright (C) 2020-2025 Dyne.org foundation
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Affero General Public License as
7 * published by the Free Software Foundation, either version 3 of the
8 * License, or (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 * GNU Affero General Public License for more details.
14 *
15 * You should have received a copy of the GNU Affero General Public License
16 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
17 */
18
19use darkfi::{
20    tx::{ContractCallLeaf, Transaction, TransactionBuilder},
21    Result,
22};
23use darkfi_deployooor_contract::{client::deploy_v1::DeployCallBuilder, DeployFunction};
24use darkfi_money_contract::{
25    client::{MoneyNote, OwnCoin},
26    model::MoneyFeeParamsV1,
27};
28use darkfi_sdk::{
29    crypto::{contract_id::DEPLOYOOOR_CONTRACT_ID, MerkleNode},
30    deploy::DeployParamsV1,
31    ContractCall,
32};
33use darkfi_serial::AsyncEncodable;
34use log::debug;
35
36use super::{Holder, TestHarness};
37
38impl TestHarness {
39    /// Create a `Deployooor::Deploy` transaction with the given WASM bincode.
40    ///
41    /// Returns the [`Transaction`], and necessary parameters.
42    pub async fn deploy_contract(
43        &mut self,
44        holder: &Holder,
45        wasm_bincode: Vec<u8>,
46        block_height: u32,
47    ) -> Result<(Transaction, DeployParamsV1, Option<MoneyFeeParamsV1>)> {
48        let wallet = self.holders.get(holder).unwrap();
49        let deploy_keypair = wallet.contract_deploy_authority;
50
51        // Build the contract call
52        let builder = DeployCallBuilder { deploy_keypair, wasm_bincode, deploy_ix: vec![] };
53        let debris = builder.build()?;
54
55        // Encode the call
56        let mut data = vec![DeployFunction::DeployV1 as u8];
57        debris.params.encode_async(&mut data).await?;
58        let call = ContractCall { contract_id: *DEPLOYOOOR_CONTRACT_ID, data };
59        let mut tx_builder =
60            TransactionBuilder::new(ContractCallLeaf { call, proofs: vec![] }, vec![])?;
61
62        // If we have tx fees enabled, make an offering
63        let mut fee_params = None;
64        let mut fee_signature_secrets = None;
65        if self.verify_fees {
66            let mut tx = tx_builder.build()?;
67            let sigs = tx.create_sigs(&[deploy_keypair.secret])?;
68            tx.signatures = vec![sigs];
69
70            let (fee_call, fee_proofs, fee_secrets, _spent_fee_coins, fee_call_params) =
71                self.append_fee_call(holder, tx, block_height, &[]).await?;
72
73            // Append the fee call to the transaction
74            tx_builder.append(ContractCallLeaf { call: fee_call, proofs: fee_proofs }, vec![])?;
75            fee_signature_secrets = Some(fee_secrets);
76            fee_params = Some(fee_call_params);
77        }
78
79        // Now build the actual transaction and sign it with necessary keys.
80        let mut tx = tx_builder.build()?;
81        let sigs = tx.create_sigs(&[deploy_keypair.secret])?;
82        tx.signatures = vec![sigs];
83        if let Some(fee_signature_secrets) = fee_signature_secrets {
84            let sigs = tx.create_sigs(&fee_signature_secrets)?;
85            tx.signatures.push(sigs);
86        }
87
88        Ok((tx, debris.params, fee_params))
89    }
90
91    /// Execute the transaction created by `deploy_contract()` for a given [`Holder`].
92    ///
93    /// Returns any found [`OwnCoin`]s.
94    pub async fn execute_deploy_tx(
95        &mut self,
96        holder: &Holder,
97        tx: Transaction,
98        _params: &DeployParamsV1,
99        fee_params: &Option<MoneyFeeParamsV1>,
100        block_height: u32,
101        append: bool,
102    ) -> Result<Vec<OwnCoin>> {
103        let wallet = self.holders.get_mut(holder).unwrap();
104
105        // Execute the transaction
106        wallet.add_transaction("deploy::deploy", tx, block_height).await?;
107
108        if !append {
109            return Ok(vec![])
110        }
111
112        if let Some(ref fee_params) = fee_params {
113            let nullifier = fee_params.input.nullifier.inner();
114            wallet
115                .money_null_smt
116                .insert_batch(vec![(nullifier, nullifier)])
117                .expect("smt.insert_batch()");
118
119            if let Some(spent_coin) = wallet
120                .unspent_money_coins
121                .iter()
122                .find(|x| x.nullifier() == fee_params.input.nullifier)
123                .cloned()
124            {
125                debug!("Found spent OwnCoin({}) for {:?}", spent_coin.coin, holder);
126                wallet.unspent_money_coins.retain(|x| x.nullifier() != fee_params.input.nullifier);
127                wallet.spent_money_coins.push(spent_coin.clone());
128            }
129
130            wallet.money_merkle_tree.append(MerkleNode::from(fee_params.output.coin.inner()));
131
132            let Ok(note) = fee_params.output.note.decrypt::<MoneyNote>(&wallet.keypair.secret)
133            else {
134                return Ok(vec![])
135            };
136
137            let owncoin = OwnCoin {
138                coin: fee_params.output.coin,
139                note: note.clone(),
140                secret: wallet.keypair.secret,
141                leaf_position: wallet.money_merkle_tree.mark().unwrap(),
142            };
143
144            debug!("Found new OwnCoin({}) for {:?}", owncoin.coin, holder);
145            wallet.unspent_money_coins.push(owncoin.clone());
146            return Ok(vec![owncoin])
147        }
148
149        Ok(vec![])
150    }
151}