mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-04-26 20:27:58 +00:00
seal_delegate_call api function (support for library contracts) (#10617)
* seal_call_code implementation - tests - benchmark * Addressing @xgreenx's comments * Fix test-linux-stable-int * Rename seal_call_code to seal_delegate_call * Pass value unchanged into lib contract * Address @athei's comments - whitespace .wat issues - wrong/missing .wat comments - redundant .wat calls/declarations - change order of functions (seal_delegate_call right after seal_call) in decls, tests, benchmark - fix comments, move doc comments to enum variants - remove unnecessary empty lines - rename runtime cost DelegateCall to DelegateCallBase - do not set CallFlags::ALLOW_REENTRY for delegate_call * Do not pass CallFlags::ALLOWS_REENTRY for delegate_call * Update comment for seal_delegate_call and CallFlags * Addressing @athei's comments (minor) * Allow reentry for a new frame after delegate_call (revert) * Same seal_caller and seal_value_transferred for lib contract - test - refactor frame args due to review - logic for seal_caller (please review) * Put caller on frame for delegate_call, minor fixes * Update comment for delegate_call * Addressing @athei's comments * Update weights generated by benchmark * Improve comments * Address @HCastano's comments * Update weights, thanks @joao-paulo-parity * Improve InvalidCallFlags error comment
This commit is contained in:
committed by
GitHub
parent
1d62516fad
commit
d14e1c641e
@@ -104,6 +104,19 @@ pub trait Ext: sealing::Sealed {
|
||||
allows_reentry: bool,
|
||||
) -> Result<ExecReturnValue, ExecError>;
|
||||
|
||||
/// Execute code in the current frame.
|
||||
///
|
||||
/// Returns the original code size of the called contract.
|
||||
///
|
||||
/// # Return Value
|
||||
///
|
||||
/// Result<ExecReturnValue, ExecError>
|
||||
fn delegate_call(
|
||||
&mut self,
|
||||
code: CodeHash<Self::T>,
|
||||
input_data: Vec<u8>,
|
||||
) -> Result<ExecReturnValue, ExecError>;
|
||||
|
||||
/// Instantiate a contract from the given code.
|
||||
///
|
||||
/// Returns the original code size of the called contract.
|
||||
@@ -347,6 +360,16 @@ pub struct Frame<T: Config> {
|
||||
nested_storage: storage::meter::NestedMeter<T>,
|
||||
/// If `false` the contract enabled its defense against reentrance attacks.
|
||||
allows_reentry: bool,
|
||||
/// The caller of the currently executing frame which was spawned by `delegate_call`.
|
||||
delegate_caller: Option<T::AccountId>,
|
||||
}
|
||||
|
||||
/// Used in a delegate call frame arguments in order to override the executable and caller.
|
||||
struct DelegatedCall<T: Config, E> {
|
||||
/// The executable which is run instead of the contracts own `executable`.
|
||||
executable: E,
|
||||
/// The account id of the caller contract.
|
||||
caller: T::AccountId,
|
||||
}
|
||||
|
||||
/// Parameter passed in when creating a new `Frame`.
|
||||
@@ -358,6 +381,10 @@ enum FrameArgs<'a, T: Config, E> {
|
||||
dest: T::AccountId,
|
||||
/// If `None` the contract info needs to be reloaded from storage.
|
||||
cached_info: Option<ContractInfo<T>>,
|
||||
/// This frame was created by `seal_delegate_call` and hence uses different code than
|
||||
/// what is stored at [`Self::dest`]. Its caller ([`Frame::delegated_caller`]) is the
|
||||
/// account which called the caller contract
|
||||
delegated_call: Option<DelegatedCall<T, E>>,
|
||||
},
|
||||
Instantiate {
|
||||
/// The contract or signed origin which instantiates the new contract.
|
||||
@@ -513,7 +540,7 @@ where
|
||||
debug_message: Option<&'a mut Vec<u8>>,
|
||||
) -> Result<ExecReturnValue, ExecError> {
|
||||
let (mut stack, executable) = Self::new(
|
||||
FrameArgs::Call { dest, cached_info: None },
|
||||
FrameArgs::Call { dest, cached_info: None, delegated_call: None },
|
||||
origin,
|
||||
gas_meter,
|
||||
storage_meter,
|
||||
@@ -604,33 +631,46 @@ where
|
||||
gas_limit: Weight,
|
||||
schedule: &Schedule<T>,
|
||||
) -> Result<(Frame<T>, E, Option<u64>), ExecError> {
|
||||
let (account_id, contract_info, executable, entry_point, account_counter) = match frame_args
|
||||
{
|
||||
FrameArgs::Call { dest, cached_info } => {
|
||||
let contract = if let Some(contract) = cached_info {
|
||||
contract
|
||||
} else {
|
||||
<ContractInfoOf<T>>::get(&dest).ok_or(<Error<T>>::ContractNotFound)?
|
||||
};
|
||||
let (account_id, contract_info, executable, delegate_caller, entry_point, account_counter) =
|
||||
match frame_args {
|
||||
FrameArgs::Call { dest, cached_info, delegated_call } => {
|
||||
let contract = if let Some(contract) = cached_info {
|
||||
contract
|
||||
} else {
|
||||
<ContractInfoOf<T>>::get(&dest).ok_or(<Error<T>>::ContractNotFound)?
|
||||
};
|
||||
|
||||
let executable = E::from_storage(contract.code_hash, schedule, gas_meter)?;
|
||||
let (executable, delegate_caller) =
|
||||
if let Some(DelegatedCall { executable, caller }) = delegated_call {
|
||||
(executable, Some(caller))
|
||||
} else {
|
||||
(E::from_storage(contract.code_hash, schedule, gas_meter)?, None)
|
||||
};
|
||||
|
||||
(dest, contract, executable, ExportedFunction::Call, None)
|
||||
},
|
||||
FrameArgs::Instantiate { sender, trie_seed, executable, salt } => {
|
||||
let account_id =
|
||||
<Contracts<T>>::contract_address(&sender, executable.code_hash(), &salt);
|
||||
let trie_id = Storage::<T>::generate_trie_id(&account_id, trie_seed);
|
||||
let contract = Storage::<T>::new_contract(
|
||||
&account_id,
|
||||
trie_id,
|
||||
executable.code_hash().clone(),
|
||||
)?;
|
||||
(account_id, contract, executable, ExportedFunction::Constructor, Some(trie_seed))
|
||||
},
|
||||
};
|
||||
(dest, contract, executable, delegate_caller, ExportedFunction::Call, None)
|
||||
},
|
||||
FrameArgs::Instantiate { sender, trie_seed, executable, salt } => {
|
||||
let account_id =
|
||||
<Contracts<T>>::contract_address(&sender, executable.code_hash(), &salt);
|
||||
let trie_id = Storage::<T>::generate_trie_id(&account_id, trie_seed);
|
||||
let contract = Storage::<T>::new_contract(
|
||||
&account_id,
|
||||
trie_id,
|
||||
executable.code_hash().clone(),
|
||||
)?;
|
||||
(
|
||||
account_id,
|
||||
contract,
|
||||
executable,
|
||||
None,
|
||||
ExportedFunction::Constructor,
|
||||
Some(trie_seed),
|
||||
)
|
||||
},
|
||||
};
|
||||
|
||||
let frame = Frame {
|
||||
delegate_caller,
|
||||
value_transferred,
|
||||
contract_info: CachedContract::Cached(contract_info),
|
||||
account_id,
|
||||
@@ -936,8 +976,11 @@ where
|
||||
CachedContract::Cached(contract) => Some(contract.clone()),
|
||||
_ => None,
|
||||
});
|
||||
let executable =
|
||||
self.push_frame(FrameArgs::Call { dest: to, cached_info }, value, gas_limit)?;
|
||||
let executable = self.push_frame(
|
||||
FrameArgs::Call { dest: to, cached_info, delegated_call: None },
|
||||
value,
|
||||
gas_limit,
|
||||
)?;
|
||||
self.run(executable, input_data)
|
||||
};
|
||||
|
||||
@@ -950,6 +993,28 @@ where
|
||||
result
|
||||
}
|
||||
|
||||
fn delegate_call(
|
||||
&mut self,
|
||||
code_hash: CodeHash<Self::T>,
|
||||
input_data: Vec<u8>,
|
||||
) -> Result<ExecReturnValue, ExecError> {
|
||||
let executable = E::from_storage(code_hash, &self.schedule, self.gas_meter())?;
|
||||
let top_frame = self.top_frame_mut();
|
||||
let contract_info = top_frame.contract_info().clone();
|
||||
let account_id = top_frame.account_id.clone();
|
||||
let value = top_frame.value_transferred.clone();
|
||||
let executable = self.push_frame(
|
||||
FrameArgs::Call {
|
||||
dest: account_id,
|
||||
cached_info: Some(contract_info),
|
||||
delegated_call: Some(DelegatedCall { executable, caller: self.caller().clone() }),
|
||||
},
|
||||
value,
|
||||
0,
|
||||
)?;
|
||||
self.run(executable, input_data)
|
||||
}
|
||||
|
||||
fn instantiate(
|
||||
&mut self,
|
||||
gas_limit: Weight,
|
||||
@@ -1030,7 +1095,11 @@ where
|
||||
}
|
||||
|
||||
fn caller(&self) -> &T::AccountId {
|
||||
self.frames().nth(1).map(|f| &f.account_id).unwrap_or(&self.origin)
|
||||
if let Some(caller) = &self.top_frame().delegate_caller {
|
||||
&caller
|
||||
} else {
|
||||
self.frames().nth(1).map(|f| &f.account_id).unwrap_or(&self.origin)
|
||||
}
|
||||
}
|
||||
|
||||
fn is_contract(&self, address: &T::AccountId) -> bool {
|
||||
|
||||
Reference in New Issue
Block a user