mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-13 05:51:02 +00:00
Custom runtime module errors (#3433)
* srml-system checks * wip * more modules compiles * node-runtime checks * build.sh passes * include dispatch error in failed event * revert some unnecessary changes * refactor based on comments * more compile error fixes * avoid unnecessary into * reorder code * fixes some tests * manually implement encode & decode to avoid i8 workaround * more test fixes * more fixes * more error fixes * Apply suggestions from code review Co-Authored-By: Tomasz Drwięga <tomusdrw@users.noreply.github.com> * address comments * test for DispatchError encoding * tyep alias for democracy * make error printable * line width * fix balances tests * fix executive test * fix system tests * bump version * ensure consistent method signature * Apply suggestions from code review Co-Authored-By: Gavin Wood <github@gavwood.com> * changes based on review * Add issue number for TODOs * fix * line width * fix test * Update core/sr-primitives/src/lib.rs Co-Authored-By: Bastian Köcher <bkchr@users.noreply.github.com> * Update core/sr-primitives/src/traits.rs Co-Authored-By: Bastian Köcher <bkchr@users.noreply.github.com> * Update srml/council/src/motions.rs Co-Authored-By: Bastian Köcher <bkchr@users.noreply.github.com> * Update srml/council/src/motions.rs Co-Authored-By: Bastian Köcher <bkchr@users.noreply.github.com> * update based on review * More concrete macro matching * fix test build issue * Update hex-literal dependency version. (#3141) * Update hex-literal dep version. * Update lock file. * Start to rework the new error handling * More work to get it back compiling * Start to fix after master merge * The great transaction error handling refactoring * Make `decl_error` errors convertible to `&'static str` * Make srml-executive build again * Fix `sr-primitives` tests * More fixes * Last round of fix ups * Fix build * Fix build * Apply suggestions from code review Co-Authored-By: Tomasz Drwięga <tomusdrw@users.noreply.github.com> * Rename some stuff * Fixes after master merge * Adds `CheckBlockGasLimit` signed extension * Remove debug stuff * Fix srml-balances test * Rename `InvalidIndex` to `CannotLookup` * Remove weird generic parameters * Rename function again * Fix import * Document the signed extension * Change from `Into` to `From` * Update srml/contracts/src/lib.rs Co-Authored-By: Sergei Pepyakin <sergei@parity.io> * Fix compilation * Update srml/contracts/src/lib.rs Co-Authored-By: Tomasz Drwięga <tomusdrw@users.noreply.github.com> * Update core/sr-primitives/src/transaction_validity.rs Co-Authored-By: Tomasz Drwięga <tomusdrw@users.noreply.github.com> * Remove unused code * Fix compilation * Some cleanups * Fix compile errors * Make `TransactionValidity` a `Result` * Apply suggestions from code review Co-Authored-By: Gavin Wood <gavin@parity.io> * Beautify the code a little bit and fix test * Make `CannotLookup` an inherent error declared by `decl_error!` * Adds some documentation * Make `ApplyOutcome` a result * Up the spec_version * Apply suggestions from code review Co-Authored-By: Gavin Wood <gavin@parity.io> Co-Authored-By: DemiMarie-parity <48690212+DemiMarie-parity@users.noreply.github.com>
This commit is contained in:
@@ -16,10 +16,12 @@
|
||||
|
||||
use crate::{GasSpent, Module, Trait, BalanceOf, NegativeImbalanceOf};
|
||||
use rstd::convert::TryFrom;
|
||||
use sr_primitives::BLOCK_FULL;
|
||||
use sr_primitives::traits::{CheckedMul, Zero, SaturatedConversion, SimpleArithmetic, UniqueSaturatedInto};
|
||||
use support::StorageValue;
|
||||
use support::traits::{Currency, ExistenceRequirement, Get, Imbalance, OnUnbalanced, WithdrawReason};
|
||||
use sr_primitives::traits::{
|
||||
CheckedMul, Zero, SaturatedConversion, SimpleArithmetic, UniqueSaturatedInto,
|
||||
};
|
||||
use support::{
|
||||
traits::{Currency, ExistenceRequirement, Imbalance, OnUnbalanced, WithdrawReason}, StorageValue,
|
||||
};
|
||||
|
||||
#[cfg(test)]
|
||||
use std::{any::Any, fmt::Debug};
|
||||
@@ -200,14 +202,6 @@ pub fn buy_gas<T: Trait>(
|
||||
transactor: &T::AccountId,
|
||||
gas_limit: Gas,
|
||||
) -> Result<(GasMeter<T>, NegativeImbalanceOf<T>), &'static str> {
|
||||
// Check if the specified amount of gas is available in the current block.
|
||||
// This cannot underflow since `gas_spent` is never greater than `T::BlockGasLimit`.
|
||||
let gas_available = T::BlockGasLimit::get() - <Module<T>>::gas_spent();
|
||||
if gas_limit > gas_available {
|
||||
// gas limit reached, revert the transaction and retry again in the future
|
||||
return Err(BLOCK_FULL);
|
||||
}
|
||||
|
||||
// Buy the specified amount of gas.
|
||||
let gas_price = <Module<T>>::gas_price();
|
||||
let cost = if gas_price.is_zero() {
|
||||
|
||||
@@ -63,6 +63,15 @@
|
||||
//! This creates a new smart contract account and calls its contract deploy handler to initialize the contract.
|
||||
//! * `call` - Makes a call to an account, optionally transferring some balance.
|
||||
//!
|
||||
//! ### Signed Extensions
|
||||
//!
|
||||
//! The contracts module defines the following extension:
|
||||
//!
|
||||
//! - [`CheckBlockGasLimit`]: Ensures that the transaction does not exceeds the block gas limit.
|
||||
//!
|
||||
//! The signed extension needs to be added as signed extra to the transaction type to be used in the
|
||||
//! runtime.
|
||||
//!
|
||||
//! ## Usage
|
||||
//!
|
||||
//! The Contract module is a work in progress. The following examples show how this Contract module can be
|
||||
@@ -100,15 +109,19 @@ use primitives::crypto::UncheckedFrom;
|
||||
use rstd::{prelude::*, marker::PhantomData};
|
||||
use codec::{Codec, Encode, Decode};
|
||||
use runtime_io::blake2_256;
|
||||
use sr_primitives::traits::{
|
||||
Hash, StaticLookup, Zero, MaybeSerializeDebug, Member
|
||||
use sr_primitives::{
|
||||
traits::{Hash, StaticLookup, Zero, MaybeSerializeDebug, Member, SignedExtension},
|
||||
weights::DispatchInfo,
|
||||
transaction_validity::{
|
||||
ValidTransaction, InvalidTransaction, TransactionValidity, TransactionValidityError,
|
||||
},
|
||||
};
|
||||
use support::dispatch::{Result, Dispatchable};
|
||||
use support::{
|
||||
Parameter, StorageMap, StorageValue, decl_module, decl_event, decl_storage, storage::child,
|
||||
parameter_types,
|
||||
};
|
||||
use support::traits::{OnFreeBalanceZero, OnUnbalanced, Currency, Get};
|
||||
use support::{traits::{OnFreeBalanceZero, OnUnbalanced, Currency, Get}, IsSubType};
|
||||
use system::{ensure_signed, RawOrigin, ensure_root};
|
||||
use primitives::storage::well_known_keys::CHILD_STORAGE_KEY_PREFIX;
|
||||
use timestamp;
|
||||
@@ -321,7 +334,7 @@ pub trait Trait: timestamp::Trait {
|
||||
type Currency: Currency<Self::AccountId>;
|
||||
|
||||
/// The outer call dispatch type.
|
||||
type Call: Parameter + Dispatchable<Origin=<Self as system::Trait>::Origin>;
|
||||
type Call: Parameter + Dispatchable<Origin=<Self as system::Trait>::Origin> + IsSubType<Module<Self>, Self>;
|
||||
|
||||
/// The overarching event type.
|
||||
type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;
|
||||
@@ -597,15 +610,17 @@ decl_module! {
|
||||
/// the sender is not eligible for the reward.
|
||||
fn claim_surcharge(origin, dest: T::AccountId, aux_sender: Option<T::AccountId>) {
|
||||
let origin = origin.into();
|
||||
let (signed, rewarded) = match origin {
|
||||
Ok(system::RawOrigin::Signed(ref account)) if aux_sender.is_none() => {
|
||||
let (signed, rewarded) = match (origin, aux_sender) {
|
||||
(Ok(system::RawOrigin::Signed(account)), None) => {
|
||||
(true, account)
|
||||
},
|
||||
Ok(system::RawOrigin::None) if aux_sender.is_some() => {
|
||||
(false, aux_sender.as_ref().expect("checked above"))
|
||||
(Ok(system::RawOrigin::None), Some(aux_sender)) => {
|
||||
(false, aux_sender)
|
||||
},
|
||||
_ => return Err("Invalid surcharge claim: origin must be signed or \
|
||||
inherent and auxiliary sender only provided on inherent")
|
||||
_ => return Err(
|
||||
"Invalid surcharge claim: origin must be signed or \
|
||||
inherent and auxiliary sender only provided on inherent"
|
||||
),
|
||||
};
|
||||
|
||||
// Add some advantage for block producers (who send unsigned extrinsics) by
|
||||
@@ -619,7 +634,7 @@ decl_module! {
|
||||
|
||||
// If poking the contract has lead to eviction of the contract, give out the rewards.
|
||||
if rent::try_evict::<T>(&dest, handicap) == rent::RentOutcome::Evicted {
|
||||
T::Currency::deposit_into_existing(rewarded, T::SurchargeReward::get())?;
|
||||
T::Currency::deposit_into_existing(&rewarded, T::SurchargeReward::get())?;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -937,3 +952,62 @@ impl Default for Schedule {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// `SignedExtension` that checks if a transaction would exhausts the block gas limit.
|
||||
#[derive(Encode, Decode, Clone, Eq, PartialEq)]
|
||||
pub struct CheckBlockGasLimit<T: Trait + Send + Sync>(PhantomData<T>);
|
||||
|
||||
impl<T: Trait + Send + Sync> Default for CheckBlockGasLimit<T> {
|
||||
fn default() -> Self {
|
||||
Self(PhantomData)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl<T: Trait + Send + Sync> std::fmt::Debug for CheckBlockGasLimit<T> {
|
||||
fn fmt(&self, _: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Trait + Send + Sync> SignedExtension for CheckBlockGasLimit<T> {
|
||||
type AccountId = T::AccountId;
|
||||
type Call = <T as Trait>::Call;
|
||||
type AdditionalSigned = ();
|
||||
type Pre = ();
|
||||
|
||||
fn additional_signed(&self) -> rstd::result::Result<(), TransactionValidityError> { Ok(()) }
|
||||
|
||||
fn validate(
|
||||
&self,
|
||||
_: &Self::AccountId,
|
||||
call: &Self::Call,
|
||||
_: DispatchInfo,
|
||||
_: usize,
|
||||
) -> TransactionValidity {
|
||||
let call = match call.is_sub_type() {
|
||||
Some(call) => call,
|
||||
None => return Ok(ValidTransaction::default()),
|
||||
};
|
||||
|
||||
match call {
|
||||
Call::claim_surcharge(_, _) | Call::update_schedule(_) =>
|
||||
Ok(ValidTransaction::default()),
|
||||
Call::put_code(gas_limit, _)
|
||||
| Call::call(_, _, gas_limit, _)
|
||||
| Call::create(_, gas_limit, _, _)
|
||||
=> {
|
||||
// Check if the specified amount of gas is available in the current block.
|
||||
// This cannot underflow since `gas_spent` is never greater than `T::BlockGasLimit`.
|
||||
let gas_available = T::BlockGasLimit::get() - <Module<T>>::gas_spent();
|
||||
if *gas_limit > gas_available {
|
||||
// gas limit reached, revert the transaction and retry again in the future
|
||||
InvalidTransaction::ExhaustsResources.into()
|
||||
} else {
|
||||
Ok(ValidTransaction::default())
|
||||
}
|
||||
},
|
||||
Call::__PhantomItem(_, _) => unreachable!("Variant is never constructed"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,27 +22,27 @@
|
||||
use crate::account_db::{AccountDb, DirectAccountDb, OverlayAccountDb};
|
||||
use crate::{
|
||||
BalanceOf, ComputeDispatchFee, ContractAddressFor, ContractInfo, ContractInfoOf, GenesisConfig,
|
||||
Module, RawAliveContractInfo, RawEvent, Trait, TrieId, TrieIdFromParentCounter,
|
||||
TrieIdGenerator, Schedule,
|
||||
Module, RawAliveContractInfo, RawEvent, Trait, TrieId, TrieIdFromParentCounter, Schedule,
|
||||
TrieIdGenerator, CheckBlockGasLimit,
|
||||
};
|
||||
use assert_matches::assert_matches;
|
||||
use hex_literal::*;
|
||||
use codec::{Decode, Encode, KeyedVec};
|
||||
use runtime_io;
|
||||
use runtime_io::with_externalities;
|
||||
use sr_primitives::testing::{Digest, DigestItem, Header, UintAuthorityId, H256};
|
||||
use sr_primitives::traits::{BlakeTwo256, Hash, IdentityLookup};
|
||||
use sr_primitives::{Perbill, BuildStorage};
|
||||
use sr_primitives::{
|
||||
Perbill, BuildStorage, transaction_validity::{InvalidTransaction, ValidTransaction},
|
||||
traits::{BlakeTwo256, Hash, IdentityLookup, SignedExtension},
|
||||
weights::{DispatchInfo, DispatchClass},
|
||||
testing::{Digest, DigestItem, Header, UintAuthorityId, H256},
|
||||
};
|
||||
use support::{
|
||||
assert_ok, assert_err, impl_outer_dispatch, impl_outer_event, impl_outer_origin, parameter_types,
|
||||
storage::child, StorageMap, StorageValue, traits::{Currency, Get},
|
||||
};
|
||||
use std::cell::RefCell;
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
use primitives::storage::well_known_keys;
|
||||
use primitives::Blake2Hasher;
|
||||
use std::{cell::RefCell, sync::atomic::{AtomicUsize, Ordering}};
|
||||
use primitives::{storage::well_known_keys, Blake2Hasher};
|
||||
use system::{self, EventRecord, Phase};
|
||||
use {balances, wabt};
|
||||
|
||||
mod contract {
|
||||
// Re-export contents of the root. This basically
|
||||
@@ -2390,3 +2390,22 @@ fn cannot_self_destruct_in_constructor() {
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_block_gas_limit_works() {
|
||||
with_externalities(
|
||||
&mut ExtBuilder::default().block_gas_limit(50).build(),
|
||||
|| {
|
||||
let info = DispatchInfo { weight: 100, class: DispatchClass::Normal };
|
||||
let check = CheckBlockGasLimit::<Test>(Default::default());
|
||||
let call: Call = crate::Call::put_code(1000, vec![]).into();
|
||||
|
||||
assert_eq!(
|
||||
check.validate(&0, &call, info, 0), InvalidTransaction::ExhaustsResources.into(),
|
||||
);
|
||||
|
||||
let call: Call = crate::Call::update_schedule(Default::default()).into();
|
||||
assert_eq!(check.validate(&0, &call, info, 0), Ok(Default::default()));
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user