entrypoint.rs 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  1. /* This file is part of DarkFi (https://dark.fi)
  2. *
  3. * Copyright (C) 2020-2023 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
  16. * License along with this program.
  17. * If not, see <https://www.gnu.org/licenses/>.
  18. */
  19. use crate::{
  20. error::MapError, ContractFunction, MAP_CONTRACT_ENTRIES_TREE, MAP_CONTRACT_ZKAS_SET_NS,
  21. };
  22. use darkfi_sdk::{
  23. crypto::{poseidon_hash, ContractId, PublicKey},
  24. db::{db_get, db_init, db_lookup, db_set, zkas_db_set},
  25. error::{ContractError, ContractResult},
  26. msg,
  27. pasta::pallas,
  28. util::set_return_data,
  29. ContractCall,
  30. };
  31. use darkfi_serial::{deserialize, serialize, Encodable, WriteExt};
  32. use crate::model::{SetParamsV1, SetUpdateV1};
  33. // A macro defining the 4 entrypoints
  34. // init: called during (re)deployment
  35. // The rest are called during a message call to the contract
  36. // metadata: called first
  37. // exec: called second
  38. // apply: called last
  39. darkfi_sdk::define_contract!(
  40. init: init_contract,
  41. exec: process_instruction,
  42. apply: process_update,
  43. metadata: get_metadata
  44. );
  45. // init takes:
  46. // - the contract ID given by the host
  47. // - deployment payload
  48. // then:
  49. // - initializes all the databases
  50. // - and bundle zkas circuits that will gate this contract's functions
  51. fn init_contract(cid: ContractId, _ix: &[u8]) -> ContractResult {
  52. // Hardcode the `set` circuit's binary into the wasm binary
  53. // during the wasm module's compilation.
  54. // TODO: do we need to update for non-native deployment?
  55. let set_v1_bincode = include_bytes!("../proof/set_v1.zk.bin");
  56. // When init is called, create a verifying key for this circuit.
  57. // The verifying key will later be used to verify proofs generated by the `set` circuit.
  58. zkas_db_set(&set_v1_bincode[..])?;
  59. // If this is a redeployment, skip the databsae initialization,
  60. // initialize otherwise.
  61. // We want MAP_CONTRACT_ENTRIES_TREE to store the key-value pairs of the
  62. // name registries.
  63. if db_lookup(cid, MAP_CONTRACT_ENTRIES_TREE).is_err() {
  64. // "Under the hood" are comments for the studious ones about how
  65. // something works inside the host.
  66. //
  67. // Under the hood: db_init is only allowed callable inside init.
  68. // https://github.com/darkrenaissance/darkfi/blob/35405831e366eaa74522ab14645a5a05ce5cfa1e/src/runtime/import/db.rs#L55-L58
  69. //
  70. // Under the hood: cid must match the contract ID of this contract
  71. // https://github.com/darkrenaissance/darkfi/blob/35405831e366eaa74522ab14645a5a05ce5cfa1e/src/runtime/import/db.rs#L105-L108
  72. db_init(cid, MAP_CONTRACT_ENTRIES_TREE)?;
  73. }
  74. Ok(())
  75. }
  76. // The `metadata` entrypoint takes 1) contract ID and 2) (call's idx, calls),
  77. // then it is supposed to return the public keys and public inputs, for
  78. // the host to verify the signatures and zero knowledge proofs, respectively.
  79. fn get_metadata(_cid: ContractId, ix: &[u8]) -> ContractResult {
  80. // Parse the index and calls from the payload
  81. let (call_idx, calls): (u32, Vec<ContractCall>) = deserialize(ix)?;
  82. if call_idx >= calls.len() as u32 {
  83. msg!("Error: call_idx >= calls.len()");
  84. return Err(ContractError::Internal);
  85. }
  86. // Selects this contract call struct
  87. let self_ = &calls[call_idx as usize];
  88. // Match on the first byte to select the function
  89. match ContractFunction::try_from(self_.data[0])? {
  90. // When the first byte is matched as `Set`
  91. ContractFunction::Set => {
  92. // Deserialize contract call, excluding the first byte
  93. let params: SetParamsV1 = deserialize(&self_.data[1..])?;
  94. // Initialize two vectors to store
  95. // a vector of public keys and
  96. // a vector of (zkas namespace, public inputs)
  97. let signature_pubkeys: Vec<PublicKey> = vec![];
  98. let mut zk_public_inputs: Vec<(String, Vec<pallas::Base>)> = vec![];
  99. zk_public_inputs.push((MAP_CONTRACT_ZKAS_SET_NS.to_string(), params.to_vec()));
  100. // Encode the two vectors into one vector
  101. let mut metadata = vec![];
  102. zk_public_inputs.encode(&mut metadata)?;
  103. signature_pubkeys.encode(&mut metadata)?;
  104. // Return data to the host using an import
  105. //
  106. // Under the hood: metadata is invoked here
  107. // https://github.com/darkrenaissance/darkfi/blob/35405831e366eaa74522ab14645a5a05ce5cfa1e/src/consensus/validator.rs#L1045C1-L1045C1
  108. //
  109. // Under the hood: The metadata is returned here
  110. // https://github.com/darkrenaissance/darkfi/blob/35405831e366eaa74522ab14645a5a05ce5cfa1e/src/runtime/import/util.rs#L43
  111. set_return_data(&metadata)?;
  112. Ok(())
  113. }
  114. }
  115. }
  116. /// Taking call_idx and calls, `set_return_data` a state update to
  117. /// return to the host **to be applied in `process_update()`.
  118. fn process_instruction(cid: ContractId, ix: &[u8]) -> ContractResult {
  119. let (call_idx, calls): (u32, Vec<ContractCall>) = deserialize(ix)?;
  120. if call_idx >= calls.len() as u32 {
  121. msg!("Error: call_idx >= calls.len()");
  122. return Err(ContractError::Internal);
  123. }
  124. match ContractFunction::try_from(ix[0])? {
  125. ContractFunction::Set => {
  126. let params: SetParamsV1 = deserialize(&calls[call_idx as usize].data[1..])?;
  127. // Calculating the slot
  128. // If the prover wants to set a top-level name,
  129. // i.e. in the canonical root name registry,
  130. // then slot = poseidon_hash(0, key)
  131. let slot = if params.car == pallas::Base::one() {
  132. poseidon_hash([pallas::Base::zero(), params.key])
  133. // else slot = poseidon_hash(account, key).
  134. // That is, if you don't have the account's secret,
  135. // you cannot overwrite the names written by the account.
  136. } else {
  137. poseidon_hash([params.account, params.key])
  138. };
  139. // Check if this slot is locked.
  140. // Allow only setting unlocked slot.
  141. let db = db_lookup(cid, MAP_CONTRACT_ENTRIES_TREE)?;
  142. match db_get(db, &serialize(&slot))? {
  143. None => {}
  144. Some(lock) => {
  145. if deserialize(&lock)? {
  146. return Err(MapError::Locked.into());
  147. }
  148. }
  149. };
  150. msg!("[SET] slot = {:?}", slot);
  151. msg!("[SET] car = {:?}", params.car);
  152. msg!("[SET] lock = {:?}", params.lock);
  153. msg!("[SET] value = {:?}", params.value);
  154. // Prepare the return data for the host.
  155. let update = SetUpdateV1 {
  156. slot,
  157. lock: params.lock,
  158. value: params.value,
  159. };
  160. let mut update_data = vec![];
  161. update_data.write_u8(ContractFunction::Set as u8)?;
  162. let _ = update.encode(&mut update_data)?;
  163. // Setting the return data for the host.
  164. // Under the hood: https://github.com/darkrenaissance/darkfi/blob/35405831e366eaa74522ab14645a5a05ce5cfa1e/src/runtime/import/util.rs#L43
  165. set_return_data(&update_data)?;
  166. Ok(())
  167. }
  168. }
  169. }
  170. /// Taking the cid and the update data set in `process_instruction`,
  171. /// write to the relevant databases.
  172. /// In particular, set in db MAP_CONTRACT_ENTRIES_TREE:
  173. /// * slot = lock
  174. /// * slot + 1 = value
  175. fn process_update(cid: ContractId, update_data: &[u8]) -> ContractResult {
  176. match ContractFunction::try_from(update_data[0])? {
  177. ContractFunction::Set => {
  178. let update: SetUpdateV1 = deserialize(&update_data[1..])?;
  179. // key(slot) = lock
  180. // key(slot + 1) = value
  181. let db = db_lookup(cid, MAP_CONTRACT_ENTRIES_TREE)?;
  182. db_set(db, &serialize(&update.slot), &serialize(&update.lock)).unwrap();
  183. db_set(
  184. db,
  185. &serialize(&(update.slot.add(&pallas::Base::one()))),
  186. &serialize(&update.value),
  187. )
  188. .unwrap();
  189. Ok(())
  190. }
  191. }
  192. }