Newer
Older
// Stores the value transferred along with this call or as endowment 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.
//
// The data is encoded as T::Balance.
Alexander Theißen
committed
seal_value_transferred(ctx, out_ptr: u32, out_len_ptr: u32) => {
charge_gas(ctx, RuntimeToken::ValueTransferred)?;
ctx, out_ptr, out_len_ptr, &ctx.ext.value_transferred().encode(), false,
already_charged
// Stores a random number for the current block and the given subject 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.
// The data is encoded as T::Hash.
Alexander Theißen
committed
seal_random(ctx, subject_ptr: u32, subject_len: u32, out_ptr: u32, out_len_ptr: u32) => {
charge_gas(ctx, RuntimeToken::Random)?;
// The length of a subject can't exceed `max_subject_len`.
if subject_len > ctx.schedule.max_subject_len {
return Err(sp_sandbox::HostError);
}
let subject_buf = read_sandbox_memory(ctx, subject_ptr, subject_len)?;
ctx, out_ptr, out_len_ptr, &ctx.ext.random(&subject_buf).encode(), false,
already_charged
},
// Load the latest block timestamp 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.
Alexander Theißen
committed
seal_now(ctx, out_ptr: u32, out_len_ptr: u32) => {
charge_gas(ctx, RuntimeToken::Now)?;
write_sandbox_output(
ctx, out_ptr, out_len_ptr, &ctx.ext.now().encode(), false, already_charged
)
},
// Stores the minimum balance (a.k.a. existential deposit) into the supplied buffer.
// The data is encoded as T::Balance.
Alexander Theißen
committed
seal_minimum_balance(ctx, out_ptr: u32, out_len_ptr: u32) => {
charge_gas(ctx, RuntimeToken::MinimumBalance)?;
write_sandbox_output(
ctx, out_ptr, out_len_ptr, &ctx.ext.minimum_balance().encode(), false, already_charged
)
// Stores the tombstone deposit 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.
// The data is encoded as T::Balance.
//
// # Note
//
// The tombstone deposit is on top of the existential deposit. So in order for
// a contract to leave a tombstone the balance of the contract must not go
// below the sum of existential deposit and the tombstone deposit. The sum
// is commonly referred as subsistence threshold in code.
Alexander Theißen
committed
seal_tombstone_deposit(ctx, out_ptr: u32, out_len_ptr: u32) => {
charge_gas(ctx, RuntimeToken::TombstoneDeposit)?;
ctx, out_ptr, out_len_ptr, &ctx.ext.tombstone_deposit().encode(), false,
already_charged
// Try to restore the given destination contract sacrificing the caller.
// This function will compute a tombstone hash from the caller's storage and the given code hash
// and if the hash matches the hash found in the tombstone at the specified address - kill
// the caller contract and restore the destination contract and set the specified `rent_allowance`.
// All caller's funds are transfered to the destination.
// If there is no tombstone at the destination address, the hashes don't match or this contract
// instance is already present on the contract call stack, a trap is generated.
//
// Otherwise, the destination contract is restored. This function is diverging and stops execution
// even on success.
//
// `dest_ptr`, `dest_len` - the pointer and the length of a buffer that encodes `T::AccountId`
// with the address of the to be restored contract.
// `code_hash_ptr`, `code_hash_len` - the pointer and the length of a buffer that encodes
// a code hash of the to be restored contract.
// `rent_allowance_ptr`, `rent_allowance_len` - the pointer and the length of a buffer that
// encodes the rent allowance that must be set in the case of successful restoration.
// `delta_ptr` is the pointer to the start of a buffer that has `delta_count` storage keys
// laid out sequentially.
//
// # Traps
//
// - Tombstone hashes do not match
// - Calling cantract is live i.e is already on the call stack.
Alexander Theißen
committed
seal_restore_to(
ctx,
dest_ptr: u32,
dest_len: u32,
code_hash_ptr: u32,
code_hash_len: u32,
rent_allowance_ptr: u32,
rent_allowance_len: u32,
delta_ptr: u32,
delta_count: u32
) => {
charge_gas(ctx, RuntimeToken::RestoreTo(delta_count))?;
let dest: <<E as Ext>::T as frame_system::Trait>::AccountId =
read_sandbox_memory_as(ctx, dest_ptr, dest_len)?;
let code_hash: CodeHash<<E as Ext>::T> =
read_sandbox_memory_as(ctx, code_hash_ptr, code_hash_len)?;
let rent_allowance: BalanceOf<<E as Ext>::T> =
read_sandbox_memory_as(ctx, rent_allowance_ptr, rent_allowance_len)?;
// We can eagerly allocate because we charged for the complete delta count already
let mut delta = Vec::with_capacity(delta_count as usize);
let mut key_ptr = delta_ptr;
for _ in 0..delta_count {
const KEY_SIZE: usize = 32;
// Read the delta into the provided buffer and collect it into the buffer.
let mut delta_key: StorageKey = [0; KEY_SIZE];
read_sandbox_memory_into_buf(ctx, key_ptr, &mut delta_key)?;
delta.push(delta_key);
// Offset key_ptr to the next element.
key_ptr = key_ptr.checked_add(KEY_SIZE as u32).ok_or_else(|| sp_sandbox::HostError)?;
}
delta
};
if let Ok(()) = ctx.ext.restore_to(
dest,
code_hash,
rent_allowance,
delta,
) {
ctx.trap_reason = Some(TrapReason::Restoration);
}
Err(sp_sandbox::HostError)
// Deposit a contract event with the data buffer and optional list of topics. There is a limit
// on the maximum number of topics specified by `max_event_topics`.
//
// - topics_ptr - a pointer to the buffer of topics encoded as `Vec<T::Hash>`. The value of this
// is ignored if `topics_len` is set to 0. The topics list can't contain duplicates.
// - topics_len - the length of the topics buffer. Pass 0 if you want to pass an empty vector.
// - data_ptr - a pointer to a raw data buffer which will saved along the event.
// - data_len - the length of the data buffer.
Alexander Theißen
committed
seal_deposit_event(ctx, topics_ptr: u32, topics_len: u32, data_ptr: u32, data_len: u32) => {
let num_topic = topics_len
.checked_div(sp_std::mem::size_of::<TopicOf<E::T>>() as u32)
.ok_or_else(|| store_err(ctx, "Zero sized topics are not allowed"))?;
charge_gas(ctx, RuntimeToken::DepositEvent {
num_topic,
len: data_len,
})?;
if data_len > ctx.ext.max_value_size() {
Err(store_err(ctx, Error::<E::T>::ValueTooLarge))?;
}
let mut topics: Vec::<TopicOf<<E as Ext>::T>> = match topics_len {
_ => read_sandbox_memory_as(ctx, topics_ptr, topics_len)?,
};
// If there are more than `max_event_topics`, then trap.
if topics.len() > ctx.schedule.max_event_topics as usize {
return Err(sp_sandbox::HostError);
}
// Check for duplicate topics. If there are any, then trap.
if has_duplicates(&mut topics) {
return Err(sp_sandbox::HostError);
}
let event_data = read_sandbox_memory(ctx, data_ptr, data_len)?;
ctx.ext.deposit_event(topics, event_data);
// Set rent allowance of the contract
//
// - value_ptr: a pointer to the buffer with value, how much to allow for rent
// Should be decodable as a `T::Balance`. Traps otherwise.
// - value_len: length of the value buffer.
Alexander Theißen
committed
seal_set_rent_allowance(ctx, value_ptr: u32, value_len: u32) => {
charge_gas(ctx, RuntimeToken::SetRentAllowance)?;
let value: BalanceOf<<E as Ext>::T> =
read_sandbox_memory_as(ctx, value_ptr, value_len)?;
ctx.ext.set_rent_allowance(value);
Ok(())
},
// Stores the rent allowance 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.
//
// The data is encoded as T::Balance.
Alexander Theißen
committed
seal_rent_allowance(ctx, out_ptr: u32, out_len_ptr: u32) => {
charge_gas(ctx, RuntimeToken::RentAllowance)?;
write_sandbox_output(
ctx, out_ptr, out_len_ptr, &ctx.ext.rent_allowance().encode(), false, already_charged
)
// Prints utf8 encoded string from the data buffer.
// Only available on `--dev` chains.
// This function may be removed at any time, superseded by a more general contract debugging feature.
Alexander Theißen
committed
seal_println(ctx, str_ptr: u32, str_len: u32) => {
let data = read_sandbox_memory(ctx, str_ptr, str_len)?;
if let Ok(utf8) = core::str::from_utf8(&data) {
}
Ok(())
},
// 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.
Alexander Theißen
committed
seal_block_number(ctx, out_ptr: u32, out_len_ptr: u32) => {
charge_gas(ctx, RuntimeToken::BlockNumber)?;
write_sandbox_output(
ctx, out_ptr, out_len_ptr, &ctx.ext.block_number().encode(), false, already_charged
)
// 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.
Alexander Theißen
committed
seal_hash_sha2_256(ctx, input_ptr: u32, input_len: u32, output_ptr: u32) => {
charge_gas(ctx, RuntimeToken::HashSha256(input_len))?;
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
compute_hash_on_intermediate_buffer(ctx, sha2_256, input_ptr, input_len, output_ptr)
},
// 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.
Alexander Theißen
committed
seal_hash_keccak_256(ctx, input_ptr: u32, input_len: u32, output_ptr: u32) => {
charge_gas(ctx, RuntimeToken::HashKeccak256(input_len))?;
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
compute_hash_on_intermediate_buffer(ctx, keccak_256, input_ptr, input_len, output_ptr)
},
// 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.
Alexander Theißen
committed
seal_hash_blake2_256(ctx, input_ptr: u32, input_len: u32, output_ptr: u32) => {
charge_gas(ctx, RuntimeToken::HashBlake256(input_len))?;
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
compute_hash_on_intermediate_buffer(ctx, blake2_256, input_ptr, input_len, output_ptr)
},
// 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.
Alexander Theißen
committed
seal_hash_blake2_128(ctx, input_ptr: u32, input_len: u32, output_ptr: u32) => {
charge_gas(ctx, RuntimeToken::HashBlake128(input_len))?;
compute_hash_on_intermediate_buffer(ctx, blake2_128, input_ptr, input_len, output_ptr)
},
/// Computes the given hash function on the supplied input.
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
///
/// Reads from the sandboxed input buffer into an intermediate buffer.
/// Returns the result directly to the output buffer of the sandboxed memory.
///
/// 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.
///
/// # Note
///
/// The `input` and `output` buffers may overlap.
fn compute_hash_on_intermediate_buffer<E, F, R>(
ctx: &mut Runtime<E>,
hash_fn: F,
input_ptr: u32,
input_len: u32,
output_ptr: u32,
) -> Result<(), sp_sandbox::HostError>
where
E: Ext,
F: FnOnce(&[u8]) -> R,
R: AsRef<[u8]>,
{
// Copy input into supervisor memory.
let input = read_sandbox_memory(ctx, input_ptr, input_len)?;
// Compute the hash on the input buffer using the given hash function.
let hash = hash_fn(&input);
// Write the resulting hash back into the sandboxed output buffer.
write_sandbox_memory(
output_ptr,
hash.as_ref(),
)?;
Ok(())
}
/// Finds duplicates in a given vector.
///
/// This function has complexity of O(n log n) and no additional memory is required, although
/// the order of items is not preserved.
fn has_duplicates<T: PartialEq + AsRef<[u8]>>(items: &mut Vec<T>) -> bool {
// Sort the vector