Newer
Older
if has_duplicates(&mut topics) {
return Err(Error::<E::T>::DuplicateTopics.into())
let event_data = ctx.read_sandbox_memory(data_ptr, data_len)?;
ctx.ext.deposit_event(topics, event_data);
/// Was used to set rent allowance of the contract.
///
/// # Note
///
/// The state rent functionality was removed. This is stub only exists for
/// backwards compatiblity.
fn seal_set_rent_allowance(
ctx: Runtime<E: Ext>,
_value_ptr: u32,
_value_len: u32,
) -> Result<(), TrapReason> {
ctx.charge_gas(RuntimeCosts::DebugMessage)?;
/// Was used to set rent allowance of the contract.
///
/// # Note
///
/// The state rent functionality was removed. This is stub only exists for
/// backwards compatiblity.
#[version(1)]
fn seal_set_rent_allowance(ctx: Runtime<E: Ext>, _value_ptr: u32) -> Result<(), TrapReason> {
ctx.charge_gas(RuntimeCosts::DebugMessage)?;
/// Was used to store the rent allowance into the supplied buffer.
///
/// # Note
///
/// The state rent functionality was removed. This is stub only exists for
/// backwards compatiblity.
fn seal_rent_allowance(
ctx: Runtime<E: Ext>,
out_ptr: u32,
out_len_ptr: u32,
) -> Result<(), TrapReason> {
ctx.charge_gas(RuntimeCosts::Balance)?;
let rent_allowance = <BalanceOf<E::T>>::max_value().encode();
Alexander Theißen
committed
Ok(ctx.write_sandbox_output(
out_ptr,
out_len_ptr,
&rent_allowance,
false,
already_charged,
Alexander Theißen
committed
)?)
/// Stores the current block number of the current contract into the supplied buffer.
///
/// The value is stored to linear memory at the address pointed to by `out_ptr`.
/// `out_len_ptr` must point to a u32 value that describes the available space at
/// `out_ptr`. This call overwrites it with the size of the value. If the available
/// space at `out_ptr` is less than the size of the value a trap is triggered.
fn seal_block_number(
ctx: Runtime<E: Ext>,
out_ptr: u32,
out_len_ptr: u32,
) -> Result<(), TrapReason> {
ctx.charge_gas(RuntimeCosts::BlockNumber)?;
Alexander Theißen
committed
Ok(ctx.write_sandbox_output(
out_ptr,
out_len_ptr,
&ctx.ext.block_number().encode(),
false,
already_charged,
Alexander Theißen
committed
)?)
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
/// Computes the SHA2 256-bit hash on the given input buffer.
///
/// Returns the result directly into the given output buffer.
///
/// # Note
///
/// - The `input` and `output` buffer may overlap.
/// - The output buffer is expected to hold at least 32 bytes (256 bits).
/// - It is the callers responsibility to provide an output buffer that is large enough to hold
/// the expected amount of bytes returned by the chosen hash function.
///
/// # 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.
/// - `output_ptr`: the pointer into the linear memory where the output data is placed. The
/// function will write the result directly into this buffer.
fn seal_hash_sha2_256(
ctx: Runtime<E: Ext>,
input_ptr: u32,
input_len: u32,
output_ptr: u32,
) -> Result<(), TrapReason> {
ctx.charge_gas(RuntimeCosts::HashSha256(input_len))?;
Alexander Theißen
committed
Ok(ctx.compute_hash_on_intermediate_buffer(sha2_256, input_ptr, input_len, output_ptr)?)
2109
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
2130
2131
/// Computes the KECCAK 256-bit hash on the given input buffer.
///
/// Returns the result directly into the given output buffer.
///
/// # Note
///
/// - The `input` and `output` buffer may overlap.
/// - The output buffer is expected to hold at least 32 bytes (256 bits).
/// - It is the callers responsibility to provide an output buffer that is large enough to hold
/// the expected amount of bytes returned by the chosen hash function.
///
/// # 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.
/// - `output_ptr`: the pointer into the linear memory where the output data is placed. The
/// function will write the result directly into this buffer.
fn seal_hash_keccak_256(
ctx: Runtime<E: Ext>,
input_ptr: u32,
input_len: u32,
output_ptr: u32,
) -> Result<(), TrapReason> {
ctx.charge_gas(RuntimeCosts::HashKeccak256(input_len))?;
Alexander Theißen
committed
Ok(ctx.compute_hash_on_intermediate_buffer(keccak_256, input_ptr, input_len, output_ptr)?)
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
/// Computes the BLAKE2 256-bit hash on the given input buffer.
///
/// Returns the result directly into the given output buffer.
///
/// # Note
///
/// - The `input` and `output` buffer may overlap.
/// - The output buffer is expected to hold at least 32 bytes (256 bits).
/// - It is the callers responsibility to provide an output buffer that is large enough to hold
/// the expected amount of bytes returned by the chosen hash function.
///
/// # 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.
/// - `output_ptr`: the pointer into the linear memory where the output data is placed. The
/// function will write the result directly into this buffer.
fn seal_hash_blake2_256(
ctx: Runtime<E: Ext>,
input_ptr: u32,
input_len: u32,
output_ptr: u32,
) -> Result<(), TrapReason> {
ctx.charge_gas(RuntimeCosts::HashBlake256(input_len))?;
Alexander Theißen
committed
Ok(ctx.compute_hash_on_intermediate_buffer(blake2_256, input_ptr, input_len, output_ptr)?)
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
/// Computes the BLAKE2 128-bit hash on the given input buffer.
///
/// Returns the result directly into the given output buffer.
///
/// # Note
///
/// - The `input` and `output` buffer may overlap.
/// - The output buffer is expected to hold at least 16 bytes (128 bits).
/// - It is the callers responsibility to provide an output buffer that is large enough to hold
/// the expected amount of bytes returned by the chosen hash function.
///
/// # 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.
/// - `output_ptr`: the pointer into the linear memory where the output data is placed. The
/// function will write the result directly into this buffer.
fn seal_hash_blake2_128(
ctx: Runtime<E: Ext>,
input_ptr: u32,
input_len: u32,
output_ptr: u32,
) -> Result<(), TrapReason> {
ctx.charge_gas(RuntimeCosts::HashBlake128(input_len))?;
Alexander Theißen
committed
Ok(ctx.compute_hash_on_intermediate_buffer(blake2_128, input_ptr, input_len, output_ptr)?)
/// Call into the chain extension provided by the chain if any.
///
/// Handling of the input values is up to the specific chain extension and so is the
/// return value. The extension can decide to use the inputs as primitive inputs or as
/// in/out arguments by interpreting them as pointers. Any caller of this function
/// must therefore coordinate with the chain that it targets.
///
/// # Note
///
/// If no chain extension exists the contract will trap with the `NoChainExtension`
/// module error.
fn seal_call_chain_extension(
ctx: Runtime<E: Ext>,
Alexander Theißen
committed
id: u32,
input_ptr: u32,
input_len: u32,
output_ptr: u32,
output_len_ptr: u32,
) -> Result<u32, TrapReason> {
use crate::chain_extension::{ChainExtension, Environment, RetVal};
if !<E::T as Config>::ChainExtension::enabled() {
return Err(Error::<E::T>::NoChainExtension.into())
}
Alexander Theißen
committed
let mut chain_extension = ctx.chain_extension.take().expect(
"Constructor initializes with `Some`. This is the only place where it is set to `None`.\
It is always reset to `Some` afterwards. qed"
);
let env = Environment::new(ctx, id, input_ptr, input_len, output_ptr, output_len_ptr);
let ret = match chain_extension.call(env)? {
RetVal::Converging(val) => Ok(val),
RetVal::Diverging { flags, data } =>
Err(TrapReason::Return(ReturnData { flags: flags.bits(), data })),
Alexander Theißen
committed
};
ctx.chain_extension = Some(chain_extension);
ret
2227
2228
2229
2230
2231
2232
2233
2234
2235
2236
2237
2238
2239
2240
2241
2242
2243
2244
2245
2246
2247
2248
/// 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.
fn seal_debug_message(
ctx: Runtime<E: Ext>,
str_ptr: u32,
str_len: u32,
) -> Result<ReturnCode, TrapReason> {
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)
2260
2261
2262
2263
2264
2265
2266
2267
2268
2269
2270
2271
2272
2273
2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
2285
2286
2287
2288
2289
2290
2291
2292
2293
2294
2295
2296
2297
2298
2299
2300
2301
2302
/// 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]
fn seal_call_runtime(
ctx: Runtime<E: Ext>,
call_ptr: u32,
call_len: u32,
) -> Result<ReturnCode, TrapReason> {
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),
}
2318
2319
2320
2321
2322
2323
2324
2325
2326
2327
2328
2329
2330
2331
2332
2333
2334
2335
2336
2337
2338
2339
2340
/// 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 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 data is placed. The
/// buffer should be 33 bytes. The function will write the result directly into this buffer.
///
/// # Errors
///
/// `ReturnCode::EcdsaRecoverFailed`
fn seal_ecdsa_recover(
ctx: Runtime<E: Ext>,
signature_ptr: u32,
message_hash_ptr: u32,
output_ptr: u32,
) -> Result<ReturnCode, TrapReason> {
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),
}
2362
2363
2364
2365
2366
2367
2368
2369
2370
2371
2372
2373
2374
2375
2376
2377
2378
2379
2380
2381
2382
2383
2384
2385
2386
2387
2388
2389
2390
2391
/// 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
///
/// - `code_hash_ptr`: A pointer to the buffer that contains the new code hash.
///
/// # Errors
///
/// `ReturnCode::CodeNotFound`
fn seal_set_code_hash(
ctx: Runtime<E: Ext>,
code_hash_ptr: u32,
) -> Result<ReturnCode, TrapReason> {
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
committed
2403
2404
2405
2406
2407
2408
2409
2410
2411
2412
2413
2414
2415
2416
2417
2418
2419
2420
2421
2422
2423
/// 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`
fn seal_ecdsa_to_eth_address(
ctx: Runtime<E: Ext>,
key_ptr: u32,
out_ptr: u32,
) -> Result<ReturnCode, TrapReason> {
Sasha Gryaznov
committed
ctx.charge_gas(RuntimeCosts::EcdsaToEthAddress)?;
let mut compressed_key: [u8; 33] = [0; 33];
Sasha Gryaznov
committed
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),
}