contracts: Allow runtime authors to define a chain extension (#7548)

* Make host functions return TrapReason

This avoids the need to manually store any trap reasons
to the `Runtime` from the host function. This adds the following
benefits:

* It properly composes with the upcoming chain extensions
* Missing to set a trap value is now a compile error

* Add chain extension

The chain extension is a way for the contract author to add new
host functions for contracts to call.

* Add tests for chain extensions

* Fix regression in set_rent.wat fixture

Not all offsets where properly updated when changing the fixtures
for the new salt on instantiate.

* Pre-charge a weight amount based off the specified length

* Improve fn write docs

* Renamed state to phantom

* Fix typo
This commit is contained in:
Alexander Theißen
2021-01-04 12:15:17 +01:00
committed by GitHub
parent e3e651f72c
commit 51c37ecc15
10 changed files with 768 additions and 37 deletions
+19 -9
View File
@@ -32,7 +32,7 @@ pub type Gas = frame_support::weights::Weight;
#[must_use]
#[derive(Debug, PartialEq, Eq)]
pub enum GasMeterResult {
Proceed,
Proceed(ChargedAmount),
OutOfGas,
}
@@ -40,11 +40,20 @@ impl GasMeterResult {
pub fn is_out_of_gas(&self) -> bool {
match *self {
GasMeterResult::OutOfGas => true,
GasMeterResult::Proceed => false,
GasMeterResult::Proceed(_) => false,
}
}
}
#[derive(Debug, PartialEq, Eq)]
pub struct ChargedAmount(Gas);
impl ChargedAmount {
pub fn amount(&self) -> Gas {
self.0
}
}
#[cfg(not(test))]
pub trait TestAuxiliaries {}
#[cfg(not(test))]
@@ -139,17 +148,18 @@ impl<T: Config> GasMeter<T> {
self.gas_left = new_value.unwrap_or_else(Zero::zero);
match new_value {
Some(_) => GasMeterResult::Proceed,
Some(_) => GasMeterResult::Proceed(ChargedAmount(amount)),
None => GasMeterResult::OutOfGas,
}
}
// Account for not fully used gas.
//
// This can be used after dispatching a runtime call to refund gas that was not
// used by the dispatchable.
pub fn refund(&mut self, gas: Gas) {
self.gas_left = self.gas_left.saturating_add(gas).max(self.gas_limit);
/// Refund previously charged gas back to the gas meter.
///
/// This can be used if a gas worst case estimation must be charged before
/// performing a certain action. This way the difference can be refundend when
/// the worst case did not happen.
pub fn refund(&mut self, amount: ChargedAmount) {
self.gas_left = self.gas_left.saturating_add(amount.0).min(self.gas_limit)
}
/// Allocate some amount of gas and perform some work with