Skip to content

Commit 6882f47

Browse files
authored
feat: add constant fee to setting block producer metadata (#868)
1 parent 8fcbeab commit 6882f47

File tree

9 files changed

+184
-38
lines changed

9 files changed

+184
-38
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

changelog.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ This changelog is based on [Keep A Changelog](https://keepachangelog.com/en/1.1.
88

99
## Changed
1010

11+
* `pallet-block-producer-metadata` is updated with a configurable fee for inserting the metadata, to make attacks on unbounded storage economically infeasible
12+
1113
## Added
1214

1315
## Fixed

demo/runtime/src/lib.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -349,7 +349,7 @@ impl pallet_balances::Config for Runtime {
349349
type WeightInfo = pallet_balances::weights::SubstrateWeight<Runtime>;
350350
type FreezeIdentifier = ();
351351
type MaxFreezes = ();
352-
type RuntimeHoldReason = ();
352+
type RuntimeHoldReason = RuntimeHoldReason;
353353
type RuntimeFreezeReason = RuntimeFreezeReason;
354354
type DoneSlashHandler = ();
355355
}
@@ -591,6 +591,11 @@ impl pallet_block_producer_fees::Config for Runtime {
591591
type BenchmarkHelper = PalletBlockProducerFeesBenchmarkHelper;
592592
}
593593

594+
parameter_types! {
595+
/// Amount of tokens to hold when upserting block producer metadata.
596+
pub const MetadataHoldAmount: Balance = 1_000_000;
597+
}
598+
594599
impl pallet_block_producer_metadata::Config for Runtime {
595600
type WeightInfo = pallet_block_producer_metadata::weights::SubstrateWeight<Runtime>;
596601

@@ -600,6 +605,10 @@ impl pallet_block_producer_metadata::Config for Runtime {
600605
Sidechain::genesis_utxo()
601606
}
602607

608+
type Currency = Balances;
609+
type HoldAmount = MetadataHoldAmount;
610+
type RuntimeHoldReason = RuntimeHoldReason;
611+
603612
#[cfg(feature = "runtime-benchmarks")]
604613
type BenchmarkHelper = PalletBlockProducerMetadataBenchmarkHelper;
605614
}

toolkit/block-producer-metadata/pallet/Cargo.toml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,15 @@ hex-literal = { workspace = true, optional = true }
2323
sp-core = { workspace = true, optional = true }
2424
sp-block-producer-metadata = { workspace = true }
2525
sp-runtime = { workspace = true, optional = true }
26-
sp-io = { workspace = true, optional = true}
26+
sp-io = { workspace = true, optional = true }
2727

2828
[dev-dependencies]
2929
sp-core = { workspace = true }
3030
sp-io = { workspace = true }
3131
sp-runtime = { workspace = true }
3232
hex-literal = { workspace = true }
3333
k256 = { workspace = true }
34+
pallet-balances = { workspace = true }
3435

3536
[features]
3637
default = ["std"]
@@ -45,6 +46,7 @@ std = [
4546
"sp-std/std",
4647
"sp-core?/std",
4748
"sp-block-producer-metadata/std",
49+
"pallet-balances/std",
4850
]
4951
runtime-benchmarks = [
5052
"frame-benchmarking/runtime-benchmarks",
@@ -53,5 +55,5 @@ runtime-benchmarks = [
5355
"sp-runtime/runtime-benchmarks",
5456
"hex-literal",
5557
"sp-core",
56-
"sp-io"
58+
"sp-io",
5759
]

toolkit/block-producer-metadata/pallet/src/benchmarking.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,18 +72,23 @@ pub trait BenchmarkHelper<BlockProducerMetadata> {
7272
fn cross_chain_signature() -> CrossChainSignature;
7373
}
7474

75-
#[benchmarks]
75+
#[benchmarks(where <T as Config>::Currency: frame_support::traits::tokens::fungible::Mutate<<T as frame_system::Config>::AccountId>)]
7676
mod benchmarks {
7777
use super::*;
78+
use frame_support::traits::{Get, tokens::fungible::Mutate};
7879

7980
#[benchmark]
8081
fn upsert_metadata() {
8182
let metadata = T::BenchmarkHelper::metadata();
8283
let cross_chain_pub_key = T::BenchmarkHelper::cross_chain_pub_key();
8384
let cross_chain_signature = T::BenchmarkHelper::cross_chain_signature();
8485

86+
// Create an account and fund it with sufficient balance
87+
let caller: T::AccountId = account("caller", 0, 0);
88+
let _ = T::Currency::mint_into(&caller, T::HoldAmount::get() * 2u32.into());
89+
8590
#[extrinsic_call]
86-
_(RawOrigin::None, metadata, cross_chain_signature, cross_chain_pub_key);
91+
_(RawOrigin::Signed(caller), metadata, cross_chain_signature, cross_chain_pub_key);
8792
}
8893

8994
impl_benchmark_test_suite!(Pallet, crate::mock::new_test_ext(), crate::mock::Test);

toolkit/block-producer-metadata/pallet/src/lib.rs

Lines changed: 49 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,9 @@
4646
//! type WeightInfo = pallet_block_producer_metadata::weights::SubstrateWeight<Runtime>;
4747
//!
4848
//! type BlockProducerMetadata = BlockProducerMetadata;
49+
//! type Currency = Balances;
50+
//! type HoldAmount = MetadataHoldAmount;
51+
//! type RuntimeHoldReason = RuntimeHoldReason;
4952
//!
5053
//! fn genesis_utxo() -> sidechain_domain::UtxoId {
5154
//! Sidechain::genesis_utxo()
@@ -55,6 +58,8 @@
5558
//!
5659
//! Here, besides providing the metadata type and using weights already provided with the pallet, we are also
5760
//! wiring the `genesis_utxo` function to fetch the chain's genesis UTXO from the `pallet_sidechain` pallet.
61+
//! Currency, HoldAmount, and RuntimeHoldReason types are required to configure the deposit mechanism for occupying storage.
62+
//! Removing Metadata is not supported, so this deposit is currently ethernal.
5863
//!
5964
//! At this point, the pallet is ready to be used.
6065
//!
@@ -80,6 +85,9 @@
8085
//! `sign-block-producer-metadata` command provided by the chain's node. This command returns the signature
8186
//! and the metadata encoded as hex bytes.
8287
//!
88+
//! When metadata is inserted for the first time, a deposit is held from the caller's account. Updates to existing
89+
//! metadata do not require additional deposits.
90+
//!
8391
//! After the signature has been obtained, the user should submit the `upsert_metadata` extrinsic (eg. using PolkadotJS)
8492
//! providing:
8593
//! - *metadata value*: when using PolkadotJS UI, care must be taken to submit the same values that were passed to the CLI
@@ -102,20 +110,27 @@ mod mock;
102110
#[cfg(test)]
103111
mod tests;
104112

113+
use frame_support::traits::tokens::fungible::Inspect;
105114
use parity_scale_codec::Encode;
106115
use sidechain_domain::{CrossChainKeyHash, CrossChainPublicKey};
107116
use sp_block_producer_metadata::MetadataSignedMessage;
108117

118+
type BalanceOf<T> =
119+
<<T as pallet::Config>::Currency as Inspect<<T as frame_system::Config>::AccountId>>::Balance;
120+
109121
#[frame_support::pallet]
110122
pub mod pallet {
111123
use super::*;
112124
use crate::weights::WeightInfo;
113-
use frame_support::pallet_prelude::*;
114-
use frame_system::pallet_prelude::OriginFor;
125+
use frame_support::{
126+
pallet_prelude::*,
127+
traits::{Get, tokens::fungible::MutateHold},
128+
};
129+
use frame_system::{ensure_signed, pallet_prelude::OriginFor};
115130
use sidechain_domain::{CrossChainSignature, UtxoId};
116131

117132
/// Current version of the pallet
118-
pub const PALLET_VERSION: u32 = 1;
133+
pub const PALLET_VERSION: u32 = 2;
119134

120135
#[pallet::pallet]
121136
pub struct Pallet<T>(_);
@@ -131,6 +146,16 @@ pub mod pallet {
131146
/// Should return the chain's genesis UTXO
132147
fn genesis_utxo() -> UtxoId;
133148

149+
/// The currency used for holding tokens
150+
type Currency: MutateHold<Self::AccountId, Reason = Self::RuntimeHoldReason>;
151+
152+
/// The amount of tokens to hold when upserting metadata
153+
#[pallet::constant]
154+
type HoldAmount: Get<BalanceOf<Self>>;
155+
156+
/// The runtime's hold reason type
157+
type RuntimeHoldReason: From<HoldReason>;
158+
134159
/// Helper providing mock values for use in benchmarks
135160
#[cfg(feature = "runtime-benchmarks")]
136161
type BenchmarkHelper: benchmarking::BenchmarkHelper<Self::BlockProducerMetadata>;
@@ -145,16 +170,26 @@ pub mod pallet {
145170
QueryKind = OptionQuery,
146171
>;
147172

173+
/// Hold reasons for this pallet
174+
#[pallet::composite_enum]
175+
pub enum HoldReason {
176+
/// Tokens held as deposit for block producer metadata
177+
MetadataDeposit,
178+
}
179+
148180
/// Error type returned by this pallet's extrinsic
149181
#[pallet::error]
150182
pub enum Error<T> {
151183
/// Signals that the signature submitted to `upsert_metadata` does not match the metadata and public key
152184
InvalidMainchainSignature,
185+
/// Insufficient balance to hold tokens as fee for upserting block producer metadata
186+
InsufficientBalance,
153187
}
154188

155189
#[pallet::call]
156190
impl<T: Config> Pallet<T> {
157191
/// Inserts or updates metadata for the block producer identified by `cross_chain_pub_key`.
192+
/// Holds a constant amount from the caller's account as a deposit for including metadata on the chain.
158193
///
159194
/// Arguments:
160195
/// - `metadata`: new metadata value
@@ -165,11 +200,12 @@ pub mod pallet {
165200
#[pallet::call_index(0)]
166201
#[pallet::weight(T::WeightInfo::upsert_metadata())]
167202
pub fn upsert_metadata(
168-
_origin: OriginFor<T>,
203+
origin: OriginFor<T>,
169204
metadata: T::BlockProducerMetadata,
170205
signature: CrossChainSignature,
171206
cross_chain_pub_key: CrossChainPublicKey,
172207
) -> DispatchResult {
208+
let origin_account = ensure_signed(origin)?;
173209
let genesis_utxo = T::genesis_utxo();
174210

175211
let cross_chain_key_hash = cross_chain_pub_key.hash();
@@ -185,6 +221,15 @@ pub mod pallet {
185221

186222
ensure!(is_valid_signature, Error::<T>::InvalidMainchainSignature);
187223

224+
if BlockProducerMetadataStorage::<T>::get(cross_chain_key_hash).is_none() {
225+
T::Currency::hold(
226+
&HoldReason::MetadataDeposit.into(),
227+
&origin_account,
228+
T::HoldAmount::get(),
229+
)
230+
.map_err(|_| Error::<T>::InsufficientBalance)?;
231+
}
232+
188233
BlockProducerMetadataStorage::<T>::insert(cross_chain_key_hash, metadata);
189234
Ok(())
190235
}

toolkit/block-producer-metadata/pallet/src/mock.rs

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
use frame_support::traits::ConstU32;
1+
use frame_support::traits::{ConstU32, ConstU128};
22
use frame_support::{
3-
construct_runtime,
3+
construct_runtime, parameter_types,
44
traits::{ConstU16, ConstU64},
55
};
66
use hex_literal::hex;
@@ -16,10 +16,12 @@ use sp_runtime::{
1616

1717
pub type Block = frame_system::mocking::MockBlock<Test>;
1818
pub type AccountId = AccountId32;
19+
pub type Balance = u128;
1920

2021
construct_runtime! {
2122
pub enum Test {
2223
System: frame_system,
24+
Balances: pallet_balances,
2325
BlockProducerMetadata: crate::pallet
2426
}
2527
}
@@ -39,7 +41,7 @@ impl frame_system::Config for Test {
3941
type BlockHashCount = ConstU64<250>;
4042
type Version = ();
4143
type PalletInfo = PalletInfo;
42-
type AccountData = ();
44+
type AccountData = pallet_balances::AccountData<Balance>;
4345
type OnNewAccount = ();
4446
type OnKilledAccount = ();
4547
type SystemWeightInfo = ();
@@ -57,6 +59,27 @@ impl frame_system::Config for Test {
5759
type PostTransactions = ();
5860
}
5961

62+
impl pallet_balances::Config for Test {
63+
type MaxLocks = ConstU32<50>;
64+
type MaxReserves = ();
65+
type ReserveIdentifier = [u8; 8];
66+
type Balance = Balance;
67+
type RuntimeEvent = RuntimeEvent;
68+
type DustRemoval = ();
69+
type ExistentialDeposit = ConstU128<1>;
70+
type AccountStore = System;
71+
type WeightInfo = pallet_balances::weights::SubstrateWeight<Test>;
72+
type FreezeIdentifier = ();
73+
type MaxFreezes = ();
74+
type RuntimeHoldReason = RuntimeHoldReason;
75+
type RuntimeFreezeReason = ();
76+
type DoneSlashHandler = ();
77+
}
78+
79+
parameter_types! {
80+
pub const MetadataHoldAmount: Balance = 1000;
81+
}
82+
6083
#[derive(
6184
Clone, Debug, MaxEncodedLen, Encode, Decode, DecodeWithMemTracking, PartialEq, Eq, TypeInfo,
6285
)]
@@ -90,16 +113,28 @@ impl crate::benchmarking::BenchmarkHelper<BlockProducerUrlMetadata>
90113
}
91114
}
92115

116+
pub(crate) const FUNDED_ACCOUNT: AccountId32 = AccountId32::new([1; 32]);
117+
93118
impl crate::pallet::Config for Test {
94119
type WeightInfo = ();
95120
type BlockProducerMetadata = BlockProducerUrlMetadata;
96121
fn genesis_utxo() -> UtxoId {
97122
UtxoId::new(hex!("59104061ffa0d66f9ba0135d6fc6a884a395b10f8ae9cb276fc2c3bfdfedc260"), 1)
98123
}
124+
type Currency = Balances;
125+
type HoldAmount = MetadataHoldAmount;
126+
type RuntimeHoldReason = RuntimeHoldReason;
99127
#[cfg(feature = "runtime-benchmarks")]
100128
type BenchmarkHelper = PalletBlockProducerMetadataBenchmarkHelper;
101129
}
102130

103131
pub fn new_test_ext() -> sp_io::TestExternalities {
104-
frame_system::GenesisConfig::<Test>::default().build_storage().unwrap().into()
132+
let mut t = frame_system::GenesisConfig::<Test>::default().build_storage().unwrap();
133+
pallet_balances::GenesisConfig::<Test> {
134+
balances: vec![(FUNDED_ACCOUNT, 100_000)],
135+
dev_accounts: None,
136+
}
137+
.assimilate_storage(&mut t)
138+
.unwrap();
139+
t.into()
105140
}

0 commit comments

Comments
 (0)