vm_runtime.rs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392
  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::{Cell, RefCell},
  20. sync::Arc,
  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, 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. import,
  35. //chain_state::{is_valid_merkle, nullifier_exists, set_update},
  36. memory::MemoryManipulation,
  37. util::serialize_payload,
  38. };
  39. use crate::{crypto::contract_id::ContractId, Error, Result};
  40. /// Name of the wasm linear memory in our guest module
  41. const MEMORY: &str = "memory";
  42. /// Hardcoded setup function of a contract
  43. pub const INITIALIZE: &str = "__initialize";
  44. /// Hardcoded entrypoint function of a contract
  45. pub const ENTRYPOINT: &str = "__entrypoint";
  46. /// Hardcoded apply function of a contract
  47. pub const UPDATE: &str = "__update";
  48. /// Gas limit for a contract
  49. const GAS_LIMIT: u64 = 200000;
  50. pub enum ContractSection {
  51. Null,
  52. Deploy,
  53. Exec,
  54. Update,
  55. }
  56. /// The wasm vm runtime instantiated for every smart contract that runs.
  57. pub struct Env {
  58. pub contract_id: ContractId,
  59. pub contract_section: ContractSection,
  60. pub contract_update: Cell<Option<(u8, Vec<u8>)>>,
  61. //pub func_id:
  62. /// Logs produced by the contract
  63. pub logs: RefCell<Vec<String>>,
  64. /// Direct memory access to the VM
  65. pub memory: Option<Memory>,
  66. }
  67. impl Env {
  68. /// Provide safe access to the memory
  69. /// (it must be initialized before it can be used)
  70. ///
  71. /// // ctx: FunctionEnvMut<Env>
  72. /// let env = ctx.data();
  73. /// let memory = env.memory_view(&ctx);
  74. ///
  75. pub fn memory_view<'a>(&'a self, store: &'a impl AsStoreRef) -> MemoryView<'a> {
  76. self.memory().view(store)
  77. }
  78. /// Get memory, that needs to have been set fist
  79. pub fn memory(&self) -> &Memory {
  80. self.memory.as_ref().unwrap()
  81. }
  82. }
  83. pub struct Runtime {
  84. pub instance: Instance,
  85. pub store: Store,
  86. pub ctx: FunctionEnv<Env>,
  87. }
  88. impl Runtime {
  89. /// Create a new wasm runtime instance that contains the given wasm module.
  90. pub fn new(wasm_bytes: &[u8], contract_id: ContractId) -> Result<Self> {
  91. info!(target: "warm_runtime::new", "Instantiating a new runtime");
  92. // This function will be called for each `Operator` encountered during
  93. // the wasm module execution. It should return the cost of the operator
  94. // that it received as its first argument.
  95. // https://docs.rs/wasmparser/latest/wasmparser/enum.Operator.html
  96. let cost_function = |operator: &Operator| -> u64 {
  97. match operator {
  98. Operator::LocalGet { .. } => 1,
  99. Operator::I32Const { .. } => 1,
  100. Operator::I32Add { .. } => 2,
  101. _ => 0,
  102. }
  103. };
  104. // `Metering` needs to be conigured with a limit and a cost function.
  105. // For each `Operator`, the metering middleware will call the cost
  106. // function and subtract the cost from the remaining points.
  107. let metering = Arc::new(Metering::new(GAS_LIMIT, cost_function));
  108. // Define the compiler and middleware, engine, and store
  109. let mut compiler_config = Singlepass::new();
  110. compiler_config.push_middleware(metering);
  111. let mut store = Store::new(compiler_config);
  112. debug!(target: "wasm_runtime::new", "Compiling module");
  113. let module = Module::new(&store, wasm_bytes)?;
  114. // This section will need changing
  115. debug!(target: "wasm_runtime::new", "Importing functions");
  116. let logs = RefCell::new(vec![]);
  117. let ctx = FunctionEnv::new(
  118. &mut store,
  119. Env {
  120. contract_id,
  121. contract_section: ContractSection::Null,
  122. contract_update: Cell::new(None),
  123. logs,
  124. memory: None,
  125. },
  126. );
  127. let imports = imports! {
  128. "env" => {
  129. "drk_log_" => Function::new_typed_with_env(
  130. &mut store,
  131. &ctx,
  132. import::util::drk_log,
  133. ),
  134. "nullifier_exists_" => Function::new_typed_with_env(
  135. &mut store,
  136. &ctx,
  137. import::chain_state::nullifier_exists,
  138. ),
  139. "is_valid_merkle_" => Function::new_typed_with_env(
  140. &mut store,
  141. &ctx,
  142. import::chain_state::is_valid_merkle,
  143. ),
  144. "set_update_" => Function::new_typed_with_env(
  145. &mut store,
  146. &ctx,
  147. import::chain_state::set_update,
  148. ),
  149. "db_init_" => Function::new_typed_with_env(
  150. &mut store,
  151. &ctx,
  152. import::db::db_init,
  153. ),
  154. "db_lookup_" => Function::new_typed_with_env(
  155. &mut store,
  156. &ctx,
  157. import::db::db_lookup,
  158. ),
  159. "db_get_" => Function::new_typed_with_env(
  160. &mut store,
  161. &ctx,
  162. import::db::db_get,
  163. ),
  164. "db_begin_tx_" => Function::new_typed_with_env(
  165. &mut store,
  166. &ctx,
  167. import::db::db_begin_tx,
  168. ),
  169. "db_set_" => Function::new_typed_with_env(
  170. &mut store,
  171. &ctx,
  172. import::db::db_set,
  173. ),
  174. "db_end_tx_" => Function::new_typed_with_env(
  175. &mut store,
  176. &ctx,
  177. import::db::db_end_tx,
  178. ),
  179. }
  180. };
  181. debug!(target: "wasm_runtime::new", "Instantiating module");
  182. let instance = Instance::new(&mut store, &module, &imports)?;
  183. let mut env_mut = ctx.as_mut(&mut store);
  184. env_mut.memory = Some(instance.exports.get_with_generics(MEMORY)?);
  185. Ok(Self { instance, store, ctx })
  186. }
  187. pub fn deploy(&mut self) -> Result<()> {
  188. let mut env_mut = self.ctx.as_mut(&mut self.store);
  189. env_mut.contract_section = ContractSection::Deploy;
  190. debug!(target: "wasm_runtime::run", "Getting initialize function");
  191. let entrypoint = self.instance.exports.get_function(INITIALIZE)?;
  192. debug!(target: "wasm_runtime::run", "Executing wasm");
  193. let ret = match entrypoint.call(&mut self.store, &[]) {
  194. Ok(retvals) => {
  195. self.print_logs();
  196. debug!(target: "wasm_runtime::run", "{}", self.gas_info());
  197. retvals
  198. }
  199. Err(e) => {
  200. self.print_logs();
  201. debug!(target: "wasm_runtime::run", "{}", self.gas_info());
  202. // WasmerRuntimeError panics are handled here. Return from run() immediately.
  203. return Err(e.into())
  204. }
  205. };
  206. debug!(target: "wasm_runtime::run", "wasm executed successfully");
  207. debug!(target: "wasm_runtime::run", "Contract returned: {:?}", ret[0]);
  208. let retval = match ret[0] {
  209. Value::I64(v) => v as u64,
  210. _ => unreachable!(),
  211. };
  212. match retval {
  213. entrypoint::SUCCESS => Ok(()),
  214. // FIXME: we should be able to see the error returned from the contract
  215. // We can put sdk::Error inside of this.
  216. _ => Err(Error::ContractInitError(retval)),
  217. }
  218. }
  219. /// Run the hardcoded `ENTRYPOINT` function with the given payload as input.
  220. pub fn exec(&mut self, payload: &[u8]) -> Result<()> {
  221. let mut env_mut = self.ctx.as_mut(&mut self.store);
  222. env_mut.contract_section = ContractSection::Exec;
  223. let pages_required = payload.len() / WASM_PAGE_SIZE + 1;
  224. self.set_memory_page_size(pages_required as u32)?;
  225. self.copy_to_memory(payload)?;
  226. debug!(target: "wasm_runtime::run", "Getting entrypoint function");
  227. let entrypoint = self.instance.exports.get_function(ENTRYPOINT)?;
  228. debug!(target: "wasm_runtime::run", "Executing wasm");
  229. // We pass 0 to entrypoint() which is the location of the payload data in the memory
  230. let ret = match entrypoint.call(&mut self.store, &[Value::I32(0 as i32)]) {
  231. Ok(retvals) => {
  232. self.print_logs();
  233. debug!(target: "wasm_runtime::run", "{}", self.gas_info());
  234. retvals
  235. }
  236. Err(e) => {
  237. self.print_logs();
  238. debug!(target: "wasm_runtime::run", "{}", self.gas_info());
  239. // WasmerRuntimeError panics are handled here. Return from run() immediately.
  240. return Err(e.into())
  241. }
  242. };
  243. debug!(target: "wasm_runtime::run", "wasm executed successfully");
  244. debug!(target: "wasm_runtime::run", "Contract returned: {:?}", ret[0]);
  245. let retval = match ret[0] {
  246. Value::I64(v) => v as u64,
  247. _ => unreachable!(),
  248. };
  249. match retval {
  250. entrypoint::SUCCESS => Ok(()),
  251. _ => Err(Error::ContractExecError(retval)),
  252. }
  253. }
  254. pub fn apply(&mut self) -> Result<()> {
  255. let mut env_mut = self.ctx.as_mut(&mut self.store);
  256. env_mut.contract_section = ContractSection::Update;
  257. let update_data = env_mut.contract_update.take().unwrap();
  258. // FIXME: Less realloc
  259. let mut payload = vec![update_data.0];
  260. payload.extend_from_slice(&update_data.1);
  261. let payload = serialize_payload(&payload);
  262. // TODO: Test if this works when state update is larger than the initial payload
  263. // The question is if we need to allocate more memory or if it's ok to just
  264. // overwrite from zero (and even if overwrite - is there enough space?)
  265. let pages_required = payload.len() / WASM_PAGE_SIZE + 1;
  266. self.set_memory_page_size(pages_required as u32)?;
  267. self.copy_to_memory(&payload)?;
  268. debug!(target: "wasm_runtime::run", "Getting initialize function");
  269. let entrypoint = self.instance.exports.get_function(UPDATE)?;
  270. debug!(target: "wasm_runtime::run", "Executing wasm");
  271. let ret = match entrypoint.call(&mut self.store, &[Value::I32(0 as i32)]) {
  272. Ok(retvals) => {
  273. self.print_logs();
  274. debug!(target: "wasm_runtime::run", "{}", self.gas_info());
  275. retvals
  276. }
  277. Err(e) => {
  278. self.print_logs();
  279. debug!(target: "wasm_runtime::run", "{}", self.gas_info());
  280. // WasmerRuntimeError panics are handled here. Return from run() immediately.
  281. return Err(e.into())
  282. }
  283. };
  284. debug!(target: "wasm_runtime::run", "wasm executed successfully");
  285. debug!(target: "wasm_runtime::run", "Contract returned: {:?}", ret[0]);
  286. let retval = match ret[0] {
  287. Value::I64(v) => v as u64,
  288. _ => unreachable!(),
  289. };
  290. match retval {
  291. entrypoint::SUCCESS => Ok(()),
  292. _ => Err(Error::ContractInitError(retval)),
  293. }
  294. }
  295. fn print_logs(&self) {
  296. let logs = self.ctx.as_ref(&self.store).logs.borrow();
  297. for msg in logs.iter() {
  298. debug!(target: "wasm_runtime::run", "Contract log: {}", msg);
  299. }
  300. }
  301. fn gas_info(&mut self) -> String {
  302. let remaining_points = get_remaining_points(&mut self.store, &self.instance);
  303. match remaining_points {
  304. MeteringPoints::Remaining(rem) => {
  305. format!("Gas used: {}/{}", GAS_LIMIT - rem, GAS_LIMIT)
  306. }
  307. MeteringPoints::Exhausted => {
  308. format!("Gas fully exhausted: {}/{}", GAS_LIMIT + 1, GAS_LIMIT)
  309. }
  310. }
  311. }
  312. /// Set the memory page size
  313. fn set_memory_page_size(&mut self, pages: u32) -> Result<()> {
  314. // Grab memory by value
  315. let memory = self.take_memory();
  316. // Modify the memory
  317. memory.grow(&mut self.store, Pages(pages))?;
  318. // Replace the memory back again
  319. self.ctx.as_mut(&mut self.store).memory = Some(memory);
  320. Ok(())
  321. }
  322. /// Take Memory by value. Needed to modify the Memory object
  323. /// Will panic if memory isn't set.
  324. fn take_memory(&mut self) -> Memory {
  325. let env_memory = &mut self.ctx.as_mut(&mut self.store).memory;
  326. let memory = std::mem::replace(env_memory, None);
  327. memory.expect("memory should be set")
  328. }
  329. /// Copy payload to the start of the memory
  330. fn copy_to_memory(&self, payload: &[u8]) -> Result<()> {
  331. // TODO: Maybe should write to first zero memory and return the pointer/offset?
  332. // Get the memory view
  333. let env = self.ctx.as_ref(&self.store);
  334. let memory_view = env.memory_view(&self.store);
  335. memory_view.write_slice(payload, 0)
  336. }
  337. }