near_vm_logic/logic.rs
1use crate::context::VMContext;
2use crate::dependencies::{External, MemoryLike};
3use crate::gas_counter::GasCounter;
4use crate::types::{PromiseIndex, PromiseResult, ReceiptIndex, ReturnData};
5use crate::utils::split_method_names;
6use crate::ValuePtr;
7use byteorder::ByteOrder;
8use near_primitives_core::config::ExtCosts::*;
9use near_primitives_core::config::{ActionCosts, ExtCosts, VMConfig};
10use near_primitives_core::profile::ProfileData;
11use near_primitives_core::runtime::fees::RuntimeFeesConfig;
12use near_primitives_core::types::{
13 AccountId, Balance, EpochHeight, Gas, ProtocolVersion, StorageUsage,
14};
15use near_runtime_utils::is_account_id_64_len_hex;
16use near_vm_errors::InconsistentStateError;
17use near_vm_errors::{HostError, VMLogicError};
18use serde::{Deserialize, Serialize};
19use std::collections::HashMap;
20use std::mem::size_of;
21
22type Result<T> = ::std::result::Result<T, VMLogicError>;
23
24const LEGACY_DEFAULT_PROTOCOL_VERSION: ProtocolVersion = 34;
25const IMPLICIT_ACCOUNT_CREATION_PROTOCOL_VERSION: ProtocolVersion = 35;
26
27pub struct VMLogic<'a> {
28 /// Provides access to the components outside the Wasm runtime for operations on the trie and
29 /// receipts creation.
30 ext: &'a mut dyn External,
31 /// Part of Context API and Economics API that was extracted from the receipt.
32 context: VMContext,
33 /// Parameters of Wasm and economic parameters.
34 config: &'a VMConfig,
35 /// Fees for creating (async) actions on runtime.
36 fees_config: &'a RuntimeFeesConfig,
37 /// If this method execution is invoked directly as a callback by one or more contract calls the
38 /// results of the methods that made the callback are stored in this collection.
39 promise_results: &'a [PromiseResult],
40 /// Pointer to the guest memory.
41 memory: &'a mut dyn MemoryLike,
42
43 /// Keeping track of the current account balance, which can decrease when we create promises
44 /// and attach balance to them.
45 current_account_balance: Balance,
46 /// Current amount of locked tokens, does not automatically change when staking transaction is
47 /// issued.
48 current_account_locked_balance: Balance,
49 /// Storage usage of the current account at the moment
50 current_storage_usage: StorageUsage,
51 gas_counter: GasCounter,
52 /// What method returns.
53 return_data: ReturnData,
54 /// Logs written by the runtime.
55 logs: Vec<String>,
56 /// Registers can be used by the guest to store blobs of data without moving them across
57 /// host-guest boundary.
58 registers: HashMap<u64, Vec<u8>>,
59
60 /// The DAG of promises, indexed by promise id.
61 promises: Vec<Promise>,
62 /// Record the accounts towards which the receipts are directed.
63 receipt_to_account: HashMap<ReceiptIndex, AccountId>,
64
65 /// Tracks the total log length. The sum of length of all logs.
66 total_log_length: u64,
67
68 /// Current protocol version that is used for the function call.
69 current_protocol_version: ProtocolVersion,
70}
71
72/// Promises API allows to create a DAG-structure that defines dependencies between smart contract
73/// calls. A single promise can be created with zero or several dependencies on other promises.
74/// * If a promise was created from a receipt (using `promise_create` or `promise_then`) it's a
75/// `Receipt`;
76/// * If a promise was created by merging several promises (using `promise_and`) then
77/// it's a `NotReceipt`, but has receipts of all promises it depends on.
78#[derive(Debug)]
79enum Promise {
80 Receipt(ReceiptIndex),
81 NotReceipt(Vec<ReceiptIndex>),
82}
83
84macro_rules! memory_get {
85 ($_type:ty, $name:ident) => {
86 fn $name(&mut self, offset: u64) -> Result<$_type> {
87 let mut array = [0u8; size_of::<$_type>()];
88 self.memory_get_into(offset, &mut array)?;
89 Ok(<$_type>::from_le_bytes(array))
90 }
91 };
92}
93
94macro_rules! memory_set {
95 ($_type:ty, $name:ident) => {
96 fn $name(&mut self, offset: u64, value: $_type) -> Result<()> {
97 self.memory_set_slice(offset, &value.to_le_bytes())
98 }
99 };
100}
101
102impl<'a> VMLogic<'a> {
103 pub fn new_with_protocol_version(
104 ext: &'a mut dyn External,
105 context: VMContext,
106 config: &'a VMConfig,
107 fees_config: &'a RuntimeFeesConfig,
108 promise_results: &'a [PromiseResult],
109 memory: &'a mut dyn MemoryLike,
110 profile: Option<ProfileData>,
111 current_protocol_version: ProtocolVersion,
112 ) -> Self {
113 ext.reset_touched_nodes_counter();
114 // Overflow should be checked before calling VMLogic.
115 let current_account_balance = context.account_balance + context.attached_deposit;
116 let current_storage_usage = context.storage_usage;
117 let max_gas_burnt = if context.is_view {
118 config.limit_config.max_gas_burnt_view
119 } else {
120 config.limit_config.max_gas_burnt
121 };
122 let current_account_locked_balance = context.account_locked_balance;
123 let gas_counter = GasCounter::new(
124 config.ext_costs.clone(),
125 max_gas_burnt,
126 context.prepaid_gas,
127 context.is_view,
128 profile,
129 );
130 Self {
131 ext,
132 context,
133 config,
134 fees_config,
135 promise_results,
136 memory,
137 current_account_balance,
138 current_account_locked_balance,
139 current_storage_usage,
140 gas_counter,
141 return_data: ReturnData::None,
142 logs: vec![],
143 registers: HashMap::new(),
144 promises: vec![],
145 receipt_to_account: HashMap::new(),
146 total_log_length: 0,
147 current_protocol_version,
148 }
149 }
150
151 /// Legacy initialization method that doesn't pass the protocol version and uses the last
152 /// protocol version before the change was introduced.
153 pub fn new(
154 ext: &'a mut dyn External,
155 context: VMContext,
156 config: &'a VMConfig,
157 fees_config: &'a RuntimeFeesConfig,
158 promise_results: &'a [PromiseResult],
159 memory: &'a mut dyn MemoryLike,
160 profile: Option<ProfileData>,
161 ) -> Self {
162 Self::new_with_protocol_version(
163 ext,
164 context,
165 config,
166 fees_config,
167 promise_results,
168 memory,
169 profile,
170 LEGACY_DEFAULT_PROTOCOL_VERSION,
171 )
172 }
173
174 // ###########################
175 // # Memory helper functions #
176 // ###########################
177
178 fn try_fit_mem(&mut self, offset: u64, len: u64) -> Result<()> {
179 if self.memory.fits_memory(offset, len) {
180 Ok(())
181 } else {
182 Err(HostError::MemoryAccessViolation.into())
183 }
184 }
185
186 fn memory_get_into(&mut self, offset: u64, buf: &mut [u8]) -> Result<()> {
187 self.gas_counter.pay_base(read_memory_base)?;
188 self.gas_counter.pay_per_byte(read_memory_byte, buf.len() as _)?;
189 self.try_fit_mem(offset, buf.len() as _)?;
190 self.memory.read_memory(offset, buf);
191 Ok(())
192 }
193
194 fn memory_get_vec(&mut self, offset: u64, len: u64) -> Result<Vec<u8>> {
195 self.gas_counter.pay_base(read_memory_base)?;
196 self.gas_counter.pay_per_byte(read_memory_byte, len)?;
197 self.try_fit_mem(offset, len)?;
198 let mut buf = vec![0; len as usize];
199 self.memory.read_memory(offset, &mut buf);
200 Ok(buf)
201 }
202
203 memory_get!(u128, memory_get_u128);
204 memory_get!(u32, memory_get_u32);
205 memory_get!(u16, memory_get_u16);
206 memory_get!(u8, memory_get_u8);
207
208 /// Reads an array of `u64` elements.
209 fn memory_get_vec_u64(&mut self, offset: u64, num_elements: u64) -> Result<Vec<u64>> {
210 let memory_len = num_elements
211 .checked_mul(size_of::<u64>() as u64)
212 .ok_or(HostError::MemoryAccessViolation)?;
213 let data = self.memory_get_vec(offset, memory_len)?;
214 let mut res = vec![0u64; num_elements as usize];
215 byteorder::LittleEndian::read_u64_into(&data, &mut res);
216 Ok(res)
217 }
218
219 fn get_vec_from_memory_or_register(&mut self, offset: u64, len: u64) -> Result<Vec<u8>> {
220 if len != std::u64::MAX {
221 self.memory_get_vec(offset, len)
222 } else {
223 self.internal_read_register(offset)
224 }
225 }
226
227 fn memory_set_slice(&mut self, offset: u64, buf: &[u8]) -> Result<()> {
228 self.gas_counter.pay_base(write_memory_base)?;
229 self.gas_counter.pay_per_byte(write_memory_byte, buf.len() as _)?;
230 self.try_fit_mem(offset, buf.len() as _)?;
231 self.memory.write_memory(offset, buf);
232 Ok(())
233 }
234
235 memory_set!(u128, memory_set_u128);
236
237 // #################
238 // # Registers API #
239 // #################
240
241 fn internal_read_register(&mut self, register_id: u64) -> Result<Vec<u8>> {
242 if let Some(data) = self.registers.get(®ister_id) {
243 self.gas_counter.pay_base(read_register_base)?;
244 self.gas_counter.pay_per_byte(read_register_byte, data.len() as _)?;
245 Ok(data.clone())
246 } else {
247 Err(HostError::InvalidRegisterId { register_id }.into())
248 }
249 }
250
251 fn internal_write_register(&mut self, register_id: u64, data: Vec<u8>) -> Result<()> {
252 self.gas_counter.pay_base(write_register_base)?;
253 self.gas_counter.pay_per_byte(write_register_byte, data.len() as u64)?;
254 if data.len() as u64 > self.config.limit_config.max_register_size
255 || self.registers.len() as u64 >= self.config.limit_config.max_number_registers
256 {
257 return Err(HostError::MemoryAccessViolation.into());
258 }
259 self.registers.insert(register_id, data);
260
261 // Calculate the new memory usage.
262 let usage: usize =
263 self.registers.values().map(|v| size_of::<u64>() + v.len() * size_of::<u8>()).sum();
264 if usage as u64 > self.config.limit_config.registers_memory_limit {
265 Err(HostError::MemoryAccessViolation.into())
266 } else {
267 Ok(())
268 }
269 }
270
271 /// Convenience function for testing.
272 pub fn wrapped_internal_write_register(&mut self, register_id: u64, data: &[u8]) -> Result<()> {
273 self.internal_write_register(register_id, data.to_vec())
274 }
275
276 /// Writes the entire content from the register `register_id` into the memory of the guest starting with `ptr`.
277 ///
278 /// # Arguments
279 ///
280 /// * `register_id` -- a register id from where to read the data;
281 /// * `ptr` -- location on guest memory where to copy the data.
282 ///
283 /// # Errors
284 ///
285 /// * If the content extends outside the memory allocated to the guest. In Wasmer, it returns `MemoryAccessViolation` error message;
286 /// * If `register_id` is pointing to unused register returns `InvalidRegisterId` error message.
287 ///
288 /// # Undefined Behavior
289 ///
290 /// If the content of register extends outside the preallocated memory on the host side, or the pointer points to a
291 /// wrong location this function will overwrite memory that it is not supposed to overwrite causing an undefined behavior.
292 ///
293 /// # Cost
294 ///
295 /// `base + read_register_base + read_register_byte * num_bytes + write_memory_base + write_memory_byte * num_bytes`
296 pub fn read_register(&mut self, register_id: u64, ptr: u64) -> Result<()> {
297 self.gas_counter.pay_base(base)?;
298 let data = self.internal_read_register(register_id)?;
299 self.memory_set_slice(ptr, &data)
300 }
301
302 /// Returns the size of the blob stored in the given register.
303 /// * If register is used, then returns the size, which can potentially be zero;
304 /// * If register is not used, returns `u64::MAX`
305 ///
306 /// # Arguments
307 ///
308 /// * `register_id` -- a register id from where to read the data;
309 ///
310 /// # Cost
311 ///
312 /// `base`
313 pub fn register_len(&mut self, register_id: u64) -> Result<u64> {
314 self.gas_counter.pay_base(base)?;
315 Ok(self.registers.get(®ister_id).map(|r| r.len() as _).unwrap_or(std::u64::MAX))
316 }
317
318 /// Copies `data` from the guest memory into the register. If register is unused will initialize
319 /// it. If register has larger capacity than needed for `data` will not re-allocate it. The
320 /// register will lose the pre-existing data if any.
321 ///
322 /// # Arguments
323 ///
324 /// * `register_id` -- a register id where to write the data;
325 /// * `data_len` -- length of the data in bytes;
326 /// * `data_ptr` -- pointer in the guest memory where to read the data from.
327 ///
328 /// # Cost
329 ///
330 /// `base + read_memory_base + read_memory_bytes * num_bytes + write_register_base + write_register_bytes * num_bytes`
331 pub fn write_register(&mut self, register_id: u64, data_len: u64, data_ptr: u64) -> Result<()> {
332 self.gas_counter.pay_base(base)?;
333 let data = self.memory_get_vec(data_ptr, data_len)?;
334 self.internal_write_register(register_id, data)
335 }
336
337 // ###################################
338 // # String reading helper functions #
339 // ###################################
340
341 /// Helper function to read and return utf8-encoding string.
342 /// If `len == u64::MAX` then treats the string as null-terminated with character `'\0'`.
343 ///
344 /// # Errors
345 ///
346 /// * If string extends outside the memory of the guest with `MemoryAccessViolation`;
347 /// * If string is not UTF-8 returns `BadUtf8`.
348 /// * If number of bytes read + `total_log_length` exceeds the `max_total_log_length` returns
349 /// `TotalLogLengthExceeded`.
350 ///
351 /// # Cost
352 ///
353 /// For not nul-terminated string:
354 /// `read_memory_base + read_memory_byte * num_bytes + utf8_decoding_base + utf8_decoding_byte * num_bytes`
355 ///
356 /// For nul-terminated string:
357 /// `(read_memory_base + read_memory_byte) * num_bytes + utf8_decoding_base + utf8_decoding_byte * num_bytes`
358 fn get_utf8_string(&mut self, len: u64, ptr: u64) -> Result<String> {
359 self.gas_counter.pay_base(utf8_decoding_base)?;
360 let mut buf;
361 let max_len =
362 self.config.limit_config.max_total_log_length.saturating_sub(self.total_log_length);
363 if len != std::u64::MAX {
364 if len > max_len {
365 return Err(HostError::TotalLogLengthExceeded {
366 length: self.total_log_length.saturating_add(len),
367 limit: self.config.limit_config.max_total_log_length,
368 }
369 .into());
370 }
371 buf = self.memory_get_vec(ptr, len)?;
372 } else {
373 buf = vec![];
374 for i in 0..=max_len {
375 // self.try_fit_mem will check for u64 overflow on the first iteration (i == 0)
376 let el = self.memory_get_u8(ptr + i)?;
377 if el == 0 {
378 break;
379 }
380 if i == max_len {
381 return Err(HostError::TotalLogLengthExceeded {
382 length: self.total_log_length.saturating_add(max_len).saturating_add(1),
383 limit: self.config.limit_config.max_total_log_length,
384 }
385 .into());
386 }
387 buf.push(el);
388 }
389 }
390 self.gas_counter.pay_per_byte(utf8_decoding_byte, buf.len() as _)?;
391 String::from_utf8(buf).map_err(|_| HostError::BadUTF8.into())
392 }
393
394 /// Helper function to read UTF-16 formatted string from guest memory.
395 /// # Errors
396 ///
397 /// * If string extends outside the memory of the guest with `MemoryAccessViolation`;
398 /// * If string is not UTF-16 returns `BadUtf16`.
399 /// * If number of bytes read + `total_log_length` exceeds the `max_total_log_length` returns
400 /// `TotalLogLengthExceeded`.
401 ///
402 /// # Cost
403 ///
404 /// For not nul-terminated string:
405 /// `read_memory_base + read_memory_byte * num_bytes + utf16_decoding_base + utf16_decoding_byte * num_bytes`
406 ///
407 /// For nul-terminated string:
408 /// `read_memory_base * num_bytes / 2 + read_memory_byte * num_bytes + utf16_decoding_base + utf16_decoding_byte * num_bytes`
409 fn get_utf16_string(&mut self, len: u64, ptr: u64) -> Result<String> {
410 self.gas_counter.pay_base(utf16_decoding_base)?;
411 let mut u16_buffer;
412 let max_len =
413 self.config.limit_config.max_total_log_length.saturating_sub(self.total_log_length);
414 if len != std::u64::MAX {
415 let input = self.memory_get_vec(ptr, len)?;
416 if len % 2 != 0 {
417 return Err(HostError::BadUTF16.into());
418 }
419 if len > max_len {
420 return Err(HostError::TotalLogLengthExceeded {
421 length: self.total_log_length.saturating_add(len),
422 limit: self.config.limit_config.max_total_log_length,
423 }
424 .into());
425 }
426 u16_buffer = vec![0u16; len as usize / 2];
427 byteorder::LittleEndian::read_u16_into(&input, &mut u16_buffer);
428 } else {
429 u16_buffer = vec![];
430 let limit = max_len / size_of::<u16>() as u64;
431 // Takes 2 bytes each iter
432 for i in 0..=limit {
433 // self.try_fit_mem will check for u64 overflow on the first iteration (i == 0)
434 let start = ptr + i * size_of::<u16>() as u64;
435 let el = self.memory_get_u16(start)?;
436 if el == 0 {
437 break;
438 }
439 if i == limit {
440 return Err(HostError::TotalLogLengthExceeded {
441 length: self
442 .total_log_length
443 .saturating_add(i * size_of::<u16>() as u64)
444 .saturating_add(size_of::<u16>() as u64),
445 limit: self.config.limit_config.max_total_log_length,
446 }
447 .into());
448 }
449 u16_buffer.push(el);
450 }
451 }
452 self.gas_counter
453 .pay_per_byte(utf16_decoding_byte, u16_buffer.len() as u64 * size_of::<u16>() as u64)?;
454 String::from_utf16(&u16_buffer).map_err(|_| HostError::BadUTF16.into())
455 }
456
457 // ####################################################
458 // # Helper functions to prevent code duplication API #
459 // ####################################################
460
461 /// Checks that the current log number didn't reach the limit yet, so we can add a new message.
462 fn check_can_add_a_log_message(&self) -> Result<()> {
463 if self.logs.len() as u64 >= self.config.limit_config.max_number_logs {
464 Err(HostError::NumberOfLogsExceeded { limit: self.config.limit_config.max_number_logs }
465 .into())
466 } else {
467 Ok(())
468 }
469 }
470
471 /// Adds a given promise to the vector of promises and returns a new promise index.
472 /// Throws `NumberPromisesExceeded` if the total number of promises exceeded the limit.
473 fn checked_push_promise(&mut self, promise: Promise) -> Result<PromiseIndex> {
474 let new_promise_idx = self.promises.len() as PromiseIndex;
475 self.promises.push(promise);
476 if self.promises.len() as u64
477 > self.config.limit_config.max_promises_per_function_call_action
478 {
479 Err(HostError::NumberPromisesExceeded {
480 number_of_promises: self.promises.len() as u64,
481 limit: self.config.limit_config.max_promises_per_function_call_action,
482 }
483 .into())
484 } else {
485 Ok(new_promise_idx)
486 }
487 }
488
489 fn checked_push_log(&mut self, message: String) -> Result<()> {
490 // The size of logged data can't be too large. No overflow.
491 self.total_log_length += message.len() as u64;
492 if self.total_log_length > self.config.limit_config.max_total_log_length {
493 return Err(HostError::TotalLogLengthExceeded {
494 length: self.total_log_length,
495 limit: self.config.limit_config.max_total_log_length,
496 }
497 .into());
498 }
499 self.logs.push(message);
500 Ok(())
501 }
502
503 // ###############
504 // # Context API #
505 // ###############
506
507 /// Saves the account id of the current contract that we execute into the register.
508 ///
509 /// # Errors
510 ///
511 /// If the registers exceed the memory limit returns `MemoryAccessViolation`.
512 ///
513 /// # Cost
514 ///
515 /// `base + write_register_base + write_register_byte * num_bytes`
516 pub fn current_account_id(&mut self, register_id: u64) -> Result<()> {
517 self.gas_counter.pay_base(base)?;
518
519 self.internal_write_register(
520 register_id,
521 self.context.current_account_id.as_bytes().to_vec(),
522 )
523 }
524
525 /// All contract calls are a result of some transaction that was signed by some account using
526 /// some access key and submitted into a memory pool (either through the wallet using RPC or by
527 /// a node itself). This function returns the id of that account. Saves the bytes of the signer
528 /// account id into the register.
529 ///
530 /// # Errors
531 ///
532 /// * If the registers exceed the memory limit returns `MemoryAccessViolation`.
533 /// * If called as view function returns `ProhibitedInView`.
534 ///
535 /// # Cost
536 ///
537 /// `base + write_register_base + write_register_byte * num_bytes`
538 pub fn signer_account_id(&mut self, register_id: u64) -> Result<()> {
539 self.gas_counter.pay_base(base)?;
540
541 if self.context.is_view {
542 return Err(HostError::ProhibitedInView {
543 method_name: "signer_account_id".to_string(),
544 }
545 .into());
546 }
547 self.internal_write_register(
548 register_id,
549 self.context.signer_account_id.as_bytes().to_vec(),
550 )
551 }
552
553 /// Saves the public key fo the access key that was used by the signer into the register. In
554 /// rare situations smart contract might want to know the exact access key that was used to send
555 /// the original transaction, e.g. to increase the allowance or manipulate with the public key.
556 ///
557 /// # Errors
558 ///
559 /// * If the registers exceed the memory limit returns `MemoryAccessViolation`.
560 /// * If called as view function returns `ProhibitedInView`.
561 ///
562 /// # Cost
563 ///
564 /// `base + write_register_base + write_register_byte * num_bytes`
565 pub fn signer_account_pk(&mut self, register_id: u64) -> Result<()> {
566 self.gas_counter.pay_base(base)?;
567
568 if self.context.is_view {
569 return Err(HostError::ProhibitedInView {
570 method_name: "signer_account_pk".to_string(),
571 }
572 .into());
573 }
574 self.internal_write_register(register_id, self.context.signer_account_pk.clone())
575 }
576
577 /// All contract calls are a result of a receipt, this receipt might be created by a transaction
578 /// that does function invocation on the contract or another contract as a result of
579 /// cross-contract call. Saves the bytes of the predecessor account id into the register.
580 ///
581 /// # Errors
582 ///
583 /// * If the registers exceed the memory limit returns `MemoryAccessViolation`.
584 /// * If called as view function returns `ProhibitedInView`.
585 ///
586 /// # Cost
587 ///
588 /// `base + write_register_base + write_register_byte * num_bytes`
589 pub fn predecessor_account_id(&mut self, register_id: u64) -> Result<()> {
590 self.gas_counter.pay_base(base)?;
591
592 if self.context.is_view {
593 return Err(HostError::ProhibitedInView {
594 method_name: "predecessor_account_id".to_string(),
595 }
596 .into());
597 }
598 self.internal_write_register(
599 register_id,
600 self.context.predecessor_account_id.as_bytes().to_vec(),
601 )
602 }
603
604 /// Reads input to the contract call into the register. Input is expected to be in JSON-format.
605 /// If input is provided saves the bytes (potentially zero) of input into register. If input is
606 /// not provided writes 0 bytes into the register.
607 ///
608 /// # Cost
609 ///
610 /// `base + write_register_base + write_register_byte * num_bytes`
611 pub fn input(&mut self, register_id: u64) -> Result<()> {
612 self.gas_counter.pay_base(base)?;
613
614 self.internal_write_register(register_id, self.context.input.clone())
615 }
616
617 /// Returns the current block height.
618 ///
619 /// # Cost
620 ///
621 /// `base`
622 // TODO #1903 rename to `block_height`
623 pub fn block_index(&mut self) -> Result<u64> {
624 self.gas_counter.pay_base(base)?;
625 Ok(self.context.block_index)
626 }
627
628 /// Returns the current block timestamp (number of non-leap-nanoseconds since January 1, 1970 0:00:00 UTC).
629 ///
630 /// # Cost
631 ///
632 /// `base`
633 pub fn block_timestamp(&mut self) -> Result<u64> {
634 self.gas_counter.pay_base(base)?;
635 Ok(self.context.block_timestamp)
636 }
637
638 /// Returns the current epoch height.
639 ///
640 /// # Cost
641 ///
642 /// `base`
643 pub fn epoch_height(&mut self) -> Result<EpochHeight> {
644 self.gas_counter.pay_base(base)?;
645 Ok(self.context.epoch_height)
646 }
647
648 /// Get the stake of an account, if the account is currently a validator. Otherwise returns 0.
649 /// writes the value into the` u128` variable pointed by `stake_ptr`.
650 ///
651 /// # Cost
652 ///
653 /// `base + memory_write_base + memory_write_size * 16 + utf8_decoding_base + utf8_decoding_byte * account_id_len + validator_stake_base`.
654 pub fn validator_stake(
655 &mut self,
656 account_id_len: u64,
657 account_id_ptr: u64,
658 stake_ptr: u64,
659 ) -> Result<()> {
660 self.gas_counter.pay_base(base)?;
661 let account_id = self.read_and_parse_account_id(account_id_ptr, account_id_len)?;
662 self.gas_counter.pay_base(validator_stake_base)?;
663 let balance = self.ext.validator_stake(&account_id)?.unwrap_or_default();
664 self.memory_set_u128(stake_ptr, balance)
665 }
666
667 /// Get the total validator stake of the current epoch.
668 /// Write the u128 value into `stake_ptr`.
669 /// writes the value into the` u128` variable pointed by `stake_ptr`.
670 ///
671 /// # Cost
672 ///
673 /// `base + memory_write_base + memory_write_size * 16 + validator_total_stake_base`
674 pub fn validator_total_stake(&mut self, stake_ptr: u64) -> Result<()> {
675 self.gas_counter.pay_base(base)?;
676 self.gas_counter.pay_base(validator_total_stake_base)?;
677 let total_stake = self.ext.validator_total_stake()?;
678 self.memory_set_u128(stake_ptr, total_stake)
679 }
680
681 /// Returns the number of bytes used by the contract if it was saved to the trie as of the
682 /// invocation. This includes:
683 /// * The data written with storage_* functions during current and previous execution;
684 /// * The bytes needed to store the access keys of the given account.
685 /// * The contract code size
686 /// * A small fixed overhead for account metadata.
687 ///
688 /// # Cost
689 ///
690 /// `base`
691 pub fn storage_usage(&mut self) -> Result<StorageUsage> {
692 self.gas_counter.pay_base(base)?;
693 Ok(self.current_storage_usage)
694 }
695
696 // #################
697 // # Economics API #
698 // #################
699
700 /// The current balance of the given account. This includes the attached_deposit that was
701 /// attached to the transaction.
702 ///
703 /// # Cost
704 ///
705 /// `base + memory_write_base + memory_write_size * 16`
706 pub fn account_balance(&mut self, balance_ptr: u64) -> Result<()> {
707 self.gas_counter.pay_base(base)?;
708 self.memory_set_u128(balance_ptr, self.current_account_balance)
709 }
710
711 /// The current amount of tokens locked due to staking.
712 ///
713 /// # Cost
714 ///
715 /// `base + memory_write_base + memory_write_size * 16`
716 pub fn account_locked_balance(&mut self, balance_ptr: u64) -> Result<()> {
717 self.gas_counter.pay_base(base)?;
718 self.memory_set_u128(balance_ptr, self.current_account_locked_balance)
719 }
720
721 /// The balance that was attached to the call that will be immediately deposited before the
722 /// contract execution starts.
723 ///
724 /// # Errors
725 ///
726 /// If called as view function returns `ProhibitedInView``.
727 ///
728 /// # Cost
729 ///
730 /// `base + memory_write_base + memory_write_size * 16`
731 pub fn attached_deposit(&mut self, balance_ptr: u64) -> Result<()> {
732 self.gas_counter.pay_base(base)?;
733
734 if self.context.is_view {
735 return Err(HostError::ProhibitedInView {
736 method_name: "attached_deposit".to_string(),
737 }
738 .into());
739 }
740 self.memory_set_u128(balance_ptr, self.context.attached_deposit)
741 }
742
743 /// The amount of gas attached to the call that can be used to pay for the gas fees.
744 ///
745 /// # Errors
746 ///
747 /// If called as view function returns `ProhibitedInView`.
748 ///
749 /// # Cost
750 ///
751 /// `base`
752 pub fn prepaid_gas(&mut self) -> Result<Gas> {
753 self.gas_counter.pay_base(base)?;
754 if self.context.is_view {
755 return Err(
756 HostError::ProhibitedInView { method_name: "prepaid_gas".to_string() }.into()
757 );
758 }
759 Ok(self.context.prepaid_gas)
760 }
761
762 /// The gas that was already burnt during the contract execution (cannot exceed `prepaid_gas`)
763 ///
764 /// # Errors
765 ///
766 /// If called as view function returns `ProhibitedInView`.
767 ///
768 /// # Cost
769 ///
770 /// `base`
771 pub fn used_gas(&mut self) -> Result<Gas> {
772 self.gas_counter.pay_base(base)?;
773 if self.context.is_view {
774 return Err(HostError::ProhibitedInView { method_name: "used_gas".to_string() }.into());
775 }
776 Ok(self.gas_counter.used_gas())
777 }
778
779 // ############
780 // # Math API #
781 // ############
782
783 /// Writes random seed into the register.
784 ///
785 /// # Errors
786 ///
787 /// If the size of the registers exceed the set limit `MemoryAccessViolation`.
788 ///
789 /// # Cost
790 ///
791 /// `base + write_register_base + write_register_byte * num_bytes`.
792 pub fn random_seed(&mut self, register_id: u64) -> Result<()> {
793 self.gas_counter.pay_base(base)?;
794 self.internal_write_register(register_id, self.context.random_seed.clone())
795 }
796
797 /// Hashes the given value using sha256 and returns it into `register_id`.
798 ///
799 /// # Errors
800 ///
801 /// If `value_len + value_ptr` points outside the memory or the registers use more memory than
802 /// the limit with `MemoryAccessViolation`.
803 ///
804 /// # Cost
805 ///
806 /// `base + write_register_base + write_register_byte * num_bytes + sha256_base + sha256_byte * num_bytes`
807 pub fn sha256(&mut self, value_len: u64, value_ptr: u64, register_id: u64) -> Result<()> {
808 self.gas_counter.pay_base(sha256_base)?;
809 let value = self.get_vec_from_memory_or_register(value_ptr, value_len)?;
810 self.gas_counter.pay_per_byte(sha256_byte, value.len() as u64)?;
811
812 use sha2::Digest;
813
814 let value_hash = sha2::Sha256::digest(&value);
815 self.internal_write_register(register_id, value_hash.as_slice().to_vec())
816 }
817
818 /// Hashes the given value using keccak256 and returns it into `register_id`.
819 ///
820 /// # Errors
821 ///
822 /// If `value_len + value_ptr` points outside the memory or the registers use more memory than
823 /// the limit with `MemoryAccessViolation`.
824 ///
825 /// # Cost
826 ///
827 /// `base + write_register_base + write_register_byte * num_bytes + keccak256_base + keccak256_byte * num_bytes`
828 pub fn keccak256(&mut self, value_len: u64, value_ptr: u64, register_id: u64) -> Result<()> {
829 self.gas_counter.pay_base(keccak256_base)?;
830 let value = self.get_vec_from_memory_or_register(value_ptr, value_len)?;
831 self.gas_counter.pay_per_byte(keccak256_byte, value.len() as u64)?;
832
833 use sha3::Digest;
834
835 let value_hash = sha3::Keccak256::digest(&value);
836 self.internal_write_register(register_id, value_hash.as_slice().to_vec())
837 }
838
839 /// Hashes the given value using keccak512 and returns it into `register_id`.
840 ///
841 /// # Errors
842 ///
843 /// If `value_len + value_ptr` points outside the memory or the registers use more memory than
844 /// the limit with `MemoryAccessViolation`.
845 ///
846 /// # Cost
847 ///
848 /// `base + write_register_base + write_register_byte * num_bytes + keccak512_base + keccak512_byte * num_bytes`
849 pub fn keccak512(&mut self, value_len: u64, value_ptr: u64, register_id: u64) -> Result<()> {
850 self.gas_counter.pay_base(keccak512_base)?;
851 let value = self.get_vec_from_memory_or_register(value_ptr, value_len)?;
852 self.gas_counter.pay_per_byte(keccak512_byte, value.len() as u64)?;
853
854 use sha3::Digest;
855
856 let value_hash = sha3::Keccak512::digest(&value);
857 self.internal_write_register(register_id, value_hash.as_slice().to_vec())
858 }
859
860 /// Called by gas metering injected into Wasm. Counts both towards `burnt_gas` and `used_gas`.
861 ///
862 /// # Errors
863 ///
864 /// * If passed gas amount somehow overflows internal gas counters returns `IntegerOverflow`;
865 /// * If we exceed usage limit imposed on burnt gas returns `GasLimitExceeded`;
866 /// * If we exceed the `prepaid_gas` then returns `GasExceeded`.
867 pub fn gas(&mut self, gas_amount: u32) -> Result<()> {
868 let value = Gas::from(gas_amount) * Gas::from(self.config.regular_op_cost);
869 self.gas_counter.pay_wasm_gas(value)
870 }
871
872 // ################
873 // # Promises API #
874 // ################
875
876 /// A helper function to pay gas fee for creating a new receipt without actions.
877 /// # Args:
878 /// * `sir`: whether contract call is addressed to itself;
879 /// * `data_dependencies`: other contracts that this execution will be waiting on (or rather
880 /// their data receipts), where bool indicates whether this is sender=receiver communication.
881 ///
882 /// # Cost
883 ///
884 /// This is a convenience function that encapsulates several costs:
885 /// `burnt_gas := dispatch cost of the receipt + base dispatch cost cost of the data receipt`
886 /// `used_gas := burnt_gas + exec cost of the receipt + base exec cost cost of the data receipt`
887 /// Notice that we prepay all base cost upon the creation of the data dependency, we are going to
888 /// pay for the content transmitted through the dependency upon the actual creation of the
889 /// DataReceipt.
890 fn pay_gas_for_new_receipt(&mut self, sir: bool, data_dependencies: &[bool]) -> Result<()> {
891 let fees_config_cfg = &self.fees_config;
892 let mut burn_gas = fees_config_cfg.action_receipt_creation_config.send_fee(sir);
893 let mut use_gas = fees_config_cfg.action_receipt_creation_config.exec_fee();
894 for dep in data_dependencies {
895 // Both creation and execution for data receipts are considered burnt gas.
896 burn_gas = burn_gas
897 .checked_add(fees_config_cfg.data_receipt_creation_config.base_cost.send_fee(*dep))
898 .ok_or(HostError::IntegerOverflow)?
899 .checked_add(fees_config_cfg.data_receipt_creation_config.base_cost.exec_fee())
900 .ok_or(HostError::IntegerOverflow)?;
901 }
902 use_gas = use_gas.checked_add(burn_gas).ok_or(HostError::IntegerOverflow)?;
903 self.gas_counter.pay_action_accumulated(burn_gas, use_gas, ActionCosts::new_receipt)
904 }
905
906 /// A helper function to subtract balance on transfer or attached deposit for promises.
907 /// # Args:
908 /// * `amount`: the amount to deduct from the current account balance.
909 fn deduct_balance(&mut self, amount: Balance) -> Result<()> {
910 self.current_account_balance =
911 self.current_account_balance.checked_sub(amount).ok_or(HostError::BalanceExceeded)?;
912 Ok(())
913 }
914
915 /// Creates a promise that will execute a method on account with given arguments and attaches
916 /// the given amount and gas. `amount_ptr` point to slices of bytes representing `u128`.
917 ///
918 /// # Errors
919 ///
920 /// * If `account_id_len + account_id_ptr` or `method_name_len + method_name_ptr` or
921 /// `arguments_len + arguments_ptr` or `amount_ptr + 16` points outside the memory of the guest
922 /// or host returns `MemoryAccessViolation`.
923 /// * If called as view function returns `ProhibitedInView`.
924 ///
925 /// # Returns
926 ///
927 /// Index of the new promise that uniquely identifies it within the current execution of the
928 /// method.
929 ///
930 /// # Cost
931 ///
932 /// Since `promise_create` is a convenience wrapper around `promise_batch_create` and
933 /// `promise_batch_action_function_call`. This also means it charges `base` cost twice.
934 pub fn promise_create(
935 &mut self,
936 account_id_len: u64,
937 account_id_ptr: u64,
938 method_name_len: u64,
939 method_name_ptr: u64,
940 arguments_len: u64,
941 arguments_ptr: u64,
942 amount_ptr: u64,
943 gas: Gas,
944 ) -> Result<u64> {
945 let new_promise_idx = self.promise_batch_create(account_id_len, account_id_ptr)?;
946 self.promise_batch_action_function_call(
947 new_promise_idx,
948 method_name_len,
949 method_name_ptr,
950 arguments_len,
951 arguments_ptr,
952 amount_ptr,
953 gas,
954 )?;
955 Ok(new_promise_idx)
956 }
957
958 /// Attaches the callback that is executed after promise pointed by `promise_idx` is complete.
959 ///
960 /// # Errors
961 ///
962 /// * If `promise_idx` does not correspond to an existing promise returns `InvalidPromiseIndex`;
963 /// * If `account_id_len + account_id_ptr` or `method_name_len + method_name_ptr` or
964 /// `arguments_len + arguments_ptr` or `amount_ptr + 16` points outside the memory of the
965 /// guest or host returns `MemoryAccessViolation`.
966 /// * If called as view function returns `ProhibitedInView`.
967 ///
968 /// # Returns
969 ///
970 /// Index of the new promise that uniquely identifies it within the current execution of the
971 /// method.
972 ///
973 /// # Cost
974 ///
975 /// Since `promise_create` is a convenience wrapper around `promise_batch_then` and
976 /// `promise_batch_action_function_call`. This also means it charges `base` cost twice.
977 pub fn promise_then(
978 &mut self,
979 promise_idx: u64,
980 account_id_len: u64,
981 account_id_ptr: u64,
982 method_name_len: u64,
983 method_name_ptr: u64,
984 arguments_len: u64,
985 arguments_ptr: u64,
986 amount_ptr: u64,
987 gas: u64,
988 ) -> Result<u64> {
989 let new_promise_idx =
990 self.promise_batch_then(promise_idx, account_id_len, account_id_ptr)?;
991 self.promise_batch_action_function_call(
992 new_promise_idx,
993 method_name_len,
994 method_name_ptr,
995 arguments_len,
996 arguments_ptr,
997 amount_ptr,
998 gas,
999 )?;
1000 Ok(new_promise_idx)
1001 }
1002
1003 /// Creates a new promise which completes when time all promises passed as arguments complete.
1004 /// Cannot be used with registers. `promise_idx_ptr` points to an array of `u64` elements, with
1005 /// `promise_idx_count` denoting the number of elements. The array contains indices of promises
1006 /// that need to be waited on jointly.
1007 ///
1008 /// # Errors
1009 ///
1010 /// * If `promise_ids_ptr + 8 * promise_idx_count` extend outside the guest memory returns
1011 /// `MemoryAccessViolation`;
1012 /// * If any of the promises in the array do not correspond to existing promises returns
1013 /// `InvalidPromiseIndex`.
1014 /// * If called as view function returns `ProhibitedInView`.
1015 /// * If the total number of receipt dependencies exceeds `max_number_input_data_dependencies`
1016 /// limit returns `NumInputDataDependenciesExceeded`.
1017 /// * If the total number of promises exceeds `max_promises_per_function_call_action` limit
1018 /// returns `NumPromisesExceeded`.
1019 ///
1020 /// # Returns
1021 ///
1022 /// Index of the new promise that uniquely identifies it within the current execution of the
1023 /// method.
1024 ///
1025 /// # Cost
1026 ///
1027 /// `base + promise_and_base + promise_and_per_promise * num_promises + cost of reading promise ids from memory`.
1028 pub fn promise_and(
1029 &mut self,
1030 promise_idx_ptr: u64,
1031 promise_idx_count: u64,
1032 ) -> Result<PromiseIndex> {
1033 self.gas_counter.pay_base(base)?;
1034 if self.context.is_view {
1035 return Err(
1036 HostError::ProhibitedInView { method_name: "promise_and".to_string() }.into()
1037 );
1038 }
1039 self.gas_counter.pay_base(promise_and_base)?;
1040 self.gas_counter.pay_per_byte(
1041 promise_and_per_promise,
1042 promise_idx_count
1043 .checked_mul(size_of::<u64>() as u64)
1044 .ok_or(HostError::IntegerOverflow)?,
1045 )?;
1046
1047 let promise_indices = self.memory_get_vec_u64(promise_idx_ptr, promise_idx_count)?;
1048
1049 let mut receipt_dependencies = vec![];
1050 for promise_idx in &promise_indices {
1051 let promise = self
1052 .promises
1053 .get(*promise_idx as usize)
1054 .ok_or(HostError::InvalidPromiseIndex { promise_idx: *promise_idx })?;
1055 match &promise {
1056 Promise::Receipt(receipt_idx) => {
1057 receipt_dependencies.push(*receipt_idx);
1058 }
1059 Promise::NotReceipt(receipt_indices) => {
1060 receipt_dependencies.extend(receipt_indices.clone());
1061 }
1062 }
1063 // Checking this in the loop to prevent abuse of too many joined vectors.
1064 if receipt_dependencies.len() as u64
1065 > self.config.limit_config.max_number_input_data_dependencies
1066 {
1067 return Err(HostError::NumberInputDataDependenciesExceeded {
1068 number_of_input_data_dependencies: receipt_dependencies.len() as u64,
1069 limit: self.config.limit_config.max_number_input_data_dependencies,
1070 }
1071 .into());
1072 }
1073 }
1074 self.checked_push_promise(Promise::NotReceipt(receipt_dependencies))
1075 }
1076
1077 /// Creates a new promise towards given `account_id` without any actions attached to it.
1078 ///
1079 /// # Errors
1080 ///
1081 /// * If `account_id_len + account_id_ptr` points outside the memory of the guest or host
1082 /// returns `MemoryAccessViolation`.
1083 /// * If called as view function returns `ProhibitedInView`.
1084 /// * If the total number of promises exceeds `max_promises_per_function_call_action` limit
1085 /// returns `NumPromisesExceeded`.
1086 ///
1087 /// # Returns
1088 ///
1089 /// Index of the new promise that uniquely identifies it within the current execution of the
1090 /// method.
1091 ///
1092 /// # Cost
1093 ///
1094 /// `burnt_gas := base + cost of reading and decoding the account id + dispatch cost of the receipt`.
1095 /// `used_gas := burnt_gas + exec cost of the receipt`.
1096 pub fn promise_batch_create(
1097 &mut self,
1098 account_id_len: u64,
1099 account_id_ptr: u64,
1100 ) -> Result<u64> {
1101 self.gas_counter.pay_base(base)?;
1102 if self.context.is_view {
1103 return Err(HostError::ProhibitedInView {
1104 method_name: "promise_batch_create".to_string(),
1105 }
1106 .into());
1107 }
1108 let account_id = self.read_and_parse_account_id(account_id_ptr, account_id_len)?;
1109 let sir = account_id == self.context.current_account_id;
1110 self.pay_gas_for_new_receipt(sir, &[])?;
1111 let new_receipt_idx = self.ext.create_receipt(vec![], account_id.clone())?;
1112 self.receipt_to_account.insert(new_receipt_idx, account_id);
1113
1114 self.checked_push_promise(Promise::Receipt(new_receipt_idx))
1115 }
1116
1117 /// Creates a new promise towards given `account_id` without any actions attached, that is
1118 /// executed after promise pointed by `promise_idx` is complete.
1119 ///
1120 /// # Errors
1121 ///
1122 /// * If `promise_idx` does not correspond to an existing promise returns `InvalidPromiseIndex`;
1123 /// * If `account_id_len + account_id_ptr` points outside the memory of the guest or host
1124 /// returns `MemoryAccessViolation`.
1125 /// * If called as view function returns `ProhibitedInView`.
1126 /// * If the total number of promises exceeds `max_promises_per_function_call_action` limit
1127 /// returns `NumPromisesExceeded`.
1128 ///
1129 /// # Returns
1130 ///
1131 /// Index of the new promise that uniquely identifies it within the current execution of the
1132 /// method.
1133 ///
1134 /// # Cost
1135 ///
1136 /// `base + cost of reading and decoding the account id + dispatch&execution cost of the receipt
1137 /// + dispatch&execution base cost for each data dependency`
1138 pub fn promise_batch_then(
1139 &mut self,
1140 promise_idx: u64,
1141 account_id_len: u64,
1142 account_id_ptr: u64,
1143 ) -> Result<u64> {
1144 self.gas_counter.pay_base(base)?;
1145 if self.context.is_view {
1146 return Err(HostError::ProhibitedInView {
1147 method_name: "promise_batch_then".to_string(),
1148 }
1149 .into());
1150 }
1151 let account_id = self.read_and_parse_account_id(account_id_ptr, account_id_len)?;
1152 // Update the DAG and return new promise idx.
1153 let promise = self
1154 .promises
1155 .get(promise_idx as usize)
1156 .ok_or(HostError::InvalidPromiseIndex { promise_idx })?;
1157 let receipt_dependencies = match &promise {
1158 Promise::Receipt(receipt_idx) => vec![*receipt_idx],
1159 Promise::NotReceipt(receipt_indices) => receipt_indices.clone(),
1160 };
1161
1162 let sir = account_id == self.context.current_account_id;
1163 let deps: Vec<_> = receipt_dependencies
1164 .iter()
1165 .map(|receipt_idx| {
1166 self.receipt_to_account
1167 .get(receipt_idx)
1168 .expect("promises and receipt_to_account should be consistent.")
1169 == &account_id
1170 })
1171 .collect();
1172 self.pay_gas_for_new_receipt(sir, &deps)?;
1173
1174 let new_receipt_idx = self.ext.create_receipt(receipt_dependencies, account_id.clone())?;
1175 self.receipt_to_account.insert(new_receipt_idx, account_id);
1176
1177 self.checked_push_promise(Promise::Receipt(new_receipt_idx))
1178 }
1179
1180 /// Helper function to return the receipt index corresponding to the given promise index.
1181 /// It also pulls account ID for the given receipt and compares it with the current account ID
1182 /// to return whether the receipt's account ID is the same.
1183 fn promise_idx_to_receipt_idx_with_sir(
1184 &self,
1185 promise_idx: u64,
1186 ) -> Result<(ReceiptIndex, bool)> {
1187 let promise = self
1188 .promises
1189 .get(promise_idx as usize)
1190 .ok_or(HostError::InvalidPromiseIndex { promise_idx })?;
1191 let receipt_idx = match &promise {
1192 Promise::Receipt(receipt_idx) => Ok(*receipt_idx),
1193 Promise::NotReceipt(_) => Err(HostError::CannotAppendActionToJointPromise),
1194 }?;
1195
1196 let account_id = self
1197 .receipt_to_account
1198 .get(&receipt_idx)
1199 .expect("promises and receipt_to_account should be consistent.");
1200 let sir = account_id == &self.context.current_account_id;
1201 Ok((receipt_idx, sir))
1202 }
1203
1204 /// Appends `CreateAccount` action to the batch of actions for the given promise pointed by
1205 /// `promise_idx`.
1206 ///
1207 /// # Errors
1208 ///
1209 /// * If `promise_idx` does not correspond to an existing promise returns `InvalidPromiseIndex`.
1210 /// * If the promise pointed by the `promise_idx` is an ephemeral promise created by
1211 /// `promise_and` returns `CannotAppendActionToJointPromise`.
1212 /// * If called as view function returns `ProhibitedInView`.
1213 ///
1214 /// # Cost
1215 ///
1216 /// `burnt_gas := base + dispatch action fee`
1217 /// `used_gas := burnt_gas + exec action fee`
1218 pub fn promise_batch_action_create_account(&mut self, promise_idx: u64) -> Result<()> {
1219 self.gas_counter.pay_base(base)?;
1220 if self.context.is_view {
1221 return Err(HostError::ProhibitedInView {
1222 method_name: "promise_batch_action_create_account".to_string(),
1223 }
1224 .into());
1225 }
1226 let (receipt_idx, sir) = self.promise_idx_to_receipt_idx_with_sir(promise_idx)?;
1227
1228 self.gas_counter.pay_action_base(
1229 &self.fees_config.action_creation_config.create_account_cost,
1230 sir,
1231 ActionCosts::create_account,
1232 )?;
1233
1234 self.ext.append_action_create_account(receipt_idx)?;
1235 Ok(())
1236 }
1237
1238 /// Appends `DeployContract` action to the batch of actions for the given promise pointed by
1239 /// `promise_idx`.
1240 ///
1241 /// # Errors
1242 ///
1243 /// * If `promise_idx` does not correspond to an existing promise returns `InvalidPromiseIndex`.
1244 /// * If the promise pointed by the `promise_idx` is an ephemeral promise created by
1245 /// `promise_and` returns `CannotAppendActionToJointPromise`.
1246 /// * If `code_len + code_ptr` points outside the memory of the guest or host returns
1247 /// `MemoryAccessViolation`.
1248 /// * If called as view function returns `ProhibitedInView`.
1249 /// * If the contract code length exceeds `max_contract_size` returns `ContractSizeExceeded`.
1250 ///
1251 /// # Cost
1252 ///
1253 /// `burnt_gas := base + dispatch action base fee + dispatch action per byte fee * num bytes + cost of reading vector from memory `
1254 /// `used_gas := burnt_gas + exec action base fee + exec action per byte fee * num bytes`
1255 pub fn promise_batch_action_deploy_contract(
1256 &mut self,
1257 promise_idx: u64,
1258 code_len: u64,
1259 code_ptr: u64,
1260 ) -> Result<()> {
1261 self.gas_counter.pay_base(base)?;
1262 if self.context.is_view {
1263 return Err(HostError::ProhibitedInView {
1264 method_name: "promise_batch_action_deploy_contract".to_string(),
1265 }
1266 .into());
1267 }
1268 let code = self.get_vec_from_memory_or_register(code_ptr, code_len)?;
1269 if code.len() as u64 > self.config.limit_config.max_contract_size {
1270 return Err(HostError::ContractSizeExceeded {
1271 size: code.len() as u64,
1272 limit: self.config.limit_config.max_contract_size,
1273 }
1274 .into());
1275 }
1276
1277 let (receipt_idx, sir) = self.promise_idx_to_receipt_idx_with_sir(promise_idx)?;
1278
1279 let num_bytes = code.len() as u64;
1280 self.gas_counter.pay_action_base(
1281 &self.fees_config.action_creation_config.deploy_contract_cost,
1282 sir,
1283 ActionCosts::deploy_contract,
1284 )?;
1285 self.gas_counter.pay_action_per_byte(
1286 &self.fees_config.action_creation_config.deploy_contract_cost_per_byte,
1287 num_bytes,
1288 sir,
1289 ActionCosts::deploy_contract,
1290 )?;
1291
1292 self.ext.append_action_deploy_contract(receipt_idx, code)?;
1293 Ok(())
1294 }
1295
1296 /// Appends `FunctionCall` action to the batch of actions for the given promise pointed by
1297 /// `promise_idx`.
1298 ///
1299 /// # Errors
1300 ///
1301 /// * If `promise_idx` does not correspond to an existing promise returns `InvalidPromiseIndex`.
1302 /// * If the promise pointed by the `promise_idx` is an ephemeral promise created by
1303 /// `promise_and` returns `CannotAppendActionToJointPromise`.
1304 /// * If `method_name_len + method_name_ptr` or `arguments_len + arguments_ptr` or
1305 /// `amount_ptr + 16` points outside the memory of the guest or host returns
1306 /// `MemoryAccessViolation`.
1307 /// * If called as view function returns `ProhibitedInView`.
1308 ///
1309 /// # Cost
1310 ///
1311 /// `burnt_gas := base + dispatch action base fee + dispatch action per byte fee * num bytes + cost of reading vector from memory
1312 /// + cost of reading u128, method_name and arguments from the memory`
1313 /// `used_gas := burnt_gas + exec action base fee + exec action per byte fee * num bytes`
1314 pub fn promise_batch_action_function_call(
1315 &mut self,
1316 promise_idx: u64,
1317 method_name_len: u64,
1318 method_name_ptr: u64,
1319 arguments_len: u64,
1320 arguments_ptr: u64,
1321 amount_ptr: u64,
1322 gas: Gas,
1323 ) -> Result<()> {
1324 self.gas_counter.pay_base(base)?;
1325 if self.context.is_view {
1326 return Err(HostError::ProhibitedInView {
1327 method_name: "promise_batch_action_function_call".to_string(),
1328 }
1329 .into());
1330 }
1331 let amount = self.memory_get_u128(amount_ptr)?;
1332 let method_name = self.get_vec_from_memory_or_register(method_name_ptr, method_name_len)?;
1333 if method_name.is_empty() {
1334 return Err(HostError::EmptyMethodName.into());
1335 }
1336 let arguments = self.get_vec_from_memory_or_register(arguments_ptr, arguments_len)?;
1337
1338 let (receipt_idx, sir) = self.promise_idx_to_receipt_idx_with_sir(promise_idx)?;
1339
1340 // Input can't be large enough to overflow
1341 let num_bytes = method_name.len() as u64 + arguments.len() as u64;
1342 self.gas_counter.pay_action_base(
1343 &self.fees_config.action_creation_config.function_call_cost,
1344 sir,
1345 ActionCosts::function_call,
1346 )?;
1347 self.gas_counter.pay_action_per_byte(
1348 &self.fees_config.action_creation_config.function_call_cost_per_byte,
1349 num_bytes,
1350 sir,
1351 ActionCosts::function_call,
1352 )?;
1353 // Prepaid gas
1354 self.gas_counter.prepay_gas(gas)?;
1355
1356 self.deduct_balance(amount)?;
1357
1358 self.ext.append_action_function_call(receipt_idx, method_name, arguments, amount, gas)?;
1359 Ok(())
1360 }
1361
1362 /// Appends `Transfer` action to the batch of actions for the given promise pointed by
1363 /// `promise_idx`.
1364 ///
1365 /// # Errors
1366 ///
1367 /// * If `promise_idx` does not correspond to an existing promise returns `InvalidPromiseIndex`.
1368 /// * If the promise pointed by the `promise_idx` is an ephemeral promise created by
1369 /// `promise_and` returns `CannotAppendActionToJointPromise`.
1370 /// * If `amount_ptr + 16` points outside the memory of the guest or host returns
1371 /// `MemoryAccessViolation`.
1372 /// * If called as view function returns `ProhibitedInView`.
1373 ///
1374 /// # Cost
1375 ///
1376 /// `burnt_gas := base + dispatch action base fee + dispatch action per byte fee * num bytes + cost of reading u128 from memory `
1377 /// `used_gas := burnt_gas + exec action base fee + exec action per byte fee * num bytes`
1378 pub fn promise_batch_action_transfer(
1379 &mut self,
1380 promise_idx: u64,
1381 amount_ptr: u64,
1382 ) -> Result<()> {
1383 self.gas_counter.pay_base(base)?;
1384 if self.context.is_view {
1385 return Err(HostError::ProhibitedInView {
1386 method_name: "promise_batch_action_transfer".to_string(),
1387 }
1388 .into());
1389 }
1390 let amount = self.memory_get_u128(amount_ptr)?;
1391
1392 let (receipt_idx, sir) = self.promise_idx_to_receipt_idx_with_sir(promise_idx)?;
1393
1394 if self.current_protocol_version >= IMPLICIT_ACCOUNT_CREATION_PROTOCOL_VERSION {
1395 // Need to check if the receiver_id can be an implicit account.
1396 let account_id = self
1397 .receipt_to_account
1398 .get(&receipt_idx)
1399 .expect("promises and receipt_to_account should be consistent.");
1400 if is_account_id_64_len_hex(&account_id) {
1401 self.gas_counter.pay_action_base(
1402 &self.fees_config.action_creation_config.create_account_cost,
1403 sir,
1404 ActionCosts::transfer,
1405 )?;
1406 self.gas_counter.pay_action_base(
1407 &self.fees_config.action_creation_config.add_key_cost.full_access_cost,
1408 sir,
1409 ActionCosts::transfer,
1410 )?;
1411 }
1412 }
1413 self.gas_counter.pay_action_base(
1414 &self.fees_config.action_creation_config.transfer_cost,
1415 sir,
1416 ActionCosts::transfer,
1417 )?;
1418
1419 self.deduct_balance(amount)?;
1420
1421 self.ext.append_action_transfer(receipt_idx, amount)?;
1422 Ok(())
1423 }
1424
1425 /// Appends `Stake` action to the batch of actions for the given promise pointed by
1426 /// `promise_idx`.
1427 ///
1428 /// # Errors
1429 ///
1430 /// * If `promise_idx` does not correspond to an existing promise returns `InvalidPromiseIndex`.
1431 /// * If the promise pointed by the `promise_idx` is an ephemeral promise created by
1432 /// `promise_and` returns `CannotAppendActionToJointPromise`.
1433 /// * If the given public key is not a valid (e.g. wrong length) returns `InvalidPublicKey`.
1434 /// * If `amount_ptr + 16` or `public_key_len + public_key_ptr` points outside the memory of the
1435 /// guest or host returns `MemoryAccessViolation`.
1436 /// * If called as view function returns `ProhibitedInView`.
1437 ///
1438 /// # Cost
1439 ///
1440 /// `burnt_gas := base + dispatch action base fee + dispatch action per byte fee * num bytes + cost of reading public key from memory `
1441 /// `used_gas := burnt_gas + exec action base fee + exec action per byte fee * num bytes`
1442 pub fn promise_batch_action_stake(
1443 &mut self,
1444 promise_idx: u64,
1445 amount_ptr: u64,
1446 public_key_len: u64,
1447 public_key_ptr: u64,
1448 ) -> Result<()> {
1449 self.gas_counter.pay_base(base)?;
1450 if self.context.is_view {
1451 return Err(HostError::ProhibitedInView {
1452 method_name: "promise_batch_action_stake".to_string(),
1453 }
1454 .into());
1455 }
1456 let amount = self.memory_get_u128(amount_ptr)?;
1457 let public_key = self.get_vec_from_memory_or_register(public_key_ptr, public_key_len)?;
1458
1459 let (receipt_idx, sir) = self.promise_idx_to_receipt_idx_with_sir(promise_idx)?;
1460
1461 self.gas_counter.pay_action_base(
1462 &self.fees_config.action_creation_config.stake_cost,
1463 sir,
1464 ActionCosts::stake,
1465 )?;
1466
1467 self.ext.append_action_stake(receipt_idx, amount, public_key)?;
1468 Ok(())
1469 }
1470
1471 /// Appends `AddKey` action to the batch of actions for the given promise pointed by
1472 /// `promise_idx`. The access key will have `FullAccess` permission.
1473 ///
1474 /// # Errors
1475 ///
1476 /// * If `promise_idx` does not correspond to an existing promise returns `InvalidPromiseIndex`.
1477 /// * If the promise pointed by the `promise_idx` is an ephemeral promise created by
1478 /// `promise_and` returns `CannotAppendActionToJointPromise`.
1479 /// * If the given public key is not a valid (e.g. wrong length) returns `InvalidPublicKey`.
1480 /// * If `public_key_len + public_key_ptr` points outside the memory of the guest or host
1481 /// returns `MemoryAccessViolation`.
1482 /// * If called as view function returns `ProhibitedInView`.
1483 ///
1484 /// # Cost
1485 ///
1486 /// `burnt_gas := base + dispatch action base fee + dispatch action per byte fee * num bytes + cost of reading public key from memory `
1487 /// `used_gas := burnt_gas + exec action base fee + exec action per byte fee * num bytes`
1488 pub fn promise_batch_action_add_key_with_full_access(
1489 &mut self,
1490 promise_idx: u64,
1491 public_key_len: u64,
1492 public_key_ptr: u64,
1493 nonce: u64,
1494 ) -> Result<()> {
1495 self.gas_counter.pay_base(base)?;
1496 if self.context.is_view {
1497 return Err(HostError::ProhibitedInView {
1498 method_name: "promise_batch_action_add_key_with_full_access".to_string(),
1499 }
1500 .into());
1501 }
1502 let public_key = self.get_vec_from_memory_or_register(public_key_ptr, public_key_len)?;
1503
1504 let (receipt_idx, sir) = self.promise_idx_to_receipt_idx_with_sir(promise_idx)?;
1505
1506 self.gas_counter.pay_action_base(
1507 &self.fees_config.action_creation_config.add_key_cost.full_access_cost,
1508 sir,
1509 ActionCosts::add_key,
1510 )?;
1511
1512 self.ext.append_action_add_key_with_full_access(receipt_idx, public_key, nonce)?;
1513 Ok(())
1514 }
1515
1516 /// Appends `AddKey` action to the batch of actions for the given promise pointed by
1517 /// `promise_idx`. The access key will have `FunctionCall` permission.
1518 ///
1519 /// # Errors
1520 ///
1521 /// * If `promise_idx` does not correspond to an existing promise returns `InvalidPromiseIndex`.
1522 /// * If the promise pointed by the `promise_idx` is an ephemeral promise created by
1523 /// `promise_and` returns `CannotAppendActionToJointPromise`.
1524 /// * If the given public key is not a valid (e.g. wrong length) returns `InvalidPublicKey`.
1525 /// * If `public_key_len + public_key_ptr`, `allowance_ptr + 16`,
1526 /// `receiver_id_len + receiver_id_ptr` or `method_names_len + method_names_ptr` points outside
1527 /// the memory of the guest or host returns `MemoryAccessViolation`.
1528 /// * If called as view function returns `ProhibitedInView`.
1529 ///
1530 /// # Cost
1531 ///
1532 /// `burnt_gas := base + dispatch action base fee + dispatch action per byte fee * num bytes + cost of reading vector from memory
1533 /// + cost of reading u128, method_names and public key from the memory + cost of reading and parsing account name`
1534 /// `used_gas := burnt_gas + exec action base fee + exec action per byte fee * num bytes`
1535 pub fn promise_batch_action_add_key_with_function_call(
1536 &mut self,
1537 promise_idx: u64,
1538 public_key_len: u64,
1539 public_key_ptr: u64,
1540 nonce: u64,
1541 allowance_ptr: u64,
1542 receiver_id_len: u64,
1543 receiver_id_ptr: u64,
1544 method_names_len: u64,
1545 method_names_ptr: u64,
1546 ) -> Result<()> {
1547 self.gas_counter.pay_base(base)?;
1548 if self.context.is_view {
1549 return Err(HostError::ProhibitedInView {
1550 method_name: "promise_batch_action_add_key_with_function_call".to_string(),
1551 }
1552 .into());
1553 }
1554 let public_key = self.get_vec_from_memory_or_register(public_key_ptr, public_key_len)?;
1555 let allowance = self.memory_get_u128(allowance_ptr)?;
1556 let allowance = if allowance > 0 { Some(allowance) } else { None };
1557 let receiver_id = self.read_and_parse_account_id(receiver_id_ptr, receiver_id_len)?;
1558 let raw_method_names =
1559 self.get_vec_from_memory_or_register(method_names_ptr, method_names_len)?;
1560 let method_names = split_method_names(&raw_method_names)?;
1561
1562 let (receipt_idx, sir) = self.promise_idx_to_receipt_idx_with_sir(promise_idx)?;
1563
1564 // +1 is to account for null-terminating characters.
1565 let num_bytes = method_names.iter().map(|v| v.len() as u64 + 1).sum::<u64>();
1566 self.gas_counter.pay_action_base(
1567 &self.fees_config.action_creation_config.add_key_cost.function_call_cost,
1568 sir,
1569 ActionCosts::function_call,
1570 )?;
1571 self.gas_counter.pay_action_per_byte(
1572 &self.fees_config.action_creation_config.add_key_cost.function_call_cost_per_byte,
1573 num_bytes,
1574 sir,
1575 ActionCosts::function_call,
1576 )?;
1577
1578 self.ext.append_action_add_key_with_function_call(
1579 receipt_idx,
1580 public_key,
1581 nonce,
1582 allowance,
1583 receiver_id,
1584 method_names,
1585 )?;
1586 Ok(())
1587 }
1588
1589 /// Appends `DeleteKey` action to the batch of actions for the given promise pointed by
1590 /// `promise_idx`.
1591 ///
1592 /// # Errors
1593 ///
1594 /// * If `promise_idx` does not correspond to an existing promise returns `InvalidPromiseIndex`.
1595 /// * If the promise pointed by the `promise_idx` is an ephemeral promise created by
1596 /// `promise_and` returns `CannotAppendActionToJointPromise`.
1597 /// * If the given public key is not a valid (e.g. wrong length) returns `InvalidPublicKey`.
1598 /// * If `public_key_len + public_key_ptr` points outside the memory of the guest or host
1599 /// returns `MemoryAccessViolation`.
1600 /// * If called as view function returns `ProhibitedInView`.
1601 ///
1602 /// # Cost
1603 ///
1604 /// `burnt_gas := base + dispatch action base fee + dispatch action per byte fee * num bytes + cost of reading public key from memory `
1605 /// `used_gas := burnt_gas + exec action base fee + exec action per byte fee * num bytes`
1606 pub fn promise_batch_action_delete_key(
1607 &mut self,
1608 promise_idx: u64,
1609 public_key_len: u64,
1610 public_key_ptr: u64,
1611 ) -> Result<()> {
1612 self.gas_counter.pay_base(base)?;
1613 if self.context.is_view {
1614 return Err(HostError::ProhibitedInView {
1615 method_name: "promise_batch_action_delete_key".to_string(),
1616 }
1617 .into());
1618 }
1619 let public_key = self.get_vec_from_memory_or_register(public_key_ptr, public_key_len)?;
1620
1621 let (receipt_idx, sir) = self.promise_idx_to_receipt_idx_with_sir(promise_idx)?;
1622
1623 self.gas_counter.pay_action_base(
1624 &self.fees_config.action_creation_config.delete_key_cost,
1625 sir,
1626 ActionCosts::delete_key,
1627 )?;
1628
1629 self.ext.append_action_delete_key(receipt_idx, public_key)?;
1630 Ok(())
1631 }
1632
1633 /// Appends `DeleteAccount` action to the batch of actions for the given promise pointed by
1634 /// `promise_idx`.
1635 ///
1636 /// # Errors
1637 ///
1638 /// * If `promise_idx` does not correspond to an existing promise returns `InvalidPromiseIndex`.
1639 /// * If the promise pointed by the `promise_idx` is an ephemeral promise created by
1640 /// `promise_and` returns `CannotAppendActionToJointPromise`.
1641 /// * If `beneficiary_id_len + beneficiary_id_ptr` points outside the memory of the guest or
1642 /// host returns `MemoryAccessViolation`.
1643 /// * If called as view function returns `ProhibitedInView`.
1644 ///
1645 /// # Cost
1646 ///
1647 /// `burnt_gas := base + dispatch action base fee + dispatch action per byte fee * num bytes + cost of reading and parsing account id from memory `
1648 /// `used_gas := burnt_gas + exec action base fee + exec action per byte fee * num bytes`
1649 pub fn promise_batch_action_delete_account(
1650 &mut self,
1651 promise_idx: u64,
1652 beneficiary_id_len: u64,
1653 beneficiary_id_ptr: u64,
1654 ) -> Result<()> {
1655 self.gas_counter.pay_base(base)?;
1656 if self.context.is_view {
1657 return Err(HostError::ProhibitedInView {
1658 method_name: "promise_batch_action_delete_account".to_string(),
1659 }
1660 .into());
1661 }
1662 let beneficiary_id =
1663 self.read_and_parse_account_id(beneficiary_id_ptr, beneficiary_id_len)?;
1664
1665 let (receipt_idx, sir) = self.promise_idx_to_receipt_idx_with_sir(promise_idx)?;
1666
1667 self.gas_counter.pay_action_base(
1668 &self.fees_config.action_creation_config.delete_account_cost,
1669 sir,
1670 ActionCosts::delete_account,
1671 )?;
1672
1673 self.ext.append_action_delete_account(receipt_idx, beneficiary_id)?;
1674 Ok(())
1675 }
1676
1677 /// If the current function is invoked by a callback we can access the execution results of the
1678 /// promises that caused the callback. This function returns the number of complete and
1679 /// incomplete callbacks.
1680 ///
1681 /// Note, we are only going to have incomplete callbacks once we have promise_or combinator.
1682 ///
1683 ///
1684 /// * If there is only one callback returns `1`;
1685 /// * If there are multiple callbacks (e.g. created through `promise_and`) returns their number;
1686 /// * If the function was called not through the callback returns `0`.
1687 ///
1688 /// # Cost
1689 ///
1690 /// `base`
1691 pub fn promise_results_count(&mut self) -> Result<u64> {
1692 self.gas_counter.pay_base(base)?;
1693 if self.context.is_view {
1694 return Err(HostError::ProhibitedInView {
1695 method_name: "promise_results_count".to_string(),
1696 }
1697 .into());
1698 }
1699 Ok(self.promise_results.len() as _)
1700 }
1701
1702 /// If the current function is invoked by a callback we can access the execution results of the
1703 /// promises that caused the callback. This function returns the result in blob format and
1704 /// places it into the register.
1705 ///
1706 /// * If promise result is complete and successful copies its blob into the register;
1707 /// * If promise result is complete and failed or incomplete keeps register unused;
1708 ///
1709 /// # Returns
1710 ///
1711 /// * If promise result is not complete returns `0`;
1712 /// * If promise result is complete and successful returns `1`;
1713 /// * If promise result is complete and failed returns `2`.
1714 ///
1715 /// # Errors
1716 ///
1717 /// * If `result_id` does not correspond to an existing result returns `InvalidPromiseResultIndex`;
1718 /// * If copying the blob exhausts the memory limit it returns `MemoryAccessViolation`.
1719 /// * If called as view function returns `ProhibitedInView`.
1720 ///
1721 /// # Cost
1722 ///
1723 /// `base + cost of writing data into a register`
1724 pub fn promise_result(&mut self, result_idx: u64, register_id: u64) -> Result<u64> {
1725 self.gas_counter.pay_base(base)?;
1726 if self.context.is_view {
1727 return Err(
1728 HostError::ProhibitedInView { method_name: "promise_result".to_string() }.into()
1729 );
1730 }
1731 match self
1732 .promise_results
1733 .get(result_idx as usize)
1734 .ok_or(HostError::InvalidPromiseResultIndex { result_idx })?
1735 {
1736 PromiseResult::NotReady => Ok(0),
1737 PromiseResult::Successful(data) => {
1738 self.internal_write_register(register_id, data.clone())?;
1739 Ok(1)
1740 }
1741 PromiseResult::Failed => Ok(2),
1742 }
1743 }
1744
1745 /// When promise `promise_idx` finishes executing its result is considered to be the result of
1746 /// the current function.
1747 ///
1748 /// # Errors
1749 ///
1750 /// * If `promise_idx` does not correspond to an existing promise returns `InvalidPromiseIndex`.
1751 /// * If called as view function returns `ProhibitedInView`.
1752 ///
1753 /// # Cost
1754 ///
1755 /// `base + promise_return`
1756 pub fn promise_return(&mut self, promise_idx: u64) -> Result<()> {
1757 self.gas_counter.pay_base(base)?;
1758 self.gas_counter.pay_base(promise_return)?;
1759 if self.context.is_view {
1760 return Err(
1761 HostError::ProhibitedInView { method_name: "promise_return".to_string() }.into()
1762 );
1763 }
1764 match self
1765 .promises
1766 .get(promise_idx as usize)
1767 .ok_or(HostError::InvalidPromiseIndex { promise_idx })?
1768 {
1769 Promise::Receipt(receipt_idx) => {
1770 self.return_data = ReturnData::ReceiptIndex(*receipt_idx);
1771 Ok(())
1772 }
1773 Promise::NotReceipt(_) => Err(HostError::CannotReturnJointPromise.into()),
1774 }
1775 }
1776
1777 // #####################
1778 // # Miscellaneous API #
1779 // #####################
1780
1781 /// Sets the blob of data as the return value of the contract.
1782 ///
1783 /// # Errors
1784 ///
1785 /// * If `value_len + value_ptr` exceeds the memory container or points to an unused register it
1786 /// returns `MemoryAccessViolation`.
1787 /// * if the length of the returned data exceeds `max_length_returned_data` returns
1788 /// `ReturnedValueLengthExceeded`.
1789 ///
1790 /// # Cost
1791 /// `base + cost of reading return value from memory or register + dispatch&exec cost per byte of the data sent * num data receivers`
1792 pub fn value_return(&mut self, value_len: u64, value_ptr: u64) -> Result<()> {
1793 self.gas_counter.pay_base(base)?;
1794 let return_val = self.get_vec_from_memory_or_register(value_ptr, value_len)?;
1795 let mut burn_gas: Gas = 0;
1796 let num_bytes = return_val.len() as u64;
1797 if num_bytes > self.config.limit_config.max_length_returned_data {
1798 return Err(HostError::ReturnedValueLengthExceeded {
1799 length: num_bytes,
1800 limit: self.config.limit_config.max_length_returned_data,
1801 }
1802 .into());
1803 }
1804 let data_cfg = &self.fees_config.data_receipt_creation_config;
1805 for data_receiver in &self.context.output_data_receivers {
1806 let sir = data_receiver == &self.context.current_account_id;
1807 // We deduct for execution here too, because if we later have an OR combinator
1808 // for promises then we might have some valid data receipts that arrive too late
1809 // to be picked up by the execution that waits on them (because it has started
1810 // after it receives the first data receipt) and then we need to issue a special
1811 // refund in this situation. Which we avoid by just paying for execution of
1812 // data receipt that might not be performed.
1813 // The gas here is considered burnt, cause we'll prepay for it upfront.
1814 burn_gas = burn_gas
1815 .checked_add(
1816 data_cfg
1817 .cost_per_byte
1818 .send_fee(sir)
1819 .checked_add(data_cfg.cost_per_byte.exec_fee())
1820 .ok_or(HostError::IntegerOverflow)?
1821 .checked_mul(num_bytes)
1822 .ok_or(HostError::IntegerOverflow)?,
1823 )
1824 .ok_or(HostError::IntegerOverflow)?;
1825 }
1826 self.gas_counter.pay_action_accumulated(burn_gas, burn_gas, ActionCosts::value_return)?;
1827 self.return_data = ReturnData::Value(return_val);
1828 Ok(())
1829 }
1830
1831 /// Terminates the execution of the program with panic `GuestPanic`.
1832 ///
1833 /// # Cost
1834 ///
1835 /// `base`
1836 pub fn panic(&mut self) -> Result<()> {
1837 self.gas_counter.pay_base(base)?;
1838 Err(HostError::GuestPanic { panic_msg: "explicit guest panic".to_string() }.into())
1839 }
1840
1841 /// Guest panics with the UTF-8 encoded string.
1842 /// If `len == u64::MAX` then treats the string as null-terminated with character `'\0'`.
1843 ///
1844 /// # Errors
1845 ///
1846 /// * If string extends outside the memory of the guest with `MemoryAccessViolation`;
1847 /// * If string is not UTF-8 returns `BadUtf8`.
1848 /// * If string is longer than `max_log_len` returns `TotalLogLengthExceeded`.
1849 ///
1850 /// # Cost
1851 /// `base + cost of reading and decoding a utf8 string`
1852 pub fn panic_utf8(&mut self, len: u64, ptr: u64) -> Result<()> {
1853 self.gas_counter.pay_base(base)?;
1854 Err(HostError::GuestPanic { panic_msg: self.get_utf8_string(len, ptr)? }.into())
1855 }
1856
1857 /// Logs the UTF-8 encoded string.
1858 /// If `len == u64::MAX` then treats the string as null-terminated with character `'\0'`.
1859 ///
1860 /// # Errors
1861 ///
1862 /// * If string extends outside the memory of the guest with `MemoryAccessViolation`;
1863 /// * If string is not UTF-8 returns `BadUtf8`.
1864 /// * If number of bytes read + `total_log_length` exceeds the `max_total_log_length` returns
1865 /// `TotalLogLengthExceeded`.
1866 /// * If the total number of logs will exceed the `max_number_logs` returns
1867 /// `NumberOfLogsExceeded`.
1868 ///
1869 /// # Cost
1870 ///
1871 /// `base + log_base + log_byte + num_bytes + utf8 decoding cost`
1872 pub fn log_utf8(&mut self, len: u64, ptr: u64) -> Result<()> {
1873 self.gas_counter.pay_base(base)?;
1874 self.check_can_add_a_log_message()?;
1875 let message = self.get_utf8_string(len, ptr)?;
1876 self.gas_counter.pay_base(log_base)?;
1877 self.gas_counter.pay_per_byte(log_byte, message.len() as u64)?;
1878 self.checked_push_log(message)
1879 }
1880
1881 /// Logs the UTF-16 encoded string. If `len == u64::MAX` then treats the string as
1882 /// null-terminated with two-byte sequence of `0x00 0x00`.
1883 ///
1884 /// # Errors
1885 ///
1886 /// * If string extends outside the memory of the guest with `MemoryAccessViolation`;
1887 /// * If string is not UTF-16 returns `BadUtf16`.
1888 /// * If number of bytes read + `total_log_length` exceeds the `max_total_log_length` returns
1889 /// `TotalLogLengthExceeded`.
1890 /// * If the total number of logs will exceed the `max_number_logs` returns
1891 /// `NumberOfLogsExceeded`.
1892 ///
1893 /// # Cost
1894 ///
1895 /// `base + log_base + log_byte * num_bytes + utf16 decoding cost`
1896 pub fn log_utf16(&mut self, len: u64, ptr: u64) -> Result<()> {
1897 self.gas_counter.pay_base(base)?;
1898 self.check_can_add_a_log_message()?;
1899 let message = self.get_utf16_string(len, ptr)?;
1900 self.gas_counter.pay_base(log_base)?;
1901 // Let's not use `encode_utf16` for gas per byte here, since it's a lot of compute.
1902 self.gas_counter.pay_per_byte(log_byte, message.len() as u64)?;
1903 self.checked_push_log(message)
1904 }
1905
1906 /// Special import kept for compatibility with AssemblyScript contracts. Not called by smart
1907 /// contracts directly, but instead called by the code generated by AssemblyScript.
1908 ///
1909 /// # Errors
1910 ///
1911 /// * If string extends outside the memory of the guest with `MemoryAccessViolation`;
1912 /// * If string is not UTF-8 returns `BadUtf8`.
1913 /// * If number of bytes read + `total_log_length` exceeds the `max_total_log_length` returns
1914 /// `TotalLogLengthExceeded`.
1915 /// * If the total number of logs will exceed the `max_number_logs` returns
1916 /// `NumberOfLogsExceeded`.
1917 ///
1918 /// # Cost
1919 ///
1920 /// `base + log_base + log_byte * num_bytes + utf16 decoding cost`
1921 pub fn abort(&mut self, msg_ptr: u32, filename_ptr: u32, line: u32, col: u32) -> Result<()> {
1922 self.gas_counter.pay_base(base)?;
1923 if msg_ptr < 4 || filename_ptr < 4 {
1924 return Err(HostError::BadUTF16.into());
1925 }
1926 self.check_can_add_a_log_message()?;
1927
1928 // Underflow checked above.
1929 let msg_len = self.memory_get_u32((msg_ptr - 4) as u64)?;
1930 let filename_len = self.memory_get_u32((filename_ptr - 4) as u64)?;
1931
1932 let msg = self.get_utf16_string(msg_len as u64, msg_ptr as u64)?;
1933 let filename = self.get_utf16_string(filename_len as u64, filename_ptr as u64)?;
1934
1935 let message = format!("{}, filename: \"{}\" line: {} col: {}", msg, filename, line, col);
1936 self.gas_counter.pay_base(log_base)?;
1937 self.gas_counter.pay_per_byte(log_byte, message.as_bytes().len() as u64)?;
1938 self.checked_push_log(format!("ABORT: {}", message))?;
1939
1940 Err(HostError::GuestPanic { panic_msg: message }.into())
1941 }
1942
1943 // ###############
1944 // # Storage API #
1945 // ###############
1946
1947 /// Reads account id from the given location in memory.
1948 ///
1949 /// # Errors
1950 ///
1951 /// * If account is not UTF-8 encoded then returns `BadUtf8`;
1952 ///
1953 /// # Cost
1954 ///
1955 /// This is a helper function that encapsulates the following costs:
1956 /// cost of reading buffer from register or memory,
1957 /// `utf8_decoding_base + utf8_decoding_byte * num_bytes`.
1958 fn read_and_parse_account_id(&mut self, ptr: u64, len: u64) -> Result<AccountId> {
1959 let buf = self.get_vec_from_memory_or_register(ptr, len)?;
1960 self.gas_counter.pay_base(utf8_decoding_base)?;
1961 self.gas_counter.pay_per_byte(utf8_decoding_byte, buf.len() as u64)?;
1962 let account_id = AccountId::from_utf8(buf).map_err(|_| HostError::BadUTF8)?;
1963 Ok(account_id)
1964 }
1965
1966 /// Writes key-value into storage.
1967 /// * If key is not in use it inserts the key-value pair and does not modify the register. Returns `0`;
1968 /// * If key is in use it inserts the key-value and copies the old value into the `register_id`. Returns `1`.
1969 ///
1970 /// # Errors
1971 ///
1972 /// * If `key_len + key_ptr` or `value_len + value_ptr` exceeds the memory container or points
1973 /// to an unused register it returns `MemoryAccessViolation`;
1974 /// * If returning the preempted value into the registers exceed the memory container it returns
1975 /// `MemoryAccessViolation`.
1976 /// * If the length of the key exceeds `max_length_storage_key` returns `KeyLengthExceeded`.
1977 /// * If the length of the value exceeds `max_length_storage_value` returns
1978 /// `ValueLengthExceeded`.
1979 /// * If called as view function returns `ProhibitedInView``.
1980 ///
1981 /// # Cost
1982 ///
1983 /// `base + storage_write_base + storage_write_key_byte * num_key_bytes + storage_write_value_byte * num_value_bytes
1984 /// + get_vec_from_memory_or_register_cost x 2`.
1985 ///
1986 /// If a value was evicted it costs additional `storage_write_value_evicted_byte * num_evicted_bytes + internal_write_register_cost`.
1987 pub fn storage_write(
1988 &mut self,
1989 key_len: u64,
1990 key_ptr: u64,
1991 value_len: u64,
1992 value_ptr: u64,
1993 register_id: u64,
1994 ) -> Result<u64> {
1995 self.gas_counter.pay_base(base)?;
1996 if self.context.is_view {
1997 return Err(
1998 HostError::ProhibitedInView { method_name: "storage_write".to_string() }.into()
1999 );
2000 }
2001 self.gas_counter.pay_base(storage_write_base)?;
2002 let key = self.get_vec_from_memory_or_register(key_ptr, key_len)?;
2003 if key.len() as u64 > self.config.limit_config.max_length_storage_key {
2004 return Err(HostError::KeyLengthExceeded {
2005 length: key.len() as u64,
2006 limit: self.config.limit_config.max_length_storage_key,
2007 }
2008 .into());
2009 }
2010 let value = self.get_vec_from_memory_or_register(value_ptr, value_len)?;
2011 if value.len() as u64 > self.config.limit_config.max_length_storage_value {
2012 return Err(HostError::ValueLengthExceeded {
2013 length: value.len() as u64,
2014 limit: self.config.limit_config.max_length_storage_value,
2015 }
2016 .into());
2017 }
2018 self.gas_counter.pay_per_byte(storage_write_key_byte, key.len() as u64)?;
2019 self.gas_counter.pay_per_byte(storage_write_value_byte, value.len() as u64)?;
2020 let nodes_before = self.ext.get_touched_nodes_count();
2021 let evicted_ptr = self.ext.storage_get(&key)?;
2022 let evicted =
2023 Self::deref_value(&mut self.gas_counter, storage_write_evicted_byte, evicted_ptr)?;
2024 self.gas_counter
2025 .pay_per_byte(touching_trie_node, self.ext.get_touched_nodes_count() - nodes_before)?;
2026 self.ext.storage_set(&key, &value)?;
2027 let storage_config = &self.fees_config.storage_usage_config;
2028 match evicted {
2029 Some(old_value) => {
2030 // Inner value can't overflow, because the value length is limited.
2031 self.current_storage_usage = self
2032 .current_storage_usage
2033 .checked_sub(old_value.len() as u64)
2034 .ok_or(InconsistentStateError::IntegerOverflow)?;
2035 // Inner value can't overflow, because the value length is limited.
2036 self.current_storage_usage = self
2037 .current_storage_usage
2038 .checked_add(value.len() as u64)
2039 .ok_or(InconsistentStateError::IntegerOverflow)?;
2040 self.internal_write_register(register_id, old_value)?;
2041 Ok(1)
2042 }
2043 None => {
2044 // Inner value can't overflow, because the key/value length is limited.
2045 self.current_storage_usage = self
2046 .current_storage_usage
2047 .checked_add(
2048 value.len() as u64
2049 + key.len() as u64
2050 + storage_config.num_extra_bytes_record,
2051 )
2052 .ok_or(InconsistentStateError::IntegerOverflow)?;
2053 Ok(0)
2054 }
2055 }
2056 }
2057
2058 fn deref_value<'s>(
2059 gas_counter: &mut GasCounter,
2060 cost_per_byte: ExtCosts,
2061 value_ptr: Option<Box<dyn ValuePtr + 's>>,
2062 ) -> Result<Option<Vec<u8>>> {
2063 match value_ptr {
2064 Some(value_ptr) => {
2065 gas_counter.pay_per_byte(cost_per_byte, value_ptr.len() as u64)?;
2066 value_ptr.deref().map(Some)
2067 }
2068 None => Ok(None),
2069 }
2070 }
2071
2072 /// Reads the value stored under the given key.
2073 /// * If key is used copies the content of the value into the `register_id`, even if the content
2074 /// is zero bytes. Returns `1`;
2075 /// * If key is not present then does not modify the register. Returns `0`;
2076 ///
2077 /// # Errors
2078 ///
2079 /// * If `key_len + key_ptr` exceeds the memory container or points to an unused register it
2080 /// returns `MemoryAccessViolation`;
2081 /// * If returning the preempted value into the registers exceed the memory container it returns
2082 /// `MemoryAccessViolation`.
2083 /// * If the length of the key exceeds `max_length_storage_key` returns `KeyLengthExceeded`.
2084 ///
2085 /// # Cost
2086 ///
2087 /// `base + storage_read_base + storage_read_key_byte * num_key_bytes + storage_read_value_byte + num_value_bytes
2088 /// cost to read key from register + cost to write value into register`.
2089 pub fn storage_read(&mut self, key_len: u64, key_ptr: u64, register_id: u64) -> Result<u64> {
2090 self.gas_counter.pay_base(base)?;
2091 self.gas_counter.pay_base(storage_read_base)?;
2092 let key = self.get_vec_from_memory_or_register(key_ptr, key_len)?;
2093 if key.len() as u64 > self.config.limit_config.max_length_storage_key {
2094 return Err(HostError::KeyLengthExceeded {
2095 length: key.len() as u64,
2096 limit: self.config.limit_config.max_length_storage_key,
2097 }
2098 .into());
2099 }
2100 self.gas_counter.pay_per_byte(storage_read_key_byte, key.len() as u64)?;
2101 let nodes_before = self.ext.get_touched_nodes_count();
2102 let read = self.ext.storage_get(&key);
2103 self.gas_counter
2104 .pay_per_byte(touching_trie_node, self.ext.get_touched_nodes_count() - nodes_before)?;
2105 let read = Self::deref_value(&mut self.gas_counter, storage_read_value_byte, read?)?;
2106 match read {
2107 Some(value) => {
2108 self.internal_write_register(register_id, value)?;
2109 Ok(1)
2110 }
2111 None => Ok(0),
2112 }
2113 }
2114
2115 /// Removes the value stored under the given key.
2116 /// * If key is used, removes the key-value from the trie and copies the content of the value
2117 /// into the `register_id`, even if the content is zero bytes. Returns `1`;
2118 /// * If key is not present then does not modify the register. Returns `0`.
2119 ///
2120 /// # Errors
2121 ///
2122 /// * If `key_len + key_ptr` exceeds the memory container or points to an unused register it
2123 /// returns `MemoryAccessViolation`;
2124 /// * If the registers exceed the memory limit returns `MemoryAccessViolation`;
2125 /// * If returning the preempted value into the registers exceed the memory container it returns
2126 /// `MemoryAccessViolation`.
2127 /// * If the length of the key exceeds `max_length_storage_key` returns `KeyLengthExceeded`.
2128 /// * If called as view function returns `ProhibitedInView``.
2129 ///
2130 /// # Cost
2131 ///
2132 /// `base + storage_remove_base + storage_remove_key_byte * num_key_bytes + storage_remove_ret_value_byte * num_value_bytes
2133 /// + cost to read the key + cost to write the value`.
2134 pub fn storage_remove(&mut self, key_len: u64, key_ptr: u64, register_id: u64) -> Result<u64> {
2135 self.gas_counter.pay_base(base)?;
2136 if self.context.is_view {
2137 return Err(
2138 HostError::ProhibitedInView { method_name: "storage_remove".to_string() }.into()
2139 );
2140 }
2141 self.gas_counter.pay_base(storage_remove_base)?;
2142 let key = self.get_vec_from_memory_or_register(key_ptr, key_len)?;
2143 if key.len() as u64 > self.config.limit_config.max_length_storage_key {
2144 return Err(HostError::KeyLengthExceeded {
2145 length: key.len() as u64,
2146 limit: self.config.limit_config.max_length_storage_key,
2147 }
2148 .into());
2149 }
2150 self.gas_counter.pay_per_byte(storage_remove_key_byte, key.len() as u64)?;
2151 let nodes_before = self.ext.get_touched_nodes_count();
2152 let removed_ptr = self.ext.storage_get(&key)?;
2153 let removed =
2154 Self::deref_value(&mut self.gas_counter, storage_remove_ret_value_byte, removed_ptr)?;
2155
2156 self.ext.storage_remove(&key)?;
2157 self.gas_counter
2158 .pay_per_byte(touching_trie_node, self.ext.get_touched_nodes_count() - nodes_before)?;
2159 let storage_config = &self.fees_config.storage_usage_config;
2160 match removed {
2161 Some(value) => {
2162 // Inner value can't overflow, because the key/value length is limited.
2163 self.current_storage_usage = self
2164 .current_storage_usage
2165 .checked_sub(
2166 value.len() as u64
2167 + key.len() as u64
2168 + storage_config.num_extra_bytes_record,
2169 )
2170 .ok_or(InconsistentStateError::IntegerOverflow)?;
2171 self.internal_write_register(register_id, value)?;
2172 Ok(1)
2173 }
2174 None => Ok(0),
2175 }
2176 }
2177
2178 /// Checks if there is a key-value pair.
2179 /// * If key is used returns `1`, even if the value is zero bytes;
2180 /// * Otherwise returns `0`.
2181 ///
2182 /// # Errors
2183 ///
2184 /// * If `key_len + key_ptr` exceeds the memory container it returns `MemoryAccessViolation`.
2185 /// * If the length of the key exceeds `max_length_storage_key` returns `KeyLengthExceeded`.
2186 ///
2187 /// # Cost
2188 ///
2189 /// `base + storage_has_key_base + storage_has_key_byte * num_bytes + cost of reading key`
2190 pub fn storage_has_key(&mut self, key_len: u64, key_ptr: u64) -> Result<u64> {
2191 self.gas_counter.pay_base(base)?;
2192 self.gas_counter.pay_base(storage_has_key_base)?;
2193 let key = self.get_vec_from_memory_or_register(key_ptr, key_len)?;
2194 if key.len() as u64 > self.config.limit_config.max_length_storage_key {
2195 return Err(HostError::KeyLengthExceeded {
2196 length: key.len() as u64,
2197 limit: self.config.limit_config.max_length_storage_key,
2198 }
2199 .into());
2200 }
2201 self.gas_counter.pay_per_byte(storage_has_key_byte, key.len() as u64)?;
2202 let nodes_before = self.ext.get_touched_nodes_count();
2203 let res = self.ext.storage_has_key(&key);
2204 self.gas_counter
2205 .pay_per_byte(touching_trie_node, self.ext.get_touched_nodes_count() - nodes_before)?;
2206 Ok(res? as u64)
2207 }
2208
2209 /// DEPRECATED
2210 /// Creates an iterator object inside the host. Returns the identifier that uniquely
2211 /// differentiates the given iterator from other iterators that can be simultaneously created.
2212 /// * It iterates over the keys that have the provided prefix. The order of iteration is defined
2213 /// by the lexicographic order of the bytes in the keys;
2214 /// * If there are no keys, it creates an empty iterator, see below on empty iterators.
2215 ///
2216 /// # Errors
2217 ///
2218 /// * If `prefix_len + prefix_ptr` exceeds the memory container it returns
2219 /// `MemoryAccessViolation`.
2220 /// * If the length of the prefix exceeds `max_length_storage_key` returns `KeyLengthExceeded`.
2221 ///
2222 /// # Cost
2223 ///
2224 /// `base + storage_iter_create_prefix_base + storage_iter_create_key_byte * num_prefix_bytes
2225 /// cost of reading the prefix`.
2226 pub fn storage_iter_prefix(&mut self, _prefix_len: u64, _prefix_ptr: u64) -> Result<u64> {
2227 Err(VMLogicError::HostError(HostError::Deprecated {
2228 method_name: "storage_iter_prefix".to_string(),
2229 }))
2230 }
2231
2232 /// DEPRECATED
2233 /// Iterates over all key-values such that keys are between `start` and `end`, where `start` is
2234 /// inclusive and `end` is exclusive. Unless lexicographically `start < end`, it creates an
2235 /// empty iterator. Note, this definition allows for `start` or `end` keys to not actually exist
2236 /// on the given trie.
2237 ///
2238 /// # Errors
2239 ///
2240 /// * If `start_len + start_ptr` or `end_len + end_ptr` exceeds the memory container or points to
2241 /// an unused register it returns `MemoryAccessViolation`.
2242 /// * If the length of the `start` exceeds `max_length_storage_key` returns `KeyLengthExceeded`.
2243 /// * If the length of the `end` exceeds `max_length_storage_key` returns `KeyLengthExceeded`.
2244 ///
2245 /// # Cost
2246 ///
2247 /// `base + storage_iter_create_range_base + storage_iter_create_from_byte * num_from_bytes
2248 /// + storage_iter_create_to_byte * num_to_bytes + reading from prefix + reading to prefix`.
2249 pub fn storage_iter_range(
2250 &mut self,
2251 _start_len: u64,
2252 _start_ptr: u64,
2253 _end_len: u64,
2254 _end_ptr: u64,
2255 ) -> Result<u64> {
2256 Err(VMLogicError::HostError(HostError::Deprecated {
2257 method_name: "storage_iter_range".to_string(),
2258 }))
2259 }
2260
2261 /// DEPRECATED
2262 /// Advances iterator and saves the next key and value in the register.
2263 /// * If iterator is not empty (after calling next it points to a key-value), copies the key
2264 /// into `key_register_id` and value into `value_register_id` and returns `1`;
2265 /// * If iterator is empty returns `0`;
2266 /// This allows us to iterate over the keys that have zero bytes stored in values.
2267 ///
2268 /// # Errors
2269 ///
2270 /// * If `key_register_id == value_register_id` returns `MemoryAccessViolation`;
2271 /// * If the registers exceed the memory limit returns `MemoryAccessViolation`;
2272 /// * If `iterator_id` does not correspond to an existing iterator returns `InvalidIteratorId`;
2273 /// * If between the creation of the iterator and calling `storage_iter_next` the range over
2274 /// which it iterates was modified returns `IteratorWasInvalidated`. Specifically, if
2275 /// `storage_write` or `storage_remove` was invoked on the key key such that:
2276 /// * in case of `storage_iter_prefix`. `key` has the given prefix and:
2277 /// * Iterator was not called next yet.
2278 /// * `next` was already called on the iterator and it is currently pointing at the `key`
2279 /// `curr` such that `curr <= key`.
2280 /// * in case of `storage_iter_range`. `start<=key<end` and:
2281 /// * Iterator was not called `next` yet.
2282 /// * `next` was already called on the iterator and it is currently pointing at the key
2283 /// `curr` such that `curr<=key<end`.
2284 ///
2285 /// # Cost
2286 ///
2287 /// `base + storage_iter_next_base + storage_iter_next_key_byte * num_key_bytes + storage_iter_next_value_byte * num_value_bytes
2288 /// + writing key to register + writing value to register`.
2289 pub fn storage_iter_next(
2290 &mut self,
2291 _iterator_id: u64,
2292 _key_register_id: u64,
2293 _value_register_id: u64,
2294 ) -> Result<u64> {
2295 Err(VMLogicError::HostError(HostError::Deprecated {
2296 method_name: "storage_iter_next".to_string(),
2297 }))
2298 }
2299
2300 /// Computes the outcome of execution.
2301 pub fn outcome(self) -> VMOutcome {
2302 VMOutcome {
2303 balance: self.current_account_balance,
2304 storage_usage: self.current_storage_usage,
2305 return_data: self.return_data,
2306 burnt_gas: self.gas_counter.burnt_gas(),
2307 used_gas: self.gas_counter.used_gas(),
2308 logs: self.logs,
2309 }
2310 }
2311
2312 /// clones the outcome of execution.
2313 pub fn clone_outcome(&self) -> VMOutcome {
2314 let logs = self.logs.clone();
2315 let return_data = self.return_data.clone();
2316 VMOutcome {
2317 balance: self.current_account_balance,
2318 storage_usage: self.current_storage_usage,
2319 return_data,
2320 burnt_gas: self.gas_counter.burnt_gas(),
2321 used_gas: self.gas_counter.used_gas(),
2322 logs,
2323 }
2324 }
2325
2326 pub fn add_contract_compile_fee(&mut self, code_len: u64) -> Result<()> {
2327 self.gas_counter.pay_per_byte(contract_compile_bytes, code_len)?;
2328 self.gas_counter.pay_base(contract_compile_base)
2329 }
2330}
2331
2332#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
2333pub struct VMOutcome {
2334 #[serde(with = "crate::serde_with::u128_dec_format")]
2335 pub balance: Balance,
2336 pub storage_usage: StorageUsage,
2337 pub return_data: ReturnData,
2338 pub burnt_gas: Gas,
2339 pub used_gas: Gas,
2340 pub logs: Vec<String>,
2341}