deploy_contract.rs 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. /* This file is part of DarkFi (https://dark.fi)
  2. *
  3. * Copyright (C) 2020-2024 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::{
  19. env::set_current_dir,
  20. fs::{read, read_dir, read_to_string, File},
  21. io::{ErrorKind, Write},
  22. path::{Path, PathBuf},
  23. str::FromStr,
  24. };
  25. use rand::{rngs::OsRng, RngCore};
  26. use darkfi::{
  27. crypto::keypair::SecretKey,
  28. node::{MemoryState, State},
  29. runtime::vm_runtime::Runtime,
  30. util::cli::{fg_green, fg_red},
  31. zkas::ZkBinary,
  32. Error, Result,
  33. };
  34. const CIRCUIT_DIR_NAME: &str = "proof";
  35. const CONTRACT_FILE_NAME: &str = "contract.wasm";
  36. const DEPLOY_KEY_NAME: &str = "deploy.key";
  37. /// Creates a new deploy key used for deploying private smart contracts.
  38. /// This key allows to update the wasm code and the zk circuits on chain
  39. /// by creating a signature. When deployed, the contract can be accessed
  40. /// by requesting the public counterpart of this secret key.
  41. pub fn create_deploy_key(mut rng: impl RngCore, path: &Path) -> Result<SecretKey> {
  42. let secret = SecretKey::random(&mut rng);
  43. let mut file = File::create(path)?;
  44. file.write_all(bs58::encode(&secret.to_bytes()).into_string().as_bytes())?;
  45. Ok(secret)
  46. }
  47. /// Reads a deploy key from a file on the filesystem and returns it.
  48. fn read_deploy_key(s: &Path) -> core::result::Result<SecretKey, std::io::Error> {
  49. eprintln!("Trying to read deploy key from file: {:?}", s);
  50. let contents = read_to_string(s)?;
  51. let secret = SecretKey::from_str(&contents).unwrap();
  52. Ok(secret)
  53. }
  54. /// Creates necessary data to deploy a given smart contract on the network.
  55. /// For consistency, we point this function to a directory where our smart
  56. /// contract and the compiled circuits are contained. This is going to give
  57. /// us a uniform approach to scm and gives a generic layout of the source:
  58. /// ```text
  59. /// smart-contract
  60. /// ├── Cargo.toml
  61. /// ├── deploy.key
  62. /// ├── Makefile
  63. /// ├── proof
  64. /// │   ├── circuit0.zk
  65. /// │   ├── circuit0.zk.bin
  66. /// │   ├── circuit1.zk
  67. /// │   └── circuit1.zk.bin
  68. /// ├── contract.wasm
  69. /// ├── src
  70. /// │   └── lib.rs
  71. /// └── tests
  72. /// ```
  73. //pub fn create_deploy_data(path: &Path) -> Result<ContractDeploy> {
  74. pub fn create_deploy_data(path: &Path) -> Result<()> {
  75. // Try to chdir into the contract directory
  76. if let Err(e) = set_current_dir(path) {
  77. eprintln!("Failed to chdir into {:?}", path);
  78. return Err(e.into())
  79. }
  80. let deploy_key: SecretKey;
  81. let deploy_key = match read_deploy_key(&PathBuf::from(DEPLOY_KEY_NAME)) {
  82. Ok(v) => deploy_key = v,
  83. Err(e) => {
  84. if e.kind() == ErrorKind::NotFound {
  85. // We didn't find a deploy key, generate a new one.
  86. eprintln!("Did not find an existing key, creating a new one.");
  87. match create_deploy_key(&mut OsRng, &PathBuf::from(DEPLOY_KEY_NAME)) {
  88. Ok(v) => {
  89. eprintln!("Created new deploy key in \"{}\".", DEPLOY_KEY_NAME);
  90. deploy_key = v;
  91. }
  92. Err(e) => {
  93. eprintln!("Failed to create new deploy key");
  94. return Err(e)
  95. }
  96. }
  97. }
  98. eprintln!("Failed to read deploy key");
  99. return Err(e.into())
  100. }
  101. };
  102. // Search for ZK circuits in the directory. If none are found, we'll bail.
  103. // The logic searches for `.zk.bin` files created by zkas.
  104. eprintln!("Searching for compiled ZK circuits in \"{}\" ...", CIRCUIT_DIR_NAME);
  105. let mut circuits = vec![];
  106. for i in read_dir(CIRCUIT_DIR_NAME)? {
  107. if let Err(e) = i {
  108. eprintln!("Error iterating over \"{}\" directory", CIRCUIT_DIR_NAME);
  109. return Err(e.into())
  110. }
  111. let f = i.unwrap();
  112. let fname = f.file_name();
  113. let fname = fname.to_str().unwrap();
  114. if fname.ends_with(".zk.bin") {
  115. // Validate that the files can be properly decoded
  116. eprintln!("{} {}", fg_green("Found:"), f.path().display());
  117. let buf = read(f.path())?;
  118. if let Err(e) = ZkBinary::decode(&buf) {
  119. eprintln!("{} Failed to decode zkas bincode in {:?}", fg_red("Error:"), f.path());
  120. return Err(e)
  121. }
  122. circuits.push(buf.clone());
  123. }
  124. }
  125. if circuits.is_empty() {
  126. return Err(Error::Custom("Found no valid ZK circuits".to_string()))
  127. }
  128. /* FIXME
  129. // Validate wasm binary. We inspect the bincode and try to load it into
  130. // the wasm runtime. If loaded, we then look for the `ENTRYPOINT` function
  131. // which we hardcode into our sdk and runtime and is the canonical way to
  132. // run wasm binaries on chain.
  133. eprintln!("Inspecting wasm binary in \"{}\"", CONTRACT_FILE_NAME);
  134. let wasm_bytes = read(CONTRACT_FILE_NAME)?;
  135. eprintln!("Initializing moch wasm runtime to check validity");
  136. let runtime = match Runtime::new(&wasm_bytes, MemoryState::new(State::dummy()?)) {
  137. Ok(v) => {
  138. eprintln!("Found {} wasm binary", fg_green("valid"));
  139. v
  140. }
  141. Err(e) => {
  142. eprintln!("Failed to initialize wasm runtime");
  143. return Err(e)
  144. }
  145. };
  146. eprintln!("Looking for entrypoint function inside the wasm");
  147. let cs = ContractSection::Exec;
  148. if let Err(e) = runtime.instance.exports.get_function(cs.name()) {
  149. eprintln!("{} Could not find entrypoint function", fg_red("Error:"));
  150. return Err(e.into())
  151. }
  152. // TODO: Create a ZK proof enforcing the deploy key relations with their public
  153. // counterparts (public key and contract address)
  154. let mut total_bytes = 0;
  155. total_bytes += wasm_bytes.len();
  156. for circuit in circuits {
  157. total_bytes += circuit.len();
  158. }
  159. */
  160. // TODO: Return the data back to the main function, and work further in creating
  161. // a transaction and broadcasting it.
  162. Ok(())
  163. }