From 59bbbe847873b6689528f19a1ccb85e60a4313cf Mon Sep 17 00:00:00 2001 From: Nazar Mokrynskyi Date: Sat, 11 Dec 2021 15:21:30 +0200 Subject: [PATCH 1/8] Add `pallet-transaction-fees` and move logic for computing byte fee into it --- Cargo.lock | 11 ++ crates/pallet-subspace/src/lib.rs | 5 - crates/pallet-subspace/src/mock.rs | 1 - crates/pallet-transaction-fees/Cargo.toml | 34 ++++++ crates/pallet-transaction-fees/README.md | 5 + crates/pallet-transaction-fees/src/lib.rs | 107 ++++++++++++++++++ crates/subspace-runtime-primitives/src/lib.rs | 6 +- crates/subspace-runtime/Cargo.toml | 2 + crates/subspace-runtime/src/lib.rs | 63 ++++++++--- substrate/substrate-test-runtime/src/lib.rs | 1 - 10 files changed, 207 insertions(+), 28 deletions(-) create mode 100644 crates/pallet-transaction-fees/Cargo.toml create mode 100644 crates/pallet-transaction-fees/README.md create mode 100644 crates/pallet-transaction-fees/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 8155e842ac656..2f9b3e793397c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4456,6 +4456,16 @@ dependencies = [ "sp-timestamp", ] +[[package]] +name = "pallet-transaction-fees" +version = "0.1.0" +dependencies = [ + "frame-support", + "frame-system", + "parity-scale-codec", + "scale-info", +] + [[package]] name = "pallet-transaction-payment" version = "4.0.0-dev" @@ -7990,6 +8000,7 @@ dependencies = [ "pallet-subspace", "pallet-sudo", "pallet-timestamp", + "pallet-transaction-fees", "pallet-transaction-payment", "pallet-transaction-payment-rpc-runtime-api", "pallet-utility", diff --git a/crates/pallet-subspace/src/lib.rs b/crates/pallet-subspace/src/lib.rs index 25026161fdb9f..ca5a8dacdf03b 100644 --- a/crates/pallet-subspace/src/lib.rs +++ b/crates/pallet-subspace/src/lib.rs @@ -200,11 +200,6 @@ mod pallet { #[pallet::constant] type RecordedHistorySegmentSize: Get; - /// Replication factor, defines minimum desired number of replicas of the blockchain to be - /// stored by the network. - #[pallet::constant] - type ReplicationFactor: Get; - /// Subspace requires some logic to be triggered on every block to query for whether an epoch /// has ended and to perform the transition to the next epoch. /// diff --git a/crates/pallet-subspace/src/mock.rs b/crates/pallet-subspace/src/mock.rs index e83f5d5d21072..444ebcc614165 100644 --- a/crates/pallet-subspace/src/mock.rs +++ b/crates/pallet-subspace/src/mock.rs @@ -165,7 +165,6 @@ impl Config for Test { type ConfirmationDepthK = ConfirmationDepthK; type RecordSize = RecordSize; type RecordedHistorySegmentSize = RecordedHistorySegmentSize; - type ReplicationFactor = ReplicationFactor; type EpochChangeTrigger = NormalEpochChange; type EraChangeTrigger = NormalEraChange; type EonChangeTrigger = NormalEonChange; diff --git a/crates/pallet-transaction-fees/Cargo.toml b/crates/pallet-transaction-fees/Cargo.toml new file mode 100644 index 0000000000000..e2003bd19a6f6 --- /dev/null +++ b/crates/pallet-transaction-fees/Cargo.toml @@ -0,0 +1,34 @@ +[package] +name = "pallet-transaction-fees" +version = "0.1.0" +authors = ["Nazar Mokrynskyi "] +edition = "2021" +license = "Apache-2.0" +homepage = "https://subspace.network" +repository = "https://github.com/subspace/subspace" +description = "Pallet for charging and re-distributing transaction fees" +readme = "README.md" +include = [ + "/src", + "/Cargo.toml", + "/README.md", +] + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[dependencies] +codec = { package = "parity-scale-codec", version = "2.3.0", default-features = false, features = ["derive"] } +frame-support = { version = "4.0.0-dev", default-features = false, git = "https://github.com/paritytech/substrate", rev = "b6c1c1bcfa5d831bfd1f278064d7af757f9b38f5" } +frame-system = { version = "4.0.0-dev", default-features = false, git = "https://github.com/paritytech/substrate", rev = "b6c1c1bcfa5d831bfd1f278064d7af757f9b38f5" } +scale-info = { version = "1.0", default-features = false, features = ["derive"] } + +[features] +default = ["std"] +std = [ + "codec/std", + "frame-support/std", + "frame-system/std", + "scale-info/std", +] +try-runtime = ["frame-support/try-runtime"] diff --git a/crates/pallet-transaction-fees/README.md b/crates/pallet-transaction-fees/README.md new file mode 100644 index 0000000000000..cca2c2b954c53 --- /dev/null +++ b/crates/pallet-transaction-fees/README.md @@ -0,0 +1,5 @@ +# Pallet Transaction Fees + +Pallet for charging and re-distributing transaction fees. + +License: Apache-2.0 diff --git a/crates/pallet-transaction-fees/src/lib.rs b/crates/pallet-transaction-fees/src/lib.rs new file mode 100644 index 0000000000000..b0c66cb580d3c --- /dev/null +++ b/crates/pallet-transaction-fees/src/lib.rs @@ -0,0 +1,107 @@ +// Copyright (C) 2021 Subspace Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Pallet for charging and re-distributing transaction fees. + +#![cfg_attr(not(feature = "std"), no_std)] +#![forbid(unsafe_code)] +#![warn(rust_2018_idioms, missing_debug_implementations)] + +use frame_support::traits::{Currency, Get}; +pub use pallet::*; + +type BalanceOf = + <::Currency as Currency<::AccountId>>::Balance; + +#[frame_support::pallet] +mod pallet { + use super::BalanceOf; + use frame_support::pallet_prelude::*; + use frame_support::traits::Currency; + use frame_system::pallet_prelude::*; + + #[pallet::config] + pub trait Config: frame_system::Config { + /// `pallet-transaction-fees` events + type Event: From> + IsType<::Event>; + + /// Minimum desired number of replicas of the blockchain to be stored by the network, + /// impacts storage fees. + #[pallet::constant] + type MinReplicationFactor: Get; + + /// How many credits there is in circulation. + #[pallet::constant] + type CreditSupply: Get>; + + /// How much space there is on the network. + #[pallet::constant] + type TotalSpacePledged: Get; + + /// How big is the history of the blockchain in archived state (thus includes erasure + /// coding, but not replication). + #[pallet::constant] + type BlockchainHistorySize: Get; + + type Currency: Currency; + } + + /// Temporary value (cleared at block finalization) which contains cached value of + /// `TransactionByteFee` for current block. + #[pallet::storage] + pub(super) type TransactionByteFee = StorageValue<_, BalanceOf>; + + /// Pallet rewards for issuing rewards to block producers. + #[pallet::pallet] + #[pallet::generate_store(pub(super) trait Store)] + pub struct Pallet(_); + + /// `pallet-transaction-fees` events + #[pallet::event] + #[pallet::generate_deposit(pub(super) fn deposit_event)] + pub enum Event {} + + #[pallet::hooks] + impl Hooks> for Pallet { + fn on_finalize(_n: BlockNumberFor) { + TransactionByteFee::::take(); + } + } +} + +impl Pallet +where + BalanceOf: From, +{ + pub fn transaction_byte_fee() -> BalanceOf { + if let Some(transaction_byte_fee) = TransactionByteFee::::get() { + return transaction_byte_fee; + } + + let credit_supply = T::CreditSupply::get(); + + let transaction_byte_fee = match T::TotalSpacePledged::get().checked_sub( + T::BlockchainHistorySize::get() * u64::from(T::MinReplicationFactor::get()), + ) { + Some(free_space) if free_space > 0 => credit_supply / BalanceOf::::from(free_space), + _ => credit_supply, + }; + + // Cache value for this block. + TransactionByteFee::::put(transaction_byte_fee); + + transaction_byte_fee + } +} diff --git a/crates/subspace-runtime-primitives/src/lib.rs b/crates/subspace-runtime-primitives/src/lib.rs index f10aa05d28033..8da145e5fc9f9 100644 --- a/crates/subspace-runtime-primitives/src/lib.rs +++ b/crates/subspace-runtime-primitives/src/lib.rs @@ -46,10 +46,10 @@ pub const RECORD_SIZE: u32 = PIECE_SIZE as u32 - WITNESS_SIZE; /// be erasure coded and together with corresponding witnesses will result in `MERKLE_NUM_LEAVES` /// pieces of archival history. pub const RECORDED_HISTORY_SEGMENT_SIZE: u32 = RECORD_SIZE * MERKLE_NUM_LEAVES / 2; -/// Replication factor, defines minimum desired number of replicas of the blockchain to be -/// stored by the network. +/// Minimum desired number of replicas of the blockchain to be stored by the network, +/// impacts storage fees. // TODO: Proper value here -pub const REPLICATION_FACTOR: u16 = 1; +pub const MIN_REPLICATION_FACTOR: u16 = 1; /// An index to a block. pub type BlockNumber = u32; diff --git a/crates/subspace-runtime/Cargo.toml b/crates/subspace-runtime/Cargo.toml index 4ab2de76646f4..9563dfe7d31c2 100644 --- a/crates/subspace-runtime/Cargo.toml +++ b/crates/subspace-runtime/Cargo.toml @@ -31,6 +31,7 @@ pallet-rewards = { version = "0.1.0", default-features = false, path = "../palle pallet-subspace = { version = "0.1.0", default-features = false, features = ["no-early-solution-range-updates"], path = "../pallet-subspace" } pallet-sudo = { version = "4.0.0-dev", default-features = false, git = "https://github.com/paritytech/substrate", rev = "b6c1c1bcfa5d831bfd1f278064d7af757f9b38f5" } pallet-timestamp = { version = "4.0.0-dev", default-features = false, git = "https://github.com/paritytech/substrate", rev = "b6c1c1bcfa5d831bfd1f278064d7af757f9b38f5" } +pallet-transaction-fees = { version = "0.1.0", default-features = false, path = "../pallet-transaction-fees" } pallet-transaction-payment = { version = "4.0.0-dev", default-features = false, git = "https://github.com/paritytech/substrate", rev = "b6c1c1bcfa5d831bfd1f278064d7af757f9b38f5" } pallet-utility = { version = "4.0.0-dev", default-features = false, git = "https://github.com/paritytech/substrate", rev = "b6c1c1bcfa5d831bfd1f278064d7af757f9b38f5" } scale-info = { version = "1.0", default-features = false, features = ["derive"] } @@ -79,6 +80,7 @@ std = [ "pallet-subspace/std", "pallet-sudo/std", "pallet-timestamp/std", + "pallet-transaction-fees/std", "pallet-transaction-payment-rpc-runtime-api/std", "pallet-transaction-payment/std", "pallet-utility/std", diff --git a/crates/subspace-runtime/src/lib.rs b/crates/subspace-runtime/src/lib.rs index 3b4781b42e773..9d5a2da7b9b3a 100644 --- a/crates/subspace-runtime/src/lib.rs +++ b/crates/subspace-runtime/src/lib.rs @@ -52,7 +52,7 @@ use subspace_core_primitives::objects::{BlockObject, BlockObjectMapping}; use subspace_core_primitives::{RootBlock, Sha256Hash, PIECE_SIZE}; pub use subspace_runtime_primitives::{ opaque, AccountId, Balance, BlockNumber, Hash, Index, Moment, Signature, CONFIRMATION_DEPTH_K, - RECORDED_HISTORY_SEGMENT_SIZE, RECORD_SIZE, REPLICATION_FACTOR, + MIN_REPLICATION_FACTOR, RECORDED_HISTORY_SEGMENT_SIZE, RECORD_SIZE, }; sp_runtime::impl_opaque_keys! { @@ -220,7 +220,6 @@ parameter_types! { pub const ConfirmationDepthK: u32 = CONFIRMATION_DEPTH_K; pub const RecordSize: u32 = RECORD_SIZE; pub const RecordedHistorySegmentSize: u32 = RECORDED_HISTORY_SEGMENT_SIZE; - pub const ReplicationFactor: u16 = REPLICATION_FACTOR; pub const ReportLongevity: u64 = EPOCH_DURATION_IN_BLOCKS as u64; } @@ -235,7 +234,6 @@ impl pallet_subspace::Config for Runtime { type ConfirmationDepthK = ConfirmationDepthK; type RecordSize = RecordSize; type RecordedHistorySegmentSize = RecordedHistorySegmentSize; - type ReplicationFactor = ReplicationFactor; type EpochChangeTrigger = pallet_subspace::NormalEpochChange; type EraChangeTrigger = pallet_subspace::NormalEraChange; type EonChangeTrigger = pallet_subspace::NormalEonChange; @@ -278,6 +276,48 @@ impl pallet_balances::Config for Runtime { type WeightInfo = pallet_balances::weights::SubstrateWeight; } +parameter_types! { + pub const ReplicationFactor: u16 = MIN_REPLICATION_FACTOR; +} + +pub struct CreditSupply; + +impl Get for CreditSupply { + fn get() -> Balance { + Balances::total_issuance() + } +} + +pub struct TotalSpacePledged; + +impl Get for TotalSpacePledged { + fn get() -> u64 { + let piece_size = u64::try_from(PIECE_SIZE) + .expect("Piece size is definitely small enough to fit into u64; qed"); + // Operations reordered to avoid u64 overflow, but essentially are: + // u64::MAX * SlotProbability / (solution_range / PIECE_SIZE) + u64::MAX / Subspace::solution_range() * piece_size * SlotProbability::get().0 + / SlotProbability::get().1 + } +} + +pub struct BlockchainHistorySize; + +impl Get for BlockchainHistorySize { + fn get() -> u64 { + Subspace::archived_history_size() + } +} + +impl pallet_transaction_fees::Config for Runtime { + type Event = Event; + type MinReplicationFactor = ReplicationFactor; + type CreditSupply = CreditSupply; + type TotalSpacePledged = TotalSpacePledged; + type BlockchainHistorySize = BlockchainHistorySize; + type Currency = Balances; +} + pub struct TransactionByteFee; impl Get for TransactionByteFee { @@ -285,21 +325,7 @@ impl Get for TransactionByteFee { if cfg!(feature = "do-not-enforce-cost-of-storage") { 1 } else { - let credit_supply = Balances::total_issuance(); - let total_space = Balance::from( - u64::MAX * SlotProbability::get().0 - / SlotProbability::get().1 - / (Subspace::solution_range() - / u64::try_from(PIECE_SIZE) - .expect("Piece size is definitely small enough to fit into u64; qed")), - ); - let blockchain_size = Balance::from(Subspace::archived_history_size()); - - match total_space.checked_sub(blockchain_size * Balance::from(ReplicationFactor::get())) - { - Some(free_space) if free_space > 0 => credit_supply / free_space, - _ => Balance::MAX, - } + TransactionFees::transaction_byte_fee() } } } @@ -394,6 +420,7 @@ construct_runtime!( Rewards: pallet_rewards = 9, Balances: pallet_balances = 4, + TransactionFees: pallet_transaction_fees = 12, TransactionPayment: pallet_transaction_payment = 5, Utility: pallet_utility = 8, diff --git a/substrate/substrate-test-runtime/src/lib.rs b/substrate/substrate-test-runtime/src/lib.rs index 69a67fd2c9fe8..fbf01c422081f 100644 --- a/substrate/substrate-test-runtime/src/lib.rs +++ b/substrate/substrate-test-runtime/src/lib.rs @@ -662,7 +662,6 @@ impl pallet_subspace::Config for Runtime { type ConfirmationDepthK = ConfirmationDepthK; type RecordSize = RecordSize; type RecordedHistorySegmentSize = RecordedHistorySegmentSize; - type ReplicationFactor = ReplicationFactor; type EpochChangeTrigger = pallet_subspace::NormalEpochChange; type EraChangeTrigger = pallet_subspace::NormalEraChange; type EonChangeTrigger = pallet_subspace::NormalEonChange; From de1cfec9cbb93ac50defee928cfc67d09949871e Mon Sep 17 00:00:00 2001 From: Nazar Mokrynskyi Date: Sun, 12 Dec 2021 16:40:46 +0200 Subject: [PATCH 2/8] Separate storage/compute fees and tip, so they can be distributed separately --- Cargo.lock | 1 + crates/pallet-transaction-fees/Cargo.toml | 2 + .../src/default_weights.rs | 26 +++++ crates/pallet-transaction-fees/src/lib.rs | 19 ++- crates/subspace-runtime/src/lib.rs | 108 ++++++++++++++++-- 5 files changed, 147 insertions(+), 9 deletions(-) create mode 100644 crates/pallet-transaction-fees/src/default_weights.rs diff --git a/Cargo.lock b/Cargo.lock index 2f9b3e793397c..bfc75509e0eee 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4464,6 +4464,7 @@ dependencies = [ "frame-system", "parity-scale-codec", "scale-info", + "sp-consensus-subspace", ] [[package]] diff --git a/crates/pallet-transaction-fees/Cargo.toml b/crates/pallet-transaction-fees/Cargo.toml index e2003bd19a6f6..b8d6ced785f0d 100644 --- a/crates/pallet-transaction-fees/Cargo.toml +++ b/crates/pallet-transaction-fees/Cargo.toml @@ -22,6 +22,7 @@ codec = { package = "parity-scale-codec", version = "2.3.0", default-features = frame-support = { version = "4.0.0-dev", default-features = false, git = "https://github.com/paritytech/substrate", rev = "b6c1c1bcfa5d831bfd1f278064d7af757f9b38f5" } frame-system = { version = "4.0.0-dev", default-features = false, git = "https://github.com/paritytech/substrate", rev = "b6c1c1bcfa5d831bfd1f278064d7af757f9b38f5" } scale-info = { version = "1.0", default-features = false, features = ["derive"] } +sp-consensus-subspace = { version = "0.1.0", default-features = false, path = "../sp-consensus-subspace" } [features] default = ["std"] @@ -30,5 +31,6 @@ std = [ "frame-support/std", "frame-system/std", "scale-info/std", + "sp-consensus-subspace/std", ] try-runtime = ["frame-support/try-runtime"] diff --git a/crates/pallet-transaction-fees/src/default_weights.rs b/crates/pallet-transaction-fees/src/default_weights.rs new file mode 100644 index 0000000000000..813e03062970b --- /dev/null +++ b/crates/pallet-transaction-fees/src/default_weights.rs @@ -0,0 +1,26 @@ +// Copyright (C) 2021 Subspace Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Default weights for the Rewards Pallet +//! This file was not auto-generated. + +use frame_support::weights::Weight; + +impl crate::WeightInfo for () { + fn on_initialize() -> Weight { + // TODO: Correct value + 1 + } +} diff --git a/crates/pallet-transaction-fees/src/lib.rs b/crates/pallet-transaction-fees/src/lib.rs index b0c66cb580d3c..714d42b28e997 100644 --- a/crates/pallet-transaction-fees/src/lib.rs +++ b/crates/pallet-transaction-fees/src/lib.rs @@ -19,15 +19,22 @@ #![forbid(unsafe_code)] #![warn(rust_2018_idioms, missing_debug_implementations)] +mod default_weights; + use frame_support::traits::{Currency, Get}; +use frame_support::weights::Weight; pub use pallet::*; type BalanceOf = <::Currency as Currency<::AccountId>>::Balance; +pub trait WeightInfo { + fn on_initialize() -> Weight; +} + #[frame_support::pallet] mod pallet { - use super::BalanceOf; + use super::{BalanceOf, WeightInfo}; use frame_support::pallet_prelude::*; use frame_support::traits::Currency; use frame_system::pallet_prelude::*; @@ -56,6 +63,8 @@ mod pallet { type BlockchainHistorySize: Get; type Currency: Currency; + + type WeightInfo: WeightInfo; } /// Temporary value (cleared at block finalization) which contains cached value of @@ -104,4 +113,12 @@ where transaction_byte_fee } + + pub fn distribute_transaction_fees( + storage_fee: BalanceOf, + compute_fee: BalanceOf, + tip: BalanceOf, + ) { + // TODO + } } diff --git a/crates/subspace-runtime/src/lib.rs b/crates/subspace-runtime/src/lib.rs index 9d5a2da7b9b3a..8226b9b3c9bbd 100644 --- a/crates/subspace-runtime/src/lib.rs +++ b/crates/subspace-runtime/src/lib.rs @@ -23,7 +23,7 @@ include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); use codec::{Compact, CompactLen, Encode}; -use frame_support::traits::Get; +use frame_support::traits::{Currency, ExistenceRequirement, Get, Imbalance, WithdrawReasons}; use frame_support::weights::{ constants::{RocksDbWeight, WEIGHT_PER_SECOND}, IdentityFee, @@ -31,19 +31,21 @@ use frame_support::weights::{ use frame_support::{construct_runtime, parameter_types}; use frame_system::limits::{BlockLength, BlockWeights}; use frame_system::EnsureNever; -use pallet_transaction_payment::CurrencyAdapter; +use pallet_balances::NegativeImbalance; use sp_api::impl_runtime_apis; use sp_consensus_subspace::{ Epoch, EquivocationProof, FarmerPublicKey, Salts, SubspaceEpochConfiguration, SubspaceGenesisConfiguration, }; use sp_core::{crypto::KeyTypeId, OpaqueMetadata}; -use sp_runtime::traits::{AccountIdLookup, BlakeTwo256, Block as BlockT, Header as HeaderT}; -use sp_runtime::{ - create_runtime_str, generic, - transaction_validity::{TransactionSource, TransactionValidity}, - ApplyExtrinsicResult, Perbill, +use sp_runtime::traits::{ + AccountIdLookup, BlakeTwo256, Block as BlockT, DispatchInfoOf, Header as HeaderT, + PostDispatchInfoOf, Zero, }; +use sp_runtime::transaction_validity::{ + InvalidTransaction, TransactionSource, TransactionValidity, TransactionValidityError, +}; +use sp_runtime::{create_runtime_str, generic, ApplyExtrinsicResult, Perbill}; use sp_std::prelude::*; #[cfg(feature = "std")] use sp_version::NativeVersion; @@ -316,6 +318,7 @@ impl pallet_transaction_fees::Config for Runtime { type TotalSpacePledged = TotalSpacePledged; type BlockchainHistorySize = BlockchainHistorySize; type Currency = Balances; + type WeightInfo = (); } pub struct TransactionByteFee; @@ -334,8 +337,97 @@ parameter_types! { pub const OperationalFeeMultiplier: u8 = 5; } +pub struct LiquidityInfo { + storage_fee: Balance, + imbalance: NegativeImbalance, +} + +/// Implementation of [`pallet_transaction_payment::OnChargeTransaction`] that charges transaction +/// fees and distributes storage/compute fees and tip separately. +pub struct OnChargeTransaction; + +impl pallet_transaction_payment::OnChargeTransaction for OnChargeTransaction { + type LiquidityInfo = Option; + type Balance = Balance; + + fn withdraw_fee( + who: &AccountId, + call: &Call, + _info: &DispatchInfoOf, + fee: Self::Balance, + tip: Self::Balance, + ) -> Result { + if fee.is_zero() { + return Ok(None); + } + + let withdraw_reason = if tip.is_zero() { + WithdrawReasons::TRANSACTION_PAYMENT + } else { + WithdrawReasons::TRANSACTION_PAYMENT | WithdrawReasons::TIP + }; + + let withdraw_result = >::withdraw( + who, + fee, + withdraw_reason, + ExistenceRequirement::KeepAlive, + ); + let imbalance = withdraw_result.map_err(|_error| InvalidTransaction::Payment)?; + + // Separate storage fee while we have access to the call data structure to calculate it. + let storage_fee = TransactionByteFee::get() + * Balance::try_from(call.encoded_size()) + .expect("Size of the call never exceeds balance units; qed"); + + Ok(Some(LiquidityInfo { + storage_fee, + imbalance, + })) + } + + fn correct_and_deposit_fee( + who: &AccountId, + _dispatch_info: &DispatchInfoOf, + _post_info: &PostDispatchInfoOf, + corrected_fee: Self::Balance, + tip: Self::Balance, + liquidity_info: Self::LiquidityInfo, + ) -> Result<(), TransactionValidityError> { + if let Some(LiquidityInfo { + storage_fee, + imbalance, + }) = liquidity_info + { + // Calculate how much refund we should return + let refund_amount = imbalance.peek().saturating_sub(corrected_fee); + // Refund to the the account that paid the fees. If this fails, the account might have + // dropped below the existential balance. In that case we don't refund anything. + let refund_imbalance = Balances::deposit_into_existing(who, refund_amount) + .unwrap_or_else(|_| >::PositiveImbalance::zero()); + // Merge the imbalance caused by paying the fees and refunding parts of it again. + let adjusted_paid = imbalance + .offset(refund_imbalance) + .same() + .map_err(|_| TransactionValidityError::Invalid(InvalidTransaction::Payment))?; + + // Split the tip from the total fee that ended up being paid. + let (tip, fee) = adjusted_paid.split(tip); + // Split paid storage and compute fees so that they can be distributed separately. + let (paid_storage_fee, paid_compute_fee) = fee.split(storage_fee); + + TransactionFees::distribute_transaction_fees( + paid_storage_fee.peek(), + paid_compute_fee.peek(), + tip.peek(), + ); + } + Ok(()) + } +} + impl pallet_transaction_payment::Config for Runtime { - type OnChargeTransaction = CurrencyAdapter; + type OnChargeTransaction = OnChargeTransaction; type TransactionByteFee = TransactionByteFee; type OperationalFeeMultiplier = OperationalFeeMultiplier; type WeightToFee = IdentityFee; From afc4b56da0572a3b196542a14547aee9c9af5e22 Mon Sep 17 00:00:00 2001 From: Nazar Mokrynskyi Date: Sun, 12 Dec 2021 16:44:20 +0200 Subject: [PATCH 3/8] Minor tweaks --- crates/pallet-rewards/src/lib.rs | 20 ++++++++++---------- crates/pallet-subspace/src/lib.rs | 11 ++++++----- crates/sp-executor/src/lib.rs | 1 - 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/crates/pallet-rewards/src/lib.rs b/crates/pallet-rewards/src/lib.rs index f7e24022eb490..97c9fcc9763a8 100644 --- a/crates/pallet-rewards/src/lib.rs +++ b/crates/pallet-rewards/src/lib.rs @@ -81,19 +81,19 @@ mod pallet { impl Pallet { fn do_initialize(_n: T::BlockNumber) { - if let Some(block_author) = frame_system::Pallet::::digest() + let block_author = frame_system::Pallet::::digest() .logs .iter() .find_map(|s| s.as_subspace_pre_digest()) .map(|pre_digest| pre_digest.solution.public_key) - { - let reward = T::BlockReward::get(); - T::Currency::deposit_creating(&block_author, reward); - - Self::deposit_event(Event::BlockReward { - block_author, - reward, - }); - } + .expect("Block author must always be present; qed"); + + let reward = T::BlockReward::get(); + T::Currency::deposit_creating(&block_author, reward); + + Self::deposit_event(Event::BlockReward { + block_author, + reward, + }); } } diff --git a/crates/pallet-subspace/src/lib.rs b/crates/pallet-subspace/src/lib.rs index ca5a8dacdf03b..a2fac2eab157b 100644 --- a/crates/pallet-subspace/src/lib.rs +++ b/crates/pallet-subspace/src/lib.rs @@ -693,14 +693,15 @@ impl Pallet { // TODO: Temporary testnet hack, we don't update solution range for the first 15_000 blocks // in order to seed the blockchain with data quickly - #[cfg(all(feature = "no-early-solution-range-updates", not(test)))] - let solution_range = if block_number < 15_000_u32.into() { - previous_solution_range + let solution_range = if cfg!(all(feature = "no-early-solution-range-updates", not(test))) { + if block_number < 15_000_u32.into() { + previous_solution_range + } else { + (previous_solution_range as f64 * adjustment_factor).round() as u64 + } } else { (previous_solution_range as f64 * adjustment_factor).round() as u64 }; - #[cfg(not(all(feature = "no-early-solution-range-updates", not(test))))] - let solution_range = (previous_solution_range as f64 * adjustment_factor).round() as u64; SolutionRange::::put(solution_range); EraStartSlot::::put(current_slot); diff --git a/crates/sp-executor/src/lib.rs b/crates/sp-executor/src/lib.rs index dd29d985d089c..87da293c9ad63 100644 --- a/crates/sp-executor/src/lib.rs +++ b/crates/sp-executor/src/lib.rs @@ -18,7 +18,6 @@ #![cfg_attr(not(feature = "std"), no_std)] use sp_runtime::traits::{Block as BlockT, Header as HeaderT}; -use sp_std::vec::Vec; sp_api::decl_runtime_apis! { /// API necessary for executor pallet. From 0e64f8babea8d2e98b31ed8911bc316f7a838003 Mon Sep 17 00:00:00 2001 From: Nazar Mokrynskyi Date: Sun, 12 Dec 2021 17:48:08 +0200 Subject: [PATCH 4/8] Issue storage/compute fees and tips rewards, introduce storage fees escrow --- crates/pallet-transaction-fees/src/lib.rs | 183 +++++++++++++++++- crates/subspace-runtime-primitives/src/lib.rs | 7 + crates/subspace-runtime/src/lib.rs | 5 + 3 files changed, 190 insertions(+), 5 deletions(-) diff --git a/crates/pallet-transaction-fees/src/lib.rs b/crates/pallet-transaction-fees/src/lib.rs index 714d42b28e997..8da4d98a969c6 100644 --- a/crates/pallet-transaction-fees/src/lib.rs +++ b/crates/pallet-transaction-fees/src/lib.rs @@ -21,9 +21,11 @@ mod default_weights; +use frame_support::sp_runtime::traits::Zero; use frame_support::traits::{Currency, Get}; use frame_support::weights::Weight; pub use pallet::*; +use sp_consensus_subspace::digests::CompatibleDigestItem; type BalanceOf = <::Currency as Currency<::AccountId>>::Balance; @@ -49,6 +51,16 @@ mod pallet { #[pallet::constant] type MinReplicationFactor: Get; + /// How much (ratio) of storage fees escrow should be given to farmer each block as a + /// reward. + #[pallet::constant] + type StorageFeesEscrowBlockReward: Get<(u64, u64)>; + + /// How much (ratio) of storage fees collected in a block should be put into storage fees + /// escrow (with remaining issued to farmer immediately). + #[pallet::constant] + type StorageFeesEscrowBlockTax: Get<(u64, u64)>; + /// How many credits there is in circulation. #[pallet::constant] type CreditSupply: Get>; @@ -67,11 +79,37 @@ mod pallet { type WeightInfo: WeightInfo; } + /// Escrow of storage fees, a portion of it is released to the block author on every block + /// and portion of storage fees goes back into this pot. + #[pallet::storage] + #[pallet::getter(fn storage_fees_escrow)] + pub(super) type CollectedStorageFeesEscrow = StorageValue<_, BalanceOf, ValueQuery>; + /// Temporary value (cleared at block finalization) which contains cached value of /// `TransactionByteFee` for current block. #[pallet::storage] pub(super) type TransactionByteFee = StorageValue<_, BalanceOf>; + /// Temporary value (cleared at block finalization) which contains current block author, so we + /// can issue rewards during block finalization. + #[pallet::storage] + pub(super) type BlockAuthor = StorageValue<_, T::AccountId>; + + /// Temporary value (cleared at block finalization) which contains current block storage fees, + /// so we can issue rewards during block finalization. + #[pallet::storage] + pub(super) type CollectedStorageFees = StorageValue<_, BalanceOf>; + + /// Temporary value (cleared at block finalization) which contains current block compute fees, + /// so we can issue rewards during block finalization. + #[pallet::storage] + pub(super) type CollectedComputeFees = StorageValue<_, BalanceOf>; + + /// Temporary value (cleared at block finalization) which contains current block tips, so we can + /// issue rewards during block finalization. + #[pallet::storage] + pub(super) type CollectedTips = StorageValue<_, BalanceOf>; + /// Pallet rewards for issuing rewards to block producers. #[pallet::pallet] #[pallet::generate_store(pub(super) trait Store)] @@ -80,12 +118,49 @@ mod pallet { /// `pallet-transaction-fees` events #[pallet::event] #[pallet::generate_deposit(pub(super) fn deposit_event)] - pub enum Event {} + pub enum Event { + /// Storage fees escrow change. + StorageFeesEscrowChange { + /// State of storage fees escrow before block execution. + before: BalanceOf, + /// State of storage fees escrow after block execution. + after: BalanceOf, + }, + /// Storage fees. + StorageFeesReward { + /// Receiver of the storage fees. + who: T::AccountId, + /// Amount of collected storage fees. + amount: BalanceOf, + }, + /// Compute fees. + ComputeFeesReward { + /// Receiver of the compute fees. + who: T::AccountId, + /// Amount of collected compute fees. + amount: BalanceOf, + }, + /// Tips. + TipsReward { + /// Receiver of the tip. + who: T::AccountId, + /// Amount of collected tips. + amount: BalanceOf, + }, + } #[pallet::hooks] - impl Hooks> for Pallet { - fn on_finalize(_n: BlockNumberFor) { - TransactionByteFee::::take(); + impl Hooks> for Pallet + where + BalanceOf: From + From, + { + fn on_initialize(now: BlockNumberFor) -> Weight { + Self::do_initialize(now); + T::WeightInfo::on_initialize() + } + + fn on_finalize(now: BlockNumberFor) { + Self::do_finalize(now); } } } @@ -94,6 +169,89 @@ impl Pallet where BalanceOf: From, { + fn do_initialize(_n: T::BlockNumber) { + let block_author: T::AccountId = frame_system::Pallet::::digest() + .logs + .iter() + .find_map(|s| s.as_subspace_pre_digest()) + .map(|pre_digest| pre_digest.solution.public_key) + .expect("Block author must always be present; qed"); + + BlockAuthor::::put(block_author); + + CollectedStorageFees::::put(BalanceOf::::zero()); + CollectedComputeFees::::put(BalanceOf::::zero()); + CollectedTips::::put(BalanceOf::::zero()); + } + + // TODO: Fees will be split between farmers and executors in the future + fn do_finalize(_n: T::BlockNumber) { + TransactionByteFee::::take(); + + let block_author = + BlockAuthor::::take().expect("`BlockAuthor` was set in `on_initialize`; qed"); + + let original_storage_fees_escrow = CollectedStorageFeesEscrow::::get(); + let collected_storage_fees = CollectedStorageFees::::take() + .expect("`CollectedStorageFees` was set in `on_initialize`; qed"); + let compute_fees = CollectedComputeFees::::take() + .expect("`CollectedComputeFees` was set in `on_initialize`; qed"); + let tips = + CollectedTips::::take().expect("`CollectedTips` was set in `on_initialize`; qed"); + + let mut storage_fees_escrow = original_storage_fees_escrow; + + // Take a portion of storage fees escrow as a farmer reward. + let storage_fees_escrow_reward = storage_fees_escrow + / T::StorageFeesEscrowBlockReward::get().1.into() + * T::StorageFeesEscrowBlockReward::get().0.into(); + storage_fees_escrow -= storage_fees_escrow_reward; + + // Take a portion of storage fees collected in this block as a farmer reward. + let collected_storage_fees_reward = collected_storage_fees + / T::StorageFeesEscrowBlockTax::get().1.into() + * (T::StorageFeesEscrowBlockTax::get().1 - T::StorageFeesEscrowBlockTax::get().0) + .into(); + storage_fees_escrow += collected_storage_fees - collected_storage_fees_reward; + + // Update storage fees escrow. + if storage_fees_escrow != original_storage_fees_escrow { + CollectedStorageFeesEscrow::::put(storage_fees_escrow); + Self::deposit_event(Event::::StorageFeesEscrowChange { + before: original_storage_fees_escrow, + after: storage_fees_escrow, + }); + } + + // Issue storage fees reward. + let storage_fees_reward = storage_fees_escrow_reward + collected_storage_fees_reward; + if !storage_fees_reward.is_zero() { + let _ = T::Currency::deposit_into_existing(&block_author, storage_fees_reward); + Self::deposit_event(Event::::StorageFeesReward { + who: block_author.clone(), + amount: storage_fees_reward, + }); + } + + // Issue compute fees reward. + if !compute_fees.is_zero() { + let _ = T::Currency::deposit_into_existing(&block_author, compute_fees); + Self::deposit_event(Event::::ComputeFeesReward { + who: block_author.clone(), + amount: compute_fees, + }); + } + + // Issue tips reward. + if !tips.is_zero() { + let _ = T::Currency::deposit_into_existing(&block_author, tips); + Self::deposit_event(Event::::TipsReward { + who: block_author, + amount: tips, + }); + } + } + pub fn transaction_byte_fee() -> BalanceOf { if let Some(transaction_byte_fee) = TransactionByteFee::::get() { return transaction_byte_fee; @@ -119,6 +277,21 @@ where compute_fee: BalanceOf, tip: BalanceOf, ) { - // TODO + CollectedStorageFees::::mutate(|storage_fees| { + *storage_fees + .as_mut() + .expect("`CollectedStorageFees` was set in `on_initialize`; qed") += storage_fee; + }); + CollectedComputeFees::::mutate(|compute_fees| { + *compute_fees + .as_mut() + .expect("`CollectedComputeFees` was set in `on_initialize`; qed") += compute_fee; + }); + // TODO: Split tips for storage and compute proportionally? + CollectedTips::::mutate(|tips| { + *tips + .as_mut() + .expect("`CollectedTips` was set in `on_initialize`; qed") += tip; + }); } } diff --git a/crates/subspace-runtime-primitives/src/lib.rs b/crates/subspace-runtime-primitives/src/lib.rs index 8da145e5fc9f9..479fc0763391a 100644 --- a/crates/subspace-runtime-primitives/src/lib.rs +++ b/crates/subspace-runtime-primitives/src/lib.rs @@ -50,6 +50,13 @@ pub const RECORDED_HISTORY_SEGMENT_SIZE: u32 = RECORD_SIZE * MERKLE_NUM_LEAVES / /// impacts storage fees. // TODO: Proper value here pub const MIN_REPLICATION_FACTOR: u16 = 1; +/// How much (ratio) of storage fees escrow should be given to farmer each block as a reward. +// TODO: Proper value here +pub const STORAGE_FEES_ESCROW_BLOCK_REWARD: (u64, u64) = (1, 100); +/// How much (ratio) of storage fees collected in a block should be put into storage fees escrow +/// (with remaining issued to farmer immediately). +// TODO: Proper value here +pub const STORAGE_FEES_ESCROW_BLOCK_TAX: (u64, u64) = (1, 2); /// An index to a block. pub type BlockNumber = u32; diff --git a/crates/subspace-runtime/src/lib.rs b/crates/subspace-runtime/src/lib.rs index 8226b9b3c9bbd..ac25a8d70d18c 100644 --- a/crates/subspace-runtime/src/lib.rs +++ b/crates/subspace-runtime/src/lib.rs @@ -55,6 +55,7 @@ use subspace_core_primitives::{RootBlock, Sha256Hash, PIECE_SIZE}; pub use subspace_runtime_primitives::{ opaque, AccountId, Balance, BlockNumber, Hash, Index, Moment, Signature, CONFIRMATION_DEPTH_K, MIN_REPLICATION_FACTOR, RECORDED_HISTORY_SEGMENT_SIZE, RECORD_SIZE, + STORAGE_FEES_ESCROW_BLOCK_REWARD, STORAGE_FEES_ESCROW_BLOCK_TAX, }; sp_runtime::impl_opaque_keys! { @@ -280,6 +281,8 @@ impl pallet_balances::Config for Runtime { parameter_types! { pub const ReplicationFactor: u16 = MIN_REPLICATION_FACTOR; + pub const StorageFeesEscrowBlockReward: (u64, u64) = STORAGE_FEES_ESCROW_BLOCK_REWARD; + pub const StorageFeesEscrowBlockTax: (u64, u64) = STORAGE_FEES_ESCROW_BLOCK_TAX; } pub struct CreditSupply; @@ -314,6 +317,8 @@ impl Get for BlockchainHistorySize { impl pallet_transaction_fees::Config for Runtime { type Event = Event; type MinReplicationFactor = ReplicationFactor; + type StorageFeesEscrowBlockReward = StorageFeesEscrowBlockReward; + type StorageFeesEscrowBlockTax = StorageFeesEscrowBlockTax; type CreditSupply = CreditSupply; type TotalSpacePledged = TotalSpacePledged; type BlockchainHistorySize = BlockchainHistorySize; From 8faee339d245b40a306df76153a354d985fb1ca8 Mon Sep 17 00:00:00 2001 From: Nazar Mokrynskyi Date: Mon, 13 Dec 2021 16:52:06 +0200 Subject: [PATCH 5/8] Use `frame_support::traits::FindAuthor` to make pallets more generic --- Cargo.lock | 2 -- crates/pallet-rewards/Cargo.toml | 4 +--- crates/pallet-rewards/src/lib.rs | 22 ++++++++++++---------- crates/pallet-subspace/src/lib.rs | 18 ++++++++++++++++++ crates/pallet-transaction-fees/Cargo.toml | 2 -- crates/pallet-transaction-fees/src/lib.rs | 20 +++++++++++--------- crates/subspace-runtime/src/lib.rs | 2 ++ 7 files changed, 44 insertions(+), 26 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index bfc75509e0eee..51802ecc09091 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4376,7 +4376,6 @@ dependencies = [ "frame-system", "parity-scale-codec", "scale-info", - "sp-consensus-subspace", ] [[package]] @@ -4464,7 +4463,6 @@ dependencies = [ "frame-system", "parity-scale-codec", "scale-info", - "sp-consensus-subspace", ] [[package]] diff --git a/crates/pallet-rewards/Cargo.toml b/crates/pallet-rewards/Cargo.toml index 5327a2bca37df..0efc20ccd86d0 100644 --- a/crates/pallet-rewards/Cargo.toml +++ b/crates/pallet-rewards/Cargo.toml @@ -6,7 +6,7 @@ edition = "2021" license = "Apache-2.0" homepage = "https://subspace.network" repository = "https://github.com/subspace/subspace" -description = "Subspace pallet for issuing rewards to block producers" +description = "Pallet for issuing rewards to block producers" readme = "README.md" include = [ "/src", @@ -22,7 +22,6 @@ codec = { package = "parity-scale-codec", version = "2.3.0", default-features = frame-support = { version = "4.0.0-dev", default-features = false, git = "https://github.com/paritytech/substrate", rev = "b6c1c1bcfa5d831bfd1f278064d7af757f9b38f5" } frame-system = { version = "4.0.0-dev", default-features = false, git = "https://github.com/paritytech/substrate", rev = "b6c1c1bcfa5d831bfd1f278064d7af757f9b38f5" } scale-info = { version = "1.0", default-features = false, features = ["derive"] } -sp-consensus-subspace = { version = "0.1.0", default-features = false, path = "../sp-consensus-subspace" } [features] default = ["std"] @@ -31,6 +30,5 @@ std = [ "frame-support/std", "frame-system/std", "scale-info/std", - "sp-consensus-subspace/std", ] try-runtime = ["frame-support/try-runtime"] diff --git a/crates/pallet-rewards/src/lib.rs b/crates/pallet-rewards/src/lib.rs index 97c9fcc9763a8..fe03ad5a78511 100644 --- a/crates/pallet-rewards/src/lib.rs +++ b/crates/pallet-rewards/src/lib.rs @@ -13,7 +13,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! Subspace pallet for issuing rewards to block producers. +//! Pallet for issuing rewards to block producers. #![cfg_attr(not(feature = "std"), no_std)] #![forbid(unsafe_code)] @@ -21,10 +21,9 @@ mod default_weights; -use frame_support::traits::{Currency, Get}; +use frame_support::traits::{Currency, FindAuthor, Get}; use frame_support::weights::Weight; pub use pallet::*; -use sp_consensus_subspace::digests::CompatibleDigestItem; pub trait WeightInfo { fn on_initialize() -> Weight; @@ -34,7 +33,7 @@ pub trait WeightInfo { mod pallet { use super::WeightInfo; use frame_support::pallet_prelude::*; - use frame_support::traits::Currency; + use frame_support::traits::{Currency, FindAuthor}; use frame_system::pallet_prelude::*; type BalanceOf = @@ -51,6 +50,8 @@ mod pallet { #[pallet::constant] type BlockReward: Get>; + type FindAuthor: FindAuthor; + type WeightInfo: WeightInfo; } @@ -81,12 +82,13 @@ mod pallet { impl Pallet { fn do_initialize(_n: T::BlockNumber) { - let block_author = frame_system::Pallet::::digest() - .logs - .iter() - .find_map(|s| s.as_subspace_pre_digest()) - .map(|pre_digest| pre_digest.solution.public_key) - .expect("Block author must always be present; qed"); + let block_author = T::FindAuthor::find_author( + frame_system::Pallet::::digest() + .logs + .iter() + .filter_map(|d| d.as_pre_runtime()), + ) + .expect("Block author must always be present; qed"); let reward = T::BlockReward::get(); T::Currency::deposit_creating(&block_author, reward); diff --git a/crates/pallet-subspace/src/lib.rs b/crates/pallet-subspace/src/lib.rs index a2fac2eab157b..eac3212337714 100644 --- a/crates/pallet-subspace/src/lib.rs +++ b/crates/pallet-subspace/src/lib.rs @@ -57,6 +57,7 @@ use sp_runtime::transaction_validity::{ use sp_runtime::{ generic::DigestItem, traits::{One, SaturatedConversion, Saturating, Zero}, + ConsensusEngineId, }; use sp_std::prelude::*; use subspace_core_primitives::{crypto, RootBlock, PIECE_SIZE, RANDOMNESS_LENGTH, SALT_SIZE}; @@ -1106,6 +1107,23 @@ impl OnTimestampSet for Pallet { } } +impl frame_support::traits::FindAuthor for Pallet { + fn find_author<'a, I>(digests: I) -> Option + where + I: 'a + IntoIterator, + { + digests.into_iter().find_map(|(id, mut data)| { + if id == SUBSPACE_ENGINE_ID { + PreDigest::decode(&mut data) + .map(|pre_digest| pre_digest.solution.public_key) + .ok() + } else { + None + } + }) + } +} + impl frame_support::traits::Lateness for Pallet { fn lateness(&self) -> T::BlockNumber { Self::lateness() diff --git a/crates/pallet-transaction-fees/Cargo.toml b/crates/pallet-transaction-fees/Cargo.toml index b8d6ced785f0d..e2003bd19a6f6 100644 --- a/crates/pallet-transaction-fees/Cargo.toml +++ b/crates/pallet-transaction-fees/Cargo.toml @@ -22,7 +22,6 @@ codec = { package = "parity-scale-codec", version = "2.3.0", default-features = frame-support = { version = "4.0.0-dev", default-features = false, git = "https://github.com/paritytech/substrate", rev = "b6c1c1bcfa5d831bfd1f278064d7af757f9b38f5" } frame-system = { version = "4.0.0-dev", default-features = false, git = "https://github.com/paritytech/substrate", rev = "b6c1c1bcfa5d831bfd1f278064d7af757f9b38f5" } scale-info = { version = "1.0", default-features = false, features = ["derive"] } -sp-consensus-subspace = { version = "0.1.0", default-features = false, path = "../sp-consensus-subspace" } [features] default = ["std"] @@ -31,6 +30,5 @@ std = [ "frame-support/std", "frame-system/std", "scale-info/std", - "sp-consensus-subspace/std", ] try-runtime = ["frame-support/try-runtime"] diff --git a/crates/pallet-transaction-fees/src/lib.rs b/crates/pallet-transaction-fees/src/lib.rs index 8da4d98a969c6..0d4fa2d13be93 100644 --- a/crates/pallet-transaction-fees/src/lib.rs +++ b/crates/pallet-transaction-fees/src/lib.rs @@ -22,10 +22,9 @@ mod default_weights; use frame_support::sp_runtime::traits::Zero; -use frame_support::traits::{Currency, Get}; +use frame_support::traits::{Currency, FindAuthor, Get}; use frame_support::weights::Weight; pub use pallet::*; -use sp_consensus_subspace::digests::CompatibleDigestItem; type BalanceOf = <::Currency as Currency<::AccountId>>::Balance; @@ -38,7 +37,7 @@ pub trait WeightInfo { mod pallet { use super::{BalanceOf, WeightInfo}; use frame_support::pallet_prelude::*; - use frame_support::traits::Currency; + use frame_support::traits::{Currency, FindAuthor}; use frame_system::pallet_prelude::*; #[pallet::config] @@ -76,6 +75,8 @@ mod pallet { type Currency: Currency; + type FindAuthor: FindAuthor; + type WeightInfo: WeightInfo; } @@ -170,12 +171,13 @@ where BalanceOf: From, { fn do_initialize(_n: T::BlockNumber) { - let block_author: T::AccountId = frame_system::Pallet::::digest() - .logs - .iter() - .find_map(|s| s.as_subspace_pre_digest()) - .map(|pre_digest| pre_digest.solution.public_key) - .expect("Block author must always be present; qed"); + let block_author = T::FindAuthor::find_author( + frame_system::Pallet::::digest() + .logs + .iter() + .filter_map(|d| d.as_pre_runtime()), + ) + .expect("Block author must always be present; qed"); BlockAuthor::::put(block_author); diff --git a/crates/subspace-runtime/src/lib.rs b/crates/subspace-runtime/src/lib.rs index ac25a8d70d18c..25feb2424b095 100644 --- a/crates/subspace-runtime/src/lib.rs +++ b/crates/subspace-runtime/src/lib.rs @@ -323,6 +323,7 @@ impl pallet_transaction_fees::Config for Runtime { type TotalSpacePledged = TotalSpacePledged; type BlockchainHistorySize = BlockchainHistorySize; type Currency = Balances; + type FindAuthor = Subspace; type WeightInfo = (); } @@ -476,6 +477,7 @@ impl pallet_rewards::Config for Runtime { type Event = Event; type Currency = Balances; type BlockReward = BlockReward; + type FindAuthor = Subspace; type WeightInfo = (); } From 1fa21322cc802e03ea3382f9aa951879121f50a9 Mon Sep 17 00:00:00 2001 From: Nazar Mokrynskyi Date: Mon, 13 Dec 2021 18:18:15 +0200 Subject: [PATCH 6/8] Create `CollectedFees` data structure for easier management --- crates/pallet-transaction-fees/src/lib.rs | 78 ++++++++++------------- 1 file changed, 34 insertions(+), 44 deletions(-) diff --git a/crates/pallet-transaction-fees/src/lib.rs b/crates/pallet-transaction-fees/src/lib.rs index 0d4fa2d13be93..f62144b1d91cb 100644 --- a/crates/pallet-transaction-fees/src/lib.rs +++ b/crates/pallet-transaction-fees/src/lib.rs @@ -21,10 +21,12 @@ mod default_weights; +use codec::{Codec, Decode, Encode}; use frame_support::sp_runtime::traits::Zero; use frame_support::traits::{Currency, FindAuthor, Get}; use frame_support::weights::Weight; pub use pallet::*; +use scale_info::TypeInfo; type BalanceOf = <::Currency as Currency<::AccountId>>::Balance; @@ -33,9 +35,17 @@ pub trait WeightInfo { fn on_initialize() -> Weight; } +#[derive(Encode, Decode, TypeInfo)] +struct CollectedFees { + storage: Balance, + compute: Balance, + // TODO: Split tips for storage and compute proportionally? + tips: Balance, +} + #[frame_support::pallet] mod pallet { - use super::{BalanceOf, WeightInfo}; + use super::{BalanceOf, CollectedFees, WeightInfo}; use frame_support::pallet_prelude::*; use frame_support::traits::{Currency, FindAuthor}; use frame_system::pallet_prelude::*; @@ -96,20 +106,10 @@ mod pallet { #[pallet::storage] pub(super) type BlockAuthor = StorageValue<_, T::AccountId>; - /// Temporary value (cleared at block finalization) which contains current block storage fees, - /// so we can issue rewards during block finalization. - #[pallet::storage] - pub(super) type CollectedStorageFees = StorageValue<_, BalanceOf>; - - /// Temporary value (cleared at block finalization) which contains current block compute fees, - /// so we can issue rewards during block finalization. - #[pallet::storage] - pub(super) type CollectedComputeFees = StorageValue<_, BalanceOf>; - - /// Temporary value (cleared at block finalization) which contains current block tips, so we can + /// Temporary value (cleared at block finalization) which contains current block fees, so we can /// issue rewards during block finalization. #[pallet::storage] - pub(super) type CollectedTips = StorageValue<_, BalanceOf>; + pub(super) type CollectedBlockFees = StorageValue<_, CollectedFees>>; /// Pallet rewards for issuing rewards to block producers. #[pallet::pallet] @@ -181,9 +181,11 @@ where BlockAuthor::::put(block_author); - CollectedStorageFees::::put(BalanceOf::::zero()); - CollectedComputeFees::::put(BalanceOf::::zero()); - CollectedTips::::put(BalanceOf::::zero()); + CollectedBlockFees::::put(CollectedFees { + storage: BalanceOf::::zero(), + compute: BalanceOf::::zero(), + tips: BalanceOf::::zero(), + }); } // TODO: Fees will be split between farmers and executors in the future @@ -194,12 +196,8 @@ where BlockAuthor::::take().expect("`BlockAuthor` was set in `on_initialize`; qed"); let original_storage_fees_escrow = CollectedStorageFeesEscrow::::get(); - let collected_storage_fees = CollectedStorageFees::::take() - .expect("`CollectedStorageFees` was set in `on_initialize`; qed"); - let compute_fees = CollectedComputeFees::::take() - .expect("`CollectedComputeFees` was set in `on_initialize`; qed"); - let tips = - CollectedTips::::take().expect("`CollectedTips` was set in `on_initialize`; qed"); + let collected_fees = CollectedBlockFees::::take() + .expect("`CollectedBlockFees` was set in `on_initialize`; qed"); let mut storage_fees_escrow = original_storage_fees_escrow; @@ -210,11 +208,11 @@ where storage_fees_escrow -= storage_fees_escrow_reward; // Take a portion of storage fees collected in this block as a farmer reward. - let collected_storage_fees_reward = collected_storage_fees + let collected_storage_fees_reward = collected_fees.storage / T::StorageFeesEscrowBlockTax::get().1.into() * (T::StorageFeesEscrowBlockTax::get().1 - T::StorageFeesEscrowBlockTax::get().0) .into(); - storage_fees_escrow += collected_storage_fees - collected_storage_fees_reward; + storage_fees_escrow += collected_fees.storage - collected_storage_fees_reward; // Update storage fees escrow. if storage_fees_escrow != original_storage_fees_escrow { @@ -236,20 +234,20 @@ where } // Issue compute fees reward. - if !compute_fees.is_zero() { - let _ = T::Currency::deposit_into_existing(&block_author, compute_fees); + if !collected_fees.compute.is_zero() { + let _ = T::Currency::deposit_into_existing(&block_author, collected_fees.compute); Self::deposit_event(Event::::ComputeFeesReward { who: block_author.clone(), - amount: compute_fees, + amount: collected_fees.compute, }); } // Issue tips reward. - if !tips.is_zero() { - let _ = T::Currency::deposit_into_existing(&block_author, tips); + if !collected_fees.tips.is_zero() { + let _ = T::Currency::deposit_into_existing(&block_author, collected_fees.tips); Self::deposit_event(Event::::TipsReward { who: block_author, - amount: tips, + amount: collected_fees.tips, }); } } @@ -279,21 +277,13 @@ where compute_fee: BalanceOf, tip: BalanceOf, ) { - CollectedStorageFees::::mutate(|storage_fees| { - *storage_fees - .as_mut() - .expect("`CollectedStorageFees` was set in `on_initialize`; qed") += storage_fee; - }); - CollectedComputeFees::::mutate(|compute_fees| { - *compute_fees - .as_mut() - .expect("`CollectedComputeFees` was set in `on_initialize`; qed") += compute_fee; - }); - // TODO: Split tips for storage and compute proportionally? - CollectedTips::::mutate(|tips| { - *tips + CollectedBlockFees::::mutate(|collected_block_fees| { + let collected_block_fees = collected_block_fees .as_mut() - .expect("`CollectedTips` was set in `on_initialize`; qed") += tip; + .expect("`CollectedBlockFees` was set in `on_initialize`; qed"); + collected_block_fees.storage += storage_fee; + collected_block_fees.compute += compute_fee; + collected_block_fees.tips += tip; }); } } From 76818e303822e3fe5c7dc80cecf78e5a1cac11c4 Mon Sep 17 00:00:00 2001 From: Nazar Mokrynskyi Date: Mon, 13 Dec 2021 18:51:18 +0200 Subject: [PATCH 7/8] Add assertions on fee collections --- crates/pallet-transaction-fees/src/lib.rs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/crates/pallet-transaction-fees/src/lib.rs b/crates/pallet-transaction-fees/src/lib.rs index f62144b1d91cb..0d088d3335b4f 100644 --- a/crates/pallet-transaction-fees/src/lib.rs +++ b/crates/pallet-transaction-fees/src/lib.rs @@ -226,7 +226,10 @@ where // Issue storage fees reward. let storage_fees_reward = storage_fees_escrow_reward + collected_storage_fees_reward; if !storage_fees_reward.is_zero() { - let _ = T::Currency::deposit_into_existing(&block_author, storage_fees_reward); + T::Currency::deposit_into_existing(&block_author, storage_fees_reward).expect( + "Farmer account must have already received the block reward before fees are \ + collected; qed", + ); Self::deposit_event(Event::::StorageFeesReward { who: block_author.clone(), amount: storage_fees_reward, @@ -235,7 +238,8 @@ where // Issue compute fees reward. if !collected_fees.compute.is_zero() { - let _ = T::Currency::deposit_into_existing(&block_author, collected_fees.compute); + T::Currency::deposit_into_existing(&block_author, collected_fees.compute) + .expect("Executor account must already exist before they execute the block; qed"); Self::deposit_event(Event::::ComputeFeesReward { who: block_author.clone(), amount: collected_fees.compute, @@ -244,7 +248,8 @@ where // Issue tips reward. if !collected_fees.tips.is_zero() { - let _ = T::Currency::deposit_into_existing(&block_author, collected_fees.tips); + T::Currency::deposit_into_existing(&block_author, collected_fees.tips) + .expect("Executor account must already exist before they execute the block; qed"); Self::deposit_event(Event::::TipsReward { who: block_author, amount: collected_fees.tips, From f8ce741f63d8ec426db00d925d18f00589e39932 Mon Sep 17 00:00:00 2001 From: Nazar Mokrynskyi Date: Tue, 14 Dec 2021 08:40:33 +0200 Subject: [PATCH 8/8] Rename `distribute_transaction_fees` into `note_transaction_fees` --- crates/pallet-transaction-fees/src/lib.rs | 2 +- crates/subspace-runtime/src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/pallet-transaction-fees/src/lib.rs b/crates/pallet-transaction-fees/src/lib.rs index 0d088d3335b4f..4fda394dd10ca 100644 --- a/crates/pallet-transaction-fees/src/lib.rs +++ b/crates/pallet-transaction-fees/src/lib.rs @@ -277,7 +277,7 @@ where transaction_byte_fee } - pub fn distribute_transaction_fees( + pub fn note_transaction_fees( storage_fee: BalanceOf, compute_fee: BalanceOf, tip: BalanceOf, diff --git a/crates/subspace-runtime/src/lib.rs b/crates/subspace-runtime/src/lib.rs index 25feb2424b095..549bb5d0a7457 100644 --- a/crates/subspace-runtime/src/lib.rs +++ b/crates/subspace-runtime/src/lib.rs @@ -422,7 +422,7 @@ impl pallet_transaction_payment::OnChargeTransaction for OnChargeTransa // Split paid storage and compute fees so that they can be distributed separately. let (paid_storage_fee, paid_compute_fee) = fee.split(storage_fee); - TransactionFees::distribute_transaction_fees( + TransactionFees::note_transaction_fees( paid_storage_fee.peek(), paid_compute_fee.peek(), tip.peek(),