vm_runtime.rs 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  1. /* This file is part of DarkFi (https://dark.fi)
  2. *
  3. * Copyright (C) 2020-2022 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. cell::RefCell,
  20. sync::{Arc, Mutex},
  21. };
  22. use darkfi_sdk::entrypoint;
  23. use log::{debug, info};
  24. use wasmer::{
  25. imports, wasmparser::Operator, AsStoreRef, CompilerConfig, Function, FunctionEnv, Instance,
  26. Memory, MemoryView, Module, Pages, Store, Value, WasmPtr, WASM_PAGE_SIZE,
  27. };
  28. use wasmer_compiler_singlepass::Singlepass;
  29. use wasmer_middlewares::{
  30. metering::{get_remaining_points, MeteringPoints},
  31. Metering,
  32. };
  33. use super::{
  34. chain_state::{is_valid_merkle, nullifier_exists},
  35. memory::MemoryManipulation,
  36. util::drk_log,
  37. };
  38. use crate::{Error, Result};
  39. /// Name of the wasm linear memory in our guest module
  40. const MEMORY: &str = "memory";
  41. /// Hardcoded entrypoint function of a contract
  42. pub const ENTRYPOINT: &str = "entrypoint";
  43. /// Gas limit for a contract
  44. const GAS_LIMIT: u64 = 200000;
  45. /// The wasm vm runtime instantiated for every smart contract that runs.
  46. pub struct Env {
  47. /// Logs produced by the contract
  48. pub logs: RefCell<Vec<String>>,
  49. /// Direct memory access to the VM
  50. pub memory: Option<Memory>,
  51. }
  52. impl Env {
  53. /// Provide safe access to the memory
  54. /// (it must be initialized before it can be used)
  55. ///
  56. /// // ctx: FunctionEnvMut<Env>
  57. /// let env = ctx.data();
  58. /// let memory = env.memory_view(&ctx);
  59. ///
  60. pub fn memory_view<'a>(&'a self, store: &'a impl AsStoreRef) -> MemoryView<'a> {
  61. self.memory().view(store)
  62. }
  63. /// Get memory, that needs to have been set fist
  64. pub fn memory(&self) -> &Memory {
  65. self.memory.as_ref().unwrap()
  66. }
  67. }
  68. /// The result of the VM execution
  69. pub struct ExecutionResult {
  70. /// The exit code returned by the wasm program
  71. pub exitcode: u8,
  72. /// Logs written from the wasm program
  73. pub logs: Vec<String>,
  74. }
  75. pub struct Runtime {
  76. pub instance: Instance,
  77. pub store: Store,
  78. pub ctx: FunctionEnv<Env>,
  79. }
  80. impl Runtime {
  81. /// Create a new wasm runtime instance that contains the given wasm module.
  82. pub fn new(wasm_bytes: &[u8]) -> Result<Self> {
  83. info!(target: "warm_runtime::new", "Instantiating a new runtime");
  84. // This function will be called for each `Operator` encountered during
  85. // the wasm module execution. It should return the cost of the operator
  86. // that it received as its first argument.
  87. // https://docs.rs/wasmparser/latest/wasmparser/enum.Operator.html
  88. let cost_function = |operator: &Operator| -> u64 {
  89. match operator {
  90. Operator::LocalGet { .. } => 1,
  91. Operator::I32Const { .. } => 1,
  92. Operator::I32Add { .. } => 2,
  93. _ => 0,
  94. }
  95. };
  96. // `Metering` needs to be conigured with a limit and a cost function.
  97. // For each `Operator`, the metering middleware will call the cost
  98. // function and subtract the cost from the remaining points.
  99. let metering = Arc::new(Metering::new(GAS_LIMIT, cost_function));
  100. // Define the compiler and middleware, engine, and store
  101. let mut compiler_config = Singlepass::new();
  102. compiler_config.push_middleware(metering);
  103. let mut store = Store::new(compiler_config);
  104. debug!(target: "wasm_runtime::new", "Compiling module");
  105. let module = Module::new(&store, wasm_bytes)?;
  106. // This section will need changing
  107. debug!(target: "wasm_runtime::new", "Importing functions");
  108. let logs = RefCell::new(vec![]);
  109. let ctx = FunctionEnv::new(&mut store, Env { logs, memory: None });
  110. let imports = imports! {
  111. "env" => {
  112. "drk_log_" => Function::new_typed_with_env(
  113. &mut store,
  114. &ctx,
  115. drk_log,
  116. ),
  117. "nullifier_exists_" => Function::new_typed_with_env(
  118. &mut store,
  119. &ctx,
  120. nullifier_exists,
  121. ),
  122. "is_valid_merkle_" => Function::new_typed_with_env(
  123. &mut store,
  124. &ctx,
  125. is_valid_merkle,
  126. ),
  127. }
  128. };
  129. debug!(target: "wasm_runtime::new", "Instantiating module");
  130. let instance = Instance::new(&mut store, &module, &imports)?;
  131. let mut env_mut = ctx.as_mut(&mut store);
  132. env_mut.memory = Some(instance.exports.get_with_generics(MEMORY)?);
  133. Ok(Self { instance, store, ctx })
  134. }
  135. /// Run the hardcoded `ENTRYPOINT` function with the given payload as input.
  136. pub fn run(&mut self, payload: &[u8]) -> Result<()> {
  137. let pages_required = payload.len() / WASM_PAGE_SIZE + 1;
  138. self.set_memory_page_size(pages_required as u32)?;
  139. self.copy_to_memory(payload)?;
  140. debug!(target: "wasm_runtime::run", "Getting entrypoint function");
  141. let entrypoint = self.instance.exports.get_function(ENTRYPOINT)?;
  142. debug!(target: "wasm_runtime::run", "Executing wasm");
  143. // We pass 0 to entrypoint() which is the location of the payload data in the memory
  144. let ret = match entrypoint.call(&mut self.store, &[Value::I32(0 as i32)]) {
  145. Ok(retvals) => {
  146. self.print_logs();
  147. debug!(target: "wasm_runtime::run", "{}", self.gas_info());
  148. retvals
  149. }
  150. Err(e) => {
  151. self.print_logs();
  152. debug!(target: "wasm_runtime::run", "{}", self.gas_info());
  153. return Err(e.into())
  154. }
  155. };
  156. debug!(target: "wasm_runtime::run", "wasm executed successfully");
  157. debug!(target: "wasm_runtime::run", "Contract returned: {:?}", ret[0]);
  158. let retval = match ret[0] {
  159. Value::I64(v) => v as u64,
  160. _ => unreachable!(),
  161. };
  162. match retval {
  163. entrypoint::SUCCESS => Ok(()),
  164. _ => Err(Error::ContractExecError(retval)),
  165. }
  166. }
  167. fn print_logs(&self) {
  168. let logs = self.ctx.as_ref(&self.store).logs.borrow();
  169. for msg in logs.iter() {
  170. debug!(target: "wasm_runtime::run", "Contract log: {}", msg);
  171. }
  172. }
  173. fn gas_info(&mut self) -> String {
  174. let remaining_points = get_remaining_points(&mut self.store, &self.instance);
  175. match remaining_points {
  176. MeteringPoints::Remaining(rem) => {
  177. format!("Gas used: {}/{}", GAS_LIMIT - rem, GAS_LIMIT)
  178. }
  179. MeteringPoints::Exhausted => {
  180. format!("Gas fully exhausted: {}/{}", GAS_LIMIT + 1, GAS_LIMIT)
  181. }
  182. }
  183. }
  184. /// Set the memory page size
  185. fn set_memory_page_size(&mut self, pages: u32) -> Result<()> {
  186. // Grab memory by value
  187. let memory = self.take_memory();
  188. // Modify the memory
  189. memory.grow(&mut self.store, Pages(pages))?;
  190. // Replace the memory back again
  191. self.ctx.as_mut(&mut self.store).memory = Some(memory);
  192. Ok(())
  193. }
  194. /// Take Memory by value. Needed to modify the Memory object
  195. /// Will panic if memory isn't set.
  196. fn take_memory(&mut self) -> Memory {
  197. let env_memory = &mut self.ctx.as_mut(&mut self.store).memory;
  198. let memory = std::mem::replace(env_memory, None);
  199. memory.expect("memory should be set")
  200. }
  201. /// Copy payload to the start of the memory
  202. fn copy_to_memory(&self, payload: &[u8]) -> Result<()> {
  203. // Get the memory view
  204. let env = self.ctx.as_ref(&self.store);
  205. let memory_view = env.memory_view(&self.store);
  206. memory_view.write_slice(payload, 0)
  207. }
  208. }