deploy.rs 25 KB

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