Newer
Older
// must therefore coordinate with the chain that it targets.
//
// # Note
//
// If no chain extension exists the contract will trap with the `NoChainExtension`
// module error.
[seal0] seal_call_chain_extension(
ctx,
func_id: u32,
input_ptr: u32,
input_len: u32,
output_ptr: u32,
output_len_ptr: u32
) -> u32 => {
use crate::chain_extension::{ChainExtension, Environment, RetVal};
if !<E::T as Config>::ChainExtension::enabled() {
return Err(Error::<E::T>::NoChainExtension.into());
}
let env = Environment::new(ctx, input_ptr, input_len, output_ptr, output_len_ptr);
match <E::T as Config>::ChainExtension::call(func_id, env)? {
RetVal::Converging(val) => Ok(val),
RetVal::Diverging{flags, data} => Err(TrapReason::Return(ReturnData {
flags: flags.bits(),
data,
})),
}
},
// Emit a custom debug message.
//
// No newlines are added to the supplied message.
// Specifying invalid UTF-8 triggers a trap.
//
// This is a no-op if debug message recording is disabled which is always the case
// when the code is executing on-chain. The message is interpreted as UTF-8 and
// appended to the debug buffer which is then supplied to the calling RPC client.
//
// # Note
//
// Even though no action is taken when debug message recording is disabled there is still
// a non trivial overhead (and weight cost) associated with calling this function. Contract
// languages should remove calls to this function (either at runtime or compile time) when
// not being executed as an RPC. For example, they could allow users to disable logging
// through compile time flags (cargo features) for on-chain deployment. Additionally, the
// return value of this function can be cached in order to prevent further calls at runtime.
[seal0] seal_debug_message(ctx, str_ptr: u32, str_len: u32) -> ReturnCode => {
ctx.charge_gas(RuntimeCosts::DebugMessage)?;
if ctx.ext.append_debug_buffer("") {
let data = ctx.read_sandbox_memory(str_ptr, str_len)?;
let msg = core::str::from_utf8(&data)
.map_err(|_| <Error<E::T>>::DebugMessageInvalidUTF8)?;
ctx.ext.append_debug_buffer(msg);
return Ok(ReturnCode::Success);
}
Ok(ReturnCode::LoggingDisabled)
},
2058
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
// Call some dispatchable of the runtime.
//
// This function decodes the passed in data as the overarching `Call` type of the
// runtime and dispatches it. The weight as specified in the runtime is charged
// from the gas meter. Any weight refunds made by the dispatchable are considered.
//
// The filter specified by `Config::CallFilter` is attached to the origin of
// the dispatched call.
//
// # Parameters
//
// - `input_ptr`: the pointer into the linear memory where the input data is placed.
// - `input_len`: the length of the input data in bytes.
//
// # Return Value
//
// Returns `ReturnCode::Success` when the dispatchable was succesfully executed and
// returned `Ok`. When the dispatchable was exeuted but returned an error
// `ReturnCode::CallRuntimeReturnedError` is returned. The full error is not
// provided because it is not guaranteed to be stable.
//
// # Comparison with `ChainExtension`
//
// Just as a chain extension this API allows the runtime to extend the functionality
// of contracts. While making use of this function is generelly easier it cannot be
// used in call cases. Consider writing a chain extension if you need to do perform
// one of the following tasks:
//
// - Return data.
// - Provide functionality **exclusively** to contracts.
// - Provide custom weights.
// - Avoid the need to keep the `Call` data structure stable.
//
// # Unstable
//
// This function is unstable and subject to change (or removal) in the future. Do not
// deploy a contract using it to a production chain.
[__unstable__] seal_call_runtime(ctx, call_ptr: u32, call_len: u32) -> ReturnCode => {
use frame_support::{dispatch::GetDispatchInfo, weights::extract_actual_weight};
ctx.charge_gas(RuntimeCosts::CopyFromContract(call_len))?;
let call: <E::T as Config>::Call = ctx.read_sandbox_memory_as_unbounded(
call_ptr, call_len
)?;
let dispatch_info = call.get_dispatch_info();
let charged = ctx.charge_gas(RuntimeCosts::CallRuntime(dispatch_info.weight))?;
let result = ctx.ext.call_runtime(call);
let actual_weight = extract_actual_weight(&result, &dispatch_info);
ctx.adjust_gas(charged, RuntimeCosts::CallRuntime(actual_weight));
match result {
Ok(_) => Ok(ReturnCode::Success),
Err(_) => Ok(ReturnCode::CallRuntimeReturnedError),
}
},
// Recovers the ECDSA public key from the given message hash and signature.
//
// Writes the public key into the given output buffer.
// Assumes the secp256k1 curve.
//
// # Parameters
//
// - `signature_ptr`: the pointer into the linear memory where the signature
![Sasha Gryaznov Sasha Gryaznov's avatar](/assets/no_avatar-849f9c04a3a0d0cea2424ae97b27447dc64a7dbfae83c036c45b403392f0e8ba.png)
Sasha Gryaznov
committed
// is placed. Should be decodable as a 65 bytes. Traps otherwise.
// - `message_hash_ptr`: the pointer into the linear memory where the message
// hash is placed. Should be decodable as a 32 bytes. Traps otherwise.
// - `output_ptr`: the pointer into the linear memory where the output
![Sasha Gryaznov Sasha Gryaznov's avatar](/assets/no_avatar-849f9c04a3a0d0cea2424ae97b27447dc64a7dbfae83c036c45b403392f0e8ba.png)
Sasha Gryaznov
committed
// data is placed. The buffer should be 33 bytes. The function
// will write the result directly into this buffer.
//
// # Errors
//
// `ReturnCode::EcdsaRecoverFailed`
[seal0] seal_ecdsa_recover(ctx, signature_ptr: u32, message_hash_ptr: u32, output_ptr: u32) -> ReturnCode => {
ctx.charge_gas(RuntimeCosts::EcdsaRecovery)?;
let mut signature: [u8; 65] = [0; 65];
ctx.read_sandbox_memory_into_buf(signature_ptr, &mut signature)?;
let mut message_hash: [u8; 32] = [0; 32];
ctx.read_sandbox_memory_into_buf(message_hash_ptr, &mut message_hash)?;
let result = ctx.ext.ecdsa_recover(&signature, &message_hash);
match result {
Ok(pub_key) => {
// Write the recovered compressed ecdsa public key back into the sandboxed output
// buffer.
ctx.write_sandbox_memory(output_ptr, pub_key.as_ref())?;
Ok(ReturnCode::Success)
},
Err(_) => Ok(ReturnCode::EcdsaRecoverFailed),
}
},
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
// Replace the contract code at the specified address with new code.
//
// # Note
//
// There are a couple of important considerations which must be taken into account when
// using this API:
//
// 1. The storage at the code address will remain untouched. This means that contract developers
// must ensure that the storage layout of the new code is compatible with that of the old code.
//
// 2. Contracts using this API can't be assumed as having deterministic addresses. Said another way,
// when using this API you lose the guarantee that an address always identifies a specific code hash.
//
// 3. If a contract calls into itself after changing its code the new call would use
// the new code. However, if the original caller panics after returning from the sub call it
// would revert the changes made by `seal_set_code_hash` and the next caller would use
// the old code.
//
// # Parameters
//
![Sasha Gryaznov Sasha Gryaznov's avatar](/assets/no_avatar-849f9c04a3a0d0cea2424ae97b27447dc64a7dbfae83c036c45b403392f0e8ba.png)
Sasha Gryaznov
committed
// - `code_hash_ptr`: A pointer to the buffer that contains the new code hash.
//
// # Errors
//
// `ReturnCode::CodeNotFound`
![Sasha Gryaznov Sasha Gryaznov's avatar](/assets/no_avatar-849f9c04a3a0d0cea2424ae97b27447dc64a7dbfae83c036c45b403392f0e8ba.png)
Sasha Gryaznov
committed
[seal0] seal_set_code_hash(ctx, code_hash_ptr: u32) -> ReturnCode => {
ctx.charge_gas(RuntimeCosts::SetCodeHash)?;
let code_hash: CodeHash<<E as Ext>::T> = ctx.read_sandbox_memory_as(code_hash_ptr)?;
match ctx.ext.set_code_hash(code_hash) {
Err(err) => {
let code = Runtime::<E>::err_into_return_code(err)?;
Ok(code)
},
Ok(()) => Ok(ReturnCode::Success)
}
},
![Sasha Gryaznov Sasha Gryaznov's avatar](/assets/no_avatar-849f9c04a3a0d0cea2424ae97b27447dc64a7dbfae83c036c45b403392f0e8ba.png)
Sasha Gryaznov
committed
// Calculates Ethereum address from the ECDSA compressed public key and stores
// it into the supplied buffer.
//
// # Parameters
//
// - `key_ptr`: a pointer to the ECDSA compressed public key. Should be decodable as a 33 bytes value.
// Traps otherwise.
// - `out_ptr`: the pointer into the linear memory where the output
// data is placed. The function will write the result
// directly into this buffer.
//
// The value is stored to linear memory at the address pointed to by `out_ptr`.
// If the available space at `out_ptr` is less than the size of the value a trap is triggered.
//
// # Errors
//
// `ReturnCode::EcdsaRecoverFailed`
[seal0] seal_ecdsa_to_eth_address(ctx, key_ptr: u32, out_ptr: u32) -> ReturnCode => {
![Sasha Gryaznov Sasha Gryaznov's avatar](/assets/no_avatar-849f9c04a3a0d0cea2424ae97b27447dc64a7dbfae83c036c45b403392f0e8ba.png)
Sasha Gryaznov
committed
ctx.charge_gas(RuntimeCosts::EcdsaToEthAddress)?;
let mut compressed_key: [u8; 33] = [0;33];
ctx.read_sandbox_memory_into_buf(key_ptr, &mut compressed_key)?;
let result = ctx.ext.ecdsa_to_eth_address(&compressed_key);
match result {
Ok(eth_address) => {
ctx.write_sandbox_memory(out_ptr, eth_address.as_ref())?;
Ok(ReturnCode::Success)
},
Err(_) => Ok(ReturnCode::EcdsaRecoverFailed),
}
},