mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-12 17:01:09 +00:00
Add ext_transfer call (#5169)
* contracts: Add ext_transfer call This call allows contracts to send balance to any account contract or not. Previously, the only way to do that was though ext_call. * Apply suggestions from code review Co-Authored-By: Nikolay Volf <nikvolf@gmail.com> * The define_env! macro does not allow for trailing comma * Update frame/contracts/src/exec.rs Co-Authored-By: Nikolay Volf <nikvolf@gmail.com> * Bump spec version * Do not use nested gas meter * Use explicit 0 or 1 as return value * Remove superflous intermediate binding Co-authored-by: Nikolay Volf <nikvolf@gmail.com>
This commit is contained in:
committed by
GitHub
parent
7e2cd0edee
commit
b817763ea9
@@ -82,7 +82,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion {
|
||||
// and set impl_version to 0. If only runtime
|
||||
// implementation changes and behavior does not, then leave spec_version as
|
||||
// is and increment impl_version.
|
||||
spec_version: 236,
|
||||
spec_version: 237,
|
||||
impl_version: 0,
|
||||
apis: RUNTIME_API_VERSIONS,
|
||||
};
|
||||
|
||||
@@ -291,6 +291,21 @@ performed. Moreover, the DB read has to be synchronous and no progress can be ma
|
||||
**complexity**: The memory and computing complexity is proportional to the size of the fetched value. This function performs a
|
||||
DB read.
|
||||
|
||||
## ext_transfer
|
||||
|
||||
This function receives the following arguments:
|
||||
|
||||
- `account` buffer of a marshaled `AccountId`,
|
||||
- `value` buffer of a marshaled `Balance`,
|
||||
|
||||
It consists of the following steps:
|
||||
|
||||
1. Loading `account` buffer from the sandbox memory (see sandboxing memory get) and then decoding it.
|
||||
2. Loading `value` buffer from the sandbox memory and then decoding it.
|
||||
4. Invoking the executive function `transfer`.
|
||||
|
||||
Loading of `account` and `value` buffers should be charged. This is because the sizes of buffers are specified by the calling code, even though marshaled representations are, essentially, of constant size. This can be fixed by assigning an upper bound for sizes of `AccountId` and `Balance`.
|
||||
|
||||
## ext_call
|
||||
|
||||
This function receives the following arguments:
|
||||
|
||||
@@ -120,6 +120,14 @@ pub trait Ext {
|
||||
input_data: Vec<u8>,
|
||||
) -> Result<(AccountIdOf<Self::T>, ExecReturnValue), ExecError>;
|
||||
|
||||
/// Transfer some amount of funds into the specified account.
|
||||
fn transfer(
|
||||
&mut self,
|
||||
to: &AccountIdOf<Self::T>,
|
||||
value: BalanceOf<Self::T>,
|
||||
gas_meter: &mut GasMeter<Self::T>,
|
||||
) -> Result<(), DispatchError>;
|
||||
|
||||
/// Call (possibly transferring some amount of funds) into the specified account.
|
||||
fn call(
|
||||
&mut self,
|
||||
@@ -331,6 +339,23 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
/// Transfer balance to `dest` without calling any contract code.
|
||||
pub fn transfer(
|
||||
&mut self,
|
||||
dest: T::AccountId,
|
||||
value: BalanceOf<T>,
|
||||
gas_meter: &mut GasMeter<T>
|
||||
) -> Result<(), DispatchError> {
|
||||
transfer(
|
||||
gas_meter,
|
||||
TransferCause::Call,
|
||||
&self.self_account.clone(),
|
||||
&dest,
|
||||
value,
|
||||
self,
|
||||
)
|
||||
}
|
||||
|
||||
/// Make a call to the specified address, optionally transferring some funds.
|
||||
pub fn call(
|
||||
&mut self,
|
||||
@@ -706,6 +731,15 @@ where
|
||||
self.ctx.instantiate(endowment, gas_meter, code_hash, input_data)
|
||||
}
|
||||
|
||||
fn transfer(
|
||||
&mut self,
|
||||
to: &T::AccountId,
|
||||
value: BalanceOf<T>,
|
||||
gas_meter: &mut GasMeter<T>,
|
||||
) -> Result<(), DispatchError> {
|
||||
self.ctx.transfer(to.clone(), value, gas_meter)
|
||||
}
|
||||
|
||||
fn call(
|
||||
&mut self,
|
||||
to: &T::AccountId,
|
||||
|
||||
@@ -244,6 +244,20 @@ mod tests {
|
||||
|
||||
Ok((address, ExecReturnValue { status: STATUS_SUCCESS, data: Vec::new() }))
|
||||
}
|
||||
fn transfer(
|
||||
&mut self,
|
||||
to: &u64,
|
||||
value: u64,
|
||||
gas_meter: &mut GasMeter<Test>,
|
||||
) -> Result<(), DispatchError> {
|
||||
self.transfers.push(TransferEntry {
|
||||
to: *to,
|
||||
value,
|
||||
data: Vec::new(),
|
||||
gas_left: gas_meter.gas_left(),
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
fn call(
|
||||
&mut self,
|
||||
to: &u64,
|
||||
@@ -254,7 +268,7 @@ mod tests {
|
||||
self.transfers.push(TransferEntry {
|
||||
to: *to,
|
||||
value,
|
||||
data: data.to_vec(),
|
||||
data: data,
|
||||
gas_left: gas_meter.gas_left(),
|
||||
});
|
||||
// Assume for now that it was just a plain transfer.
|
||||
@@ -357,6 +371,14 @@ mod tests {
|
||||
) -> Result<(u64, ExecReturnValue), ExecError> {
|
||||
(**self).instantiate(code, value, gas_meter, input_data)
|
||||
}
|
||||
fn transfer(
|
||||
&mut self,
|
||||
to: &u64,
|
||||
value: u64,
|
||||
gas_meter: &mut GasMeter<Test>,
|
||||
) -> Result<(), DispatchError> {
|
||||
(**self).transfer(to, value, gas_meter)
|
||||
}
|
||||
fn call(
|
||||
&mut self,
|
||||
to: &u64,
|
||||
@@ -453,6 +475,59 @@ mod tests {
|
||||
}
|
||||
|
||||
const CODE_TRANSFER: &str = r#"
|
||||
(module
|
||||
;; ext_transfer(
|
||||
;; account_ptr: u32,
|
||||
;; account_len: u32,
|
||||
;; value_ptr: u32,
|
||||
;; value_len: u32,
|
||||
;;) -> u32
|
||||
(import "env" "ext_transfer" (func $ext_transfer (param i32 i32 i32 i32) (result i32)))
|
||||
(import "env" "memory" (memory 1 1))
|
||||
(func (export "call")
|
||||
(drop
|
||||
(call $ext_transfer
|
||||
(i32.const 4) ;; Pointer to "account" address.
|
||||
(i32.const 8) ;; Length of "account" address.
|
||||
(i32.const 12) ;; Pointer to the buffer with value to transfer
|
||||
(i32.const 8) ;; Length of the buffer with value to transfer.
|
||||
)
|
||||
)
|
||||
)
|
||||
(func (export "deploy"))
|
||||
|
||||
;; Destination AccountId to transfer the funds.
|
||||
;; Represented by u64 (8 bytes long) in little endian.
|
||||
(data (i32.const 4) "\07\00\00\00\00\00\00\00")
|
||||
|
||||
;; Amount of value to transfer.
|
||||
;; Represented by u64 (8 bytes long) in little endian.
|
||||
(data (i32.const 12) "\99\00\00\00\00\00\00\00")
|
||||
)
|
||||
"#;
|
||||
|
||||
#[test]
|
||||
fn contract_transfer() {
|
||||
let mut mock_ext = MockExt::default();
|
||||
let _ = execute(
|
||||
CODE_TRANSFER,
|
||||
vec![],
|
||||
&mut mock_ext,
|
||||
&mut GasMeter::with_limit(50_000, 1),
|
||||
).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
&mock_ext.transfers,
|
||||
&[TransferEntry {
|
||||
to: 7,
|
||||
value: 153,
|
||||
data: Vec::new(),
|
||||
gas_left: 49978,
|
||||
}]
|
||||
);
|
||||
}
|
||||
|
||||
const CODE_CALL: &str = r#"
|
||||
(module
|
||||
;; ext_call(
|
||||
;; callee_ptr: u32,
|
||||
@@ -492,10 +567,10 @@ mod tests {
|
||||
"#;
|
||||
|
||||
#[test]
|
||||
fn contract_transfer() {
|
||||
fn contract_call() {
|
||||
let mut mock_ext = MockExt::default();
|
||||
let _ = execute(
|
||||
CODE_TRANSFER,
|
||||
CODE_CALL,
|
||||
vec![],
|
||||
&mut mock_ext,
|
||||
&mut GasMeter::with_limit(50_000, 1),
|
||||
|
||||
@@ -405,6 +405,36 @@ define_env!(Env, <E: Ext>,
|
||||
}
|
||||
},
|
||||
|
||||
// Transfer some value to another account.
|
||||
//
|
||||
// If the value transfer was succesful zero is returned. Otherwise one is returned.
|
||||
// The scratch buffer is not touched. The receiver can be a plain account or
|
||||
// a contract.
|
||||
//
|
||||
// - account_ptr: a pointer to the address of the beneficiary account
|
||||
// Should be decodable as an `T::AccountId`. Traps otherwise.
|
||||
// - account_len: length of the address buffer.
|
||||
// - value_ptr: a pointer to the buffer with value, how much value to send.
|
||||
// Should be decodable as a `T::Balance`. Traps otherwise.
|
||||
// - value_len: length of the value buffer.
|
||||
ext_transfer(
|
||||
ctx,
|
||||
account_ptr: u32,
|
||||
account_len: u32,
|
||||
value_ptr: u32,
|
||||
value_len: u32
|
||||
) -> u32 => {
|
||||
let callee: <<E as Ext>::T as frame_system::Trait>::AccountId =
|
||||
read_sandbox_memory_as(ctx, account_ptr, account_len)?;
|
||||
let value: BalanceOf<<E as Ext>::T> =
|
||||
read_sandbox_memory_as(ctx, value_ptr, value_len)?;
|
||||
|
||||
match ctx.ext.transfer(&callee, value, ctx.gas_meter) {
|
||||
Ok(_) => Ok(0),
|
||||
Err(_) => Ok(1),
|
||||
}
|
||||
},
|
||||
|
||||
// Make a call to another contract.
|
||||
//
|
||||
// If the called contract runs to completion, then this returns the status code the callee
|
||||
|
||||
Reference in New Issue
Block a user