Function Call
In this section we provide an explanation how the FunctionCall
action execution works, what are
the inputs and what are the outputs. Suppose runtime received the following ActionReceipt:
ActionReceipt {
id: "A1",
signer_id: "alice",
signer_public_key: "6934...e248",
receiver_id: "dex",
predecessor_id: "alice",
input_data_ids: [],
output_data_receivers: [],
actions: [FunctionCall { gas: 100000, deposit: 100000u128, method_name: "exchange", args: "{arg1, arg2, ...}", ... }],
}
input_data_ids
to PromiseResult
s
ActionReceipt.input_data_ids
must be satisfied before execution (see
Receipt Matching). Each of ActionReceipt.input_data_ids
will be converted to
the PromiseResult::Successful(Vec<u8>)
if data_id.data
is Some(Vec<u8>)
otherwise if
data_id.data
is None
promise will be PromiseResult::Failed
.
Input
The FunctionCall
executes in the receiver_id
account environment.
- a vector of Promise Results which can be accessed by a
promise_result
import PromisesAPIpromise_result
) - the original Transaction
signer_id
,signer_public_key
data from the ActionReceipt (e.g.method_name
,args
,predecessor_id
,deposit
,prepaid_gas
(which isgas
in FunctionCall)) - a general blockchain data (e.g.
block_index
,block_timestamp
) - read data from the account storage
A full list of the data available for the contract can be found in Context API and Trie
Execution
In order to implement this action, the runtime will:
- load the contract code from the
receiver_id
account’s storage; - parse, validate and instrument the contract code (see Preparation);
- optionally, convert the contract code to a different executable format;
- instantiate the WASM module, linking runtime-provided functions defined in the Bindings Spec & running the start function; and
- invoke the function that has been exported from the wasm module with the name matching
that specified in the
FunctionCall.method_name
field.
Note that some of these steps may be executed during the
DeployContractAction
instead. This is largely an
optimization of the FunctionCall
gas fee, and must not result in an observable behavioral
difference of the FunctionCall
action.
During the execution of the contract, the runtime will:
- count burnt gas on execution;
- count used gas (which is
burnt gas
+ gas attached to the new created receipts); - measure the increase in account’s storage usage as a result of this call;
- collect logs produced by the contract;
- set the return data; and
- create new receipts through PromisesAPI.
Output
The output of the FunctionCall
:
- storage updates - changes to the account trie storage which will be applied on a successful call
burnt_gas
,used_gas
- see Runtime Feesbalance
- unspent account balance (account balance could be spent on deposits of newly createdFunctionCall
s orTransferAction
s to other contracts)storage_usage
- storage_usage after ActionReceipt applicationlogs
- during contract execution, utf8/16 string log records could be created. Logs are not persistent currently.new_receipts
- newActionReceipts
created during the execution. These receipts are going to be sent to the respectivereceiver_id
s (see Receipt Matching explanation)- result could be either
ReturnData::Value(Vec<u8>)
orReturnData::ReceiptIndex(u64)
`
Value Result
If applied ActionReceipt
contains output_data_receivers
,
runtime will create DataReceipt
for each of data_id
and receiver_id
and data
equals
returned value. Eventually, these DataReceipt
will be delivered to the corresponding receivers.
ReceiptIndex Result
Successful result could not return any Value, but generates a bunch of new ActionReceipts instead.
One example could be a callback. In this case, we assume the new Receipt will send its Value
Result to the output_data_receivers
of the current
ActionReceipt
.
Errors
As with other actions, errors can be divided into two categories: validation error and execution error.
Validation Error
If there is zero gas attached to the function call, a
/// The attached amount of gas in a FunctionCall action has to be a positive number.
FunctionCallZeroAttachedGas,error will be returned
If the length of the method name to be called exceeds
max_length_method_name
, a genesis parameter whose current value is256
, a/// The length of the method name exceeded the limit in a Function Call action.
FunctionCallMethodNameLengthExceeded { length: u64, limit: u64 }error is returned.
If the length of the argument to the function call exceeds
max_arguments_length
, a genesis parameter whose current value is4194304
(4MB), a/// The length of the arguments exceeded the limit in a Function Call action.
FunctionCallArgumentsLengthExceeded { length: u64, limit: u64 }error is returned.
Execution Error
There are three types of errors which may occur when applying a function call action:
FunctionCallError
, ExternalError
, and StorageError
.
FunctionCallError
includes everything from around the execution of the wasm binary, from compiling wasm to native to traps occurred while executing the compiled native binary. More specifically, it includes the following errors:pub enum FunctionCallError {
/// Wasm compilation error
CompilationError(CompilationError),
/// Wasm binary env link error
LinkError {
msg: String,
},
/// Import/export resolve error
MethodResolveError(MethodResolveError),
/// A trap happened during execution of a binary
WasmTrap(WasmTrap),
WasmUnknownError,
HostError(HostError),
}
CompilationError
includes errors that can occur during the compilation of wasm binary.LinkError
is returned when wasmer runtime is unable to link the wasm module with provided imports.MethodResolveError
occurs when the method in the action cannot be found in the contract code.WasmTrap
error happens when a trap occurs during the execution of the binary. Traps here includepub enum WasmTrap {
/// An `unreachable` opcode was executed.
Unreachable,
/// Call indirect incorrect signature trap.
IncorrectCallIndirectSignature,
/// Memory out of bounds trap.
MemoryOutOfBounds,
/// Call indirect out of bounds trap.
CallIndirectOOB,
/// An arithmetic exception, e.g. divided by zero.
IllegalArithmetic,
/// Misaligned atomic access trap.
MisalignedAtomicAccess,
/// Breakpoint trap.
BreakpointTrap,
/// Stack overflow.
StackOverflow,
/// Generic trap.
GenericTrap,
}WasmUnknownError
occurs when something inside wasmer goes wrongHostError
includes errors that might be returned during the execution of a host function. Those errors arepub enum HostError {
/// String encoding is bad UTF-16 sequence
BadUTF16,
/// String encoding is bad UTF-8 sequence
BadUTF8,
/// Exceeded the prepaid gas
GasExceeded,
/// Exceeded the maximum amount of gas allowed to burn per contract
GasLimitExceeded,
/// Exceeded the account balance
BalanceExceeded,
/// Tried to call an empty method name
EmptyMethodName,
/// Smart contract panicked
GuestPanic { panic_msg: String },
/// IntegerOverflow happened during a contract execution
IntegerOverflow,
/// `promise_idx` does not correspond to existing promises
InvalidPromiseIndex { promise_idx: u64 },
/// Actions can only be appended to non-joint promise.
CannotAppendActionToJointPromise,
/// Returning joint promise is currently prohibited
CannotReturnJointPromise,
/// Accessed invalid promise result index
InvalidPromiseResultIndex { result_idx: u64 },
/// Accessed invalid register id
InvalidRegisterId { register_id: u64 },
/// Iterator `iterator_index` was invalidated after its creation by performing a mutable operation on trie
IteratorWasInvalidated { iterator_index: u64 },
/// Accessed memory outside the bounds
MemoryAccessViolation,
/// VM Logic returned an invalid receipt index
InvalidReceiptIndex { receipt_index: u64 },
/// Iterator index `iterator_index` does not exist
InvalidIteratorIndex { iterator_index: u64 },
/// VM Logic returned an invalid account id
InvalidAccountId,
/// VM Logic returned an invalid method name
InvalidMethodName,
/// VM Logic provided an invalid public key
InvalidPublicKey,
/// `method_name` is not allowed in view calls
ProhibitedInView { method_name: String },
/// The total number of logs will exceed the limit.
NumberOfLogsExceeded { limit: u64 },
/// The storage key length exceeded the limit.
KeyLengthExceeded { length: u64, limit: u64 },
/// The storage value length exceeded the limit.
ValueLengthExceeded { length: u64, limit: u64 },
/// The total log length exceeded the limit.
TotalLogLengthExceeded { length: u64, limit: u64 },
/// The maximum number of promises within a FunctionCall exceeded the limit.
NumberPromisesExceeded { number_of_promises: u64, limit: u64 },
/// The maximum number of input data dependencies exceeded the limit.
NumberInputDataDependenciesExceeded { number_of_input_data_dependencies: u64, limit: u64 },
/// The returned value length exceeded the limit.
ReturnedValueLengthExceeded { length: u64, limit: u64 },
/// The contract size for DeployContract action exceeded the limit.
ContractSizeExceeded { size: u64, limit: u64 },
/// The host function was deprecated.
Deprecated { method_name: String },
}
ExternalError
includes errors that occur during the execution insideExternal
, which is an interface between runtime and the rest of the system. The possible errors are:pub enum ExternalError {
/// Unexpected error which is typically related to the node storage corruption.
/// It's possible the input state is invalid or malicious.
StorageError(StorageError),
/// Error when accessing validator information. Happens inside epoch manager.
ValidatorError(EpochError),
}StorageError
occurs when state or storage is corrupted.