deploy.rs 25 KB

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