Enable mocking contracts (#1331)

# Description
This PR introduces two changes:
- the previous `Tracing` trait has been modified to accept contract
address instead of code hash (seems to be way more convenient)
- a new trait `CallInterceptor` that allows intercepting contract calls;
in particular the default implementation for `()` will just proceed in a
standard way (after compilation optimizations, there will be no
footprint of that); however, implementing type might decide to mock
invocation and return `ExecResult` instead

Note: one might try merging `before_call` and `intercept_call`. However,
IMHO this would be bad, since it would mix two completely different
abstractions - tracing without any effects and actual intervention into
execution process.

This will unblock working on mocking contracts utility in drink and
similar tools (https://github.com/Cardinal-Cryptography/drink/issues/33)

# Checklist

- [x] My PR includes a detailed description as outlined in the
"Description" section above
- [ ] My PR follows the [labeling
requirements](https://github.com/paritytech/polkadot-sdk/blob/master/docs/CONTRIBUTING.md#process)
of this project (at minimum one label for `T` required)
- [x] I have made corresponding changes to the documentation (if
applicable)
- [x] I have added tests that prove my fix is effective or that my
feature works (if applicable)
This commit is contained in:
Piotr Mikołajczyk
2023-09-29 15:39:13 +02:00
committed by GitHub
parent ef3adf9a01
commit d8d90a82a7
3 changed files with 148 additions and 40 deletions
+10 -7
View File
@@ -16,7 +16,7 @@
// limitations under the License.
use crate::{
debug::{CallSpan, Tracing},
debug::{CallInterceptor, CallSpan, Tracing},
gas::GasMeter,
storage::{self, meter::Diff, WriteOutcome},
BalanceOf, CodeHash, CodeInfo, CodeInfoOf, Config, ContractInfo, ContractInfoOf,
@@ -908,13 +908,16 @@ where
// Every non delegate call or instantiate also optionally transfers the balance.
self.initial_transfer()?;
let call_span =
T::Debug::new_call_span(executable.code_hash(), entry_point, &input_data);
let contract_address = &top_frame!(self).account_id;
// Call into the Wasm blob.
let output = executable
.execute(self, &entry_point, input_data)
.map_err(|e| ExecError { error: e.error, origin: ErrorOrigin::Callee })?;
let call_span = T::Debug::new_call_span(contract_address, entry_point, &input_data);
let output = T::Debug::intercept_call(contract_address, &entry_point, &input_data)
.unwrap_or_else(|| {
executable
.execute(self, &entry_point, input_data)
.map_err(|e| ExecError { error: e.error, origin: ErrorOrigin::Callee })
})?;
call_span.after_call(&output);