Unverified Commit d55fcd0e authored by Hero Bird's avatar Hero Bird Committed by GitHub

Add support for ext_transfer (#351)

* [core, lang] add support for ext_transfer

* [core] apply rustfmt

* fix typo
Co-Authored-By: Andrew Jones's avatarAndrew Jones <ascjones@gmail.com>

* [core] fix remaining bugs in the on-chain engine implementation
Co-authored-by: Andrew Jones's avatarAndrew Jones <ascjones@gmail.com>
parent 9f6b9888
Pipeline #83284 passed with stages
in 7 minutes and 17 seconds
......@@ -395,6 +395,26 @@ pub fn restore_contract<T>(
})
}
/// Transfers value from the contract to the destination account ID.
///
/// # Note
///
/// This is more efficient and simpler than the alternative to make a no-op
/// contract call or invoke a runtime function that performs the
/// transaction.
///
/// # Errors
///
/// If the contract doesn't have sufficient funds.
pub fn transfer<T>(destination: T::AccountId, value: T::Balance) -> Result<()>
where
T: EnvTypes,
{
<EnvInstance as OnInstance>::on_instance(|instance| {
TypedEnv::transfer::<T>(instance, destination, value)
})
}
/// Returns the input to the executed contract.
///
/// # Note
......
......@@ -234,6 +234,15 @@ pub trait TypedEnv: Env {
) where
T: EnvTypes;
/// Transfers value from the contract to the destination account ID.
///
/// # Note
///
/// For more details visit: [`ink_core::env::transfer`]
fn transfer<T>(&mut self, destination: T::AccountId, value: T::Balance) -> Result<()>
where
T: EnvTypes;
/// Returns a random hash seed.
///
/// # Note
......
......@@ -78,6 +78,25 @@ impl AccountsDb {
}
}
/// Returns the account at the given account ID or creates it.
pub fn get_or_create_account<T>(&mut self, at: &T::AccountId) -> &mut Account
where
T: EnvTypes,
{
// Note: We cannot do a normal match for `Some(account)` here since
// the borrow-checker somehow cannot make sense of it according
// to its lifetime analysis. Consider this to be a hack until
// the borrow-checker eventually let's us do this.
if self.get_account::<T>(&at).is_some() {
self.get_account_mut::<T>(at)
.expect("just checked that account exists")
} else {
self.add_user_account::<T>(at.clone(), 0.into());
self.get_account_mut::<T>(at)
.expect("just added the account so it must exist")
}
}
/// Returns the account for the given account ID if any.
pub fn get_account<T>(&self, at: &T::AccountId) -> Option<&Account>
where
......
......@@ -24,6 +24,7 @@ use crate::env::{
ReturnType,
},
Env,
EnvError,
EnvTypes,
Result,
Topics,
......@@ -260,6 +261,34 @@ impl TypedEnv for EnvInstance {
unimplemented!("off-chain environment does not support contract restoration")
}
fn transfer<T>(&mut self, destination: T::AccountId, value: T::Balance) -> Result<()>
where
T: EnvTypes,
{
let src_id = self.account_id::<T>()?;
let src_value = self
.accounts
.get_account::<T>(&src_id)
.expect("account of executed contract must exist")
.balance::<T>()?;
if src_value < value {
return Err(EnvError::TransferCallFailed)
}
let dst_value = self
.accounts
.get_or_create_account::<T>(&destination)
.balance::<T>()?;
self.accounts
.get_account_mut::<T>(&src_id)
.expect("account of executed contract must exist")
.set_balance::<T>(src_value - value)?;
self.accounts
.get_account_mut::<T>(&destination)
.expect("the account must exist already or has just been created")
.set_balance::<T>(dst_value + value)?;
Ok(())
}
fn random<T>(&mut self, subject: &[u8]) -> Result<T::Hash>
where
T: EnvTypes,
......
......@@ -47,6 +47,13 @@ mod sys {
input_data_len: u32,
) -> u32;
pub fn ext_transfer(
account_id_ptr: u32,
account_id_len: u32,
value_ptr: u32,
value_len: u32,
) -> u32;
pub fn ext_deposit_event(
topics_ptr: u32,
topics_len: u32,
......@@ -141,6 +148,22 @@ pub fn call(callee: &[u8], gas_limit: u64, value: &[u8], call_data: &[u8]) -> Re
}
}
pub fn transfer(account_id: &[u8], value: &[u8]) -> Result<()> {
let ret_code = unsafe {
sys::ext_transfer(
account_id.as_ptr() as u32,
account_id.len() as u32,
value.as_ptr() as u32,
value.len() as u32,
)
};
match ret_code {
0 => Ok(()),
1 => Err(EnvError::TransferCallFailed),
_unknown => panic!("encountered unknown error code upon transfer"),
}
}
pub fn deposit_event(topics: &[u8], data: &[u8]) {
unsafe {
sys::ext_deposit_event(
......
......@@ -330,6 +330,23 @@ impl TypedEnv for EnvInstance {
ext::restore_to(account_id, code_hash, rent_allowance, filtered_keys);
}
fn transfer<T>(&mut self, destination: T::AccountId, value: T::Balance) -> Result<()>
where
T: EnvTypes,
{
// Reset the contract-side buffer to append onto clean slate.
self.reset_buffer();
// Append the encoded `destination` and `value` in order and remember
// their encoded regions within the buffer.
let destination = self.append_encode_into_buffer(destination);
let value = self.append_encode_into_buffer(value);
// Resolve the encoded regions into actual byte slices.
let destination = &self.buffer[destination];
let value = &self.buffer[value];
// Perform the actual transfer call.
ext::transfer(destination, value)
}
fn random<T>(&mut self, subject: &[u8]) -> Result<T::Hash>
where
T: EnvTypes,
......
......@@ -39,6 +39,8 @@ pub enum EnvError {
MissingRuntimeStorageEntry,
/// The queried contract storage entry is missing.
MissingContractStorageEntry,
/// A call to transfer value from the contract failed.
TransferCallFailed,
}
/// A result of environmental operations.
......
......@@ -263,6 +263,18 @@ where
env::restore_contract::<T>(account_id, code_hash, rent_allowance, filtered_keys)
}
/// Transfers value from the contract to the destination account ID.
///
/// # Note
///
/// For more details visit: [`ink_core::env::transfer`]
pub fn transfer(self, destination: T::AccountId, value: T::Balance) -> Result<()>
where
T: EnvTypes,
{
env::transfer::<T>(destination, value)
}
/// Returns a random hash seed.
///
/// # Note
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment