entrypoint.rs 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  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 License
  16. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  17. */
  18. use darkfi_money_contract::{
  19. CONSENSUS_CONTRACT_COINS_TREE, CONSENSUS_CONTRACT_COIN_MERKLE_TREE,
  20. CONSENSUS_CONTRACT_COIN_ROOTS_TREE, CONSENSUS_CONTRACT_DB_VERSION,
  21. CONSENSUS_CONTRACT_INFO_TREE, CONSENSUS_CONTRACT_NULLIFIERS_TREE,
  22. CONSENSUS_CONTRACT_ZKAS_BURN_NS_V1, CONSENSUS_CONTRACT_ZKAS_MINT_NS_V1,
  23. MONEY_CONTRACT_ZKAS_BURN_NS_V1, MONEY_CONTRACT_ZKAS_MINT_NS_V1,
  24. };
  25. use darkfi_sdk::{
  26. crypto::{ContractId, MerkleTree},
  27. db::{db_init, db_lookup, db_set, set_return_data, SMART_CONTRACT_ZKAS_DB_NAME},
  28. error::{ContractError, ContractResult},
  29. msg, ContractCall,
  30. };
  31. use darkfi_serial::{deserialize, serialize, Encodable, WriteExt};
  32. use crate::{
  33. model::{ConsensusStakeUpdateV1, ConsensusUnstakeUpdateV1},
  34. ConsensusFunction,
  35. };
  36. /// `Consensus::Stake` functions
  37. mod stake_v1;
  38. use stake_v1::{
  39. consensus_stake_get_metadata_v1, consensus_stake_process_instruction_v1,
  40. consensus_stake_process_update_v1,
  41. };
  42. /// `Consensus::Unstake` functions
  43. mod unstake_v1;
  44. use unstake_v1::{
  45. consensus_unstake_get_metadata_v1, consensus_unstake_process_instruction_v1,
  46. consensus_unstake_process_update_v1,
  47. };
  48. darkfi_sdk::define_contract!(
  49. init: init_contract,
  50. exec: process_instruction,
  51. apply: process_update,
  52. metadata: get_metadata
  53. );
  54. /// This entrypoint function runs when the contract is (re)deployed and initialized.
  55. /// We use this function to initialize all the necessary databases and prepare them
  56. /// with initial data if necessary. This is also the place where we bundle the zkas
  57. /// circuits that are to be used with functions provided by the contract.
  58. fn init_contract(cid: ContractId, _ix: &[u8]) -> ContractResult {
  59. // The zkas circuit can simply be embedded in the wasm and set up by
  60. // the initialization. Note that the tree should then be called "zkas".
  61. // The lookups can be done by `contract_id+_zkas+namespace`.
  62. // TODO: For the zkas tree, external host checks should be done to ensure
  63. // that the bincode is actually valid and not arbitrary.
  64. let zkas_db = match db_lookup(cid, SMART_CONTRACT_ZKAS_DB_NAME) {
  65. Ok(v) => v,
  66. Err(_) => db_init(cid, SMART_CONTRACT_ZKAS_DB_NAME)?,
  67. };
  68. let money_mint_v1_bincode = include_bytes!("../../money/proof/mint_v1.zk.bin");
  69. let money_burn_v1_bincode = include_bytes!("../../money/proof/burn_v1.zk.bin");
  70. // TODO: for now we use same proof for mint and burn as Money
  71. let consensus_mint_v1_bincode = include_bytes!("../../money/proof/mint_v1.zk.bin");
  72. let consensus_burn_v1_bincode = include_bytes!("../../money/proof/burn_v1.zk.bin");
  73. db_set(zkas_db, &serialize(&MONEY_CONTRACT_ZKAS_MINT_NS_V1), &money_mint_v1_bincode[..])?;
  74. db_set(zkas_db, &serialize(&MONEY_CONTRACT_ZKAS_BURN_NS_V1), &money_burn_v1_bincode[..])?;
  75. db_set(
  76. zkas_db,
  77. &serialize(&CONSENSUS_CONTRACT_ZKAS_MINT_NS_V1),
  78. &consensus_mint_v1_bincode[..],
  79. )?;
  80. db_set(
  81. zkas_db,
  82. &serialize(&CONSENSUS_CONTRACT_ZKAS_BURN_NS_V1),
  83. &consensus_burn_v1_bincode[..],
  84. )?;
  85. // Set up a database tree to hold Merkle roots of all coins
  86. // k=MerkleNode, v=[]
  87. if db_lookup(cid, CONSENSUS_CONTRACT_COIN_ROOTS_TREE).is_err() {
  88. db_init(cid, CONSENSUS_CONTRACT_COIN_ROOTS_TREE)?;
  89. }
  90. // Set up a database tree to hold all coins ever seen
  91. // k=Coin, v=[]
  92. if db_lookup(cid, CONSENSUS_CONTRACT_COINS_TREE).is_err() {
  93. db_init(cid, CONSENSUS_CONTRACT_COINS_TREE)?;
  94. }
  95. // Set up a database tree to hold nullifiers of all spent coins
  96. // k=Nullifier, v=[]
  97. if db_lookup(cid, CONSENSUS_CONTRACT_NULLIFIERS_TREE).is_err() {
  98. db_init(cid, CONSENSUS_CONTRACT_NULLIFIERS_TREE)?;
  99. }
  100. // Set up a database tree for arbitrary data
  101. let info_db = match db_lookup(cid, CONSENSUS_CONTRACT_INFO_TREE) {
  102. Ok(v) => v,
  103. Err(_) => {
  104. let info_db = db_init(cid, CONSENSUS_CONTRACT_INFO_TREE)?;
  105. // Create the incrementalmerkletree for seen coins
  106. let coin_tree = MerkleTree::new(100);
  107. let mut coin_tree_data = vec![];
  108. coin_tree_data.write_u32(0)?;
  109. coin_tree.encode(&mut coin_tree_data)?;
  110. db_set(info_db, &serialize(&CONSENSUS_CONTRACT_COIN_MERKLE_TREE), &coin_tree_data)?;
  111. info_db
  112. }
  113. };
  114. // Update db version
  115. db_set(
  116. info_db,
  117. &serialize(&CONSENSUS_CONTRACT_DB_VERSION),
  118. &serialize(&env!("CARGO_PKG_VERSION")),
  119. )?;
  120. Ok(())
  121. }
  122. /// This function is used by the wasm VM's host to fetch the necessary metadata
  123. /// for verifying signatures and zk proofs. The payload given here are all the
  124. /// contract calls in the transaction.
  125. fn get_metadata(cid: ContractId, ix: &[u8]) -> ContractResult {
  126. let (call_idx, calls): (u32, Vec<ContractCall>) = deserialize(ix)?;
  127. if call_idx >= calls.len() as u32 {
  128. msg!("Error: call_idx >= calls.len()");
  129. return Err(ContractError::Internal)
  130. }
  131. match ConsensusFunction::try_from(calls[call_idx as usize].data[0])? {
  132. ConsensusFunction::StakeV1 => {
  133. // We pass everything into the correct function, and it will return
  134. // the metadata for us, which we can then copy into the host with
  135. // the `set_return_data` function. On the host, this metadata will
  136. // be used to do external verification (zk proofs, and signatures).
  137. let metadata = consensus_stake_get_metadata_v1(cid, call_idx, calls)?;
  138. Ok(set_return_data(&metadata)?)
  139. }
  140. ConsensusFunction::UnstakeV1 => {
  141. let metadata = consensus_unstake_get_metadata_v1(cid, call_idx, calls)?;
  142. Ok(set_return_data(&metadata)?)
  143. }
  144. }
  145. }
  146. /// This function verifies a state transition and produces a state update
  147. /// if everything is successful. This step should happen **after** the host
  148. /// has successfully verified the metadata from `get_metadata()`.
  149. fn process_instruction(cid: ContractId, ix: &[u8]) -> ContractResult {
  150. let (call_idx, calls): (u32, Vec<ContractCall>) = deserialize(ix)?;
  151. if call_idx >= calls.len() as u32 {
  152. msg!("Error: call_idx >= calls.len()");
  153. return Err(ContractError::Internal)
  154. }
  155. match ConsensusFunction::try_from(calls[call_idx as usize].data[0])? {
  156. ConsensusFunction::StakeV1 => {
  157. // Again, we pass everything into the correct function.
  158. // If it executes successfully, we'll get a state update
  159. // which we can copy into the host using `set_return_data`.
  160. // This update can then be written with `process_update()`
  161. // if everything is in order.
  162. let update_data = consensus_stake_process_instruction_v1(cid, call_idx, calls)?;
  163. Ok(set_return_data(&update_data)?)
  164. }
  165. ConsensusFunction::UnstakeV1 => {
  166. let update_data = consensus_unstake_process_instruction_v1(cid, call_idx, calls)?;
  167. Ok(set_return_data(&update_data)?)
  168. }
  169. }
  170. }
  171. /// This function attempts to write a given state update provided the previous steps
  172. /// of the contract call execution all were successful. It's the last in line, and
  173. /// assumes that the transaction/call was successful. The payload given to the function
  174. /// is the update data retrieved from `process_instruction()`.
  175. fn process_update(cid: ContractId, update_data: &[u8]) -> ContractResult {
  176. match ConsensusFunction::try_from(update_data[0])? {
  177. ConsensusFunction::StakeV1 => {
  178. let update: ConsensusStakeUpdateV1 = deserialize(&update_data[1..])?;
  179. Ok(consensus_stake_process_update_v1(cid, update)?)
  180. }
  181. ConsensusFunction::UnstakeV1 => {
  182. let update: ConsensusUnstakeUpdateV1 = deserialize(&update_data[1..])?;
  183. Ok(consensus_unstake_process_update_v1(cid, update)?)
  184. }
  185. }
  186. }