This repository has been archived by the owner on Nov 15, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 2.6k
pallet-contracts: Migrate to weights mechanism #4147
Closed
Closed
Changes from all commits
Commits
Show all changes
44 commits
Select commit
Hold shift + click to select a range
d409505
Initial transformation towards using weights
pepyakin 169d461
Clean
pepyakin 64ff194
Gas price calculation for weight
pepyakin 00dd58e
Doc
pepyakin c0a89b2
Eradicate gas block limit and fix the test.
pepyakin 78bb525
Set proper gas_price
pepyakin a1340cd
Bump runtime version
pepyakin 8212f50
WIP, workaround by scaling gas usage
pepyakin f3794f7
Merge origin/master
pepyakin b9b1a08
Add a screeming TODO
pepyakin 5d63cb2
Migrate to new weight system
pepyakin a3c1abc
Clean and docs
pepyakin 36c86ad
Make construct_block less of a footgun
pepyakin f32fd65
WIP
pepyakin 6e925db
Fix the test
pepyakin dbd2d3e
Fix the tests
pepyakin afc8167
Merge 'origin/master' into ser-contract-weights-2
pepyakin d3afa08
'master' of github.com:paritytech/substrate
pepyakin d9f5e8b
Fix up merge
pepyakin aa37774
Clean imports
pepyakin 8e4448c
A few cleanings
pepyakin dc85fa5
Merge branch 'master' of github.com:paritytech/substrate
pepyakin c24de55
Fix node-executor tests.
pepyakin 86c09d9
Merge remote-tracking branch 'origin' into ser-contract-weights-2
pepyakin f9db9c0
Remove unnecessary `.map_err`.
pepyakin 2110696
Fix the tests.
pepyakin 5ef036f
Clean up.
pepyakin 49d1ea5
Apply suggestions from code review
pepyakin 9b18e0e
Update the expect message and add a comment.
pepyakin 33a3ea6
Add associated issue and TODO for tx loophole
pepyakin 74dbd4a
Add some clarifying comments regarding GasUsageReport.
pepyakin a399472
Added System::remaining_weight convenience function
pepyakin d6f5cef
Merge origin/master
pepyakin 775c73c
Merge remote-tracking branch 'origin/master' into ser-contract-weights-2
pepyakin 9fd2b90
Leverage FunctionOf for setting put_code price.
pepyakin b39ca97
Remove a stale TODO
pepyakin a5c331d
Update docs.
pepyakin 81930e8
Rename CheckGasBlockLimit to GasWeightConversion.
pepyakin cc233d3
Tidy up.
pepyakin 624b3ef
Renaming
pepyakin c794526
Merge master
pepyakin 57850d0
Clean
pepyakin 4585dc1
Bump runtime version
pepyakin cc8ec73
Fix the build.
pepyakin File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,195 @@ | ||
// Copyright 2020 Parity Technologies (UK) Ltd. | ||
// This file is part of Substrate. | ||
|
||
// Substrate is free software: you can redistribute it and/or modify | ||
// it under the terms of the GNU General Public License as published by | ||
// the Free Software Foundation, either version 3 of the License, or | ||
// (at your option) any later version. | ||
|
||
// Substrate is distributed in the hope that it will be useful, | ||
// but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
// GNU General Public License for more details. | ||
|
||
// You should have received a copy of the GNU General Public License | ||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>. | ||
|
||
use crate::{Call, GasPrice, GasUsageReport, NegativeImbalanceOf, Trait}; | ||
use frame_support::{ | ||
storage::StorageValue, | ||
traits::{Currency, ExistenceRequirement, Imbalance, OnUnbalanced, WithdrawReason}, | ||
weights::{DispatchInfo, Weight}, | ||
IsSubType, | ||
}; | ||
use sp_runtime::{ | ||
traits::{CheckedDiv, Convert, SignedExtension, UniqueSaturatedInto}, | ||
transaction_validity::{ | ||
InvalidTransaction, TransactionValidity, TransactionValidityError, ValidTransaction, | ||
}, | ||
}; | ||
use sp_std::{convert::TryFrom, fmt, marker::PhantomData, prelude::*}; | ||
|
||
/// Data which is collected during the pre-dispatch phase and needed at the post_dispatch phase. | ||
#[cfg_attr(test, derive(Debug))] | ||
pub struct GasCallEnvelope<AccountId, NegativeImbalance> { | ||
/// The account id who should receive the refund from the gas leftovers. | ||
transactor: AccountId, | ||
/// The negative imbalance obtained by withdrawing the value up to the requested gas limit. | ||
imbalance: NegativeImbalance, | ||
} | ||
|
||
/// A `SignedExtension` required to properly handle gas limits requested by contract executing | ||
/// transactions. | ||
#[derive(codec::Encode, codec::Decode, Clone, Eq, PartialEq)] | ||
pub struct GasWeightConversion<T: Trait + Send + Sync>(PhantomData<T>); | ||
|
||
impl<T: Trait + Send + Sync> GasWeightConversion<T> { | ||
pub fn new() -> Self { | ||
Self(PhantomData) | ||
} | ||
|
||
/// Perform pre-dispatch checks for the given call and origin. | ||
fn perform_pre_dispatch_checks( | ||
who: &T::AccountId, | ||
call: &<T as Trait>::Call, | ||
) -> Result< | ||
Option<GasCallEnvelope<T::AccountId, NegativeImbalanceOf<T>>>, | ||
TransactionValidityError, | ||
> { | ||
// This signed extension only deals with this module's `Call`. | ||
let call = match call.is_sub_type() { | ||
Some(call) => call, | ||
None => return Ok(None), | ||
}; | ||
|
||
match *call { | ||
Call::claim_surcharge(_, _) | Call::update_schedule(_) | Call::put_code(_) => Ok(None), | ||
Call::call(_, _, gas_limit, _) | Call::instantiate(_, gas_limit, _, _) => { | ||
// Compute how much block weight this transaction can take at its limit, i.e. | ||
// if this transaction depleted all provided gas to zero. | ||
let gas_weight_limit = Weight::try_from(gas_limit) | ||
.map_err(|_| InvalidTransaction::ExhaustsResources)?; | ||
let weight_available = <frame_system::Module<T>>::remaining_weight().into(); | ||
if gas_weight_limit > weight_available { | ||
// We discard the transaction if the requested limit exceeds the available | ||
// amount of weight in the current block. | ||
// | ||
// Note that this transaction is left out only from the current block and txq | ||
// might attempt to include this transaction again. | ||
return Err(InvalidTransaction::ExhaustsResources.into()); | ||
} | ||
|
||
// Compute the fee corresponding for the given gas_weight_limit and attempt | ||
// withdrawing from the origin of this transaction. | ||
let fee = T::WeightToFee::convert(gas_weight_limit); | ||
|
||
// Compute and store the effective price per unit of gas. | ||
let gas_price = { | ||
let divisor = gas_weight_limit.unique_saturated_into(); | ||
fee.checked_div(&divisor).unwrap_or(1.into()) | ||
}; | ||
|
||
// | ||
// The place where we set GasPrice for the execution of this transaction. | ||
// | ||
<GasPrice<T>>::put(gas_price); | ||
|
||
let imbalance = match T::Currency::withdraw( | ||
who, | ||
fee, | ||
WithdrawReason::TransactionPayment.into(), | ||
ExistenceRequirement::KeepAlive, | ||
) { | ||
Ok(imbalance) => imbalance, | ||
Err(_) => return Err(InvalidTransaction::Payment.into()), | ||
}; | ||
|
||
Ok(Some(GasCallEnvelope { | ||
transactor: who.clone(), | ||
imbalance, | ||
})) | ||
} | ||
Call::__PhantomItem(_, _) => unreachable!("Variant is never constructed"), | ||
} | ||
} | ||
} | ||
|
||
impl<T: Trait + Send + Sync> Default for GasWeightConversion<T> { | ||
fn default() -> Self { | ||
Self(PhantomData) | ||
} | ||
} | ||
|
||
impl<T: Trait + Send + Sync> fmt::Debug for GasWeightConversion<T> { | ||
#[cfg(feature = "std")] | ||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||
write!(f, "GasWeightConversion") | ||
} | ||
|
||
#[cfg(not(feature = "std"))] | ||
fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result { | ||
Ok(()) | ||
} | ||
} | ||
|
||
impl<T: Trait + Send + Sync> SignedExtension for GasWeightConversion<T> { | ||
const IDENTIFIER: &'static str = "GasWeightConversion"; | ||
type AccountId = T::AccountId; | ||
type Call = <T as Trait>::Call; | ||
type AdditionalSigned = (); | ||
type DispatchInfo = DispatchInfo; | ||
/// Optional pre-dispatch check data. | ||
/// | ||
/// It is present only for the contract calls that operate with gas. | ||
type Pre = Option<GasCallEnvelope<T::AccountId, NegativeImbalanceOf<T>>>; | ||
|
||
fn additional_signed(&self) -> Result<(), TransactionValidityError> { | ||
Ok(()) | ||
} | ||
|
||
fn pre_dispatch( | ||
self, | ||
who: &Self::AccountId, | ||
call: &Self::Call, | ||
_: DispatchInfo, | ||
_: usize, | ||
) -> Result<Self::Pre, TransactionValidityError> { | ||
Self::perform_pre_dispatch_checks(who, call) | ||
} | ||
|
||
fn validate( | ||
&self, | ||
who: &Self::AccountId, | ||
call: &Self::Call, | ||
_: Self::DispatchInfo, | ||
_: usize, | ||
) -> TransactionValidity { | ||
Self::perform_pre_dispatch_checks(who, call).map(|_| ValidTransaction::default()) | ||
} | ||
|
||
fn post_dispatch(pre: Self::Pre, _info: DispatchInfo, _len: usize) { | ||
if let Some(GasCallEnvelope { | ||
transactor, | ||
imbalance, | ||
}) = pre | ||
{ | ||
// Take the report of consumed and left gas after the execution of the current | ||
// transaction. | ||
let (gas_left, gas_spent) = GasUsageReport::take(); | ||
|
||
// These should be OK since we don't buy more | ||
let unused_weight = gas_left as Weight; | ||
let spent_weight = gas_spent as Weight; | ||
|
||
let refund = T::WeightToFee::convert(unused_weight); | ||
|
||
// Refund the unused gas. | ||
let refund_imbalance = T::Currency::deposit_creating(&transactor, refund); | ||
if let Ok(imbalance) = imbalance.offset(refund_imbalance) { | ||
T::GasPayment::on_unbalanced(imbalance); | ||
} | ||
|
||
<frame_system::Module<T>>::register_extra_weight_unchecked(spent_weight); | ||
} | ||
} | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You call this from
SignedExtension::validate
andSignedExtension::pre_dispatch
? Aren't you charging thewho
double by doing this?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Whatever we do in
validate()
is not persistent. The state is dropped thereafter.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But changes in
pre_dispatch
are persisted, right?