1use std::io::Cursor;
20
21use darkfi_sdk::wasm;
22use darkfi_serial::Decodable;
23use tracing::{debug, error};
24use wasmer::{FunctionEnvMut, StoreMut, WasmPtr};
25
26use super::acl::acl_allow;
27use crate::{
28 runtime::vm_runtime::{ContractSection, Env},
29 Result,
30};
31
32pub(crate) fn drk_log(mut ctx: FunctionEnvMut<Env>, ptr: WasmPtr<u8>, len: u32) {
34 let (env, mut store) = ctx.data_and_store_mut();
35
36 env.subtract_gas(&mut store, len as u64);
38
39 let memory_view = env.memory_view(&store);
40 match ptr.read_utf8_string(&memory_view, len) {
41 Ok(msg) => {
42 let mut logs = env.logs.borrow_mut();
43 logs.push(msg);
44 std::mem::drop(logs);
45 }
46 Err(_) => {
47 error!(
48 target: "runtime::util::drk_log",
49 "[WASM] [{}] drk_log(): Failed to read UTF-8 string from VM memory",
50 env.contract_id,
51 );
52 }
53 }
54}
55
56pub(crate) fn wasm_mem_read(
59 env: &Env,
60 store: &StoreMut<'_>,
61 ptr: WasmPtr<u8>,
62 ptr_len: u32,
63) -> Result<Cursor<Vec<u8>>> {
64 let memory_view = env.memory_view(&store);
65 let mem_slice = ptr.slice(&memory_view, ptr_len)?;
66
67 let mut buf = vec![0u8; ptr_len as usize];
70 mem_slice.read_slice(&mut buf)?;
71
72 Ok(Cursor::new(buf))
74}
75
76pub(crate) fn set_return_data(mut ctx: FunctionEnvMut<Env>, ptr: WasmPtr<u8>, len: u32) -> i64 {
84 let (env, mut store) = ctx.data_and_store_mut();
85 let cid = &env.contract_id;
86
87 if let Err(e) = acl_allow(env, &[ContractSection::Metadata, ContractSection::Exec]) {
89 error!(
90 target: "runtime::util::set_return_data",
91 "[WASM] [{cid}] set_return_data(): Called in unauthorized section: {e}"
92 );
93 return darkfi_sdk::error::CALLER_ACCESS_DENIED
94 }
95
96 env.subtract_gas(&mut store, len as u64);
98
99 let memory_view = env.memory_view(&store);
100 let Ok(slice) = ptr.slice(&memory_view, len) else { return darkfi_sdk::error::INTERNAL_ERROR };
101 let Ok(return_data) = slice.read_to_vec() else { return darkfi_sdk::error::INTERNAL_ERROR };
102
103 if env.contract_return_data.take().is_some() {
105 return darkfi_sdk::error::SET_RETVAL_ERROR
106 }
107 env.contract_return_data.set(Some(return_data));
108
109 wasm::entrypoint::SUCCESS
110}
111
112pub(crate) fn get_object_bytes(mut ctx: FunctionEnvMut<Env>, ptr: WasmPtr<u8>, idx: u32) -> i64 {
119 let (env, mut store) = ctx.data_and_store_mut();
121 let cid = env.contract_id;
122
123 if let Err(e) =
125 acl_allow(env, &[ContractSection::Deploy, ContractSection::Metadata, ContractSection::Exec])
126 {
127 error!(
128 target: "runtime::util::get_object_bytes",
129 "[WASM] [{cid}] get_object_bytes(): Called in unauthorized section: {e}"
130 );
131 return darkfi_sdk::error::CALLER_ACCESS_DENIED
132 }
133
134 let objects = env.objects.borrow();
136 if idx as usize >= objects.len() {
137 error!(
138 target: "runtime::util::get_object_bytes",
139 "[WASM] [{cid}] get_object_bytes(): Tried to access object out of bounds"
140 );
141 return darkfi_sdk::error::DATA_TOO_LARGE
142 }
143 let obj = objects[idx as usize].clone();
144 drop(objects);
145
146 if obj.len() > u32::MAX as usize {
147 return darkfi_sdk::error::DATA_TOO_LARGE
148 }
149
150 env.subtract_gas(&mut store, obj.len() as u64);
152
153 let memory_view = env.memory_view(&store);
155 let Ok(slice) = ptr.slice(&memory_view, obj.len() as u32) else {
156 error!(
157 target: "runtime::util::get_object_bytes",
158 "[WASM] [{cid}] get_object_bytes(): Failed to make slice from ptr"
159 );
160 return darkfi_sdk::error::INTERNAL_ERROR
161 };
162
163 if let Err(e) = slice.write_slice(&obj) {
165 error!(
166 target: "runtime::util::get_object_bytes",
167 "[WASM] [{cid}] get_object_bytes(): Failed to write to memory slice: {e}"
168 );
169 return darkfi_sdk::error::INTERNAL_ERROR
170 };
171
172 wasm::entrypoint::SUCCESS
173}
174
175pub(crate) fn get_object_size(mut ctx: FunctionEnvMut<Env>, idx: u32) -> i64 {
180 let (env, mut store) = ctx.data_and_store_mut();
182 let cid = env.contract_id;
183
184 if let Err(e) =
186 acl_allow(env, &[ContractSection::Deploy, ContractSection::Metadata, ContractSection::Exec])
187 {
188 error!(
189 target: "runtime::util::get_object_size",
190 "[WASM] [{cid}] get_object_size(): Called in unauthorized section: {e}"
191 );
192 return darkfi_sdk::error::CALLER_ACCESS_DENIED
193 }
194
195 let objects = env.objects.borrow();
197 if idx as usize >= objects.len() {
198 error!(
199 target: "runtime::util::get_object_size",
200 "[WASM] [{cid}] get_object_size(): Tried to access object out of bounds"
201 );
202 return darkfi_sdk::error::DATA_TOO_LARGE
203 }
204
205 let obj = &objects[idx as usize];
206 let obj_len = obj.len();
207 drop(objects);
208
209 if obj_len > u32::MAX as usize {
210 return darkfi_sdk::error::DATA_TOO_LARGE
211 }
212
213 env.subtract_gas(&mut store, obj_len as u64);
216
217 obj_len as i64
218}
219
220pub(crate) fn get_verifying_block_height(mut ctx: FunctionEnvMut<Env>) -> i64 {
224 let (env, mut store) = ctx.data_and_store_mut();
225 let cid = env.contract_id;
226
227 if let Err(e) =
228 acl_allow(env, &[ContractSection::Deploy, ContractSection::Metadata, ContractSection::Exec])
229 {
230 error!(
231 target: "runtime::util::get_verifying_block_height",
232 "[WASM] [{cid}] get_verifying_block_height(): Called in unauthorized section: {e}"
233 );
234 return darkfi_sdk::error::CALLER_ACCESS_DENIED
235 }
236
237 env.subtract_gas(&mut store, 4);
240
241 env.verifying_block_height as i64
242}
243
244pub(crate) fn get_block_target(mut ctx: FunctionEnvMut<Env>) -> i64 {
248 let (env, mut store) = ctx.data_and_store_mut();
249 let cid = env.contract_id;
250
251 if let Err(e) =
252 acl_allow(env, &[ContractSection::Deploy, ContractSection::Metadata, ContractSection::Exec])
253 {
254 error!(
255 target: "runtime::util::get_block_target",
256 "[WASM] [{cid}] get_block_target(): Called in unauthorized section: {e}"
257 );
258 return darkfi_sdk::error::CALLER_ACCESS_DENIED
259 }
260
261 env.subtract_gas(&mut store, 4);
264
265 env.block_target as i64
266}
267
268pub(crate) fn get_tx_hash(mut ctx: FunctionEnvMut<Env>) -> i64 {
272 let (env, mut store) = ctx.data_and_store_mut();
273 let cid = env.contract_id;
274
275 if let Err(e) =
276 acl_allow(env, &[ContractSection::Deploy, ContractSection::Metadata, ContractSection::Exec])
277 {
278 error!(
279 target: "runtime::util::get_tx_hash",
280 "[WASM] [{cid}] get_tx_hash(): Called in unauthorized section: {e}"
281 );
282 return darkfi_sdk::error::CALLER_ACCESS_DENIED
283 }
284
285 env.subtract_gas(&mut store, 32);
287
288 let mut objects = env.objects.borrow_mut();
291 objects.push(env.tx_hash.inner().to_vec());
292 (objects.len() - 1) as i64
293}
294
295pub(crate) fn get_call_index(mut ctx: FunctionEnvMut<Env>) -> i64 {
299 let (env, mut store) = ctx.data_and_store_mut();
300 let cid = env.contract_id;
301
302 if let Err(e) =
303 acl_allow(env, &[ContractSection::Deploy, ContractSection::Metadata, ContractSection::Exec])
304 {
305 error!(
306 target: "runtime::util::get_call_index",
307 "[WASM] [{cid}] get_call_index(): Called in unauthorized section: {e}"
308 );
309 return darkfi_sdk::error::CALLER_ACCESS_DENIED
310 }
311
312 env.subtract_gas(&mut store, 1);
315
316 env.call_idx as i64
317}
318
319pub(crate) fn get_blockchain_time(mut ctx: FunctionEnvMut<Env>) -> i64 {
324 let (env, mut store) = ctx.data_and_store_mut();
325 let cid = &env.contract_id;
326
327 if let Err(e) =
328 acl_allow(env, &[ContractSection::Deploy, ContractSection::Metadata, ContractSection::Exec])
329 {
330 error!(
331 target: "runtime::util::get_blockchain_time",
332 "[WASM] [{cid}] get_blockchain_time(): Called in unauthorized section: {e}"
333 );
334 return darkfi_sdk::error::CALLER_ACCESS_DENIED
335 }
336
337 let timestamp = match env.blockchain.lock().unwrap().last_block_timestamp() {
339 Ok(b) => b,
340 Err(e) => {
341 error!(
342 target: "runtime::util::get_blockchain_time",
343 "[WASM] [{cid}] get_blockchain_time(): Internal error getting from blocks tree: {e}"
344 );
345 return darkfi_sdk::error::DB_GET_FAILED
346 }
347 };
348
349 env.subtract_gas(&mut store, 8);
352
353 let mut ret = Vec::with_capacity(8);
355 ret.extend_from_slice(×tamp.inner().to_be_bytes());
356
357 let mut objects = env.objects.borrow_mut();
359 objects.push(ret.to_vec());
360 if objects.len() > u32::MAX as usize {
361 return darkfi_sdk::error::DATA_TOO_LARGE
362 }
363
364 (objects.len() - 1) as i64
365}
366
367pub(crate) fn get_last_block_height(mut ctx: FunctionEnvMut<Env>) -> i64 {
375 let (env, mut store) = ctx.data_and_store_mut();
376 let cid = &env.contract_id;
377
378 if let Err(e) =
380 acl_allow(env, &[ContractSection::Deploy, ContractSection::Metadata, ContractSection::Exec])
381 {
382 error!(
383 target: "runtime::util::get_last_block_height",
384 "[WASM] [{cid}] get_last_block_height(): Called in unauthorized section: {e}"
385 );
386 return darkfi_sdk::error::CALLER_ACCESS_DENIED
387 }
388
389 let height = match env.blockchain.lock().unwrap().last_block_height() {
391 Ok(b) => b,
392 Err(e) => {
393 error!(
394 target: "runtime::util::get_last_block_height",
395 "[WASM] [{cid}] get_last_block_height(): Internal error getting from blocks tree: {e}"
396 );
397 return darkfi_sdk::error::DB_GET_FAILED
398 }
399 };
400
401 env.subtract_gas(&mut store, 8);
404
405 let mut ret = Vec::with_capacity(8);
407 ret.extend_from_slice(&darkfi_serial::serialize(&height));
408
409 let mut objects = env.objects.borrow_mut();
411 objects.push(ret.to_vec());
412 if objects.len() > u32::MAX as usize {
413 return darkfi_sdk::error::DATA_TOO_LARGE
414 }
415
416 (objects.len() - 1) as i64
417}
418
419pub(crate) fn get_tx(mut ctx: FunctionEnvMut<Env>, ptr: WasmPtr<u8>) -> i64 {
428 let (env, mut store) = ctx.data_and_store_mut();
429 let cid = env.contract_id;
430
431 if let Err(e) =
432 acl_allow(env, &[ContractSection::Deploy, ContractSection::Metadata, ContractSection::Exec])
433 {
434 error!(
435 target: "runtime::util::get_tx",
436 "[WASM] [{cid}] get_tx(): Called in unauthorized section: {e}"
437 );
438 return darkfi_sdk::error::CALLER_ACCESS_DENIED
439 }
440
441 env.subtract_gas(&mut store, blake3::OUT_LEN as u64);
443
444 let memory_view = env.memory_view(&store);
446 let Ok(mem_slice) = ptr.slice(&memory_view, blake3::OUT_LEN as u32) else {
447 error!(
448 target: "runtime::util::get_tx",
449 "[WASM] [{cid}] get_tx(): Failed to make slice from ptr"
450 );
451 return darkfi_sdk::error::DB_GET_FAILED
452 };
453
454 let mut buf = vec![0_u8; blake3::OUT_LEN];
455 if let Err(e) = mem_slice.read_slice(&mut buf) {
456 error!(
457 target: "runtime::util::get_tx",
458 "[WASM] [{cid}] get_tx(): Failed to read from memory slice: {e}"
459 );
460 return darkfi_sdk::error::DB_GET_FAILED
461 };
462
463 let mut buf_reader = Cursor::new(buf);
464
465 let hash: [u8; blake3::OUT_LEN] = match Decodable::decode(&mut buf_reader) {
467 Ok(v) => v,
468 Err(e) => {
469 error!(
470 target: "runtime::util::get_tx",
471 "[WASM] [{cid}] get_tx(): Failed to decode hash from vec: {e}"
472 );
473 return darkfi_sdk::error::DB_GET_FAILED
474 }
475 };
476
477 if buf_reader.position() != blake3::OUT_LEN as u64 {
480 error!(
481 target: "runtime::util::get_tx",
482 "[WASM] [{cid}] get_tx(): Trailing bytes in argument stream"
483 );
484 return darkfi_sdk::error::DB_GET_FAILED
485 }
486
487 let ret = match env.blockchain.lock().unwrap().transactions.get_raw(&hash) {
489 Ok(v) => v,
490 Err(e) => {
491 error!(
492 target: "runtime::util::get_tx",
493 "[WASM] [{cid}] get_tx(): Internal error getting from tree: {e}"
494 );
495 return darkfi_sdk::error::DB_GET_FAILED
496 }
497 };
498
499 let Some(return_data) = ret else {
501 debug!(
502 target: "runtime::util::get_tx",
503 "[WASM] [{cid}] get_tx(): Return data is empty"
504 );
505 return darkfi_sdk::error::DB_GET_EMPTY
506 };
507
508 if return_data.len() > u32::MAX as usize {
509 return darkfi_sdk::error::DATA_TOO_LARGE
510 }
511
512 env.subtract_gas(&mut store, return_data.len() as u64);
514
515 let mut objects = env.objects.borrow_mut();
517 if objects.len() == u32::MAX as usize {
518 return darkfi_sdk::error::DATA_TOO_LARGE
519 }
520
521 objects.push(return_data.to_vec());
524 (objects.len() - 1) as i64
525}
526
527pub(crate) fn get_tx_location(mut ctx: FunctionEnvMut<Env>, ptr: WasmPtr<u8>) -> i64 {
536 let (env, mut store) = ctx.data_and_store_mut();
537 let cid = env.contract_id;
538
539 if let Err(e) =
540 acl_allow(env, &[ContractSection::Deploy, ContractSection::Metadata, ContractSection::Exec])
541 {
542 error!(
543 target: "runtime::util::get_tx_location",
544 "[WASM] [{cid}] get_tx_location(): Called in unauthorized section: {e}"
545 );
546 return darkfi_sdk::error::CALLER_ACCESS_DENIED
547 }
548
549 env.subtract_gas(&mut store, blake3::OUT_LEN as u64);
551
552 let memory_view = env.memory_view(&store);
554 let Ok(mem_slice) = ptr.slice(&memory_view, blake3::OUT_LEN as u32) else {
555 error!(
556 target: "runtime::util::get_tx_location",
557 "[WASM] [{cid}] get_tx_location(): Failed to make slice from ptr"
558 );
559 return darkfi_sdk::error::DB_GET_FAILED
560 };
561
562 let mut buf = vec![0_u8; blake3::OUT_LEN];
563 if let Err(e) = mem_slice.read_slice(&mut buf) {
564 error!(
565 target: "runtime::util::get_tx_location",
566 "[WASM] [{cid}] get_tx_location(): Failed to read from memory slice: {e}"
567 );
568 return darkfi_sdk::error::DB_GET_FAILED
569 };
570
571 let mut buf_reader = Cursor::new(buf);
572
573 let hash: [u8; blake3::OUT_LEN] = match Decodable::decode(&mut buf_reader) {
575 Ok(v) => v,
576 Err(e) => {
577 error!(
578 target: "runtime::util::get_tx_location",
579 "[WASM] [{cid}] get_tx_location(): Failed to decode hash from vec: {e}"
580 );
581 return darkfi_sdk::error::DB_GET_FAILED
582 }
583 };
584
585 if buf_reader.position() != blake3::OUT_LEN as u64 {
588 error!(
589 target: "runtime::util::get_tx_location",
590 "[WASM] [{cid}] get_tx_location(): Trailing bytes in argument stream"
591 );
592 return darkfi_sdk::error::DB_GET_FAILED
593 }
594
595 let ret = match env.blockchain.lock().unwrap().transactions.get_location_raw(&hash) {
597 Ok(v) => v,
598 Err(e) => {
599 error!(
600 target: "runtime::util::get_tx_location",
601 "[WASM] [{cid}] get_tx_location(): Internal error getting from tree: {e}"
602 );
603 return darkfi_sdk::error::DB_GET_FAILED
604 }
605 };
606
607 let Some(return_data) = ret else {
609 debug!(
610 target: "runtime::util::get_tx_location",
611 "[WASM] [{cid}] get_tx_location(): Return data is empty"
612 );
613 return darkfi_sdk::error::DB_GET_EMPTY
614 };
615
616 if return_data.len() > u32::MAX as usize {
617 return darkfi_sdk::error::DATA_TOO_LARGE
618 }
619
620 env.subtract_gas(&mut store, return_data.len() as u64);
622
623 let mut objects = env.objects.borrow_mut();
625 if objects.len() == u32::MAX as usize {
626 return darkfi_sdk::error::DATA_TOO_LARGE
627 }
628
629 objects.push(return_data.to_vec());
632 (objects.len() - 1) as i64
633}