Unverified Commit 764a44a9 authored by Robin Freyler's avatar Robin Freyler Committed by GitHub
Browse files

Optimize get property for most primitive types (#974)

* implement get_propery optimization for caller, gas_left and account_id

* implement optimization for little-endian types

* make most of the property query functions infallible

* adjust ERC-20 example for changes

* apply rustfmt

* fix ink_lang_macro doc tests

* fix contract-terminate example contract

* fix contract-transfer example

* fix ERC-20 and trait-ERC-20 example contracts

* fix ERC-721 example

* fix UI test

* fix some off-chain tests

* apply rustfmt
parent ee9015df
Pipeline #163165 passed with stages
in 32 minutes and 6 seconds
......@@ -48,7 +48,7 @@ use ink_primitives::Key;
/// # Errors
///
/// If the returned caller cannot be properly decoded.
pub fn caller<T>() -> Result<T::AccountId>
pub fn caller<T>() -> T::AccountId
where
T: Environment,
{
......@@ -62,7 +62,7 @@ where
/// # Errors
///
/// If the returned value cannot be properly decoded.
pub fn transferred_balance<T>() -> Result<T::Balance>
pub fn transferred_balance<T>() -> T::Balance
where
T: Environment,
{
......@@ -76,7 +76,7 @@ where
/// # Errors
///
/// If the returned value cannot be properly decoded.
pub fn weight_to_fee<T>(gas: u64) -> Result<T::Balance>
pub fn weight_to_fee<T>(gas: u64) -> T::Balance
where
T: Environment,
{
......@@ -90,7 +90,7 @@ where
/// # Errors
///
/// If the returned value cannot be properly decoded.
pub fn gas_left<T>() -> Result<u64>
pub fn gas_left<T>() -> u64
where
T: Environment,
{
......@@ -104,7 +104,7 @@ where
/// # Errors
///
/// If the returned value cannot be properly decoded.
pub fn block_timestamp<T>() -> Result<T::Timestamp>
pub fn block_timestamp<T>() -> T::Timestamp
where
T: Environment,
{
......@@ -122,7 +122,7 @@ where
/// # Errors
///
/// If the returned value cannot be properly decoded.
pub fn account_id<T>() -> Result<T::AccountId>
pub fn account_id<T>() -> T::AccountId
where
T: Environment,
{
......@@ -136,7 +136,7 @@ where
/// # Errors
///
/// If the returned value cannot be properly decoded.
pub fn balance<T>() -> Result<T::Balance>
pub fn balance<T>() -> T::Balance
where
T: Environment,
{
......@@ -150,7 +150,7 @@ where
/// # Errors
///
/// If the returned value cannot be properly decoded.
pub fn rent_allowance<T>() -> Result<T::Balance>
pub fn rent_allowance<T>() -> T::Balance
where
T: Environment,
{
......@@ -200,7 +200,7 @@ where
/// # Errors
///
/// If the returned value cannot be properly decoded.
pub fn block_number<T>() -> Result<T::BlockNumber>
pub fn block_number<T>() -> T::BlockNumber
where
T: Environment,
{
......@@ -214,7 +214,7 @@ where
/// # Errors
///
/// If the returned value cannot be properly decoded.
pub fn minimum_balance<T>() -> Result<T::Balance>
pub fn minimum_balance<T>() -> T::Balance
where
T: Environment,
{
......@@ -228,7 +228,7 @@ where
/// # Errors
///
/// If the returned value cannot be properly decoded.
pub fn tombstone_deposit<T>() -> Result<T::Balance>
pub fn tombstone_deposit<T>() -> T::Balance
where
T: Environment,
{
......
......@@ -186,56 +186,56 @@ pub trait TypedEnvBackend: EnvBackend {
/// # Note
///
/// For more details visit: [`caller`][`crate::caller`]
fn caller<T: Environment>(&mut self) -> Result<T::AccountId>;
fn caller<T: Environment>(&mut self) -> T::AccountId;
/// Returns the transferred balance for the contract execution.
///
/// # Note
///
/// For more details visit: [`transferred_balance`][`crate::transferred_balance`]
fn transferred_balance<T: Environment>(&mut self) -> Result<T::Balance>;
fn transferred_balance<T: Environment>(&mut self) -> T::Balance;
/// Returns the price for the specified amount of gas.
///
/// # Note
///
/// For more details visit: [`weight_to_fee`][`crate::weight_to_fee`]
fn weight_to_fee<T: Environment>(&mut self, gas: u64) -> Result<T::Balance>;
fn weight_to_fee<T: Environment>(&mut self, gas: u64) -> T::Balance;
/// Returns the amount of gas left for the contract execution.
///
/// # Note
///
/// For more details visit: [`gas_left`][`crate::gas_left`]
fn gas_left<T: Environment>(&mut self) -> Result<u64>;
fn gas_left<T: Environment>(&mut self) -> u64;
/// Returns the timestamp of the current block.
///
/// # Note
///
/// For more details visit: [`block_timestamp`][`crate::block_timestamp`]
fn block_timestamp<T: Environment>(&mut self) -> Result<T::Timestamp>;
fn block_timestamp<T: Environment>(&mut self) -> T::Timestamp;
/// Returns the address of the executed contract.
///
/// # Note
///
/// For more details visit: [`account_id`][`crate::account_id`]
fn account_id<T: Environment>(&mut self) -> Result<T::AccountId>;
fn account_id<T: Environment>(&mut self) -> T::AccountId;
/// Returns the balance of the executed contract.
///
/// # Note
///
/// For more details visit: [`balance`][`crate::balance`]
fn balance<T: Environment>(&mut self) -> Result<T::Balance>;
fn balance<T: Environment>(&mut self) -> T::Balance;
/// Returns the current rent allowance for the executed contract.
///
/// # Note
///
/// For more details visit: [`rent_allowance`][`crate::rent_allowance`]
fn rent_allowance<T: Environment>(&mut self) -> Result<T::Balance>;
fn rent_allowance<T: Environment>(&mut self) -> T::Balance;
/// Returns information needed for rent calculations.
///
......@@ -259,21 +259,21 @@ pub trait TypedEnvBackend: EnvBackend {
/// # Note
///
/// For more details visit: [`block_number`][`crate::block_number`]
fn block_number<T: Environment>(&mut self) -> Result<T::BlockNumber>;
fn block_number<T: Environment>(&mut self) -> T::BlockNumber;
/// Returns the minimum balance that is required for creating an account.
///
/// # Note
///
/// For more details visit: [`minimum_balance`][`crate::minimum_balance`]
fn minimum_balance<T: Environment>(&mut self) -> Result<T::Balance>;
fn minimum_balance<T: Environment>(&mut self) -> T::Balance;
/// Returns the tombstone deposit of the contract chain.
///
/// # Note
///
/// For more details visit: [`tombstone_deposit`][`crate::tombstone_deposit`]
fn tombstone_deposit<T: Environment>(&mut self) -> Result<T::Balance>;
fn tombstone_deposit<T: Environment>(&mut self) -> T::Balance;
/// Emits an event with the given event data.
///
......
......@@ -311,32 +311,53 @@ impl EnvBackend for EnvInstance {
}
impl TypedEnvBackend for EnvInstance {
fn caller<T: Environment>(&mut self) -> Result<T::AccountId> {
fn caller<T: Environment>(&mut self) -> T::AccountId {
self.get_property::<T::AccountId>(Engine::caller)
.unwrap_or_else(|error| {
panic!("could not read `caller` property: {:?}", error)
})
}
fn transferred_balance<T: Environment>(&mut self) -> Result<T::Balance> {
fn transferred_balance<T: Environment>(&mut self) -> T::Balance {
self.get_property::<T::Balance>(Engine::value_transferred)
.unwrap_or_else(|error| {
panic!("could not read `transferred_value` property: {:?}", error)
})
}
fn gas_left<T: Environment>(&mut self) -> Result<u64> {
fn gas_left<T: Environment>(&mut self) -> u64 {
self.get_property::<u64>(Engine::gas_left)
.unwrap_or_else(|error| {
panic!("could not read `gas_left` property: {:?}", error)
})
}
fn block_timestamp<T: Environment>(&mut self) -> Result<T::Timestamp> {
fn block_timestamp<T: Environment>(&mut self) -> T::Timestamp {
self.get_property::<T::Timestamp>(Engine::block_timestamp)
.unwrap_or_else(|error| {
panic!("could not read `block_timestamp` property: {:?}", error)
})
}
fn account_id<T: Environment>(&mut self) -> Result<T::AccountId> {
fn account_id<T: Environment>(&mut self) -> T::AccountId {
self.get_property::<T::AccountId>(Engine::address)
.unwrap_or_else(|error| {
panic!("could not read `account_id` property: {:?}", error)
})
}
fn balance<T: Environment>(&mut self) -> Result<T::Balance> {
fn balance<T: Environment>(&mut self) -> T::Balance {
self.get_property::<T::Balance>(Engine::balance)
.unwrap_or_else(|error| {
panic!("could not read `balance` property: {:?}", error)
})
}
fn rent_allowance<T: Environment>(&mut self) -> Result<T::Balance> {
fn rent_allowance<T: Environment>(&mut self) -> T::Balance {
self.get_property::<T::Balance>(Engine::rent_allowance)
.unwrap_or_else(|error| {
panic!("could not read `rent_allowance` property: {:?}", error)
})
}
fn rent_params<T>(&mut self) -> Result<RentParams<T>>
......@@ -356,16 +377,25 @@ impl TypedEnvBackend for EnvInstance {
unimplemented!("off-chain environment does not support rent status")
}
fn block_number<T: Environment>(&mut self) -> Result<T::BlockNumber> {
fn block_number<T: Environment>(&mut self) -> T::BlockNumber {
self.get_property::<T::BlockNumber>(Engine::block_number)
.unwrap_or_else(|error| {
panic!("could not read `block_number` property: {:?}", error)
})
}
fn minimum_balance<T: Environment>(&mut self) -> Result<T::Balance> {
fn minimum_balance<T: Environment>(&mut self) -> T::Balance {
self.get_property::<T::Balance>(Engine::minimum_balance)
.unwrap_or_else(|error| {
panic!("could not read `minimum_balance` property: {:?}", error)
})
}
fn tombstone_deposit<T: Environment>(&mut self) -> Result<T::Balance> {
fn tombstone_deposit<T: Environment>(&mut self) -> T::Balance {
self.get_property::<T::Balance>(Engine::tombstone_deposit)
.unwrap_or_else(|error| {
panic!("could not read `tombstone_deposit` property: {:?}", error)
})
}
fn emit_event<T, Event>(&mut self, event: Event)
......@@ -470,10 +500,12 @@ impl TypedEnvBackend for EnvInstance {
.map_err(Into::into)
}
fn weight_to_fee<T: Environment>(&mut self, gas: u64) -> Result<T::Balance> {
fn weight_to_fee<T: Environment>(&mut self, gas: u64) -> T::Balance {
let mut output: [u8; BUFFER_SIZE] = [0; BUFFER_SIZE];
self.engine.weight_to_fee(gas, &mut &mut output[..]);
scale::Decode::decode(&mut &output[..]).map_err(Into::into)
scale::Decode::decode(&mut &output[..]).unwrap_or_else(|error| {
panic!("could not read `weight_to_fee` property: {:?}", error)
})
}
fn random<T>(&mut self, subject: &[u8]) -> Result<(T::Hash, T::BlockNumber)>
......
......@@ -266,7 +266,7 @@ impl EnvInstance {
where
T: Environment,
{
let src_id = self.account_id::<T>()?;
let src_id = self.account_id::<T>();
let src_value = self
.accounts
.get_account::<T>(&src_id)
......@@ -300,12 +300,12 @@ impl EnvInstance {
T: Environment,
{
// Send the remaining balance to the beneficiary
let all: T::Balance = self.balance::<T>().expect("could not decode balance");
let all: T::Balance = self.balance::<T>();
self.transfer_impl::<T>(&beneficiary, all)
.expect("transfer did not work ");
// Remove account
let contract_id = self.account_id::<T>().expect("could not decode account id");
let contract_id = self.account_id::<T>();
self.accounts.remove_account::<T>(contract_id);
// The on-chain implementation would set a tombstone with a code hash here
......@@ -324,70 +324,72 @@ impl EnvInstance {
}
impl TypedEnvBackend for EnvInstance {
fn caller<T: Environment>(&mut self) -> Result<T::AccountId> {
fn caller<T: Environment>(&mut self) -> T::AccountId {
self.exec_context()
.expect(UNINITIALIZED_EXEC_CONTEXT)
.caller::<T>()
.map_err(|_| scale::Error::from("could not decode caller"))
.map_err(Into::into)
.unwrap_or_else(|error| {
panic!("could not read `caller` property: {:?}", error)
})
}
fn transferred_balance<T: Environment>(&mut self) -> Result<T::Balance> {
fn transferred_balance<T: Environment>(&mut self) -> T::Balance {
self.exec_context()
.expect(UNINITIALIZED_EXEC_CONTEXT)
.transferred_value::<T>()
.map_err(|_| scale::Error::from("could not decode transferred balance"))
.map_err(Into::into)
.unwrap_or_else(|error| {
panic!("could not read `transferred_balance` property: {:?}", error)
})
}
/// Emulates gas price calculation
fn weight_to_fee<T: Environment>(&mut self, gas: u64) -> Result<T::Balance> {
fn weight_to_fee<T: Environment>(&mut self, gas: u64) -> T::Balance {
use crate::arithmetic::Saturating as _;
let gas_price = self
.chain_spec
.gas_price::<T>()
.map_err(|_| scale::Error::from("could not decode gas price"))?;
Ok(gas_price
.saturating_mul(gas.try_into().unwrap_or_else(|_| Bounded::max_value())))
let gas_price = self.chain_spec.gas_price::<T>().unwrap_or_else(|error| {
panic!("could not read `gas_price` property: {:?}", error)
});
gas_price.saturating_mul(gas.try_into().unwrap_or_else(|_| Bounded::max_value()))
}
fn gas_left<T: Environment>(&mut self) -> Result<u64> {
Ok(self
.exec_context()
fn gas_left<T: Environment>(&mut self) -> u64 {
self.exec_context()
.expect(UNINITIALIZED_EXEC_CONTEXT)
.gas::<T>())
.gas::<T>()
}
fn block_timestamp<T: Environment>(&mut self) -> Result<T::Timestamp> {
fn block_timestamp<T: Environment>(&mut self) -> T::Timestamp {
self.current_block()
.expect(UNINITIALIZED_EXEC_CONTEXT)
.timestamp::<T>()
.map_err(|_| scale::Error::from("could not decode block time"))
.map_err(Into::into)
.unwrap_or_else(|error| {
panic!("could not read `block_timestamp` property: {:?}", error)
})
}
fn account_id<T: Environment>(&mut self) -> Result<T::AccountId> {
fn account_id<T: Environment>(&mut self) -> T::AccountId {
self.exec_context()
.expect(UNINITIALIZED_EXEC_CONTEXT)
.callee::<T>()
.map_err(|_| scale::Error::from("could not decode callee"))
.map_err(Into::into)
.unwrap_or_else(|error| {
panic!("could not read `account_id` property: {:?}", error)
})
}
fn balance<T: Environment>(&mut self) -> Result<T::Balance> {
fn balance<T: Environment>(&mut self) -> T::Balance {
self.callee_account()
.balance::<T>()
.map_err(|_| scale::Error::from("could not decode callee balance"))
.map_err(Into::into)
.unwrap_or_else(|error| {
panic!("could not read `balance` property: {:?}", error)
})
}
fn rent_allowance<T: Environment>(&mut self) -> Result<T::Balance> {
fn rent_allowance<T: Environment>(&mut self) -> T::Balance {
self.callee_account()
.rent_allowance::<T>()
.map_err(|_| scale::Error::from("could not decode callee rent allowance"))
.map_err(Into::into)
.unwrap_or_else(|error| {
panic!("could not read `rent_allowance` property: {:?}", error)
})
}
fn rent_params<T>(&mut self) -> Result<RentParams<T>>
......@@ -396,20 +398,20 @@ impl TypedEnvBackend for EnvInstance {
{
use crate::arithmetic::Saturating as _;
let total_balance = self.balance::<T>()?;
let total_balance = self.balance::<T>();
// the off-chain environment does currently not support reserved balance,
// hence we just use the total balance here.
let free_balance = self.balance::<T>()?;
let free_balance = self.balance::<T>();
let deposit_per_contract = self.chain_spec.deposit_per_contract::<T>()?;
let deposit_per_storage_byte = self.chain_spec.deposit_per_storage_byte::<T>()?;
let deposit_per_storage_item = self.chain_spec.deposit_per_storage_item::<T>()?;
let rent_fraction = self.chain_spec.rent_fraction::<T>()?;
let minimum_balance: T::Balance = self.minimum_balance::<T>()?;
let tombstone_deposit = self.tombstone_deposit::<T>()?;
let minimum_balance: T::Balance = self.minimum_balance::<T>();
let tombstone_deposit = self.tombstone_deposit::<T>();
let subsistence_threshold = minimum_balance.saturating_add(tombstone_deposit);
let rent_allowance = self.rent_allowance::<T>()?;
let rent_allowance = self.rent_allowance::<T>();
Ok(RentParams {
deposit_per_contract,
......@@ -440,26 +442,29 @@ impl TypedEnvBackend for EnvInstance {
unimplemented!("off-chain environment does not support rent status")
}
fn block_number<T: Environment>(&mut self) -> Result<T::BlockNumber> {
fn block_number<T: Environment>(&mut self) -> T::BlockNumber {
self.current_block()
.expect(UNINITIALIZED_EXEC_CONTEXT)
.number::<T>()
.map_err(|_| scale::Error::from("could not decode block number"))
.map_err(Into::into)
.unwrap_or_else(|error| {
panic!("could not read `block_number` property: {:?}", error)
})
}
fn minimum_balance<T: Environment>(&mut self) -> Result<T::Balance> {
fn minimum_balance<T: Environment>(&mut self) -> T::Balance {
self.chain_spec
.minimum_balance::<T>()
.map_err(|_| scale::Error::from("could not decode minimum balance"))
.map_err(Into::into)
.unwrap_or_else(|error| {
panic!("could not read `minimum_balance` property: {:?}", error)
})
}
fn tombstone_deposit<T: Environment>(&mut self) -> Result<T::Balance> {
fn tombstone_deposit<T: Environment>(&mut self) -> T::Balance {
self.chain_spec
.tombstone_deposit::<T>()
.map_err(|_| scale::Error::from("could not decode tombstone deposit"))
.map_err(Into::into)
.unwrap_or_else(|error| {
panic!("could not read `tombstone_deposit` property: {:?}", error)
})
}
fn emit_event<T, Event>(&mut self, new_event: Event)
......
......@@ -84,18 +84,12 @@ fn gas_price() -> crate::Result<()> {
chain_spec.set_gas_price::<crate::DefaultEnvironment>(gas_price.into())
})?;
assert_eq!(
2u128,
crate::weight_to_fee::<crate::DefaultEnvironment>(1).unwrap()
);
assert_eq!(2u128, crate::weight_to_fee::<crate::DefaultEnvironment>(1));
assert_eq!(
20u128,
crate::weight_to_fee::<crate::DefaultEnvironment>(10).unwrap()
);
assert_eq!(
6u128,
crate::weight_to_fee::<crate::DefaultEnvironment>(3).unwrap()
crate::weight_to_fee::<crate::DefaultEnvironment>(10)
);
assert_eq!(6u128, crate::weight_to_fee::<crate::DefaultEnvironment>(3));
Ok(())
})
......
......@@ -44,6 +44,7 @@ use crate::{
EnvBackend,
Environment,
Error,
FromLittleEndian,
Result,
ReturnFlags,
TypedEnvBackend,
......@@ -176,6 +177,34 @@ impl EnvInstance {
ScopedBuffer::from(&mut self.buffer[..])
}
/// Returns the contract property value into the given result buffer.
///
/// # Note
///
/// This skips the potentially costly decoding step that is often equivalent to a `memcpy`.
fn get_property_inplace<T>(&mut self, ext_fn: fn(output: &mut &mut [u8])) -> T
where
T: Default + AsMut<[u8]>,
{
let mut result = T::default();
ext_fn(&mut result.as_mut());
result
}
/// Returns the contract property value from its little-endian representation.
///
/// # Note
///
/// This skips the potentially costly decoding step that is often equivalent to a `memcpy`.
fn get_property_little_endian<T>(&mut self, ext_fn: fn(output: &mut &mut [u8])) -> T
where
T: FromLittleEndian,
{
let mut result = <T as FromLittleEndian>::Bytes::default();
ext_fn(&mut result.as_mut());
<T as FromLittleEndian>::from_le_bytes(result)
}
/// Returns the contract property value.
fn get_property<T>(&mut self, ext_fn: fn(output: &mut &mut [u8])) -> Result<T>
where
......@@ -311,44 +340,44 @@ impl EnvBackend for EnvInstance {
}
impl TypedEnvBackend for EnvInstance {
fn caller<T: Environment>(&mut self) -> Result<T::AccountId> {
self.get_property::<T::AccountId>(ext::caller)
fn caller<T: Environment>(&mut self) -> T::AccountId {
self.get_property_inplace::<T::AccountId>(ext::caller)
}
fn transferred_balance<T: Environment>(&mut self) -> Result<T::Balance> {
self.get_property::<T::Balance>(ext::value_transferred)
fn transferred_balance<T: Environment>(&mut self) -> T::Balance {
self.get_property_little_endian::<T::Balance>(ext::value_transferred)
}
fn gas_left<T: Environment>(&mut self) -> Result<u64> {
self.get_property::<u64>(ext::gas_left)
fn gas_left<T: Environment>(&mut self) -> u64 {
self.get_property_little_endian::<u64>(ext::gas_left)
}
fn block_timestamp<T: Environment>(&mut self) -> Result<T::Timestamp> {
self.get_property::<T::Timestamp>(ext::now)
fn block_timestamp<T: Environment>(&mut self) -> T::Timestamp {
self.get_property_little_endian::<T::Timestamp>(ext::now)
}
fn account_id<T: Environment>(&mut self) -> Result<T::AccountId> {
self.get_property::<T::AccountId>(ext::address)
fn account_id<T: Environment>(&mut self) -> T::AccountId {
self.get_property_inplace::<T::AccountId>(ext::address)
}
fn balance<T: Environment>(&mut self) -> Result<T::Balance> {
self.get_property::<T::Balance>(ext::balance)
fn balance<T: Environment>(&mut self) -> T::Balance {
self.get_property_little_endian::<T::Balance>(ext::balance)
}