deploy.rs 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648
  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. use std::collections::HashMap;
  19. use lazy_static::lazy_static;
  20. use rand::rngs::OsRng;
  21. use darkfi::{
  22. tx::{ContractCallLeaf, Transaction, TransactionBuilder},
  23. zk::{proof::ProvingKey, vm::ZkCircuit, vm_heap::empty_witnesses},
  24. zkas::ZkBinary,
  25. Error, Result,
  26. };
  27. use darkfi_deployooor_contract::{
  28. client::{deploy_v1::DeployCallBuilder, lock_v1::LockCallBuilder},
  29. model::LockParamsV1,
  30. DeployFunction,
  31. };
  32. use darkfi_money_contract::MONEY_CONTRACT_ZKAS_FEE_NS_V1;
  33. use darkfi_sdk::{
  34. crypto::{
  35. ContractId, Keypair, PublicKey, SecretKey, DEPLOYOOOR_CONTRACT_ID, MONEY_CONTRACT_ID,
  36. },
  37. deploy::DeployParamsV1,
  38. tx::TransactionHash,
  39. ContractCall,
  40. };
  41. use darkfi_serial::{deserialize_async, serialize, serialize_async, AsyncEncodable};
  42. use rusqlite::types::Value;
  43. use crate::{convert_named_params, error::WalletDbResult, rpc::ScanCache, Drk};
  44. // Wallet SQL table constant names. These have to represent the `wallet.sql`
  45. // SQL schema. Table names are prefixed with the contract ID to avoid collisions.
  46. lazy_static! {
  47. pub static ref DEPLOY_AUTH_TABLE: String =
  48. format!("{}_deploy_auth", DEPLOYOOOR_CONTRACT_ID.to_string());
  49. pub static ref DEPLOY_HISTORY_TABLE: String =
  50. format!("{}_deploy_history", DEPLOYOOOR_CONTRACT_ID.to_string());
  51. }
  52. // DEPLOY_AUTH_TABLE
  53. pub const DEPLOY_AUTH_COL_CONTRACT_ID: &str = "contract_id";
  54. pub const DEPLOY_AUTH_COL_SECRET_KEY: &str = "secret_key";
  55. pub const DEPLOY_AUTH_COL_IS_LOCKED: &str = "is_locked";
  56. pub const DEPLOY_AUTH_COL_LOCK_HEIGHT: &str = "lock_height";
  57. // DEPLOY_HISTORY_TABLE
  58. pub const DEPLOY_HISTORY_COL_TX_HASH: &str = "tx_hash";
  59. pub const DEPLOY_HISTORY_COL_CONTRACT: &str = "contract";
  60. pub const DEPLOY_HISTORY_COL_TYPE: &str = "type";
  61. pub const DEPLOY_HISTORY_COL_BLOCK_HEIGHT: &str = "block_height";
  62. pub const DEPLOY_HISTORY_COL_WASM_BINCODE: &str = "wasm_bincode";
  63. pub const DEPLOY_HISTORY_COL_DEPLOY_IX: &str = "deploy_ix";
  64. impl Drk {
  65. /// Initialize wallet with tables for the Deployooor contract.
  66. pub fn initialize_deployooor(&self) -> WalletDbResult<()> {
  67. // Initialize Deployooor wallet schema
  68. let wallet_schema = include_str!("../deploy.sql");
  69. self.wallet.exec_batch_sql(wallet_schema)?;
  70. Ok(())
  71. }
  72. /// Generate a new deploy authority keypair and place it into the wallet
  73. pub async fn deploy_auth_keygen(&self, output: &mut Vec<String>) -> WalletDbResult<()> {
  74. output.push(String::from("Generating a new keypair"));
  75. let secret_key = SecretKey::random(&mut OsRng);
  76. let contract_id = ContractId::derive_public(PublicKey::from_secret(secret_key));
  77. let lock_height: Option<u32> = None;
  78. let query = format!(
  79. "INSERT INTO {} ({}, {}, {}, {}) VALUES (?1, ?2, ?3, ?4);",
  80. *DEPLOY_AUTH_TABLE,
  81. DEPLOY_AUTH_COL_CONTRACT_ID,
  82. DEPLOY_AUTH_COL_SECRET_KEY,
  83. DEPLOY_AUTH_COL_IS_LOCKED,
  84. DEPLOY_AUTH_COL_LOCK_HEIGHT,
  85. );
  86. self.wallet.exec_sql(
  87. &query,
  88. rusqlite::params![
  89. serialize_async(&contract_id).await,
  90. serialize_async(&secret_key).await,
  91. 0,
  92. lock_height
  93. ],
  94. )?;
  95. output.push(String::from("Created new contract deploy authority"));
  96. output.push(format!("Contract ID: {contract_id}"));
  97. Ok(())
  98. }
  99. /// Insert a deploy authority history record into the wallet.
  100. pub fn put_deploy_history_record(
  101. &self,
  102. tx_hash: &TransactionHash,
  103. contract: &ContractId,
  104. tx_type: &str,
  105. block_height: &u32,
  106. wasm_bincode: &Option<Vec<u8>>,
  107. deploy_ix: &Option<Vec<u8>>,
  108. ) -> WalletDbResult<()> {
  109. let query = format!(
  110. "INSERT INTO {} ({}, {}, {}, {}, {}, {}) VALUES (?1, ?2, ?3, ?4, ?5, ?6);",
  111. *DEPLOY_HISTORY_TABLE,
  112. DEPLOY_HISTORY_COL_TX_HASH,
  113. DEPLOY_HISTORY_COL_CONTRACT,
  114. DEPLOY_HISTORY_COL_TYPE,
  115. DEPLOY_HISTORY_COL_BLOCK_HEIGHT,
  116. DEPLOY_HISTORY_COL_WASM_BINCODE,
  117. DEPLOY_HISTORY_COL_DEPLOY_IX,
  118. );
  119. self.wallet.exec_sql(
  120. &query,
  121. rusqlite::params![
  122. tx_hash.to_string(),
  123. serialize(contract),
  124. tx_type,
  125. block_height,
  126. serialize(wasm_bincode),
  127. serialize(deploy_ix),
  128. ],
  129. )?;
  130. Ok(())
  131. }
  132. /// Reset all contract deploy authorities locked status in the wallet.
  133. pub fn reset_deploy_authorities(&self, output: &mut Vec<String>) -> WalletDbResult<()> {
  134. output.push(String::from("Resetting deploy authorities locked status"));
  135. let query = format!(
  136. "UPDATE {} SET {} = 0, {} = NULL;",
  137. *DEPLOY_AUTH_TABLE, DEPLOY_AUTH_COL_IS_LOCKED, DEPLOY_AUTH_COL_LOCK_HEIGHT
  138. );
  139. self.wallet.exec_sql(&query, &[])?;
  140. output.push(String::from("Successfully reset deploy authorities locked status"));
  141. Ok(())
  142. }
  143. /// Remove deploy authorities locked status in the wallet that
  144. /// where locked after provided height.
  145. pub fn unlock_deploy_authorities_after(
  146. &self,
  147. height: &u32,
  148. output: &mut Vec<String>,
  149. ) -> WalletDbResult<()> {
  150. output.push(format!("Resetting deploy authorities locked status after: {height}"));
  151. let query = format!(
  152. "UPDATE {} SET {} = 0, {} = NULL WHERE {} > ?1;",
  153. *DEPLOY_AUTH_TABLE,
  154. DEPLOY_AUTH_COL_IS_LOCKED,
  155. DEPLOY_AUTH_COL_LOCK_HEIGHT,
  156. DEPLOY_AUTH_COL_LOCK_HEIGHT
  157. );
  158. self.wallet.exec_sql(&query, rusqlite::params![Some(*height)])?;
  159. output.push(String::from("Successfully reset deploy authorities locked status"));
  160. Ok(())
  161. }
  162. /// Reset all contracts history records in the wallet.
  163. pub fn reset_deploy_history(&self, output: &mut Vec<String>) -> WalletDbResult<()> {
  164. output.push(String::from("Resetting deployment history"));
  165. let query = format!("DELETE FROM {};", *DEPLOY_HISTORY_TABLE);
  166. self.wallet.exec_sql(&query, &[])?;
  167. output.push(String::from("Successfully deployment history"));
  168. Ok(())
  169. }
  170. /// Remove the contracts history records in the wallet that were
  171. /// created after provided height.
  172. pub fn remove_deploy_history_after(
  173. &self,
  174. height: &u32,
  175. output: &mut Vec<String>,
  176. ) -> WalletDbResult<()> {
  177. output.push(format!("Removing deployment history records after: {height}"));
  178. let query = format!(
  179. "DELETE FROM {} WHERE {} > ?1;",
  180. *DEPLOY_HISTORY_TABLE, DEPLOY_HISTORY_COL_BLOCK_HEIGHT
  181. );
  182. self.wallet.exec_sql(&query, rusqlite::params![height])?;
  183. output.push(String::from("Successfully removed deployment history records"));
  184. Ok(())
  185. }
  186. /// List contract deploy authorities from the wallet
  187. pub async fn list_deploy_auth(
  188. &self,
  189. ) -> Result<Vec<(ContractId, SecretKey, bool, Option<u32>)>> {
  190. let rows = match self.wallet.query_multiple(&DEPLOY_AUTH_TABLE, &[], &[]) {
  191. Ok(r) => r,
  192. Err(e) => {
  193. return Err(Error::DatabaseError(format!(
  194. "[list_deploy_auth] Deploy auth retrieval failed: {e}",
  195. )))
  196. }
  197. };
  198. let mut ret = Vec::with_capacity(rows.len());
  199. for row in rows {
  200. let Value::Blob(ref contract_id_bytes) = row[0] else {
  201. return Err(Error::ParseFailed(
  202. "[list_deploy_auth] Failed to parse contract id bytes",
  203. ))
  204. };
  205. let contract_id: ContractId = deserialize_async(contract_id_bytes).await?;
  206. let Value::Blob(ref secret_key_bytes) = row[1] else {
  207. return Err(Error::ParseFailed(
  208. "[list_deploy_auth] Failed to parse secret key bytes",
  209. ))
  210. };
  211. let secret_key: SecretKey = deserialize_async(secret_key_bytes).await?;
  212. let Value::Integer(locked) = row[2] else {
  213. return Err(Error::ParseFailed("[list_deploy_auth] Failed to parse \"is_locked\""))
  214. };
  215. let lock_height = match row[3] {
  216. Value::Integer(lock_height) => {
  217. let Ok(lock_height) = u32::try_from(lock_height) else {
  218. return Err(Error::ParseFailed(
  219. "[list_deploy_auth] Lock height parsing failed",
  220. ))
  221. };
  222. Some(lock_height)
  223. }
  224. Value::Null => None,
  225. _ => {
  226. return Err(Error::ParseFailed("[list_deploy_auth] Lock height parsing failed"))
  227. }
  228. };
  229. ret.push((contract_id, secret_key, locked != 0, lock_height))
  230. }
  231. Ok(ret)
  232. }
  233. /// Retrieve a deploy authority keypair and status for provided
  234. /// contract id.
  235. async fn get_deploy_auth(&self, contract_id: &ContractId) -> Result<(Keypair, bool)> {
  236. // Find the deploy authority keypair
  237. let row = match self.wallet.query_single(
  238. &DEPLOY_AUTH_TABLE,
  239. &[DEPLOY_AUTH_COL_SECRET_KEY, DEPLOY_AUTH_COL_IS_LOCKED],
  240. convert_named_params! {(DEPLOY_AUTH_COL_CONTRACT_ID, serialize_async(contract_id).await)},
  241. ) {
  242. Ok(v) => v,
  243. Err(e) => {
  244. return Err(Error::DatabaseError(format!(
  245. "[get_deploy_auth] Failed to retrieve deploy authority keypair: {e}"
  246. )))
  247. }
  248. };
  249. let Value::Blob(ref secret_key_bytes) = row[0] else {
  250. return Err(Error::ParseFailed("[get_deploy_auth] Failed to parse secret key bytes"))
  251. };
  252. let secret_key: SecretKey = deserialize_async(secret_key_bytes).await?;
  253. let keypair = Keypair::new(secret_key);
  254. let Value::Integer(locked) = row[1] else {
  255. return Err(Error::ParseFailed("[get_deploy_auth] Failed to parse \"is_locked\""))
  256. };
  257. Ok((keypair, locked != 0))
  258. }
  259. /// Retrieve contract deploy authorities keys map from the wallet.
  260. pub async fn get_deploy_auths_keys_map(&self) -> Result<HashMap<[u8; 32], SecretKey>> {
  261. let rows = match self.wallet.query_multiple(
  262. &DEPLOY_AUTH_TABLE,
  263. &[DEPLOY_AUTH_COL_SECRET_KEY],
  264. &[],
  265. ) {
  266. Ok(r) => r,
  267. Err(e) => {
  268. return Err(Error::DatabaseError(format!(
  269. "[get_deploy_auths_keys_map] Failed to retrieve deploy authorities secret keys: {e}",
  270. )))
  271. }
  272. };
  273. let mut ret = HashMap::new();
  274. for row in rows {
  275. let Value::Blob(ref secret_key_bytes) = row[0] else {
  276. return Err(Error::ParseFailed(
  277. "[get_deploy_auths_keys_map] Failed to parse secret key bytes",
  278. ))
  279. };
  280. let secret_key: SecretKey = deserialize_async(secret_key_bytes).await?;
  281. ret.insert(PublicKey::from_secret(secret_key).to_bytes(), secret_key);
  282. }
  283. Ok(ret)
  284. }
  285. /// Retrieve all deploy history records basic information, for
  286. /// provided contract id.
  287. pub async fn get_deploy_auth_history(
  288. &self,
  289. contract_id: &ContractId,
  290. ) -> Result<Vec<(String, String, u32)>> {
  291. let rows = match self.wallet.query_multiple(
  292. &DEPLOY_HISTORY_TABLE,
  293. &[DEPLOY_HISTORY_COL_TX_HASH, DEPLOY_HISTORY_COL_TYPE, DEPLOY_HISTORY_COL_BLOCK_HEIGHT],
  294. convert_named_params! {(DEPLOY_HISTORY_COL_CONTRACT, serialize_async(contract_id).await)},
  295. ) {
  296. Ok(r) => r,
  297. Err(e) => {
  298. return Err(Error::DatabaseError(format!(
  299. "[get_deploy_auth_history] Failed to retrieve deploy authority history records: {e}",
  300. )))
  301. }
  302. };
  303. let mut ret = Vec::with_capacity(rows.len());
  304. for row in rows {
  305. let Value::Text(ref tx_hash) = row[0] else {
  306. return Err(Error::ParseFailed(
  307. "[get_deploy_auth_history] Transaction hash parsing failed",
  308. ))
  309. };
  310. let Value::Text(ref tx_type) = row[1] else {
  311. return Err(Error::ParseFailed("[get_deploy_auth_history] Type parsing failed"))
  312. };
  313. let Value::Integer(block_height) = row[2] else {
  314. return Err(Error::ParseFailed(
  315. "[get_deploy_auth_history] Block height parsing failed",
  316. ))
  317. };
  318. let Ok(block_height) = u32::try_from(block_height) else {
  319. return Err(Error::ParseFailed(
  320. "[get_deploy_auth_history] Block height parsing failed",
  321. ))
  322. };
  323. ret.push((tx_hash.clone(), tx_type.clone(), block_height));
  324. }
  325. Ok(ret)
  326. }
  327. /// Retrieve deploy history record WASM bincode and deployed
  328. /// instruction, for provided transaction hash.
  329. pub async fn get_deploy_history_record_data(
  330. &self,
  331. tx_hash: &str,
  332. ) -> Result<(Option<Vec<u8>>, Option<Vec<u8>>)> {
  333. let row = match self.wallet.query_single(
  334. &DEPLOY_HISTORY_TABLE,
  335. &[DEPLOY_HISTORY_COL_WASM_BINCODE, DEPLOY_HISTORY_COL_DEPLOY_IX],
  336. convert_named_params! {(DEPLOY_HISTORY_COL_TX_HASH, tx_hash)},
  337. ) {
  338. Ok(v) => v,
  339. Err(e) => {
  340. return Err(Error::DatabaseError(format!(
  341. "[get_deploy_history_record] Failed to retrieve deploy history record: {e}"
  342. )))
  343. }
  344. };
  345. let Value::Blob(ref wasm_bincode_bytes) = row[0] else {
  346. return Err(Error::ParseFailed(
  347. "[get_deploy_history_record] Failed to parse wasm bincode bytes",
  348. ))
  349. };
  350. let wasm_bincode: Option<Vec<u8>> = deserialize_async(wasm_bincode_bytes).await?;
  351. let Value::Blob(ref deploy_ix_bytes) = row[1] else {
  352. return Err(Error::ParseFailed(
  353. "[get_deploy_history_record] Failed to parse deploy ix bytes",
  354. ))
  355. };
  356. let deploy_ix: Option<Vec<u8>> = deserialize_async(deploy_ix_bytes).await?;
  357. Ok((wasm_bincode, deploy_ix))
  358. }
  359. /// Auxiliary function to apply `DeployFunction::DeployV1` call
  360. /// data to the wallet.
  361. /// Returns a flag indicating if the provided call refers to our
  362. /// own wallet.
  363. fn apply_deploy_deploy_data(
  364. &self,
  365. scan_cache: &ScanCache,
  366. params: &DeployParamsV1,
  367. tx_hash: &TransactionHash,
  368. block_height: &u32,
  369. ) -> Result<bool> {
  370. // Check if we have the deploy authority key
  371. let Some(_) = scan_cache.own_deploy_auths.get(&params.public_key.to_bytes()) else {
  372. return Ok(false)
  373. };
  374. // Create a new history record containing the deployment data
  375. if let Err(e) = self.put_deploy_history_record(
  376. tx_hash,
  377. &ContractId::derive_public(params.public_key),
  378. "DEPLOYMENT",
  379. block_height,
  380. &Some(params.wasm_bincode.clone()),
  381. &Some(params.ix.clone()),
  382. ) {
  383. return Err(Error::DatabaseError(format!(
  384. "[apply_deploy_deploy_data] Inserting deploy history recod failed: {e}"
  385. )))
  386. }
  387. Ok(true)
  388. }
  389. /// Auxiliary function to apply `DeployFunction::LockV1` call
  390. /// data to the wallet.
  391. /// Returns a flag indicating if the provided call refers to our
  392. /// own wallet.
  393. async fn apply_deploy_lock_data(
  394. &self,
  395. scan_cache: &ScanCache,
  396. public_key: &PublicKey,
  397. tx_hash: &TransactionHash,
  398. lock_height: &u32,
  399. ) -> Result<bool> {
  400. // Check if we have the deploy authority key
  401. let Some(secret_key) = scan_cache.own_deploy_auths.get(&public_key.to_bytes()) else {
  402. return Ok(false)
  403. };
  404. // Lock contract
  405. let secret_key = serialize_async(secret_key).await;
  406. let query = format!(
  407. "UPDATE {} SET {} = 1, {} = ?1 WHERE {} = ?2;",
  408. *DEPLOY_AUTH_TABLE,
  409. DEPLOY_AUTH_COL_IS_LOCKED,
  410. DEPLOY_AUTH_COL_LOCK_HEIGHT,
  411. DEPLOY_AUTH_COL_SECRET_KEY
  412. );
  413. if let Err(e) =
  414. self.wallet.exec_sql(&query, rusqlite::params![Some(*lock_height), secret_key])
  415. {
  416. return Err(Error::DatabaseError(format!(
  417. "[apply_deploy_lock_data] Lock deploy authority failed: {e}"
  418. )))
  419. }
  420. // Create a new history record for the lock transaction
  421. if let Err(e) = self.put_deploy_history_record(
  422. tx_hash,
  423. &ContractId::derive_public(*public_key),
  424. "LOCK",
  425. lock_height,
  426. &None,
  427. &None,
  428. ) {
  429. return Err(Error::DatabaseError(format!(
  430. "[apply_deploy_lock_data] Inserting deploy history recod failed: {e}"
  431. )))
  432. }
  433. Ok(true)
  434. }
  435. /// Append data related to DeployoOor contract transactions into
  436. /// the wallet database and update the provided scan cache.
  437. /// Returns a flag indicating if provided data refer to our own
  438. /// wallet.
  439. pub async fn apply_tx_deploy_data(
  440. &self,
  441. scan_cache: &mut ScanCache,
  442. data: &[u8],
  443. tx_hash: &TransactionHash,
  444. block_height: &u32,
  445. ) -> Result<bool> {
  446. // Run through the transaction call data and see what we got:
  447. match DeployFunction::try_from(data[0])? {
  448. DeployFunction::DeployV1 => {
  449. scan_cache.log(String::from("[apply_tx_deploy_data] Found Deploy::DeployV1 call"));
  450. let params: DeployParamsV1 = deserialize_async(&data[1..]).await?;
  451. self.apply_deploy_deploy_data(scan_cache, &params, tx_hash, block_height)
  452. }
  453. DeployFunction::LockV1 => {
  454. scan_cache.log(String::from("[apply_tx_deploy_data] Found Deploy::LockV1 call"));
  455. let params: LockParamsV1 = deserialize_async(&data[1..]).await?;
  456. self.apply_deploy_lock_data(scan_cache, &params.public_key, tx_hash, block_height)
  457. .await
  458. }
  459. }
  460. }
  461. /// Create a feeless contract deployment transaction.
  462. pub async fn deploy_contract(
  463. &self,
  464. deploy_auth: &ContractId,
  465. wasm_bincode: Vec<u8>,
  466. deploy_ix: Vec<u8>,
  467. ) -> Result<Transaction> {
  468. // Fetch the keypair and its status
  469. let (deploy_keypair, is_locked) = self.get_deploy_auth(deploy_auth).await?;
  470. // Check lock status
  471. if is_locked {
  472. return Err(Error::Custom("[deploy_contract] Contract is locked".to_string()))
  473. }
  474. // Now we need to do a lookup for the zkas proof bincodes, and create
  475. // the circuit objects and proving keys so we can build the transaction.
  476. // We also do this through the RPC.
  477. let zkas_bins = self.lookup_zkas(&MONEY_CONTRACT_ID).await?;
  478. let Some(fee_zkbin) = zkas_bins.iter().find(|x| x.0 == MONEY_CONTRACT_ZKAS_FEE_NS_V1)
  479. else {
  480. return Err(Error::Custom("[deploy_contract] Fee circuit not found".to_string()))
  481. };
  482. let fee_zkbin = ZkBinary::decode(&fee_zkbin.1)?;
  483. let fee_circuit = ZkCircuit::new(empty_witnesses(&fee_zkbin)?, &fee_zkbin);
  484. // Creating Fee circuit proving keys
  485. let fee_pk = ProvingKey::build(fee_zkbin.k, &fee_circuit);
  486. // Create the contract call
  487. let deploy_call = DeployCallBuilder { deploy_keypair, wasm_bincode, deploy_ix };
  488. let deploy_debris = deploy_call.build()?;
  489. // Encode the call
  490. let mut data = vec![DeployFunction::DeployV1 as u8];
  491. deploy_debris.params.encode_async(&mut data).await?;
  492. let call = ContractCall { contract_id: *DEPLOYOOOR_CONTRACT_ID, data };
  493. // Create the TransactionBuilder containing above call
  494. let mut tx_builder =
  495. TransactionBuilder::new(ContractCallLeaf { call, proofs: vec![] }, vec![])?;
  496. // We first have to execute the fee-less tx to gather its used gas, and then we feed
  497. // it into the fee-creating function.
  498. let mut tx = tx_builder.build()?;
  499. let sigs = tx.create_sigs(&[deploy_keypair.secret])?;
  500. tx.signatures.push(sigs);
  501. let tree = self.get_money_tree().await?;
  502. let (fee_call, fee_proofs, fee_secrets) =
  503. self.append_fee_call(&tx, &tree, &fee_pk, &fee_zkbin, None).await?;
  504. // Append the fee call to the transaction
  505. tx_builder.append(ContractCallLeaf { call: fee_call, proofs: fee_proofs }, vec![])?;
  506. // Now build the actual transaction and sign it with all necessary keys.
  507. let mut tx = tx_builder.build()?;
  508. let sigs = tx.create_sigs(&[deploy_keypair.secret])?;
  509. tx.signatures.push(sigs);
  510. let sigs = tx.create_sigs(&fee_secrets)?;
  511. tx.signatures.push(sigs);
  512. Ok(tx)
  513. }
  514. /// Create a feeless contract redeployment lock transaction.
  515. pub async fn lock_contract(&self, deploy_auth: &ContractId) -> Result<Transaction> {
  516. // Fetch the keypair and its status
  517. let (deploy_keypair, is_locked) = self.get_deploy_auth(deploy_auth).await?;
  518. // Check lock status
  519. if is_locked {
  520. return Err(Error::Custom("[lock_contract] Contract is already locked".to_string()))
  521. }
  522. // Now we need to do a lookup for the zkas proof bincodes, and create
  523. // the circuit objects and proving keys so we can build the transaction.
  524. // We also do this through the RPC.
  525. let zkas_bins = self.lookup_zkas(&MONEY_CONTRACT_ID).await?;
  526. let Some(fee_zkbin) = zkas_bins.iter().find(|x| x.0 == MONEY_CONTRACT_ZKAS_FEE_NS_V1)
  527. else {
  528. return Err(Error::Custom("[lock_contract] Fee circuit not found".to_string()))
  529. };
  530. let fee_zkbin = ZkBinary::decode(&fee_zkbin.1)?;
  531. let fee_circuit = ZkCircuit::new(empty_witnesses(&fee_zkbin)?, &fee_zkbin);
  532. // Creating Fee circuit proving keys
  533. let fee_pk = ProvingKey::build(fee_zkbin.k, &fee_circuit);
  534. // Create the contract call
  535. let lock_call = LockCallBuilder { deploy_keypair };
  536. let lock_debris = lock_call.build()?;
  537. // Encode the call
  538. let mut data = vec![DeployFunction::LockV1 as u8];
  539. lock_debris.params.encode_async(&mut data).await?;
  540. let call = ContractCall { contract_id: *DEPLOYOOOR_CONTRACT_ID, data };
  541. // Create the TransactionBuilder containing above call
  542. let mut tx_builder =
  543. TransactionBuilder::new(ContractCallLeaf { call, proofs: vec![] }, vec![])?;
  544. // We first have to execute the fee-less tx to gather its used gas, and then we feed
  545. // it into the fee-creating function.
  546. let mut tx = tx_builder.build()?;
  547. let sigs = tx.create_sigs(&[deploy_keypair.secret])?;
  548. tx.signatures.push(sigs);
  549. let tree = self.get_money_tree().await?;
  550. let (fee_call, fee_proofs, fee_secrets) =
  551. self.append_fee_call(&tx, &tree, &fee_pk, &fee_zkbin, None).await?;
  552. // Append the fee call to the transaction
  553. tx_builder.append(ContractCallLeaf { call: fee_call, proofs: fee_proofs }, vec![])?;
  554. // Now build the actual transaction and sign it with all necessary keys.
  555. let mut tx = tx_builder.build()?;
  556. let sigs = tx.create_sigs(&[deploy_keypair.secret])?;
  557. tx.signatures.push(sigs);
  558. let sigs = tx.create_sigs(&fee_secrets)?;
  559. tx.signatures.push(sigs);
  560. Ok(tx)
  561. }
  562. }