feat: add storage hub runtime (#40)

In this PR we add StorageHub pallets to the runtime. It will only be
added if the `storage-hub` feature is activated.

---------

Co-authored-by: Gonza Montiel <gonzamontiel@users.noreply.github.com>
This commit is contained in:
undercover-cactus 2025-07-01 14:01:27 +02:00 committed by GitHub
parent a7d45969d5
commit 3ea6e57b66
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 1950 additions and 214 deletions

911
operator/Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -67,6 +67,7 @@ serde = { version = "1.0.197", default-features = false, features = ["derive"] }
serde-big-array = { version = "0.3.2" }
serde_json = { version = "1.0.127", default-features = false }
sha3 = { version = "0.10", default-features = false }
smallvec = "1.11.0"
ssz_rs = { version = "0.9.0", default-features = false }
ssz_rs_derive = { version = "0.9.0", default-features = false }
static_assertions = { version = "1.1.0", default-features = false }
@ -214,3 +215,31 @@ fc-mapping-sync = { git = "https://github.com/polkadot-evm/frontier", branch = "
fc-rpc = { git = "https://github.com/polkadot-evm/frontier", branch = "stable2412", default-features = false }
fc-rpc-core = { git = "https://github.com/polkadot-evm/frontier", branch = "stable2412", default-features = false }
fc-storage = { git = "https://github.com/polkadot-evm/frontier", branch = "stable2412", default-features = false }
# StorageHub
storage-hub-runtime = { git = "https://github.com/Moonsong-Labs/storage-hub.git", rev = "e32b6a8fbbdf241d19d211c3325be7329869b85d", default-features = false }
pallet-bucket-nfts = { git = "https://github.com/Moonsong-Labs/storage-hub.git", rev = "e32b6a8fbbdf241d19d211c3325be7329869b85d", default-features = false }
pallet-cr-randomness = { git = "https://github.com/Moonsong-Labs/storage-hub.git", rev = "e32b6a8fbbdf241d19d211c3325be7329869b85d", default-features = false }
pallet-file-system = { git = "https://github.com/Moonsong-Labs/storage-hub.git", rev = "e32b6a8fbbdf241d19d211c3325be7329869b85d", default-features = false }
pallet-file-system-runtime-api = { git = "https://github.com/Moonsong-Labs/storage-hub.git", rev = "e32b6a8fbbdf241d19d211c3325be7329869b85d", default-features = false }
pallet-payment-streams = { git = "https://github.com/Moonsong-Labs/storage-hub.git", rev = "e32b6a8fbbdf241d19d211c3325be7329869b85d", default-features = false }
pallet-payment-streams-runtime-api = { git = "https://github.com/Moonsong-Labs/storage-hub.git", rev = "e32b6a8fbbdf241d19d211c3325be7329869b85d", default-features = false }
pallet-proofs-dealer = { git = "https://github.com/Moonsong-Labs/storage-hub.git", rev = "e32b6a8fbbdf241d19d211c3325be7329869b85d", default-features = false }
pallet-proofs-dealer-runtime-api = { git = "https://github.com/Moonsong-Labs/storage-hub.git", rev = "e32b6a8fbbdf241d19d211c3325be7329869b85d", default-features = false }
pallet-randomness = { git = "https://github.com/Moonsong-Labs/storage-hub.git", rev = "e32b6a8fbbdf241d19d211c3325be7329869b85d", default-features = false }
pallet-storage-providers = { git = "https://github.com/Moonsong-Labs/storage-hub.git", rev = "e32b6a8fbbdf241d19d211c3325be7329869b85d", default-features = false }
pallet-storage-providers-runtime-api = { git = "https://github.com/Moonsong-Labs/storage-hub.git", rev = "e32b6a8fbbdf241d19d211c3325be7329869b85d", default-features = false }
shp-constants = { git = "https://github.com/Moonsong-Labs/storage-hub.git", rev = "e32b6a8fbbdf241d19d211c3325be7329869b85d", default-features = false }
shp-file-metadata = { git = "https://github.com/Moonsong-Labs/storage-hub.git", rev = "e32b6a8fbbdf241d19d211c3325be7329869b85d", default-features = false }
shp-traits = { git = "https://github.com/Moonsong-Labs/storage-hub.git", rev = "e32b6a8fbbdf241d19d211c3325be7329869b85d", default-features = false }
shp-treasury-funding = { git = "https://github.com/Moonsong-Labs/storage-hub.git", rev = "e32b6a8fbbdf241d19d211c3325be7329869b85d", default-features = false }
shp-forest-verifier = { git = "https://github.com/Moonsong-Labs/storage-hub.git", rev = "e32b6a8fbbdf241d19d211c3325be7329869b85d", default-features = false }
shp-file-key-verifier = { git = "https://github.com/Moonsong-Labs/storage-hub.git", rev = "e32b6a8fbbdf241d19d211c3325be7329869b85d", default-features = false }
shp-data-price-updater = { git = "https://github.com/Moonsong-Labs/storage-hub.git", rev = "e32b6a8fbbdf241d19d211c3325be7329869b85d", default-features = false }
pallet-nfts = { git = "https://github.com/paritytech/polkadot-sdk.git", branch = "stable2412", default-features = false }
# TODO: Change sp-trie to the next stable version when it's released with the applied fix of this [PR](https://github.com/paritytech/polkadot-sdk/pull/6486)
sp-trie = { git = "https://github.com/paritytech/polkadot-sdk.git", branch = "stable2412", default-features = false }
num-bigint = { version = "0.4.3", default-features = false }

View file

@ -263,7 +263,7 @@ pub fn run() -> sc_cli::Result<()> {
None => {
let runner = cli.create_runner(&cli.run)?;
runner.run_node_until_exit(|config| async move {
match config.network.network_backend.unwrap_or_default() {
match config.network.network_backend {
sc_network::config::NetworkBackendType::Libp2p => match config.chain_spec {
ref spec if spec.is_mainnet() => {
service::new_full::<

View file

@ -32,6 +32,7 @@ frame-try-runtime = { workspace = true, optional = true }
hex = { workspace = true }
hex-literal = { workspace = true }
log = { workspace = true }
num-bigint = { workspace = true, optional = true }
pallet-authorship = { workspace = true }
pallet-babe = { workspace = true }
pallet-balances = { workspace = true }
@ -68,6 +69,7 @@ scale-info = { workspace = true, features = ["derive", "serde"] }
serde_json = { workspace = true, default-features = false, features = [
"alloc",
] }
smallvec = { workspace = true }
snowbridge-beacon-primitives = { workspace = true }
snowbridge-core = { workspace = true }
snowbridge-inbound-queue-primitives = { workspace = true }
@ -102,6 +104,28 @@ xcm = { workspace = true }
xcm-builder = { workspace = true }
xcm-executor = { workspace = true }
# StorageHub
pallet-bucket-nfts = { workspace = true, optional = true }
pallet-nfts = { workspace = true, optional = true }
pallet-cr-randomness = { workspace = true, optional = true }
pallet-file-system = { workspace = true, optional = true }
pallet-file-system-runtime-api = { workspace = true, optional = true }
pallet-payment-streams = { workspace = true, optional = true }
pallet-payment-streams-runtime-api = { workspace = true, optional = true }
pallet-proofs-dealer = { workspace = true, optional = true }
pallet-proofs-dealer-runtime-api = { workspace = true, optional = true }
pallet-randomness = { workspace = true, optional = true }
pallet-storage-providers = { workspace = true, optional = true }
pallet-storage-providers-runtime-api = { workspace = true, optional = true }
shp-constants = { workspace = true, optional = true }
shp-file-metadata = { workspace = true, optional = true }
shp-traits = { workspace = true, optional = true }
shp-treasury-funding = { workspace = true, optional = true }
shp-forest-verifier = { workspace = true, optional = true }
shp-file-key-verifier = { workspace = true, optional = true }
shp-data-price-updater = { workspace = true, optional = true }
sp-trie = { workspace = true, optional = true }
[build-dependencies]
substrate-wasm-builder = { workspace = true, optional = true, default-features = true }
@ -118,6 +142,29 @@ snowbridge-pallet-system-v2 = { workspace = true }
snowbridge-outbound-queue-primitives = { workspace = true }
[features]
storage-hub = [
"dep:num-bigint",
"dep:pallet-bucket-nfts",
"dep:pallet-nfts",
"dep:pallet-cr-randomness",
"dep:pallet-file-system",
"dep:pallet-file-system-runtime-api",
"dep:pallet-payment-streams",
"dep:pallet-payment-streams-runtime-api",
"dep:pallet-proofs-dealer",
"dep:pallet-proofs-dealer-runtime-api",
"dep:pallet-randomness",
"dep:pallet-storage-providers",
"dep:pallet-storage-providers-runtime-api",
"dep:shp-constants",
"dep:shp-file-metadata",
"dep:shp-traits",
"dep:shp-treasury-funding",
"dep:shp-forest-verifier",
"dep:shp-file-key-verifier",
"dep:shp-data-price-updater",
"dep:sp-trie"
]
default = ["std"]
std = [
"codec/std",
@ -196,6 +243,33 @@ std = [
"substrate-wasm-builder",
"pallet-outbound-commitment-store/std",
"pallet-datahaven-native-transfer/std",
# StorageHub
"pallet-authorship/std",
"pallet-balances/std",
"pallet-bucket-nfts/std",
"pallet-nfts/std",
"pallet-cr-randomness/std",
"pallet-file-system/std",
"pallet-file-system-runtime-api/std",
"pallet-payment-streams/std",
"pallet-payment-streams-runtime-api/std",
"pallet-proofs-dealer/std",
"pallet-proofs-dealer-runtime-api/std",
"pallet-randomness/std",
"pallet-session/std",
"pallet-storage-providers/std",
"pallet-storage-providers-runtime-api/std",
"pallet-sudo/std",
"pallet-timestamp/std",
"pallet-transaction-payment-rpc-runtime-api/std",
"pallet-transaction-payment/std",
"shp-constants/std",
"shp-file-metadata/std",
"shp-forest-verifier/std",
"shp-traits/std",
"shp-treasury-funding/std",
"shp-file-key-verifier/std",
]
runtime-benchmarks = [

View file

@ -23,6 +23,9 @@
//
// For more information, please refer to <http://unlicense.org>
#[cfg(feature = "storage-hub")]
mod storagehub;
pub mod runtime_params;
use super::{

View file

@ -1,10 +1,14 @@
use crate::Runtime;
use crate::{Balance, BlockNumber, Runtime, NANO_UNIT, UNIT};
use frame_support::dynamic_params::{dynamic_pallet_params, dynamic_params};
use hex_literal::hex;
use sp_core::{ConstU32, H160, H256};
use sp_runtime::{BoundedVec, Perbill};
use sp_std::vec;
#[cfg(feature = "storage-hub")]
use crate::configs::storagehub::{ChallengeTicksTolerance, ReplicationTargetType, SpMinDeposit};
#[cfg(not(feature = "storage-hub"))]
#[dynamic_params(RuntimeParameters, pallet_parameters::Parameters::<Runtime>)]
pub mod dynamic_params {
use super::*;
@ -50,6 +54,308 @@ pub mod dynamic_params {
}
}
#[cfg(feature = "storage-hub")]
#[dynamic_params(RuntimeParameters, pallet_parameters::Parameters::<Runtime>)]
pub mod dynamic_params {
use super::*;
#[dynamic_pallet_params]
#[codec(index = 0)]
pub mod runtime_config {
use super::*;
#[codec(index = 0)]
#[allow(non_upper_case_globals)]
/// Set the initial address of the Snowbridge Gateway contract on Ethereum.
/// The fact that this is a parameter means that we can set it initially to the zero address,
/// and then change it later via governance, to the actual address of the deployed contract.
pub static EthereumGatewayAddress: H160 = H160::repeat_byte(0x0);
#[codec(index = 1)]
#[allow(non_upper_case_globals)]
/// Set the initial address of the Rewards Registry contract on Ethereum.
/// The fact that this is a parameter means that we can set it initially to the zero address,
/// and then change it later via governance, to the actual address of the deployed contract.
pub static RewardsRegistryAddress: H160 = H160::repeat_byte(0x0);
#[codec(index = 2)]
#[allow(non_upper_case_globals)]
/// The Selector is the first 4 bytes of the keccak256 hash of the function signature("updateRewardsMerkleRoot(bytes32)")
pub static RewardsUpdateSelector: BoundedVec<u8, ConstU32<4>> =
BoundedVec::truncate_from(vec![0xdc, 0x3d, 0x04, 0xec]);
#[codec(index = 3)]
#[allow(non_upper_case_globals)]
/// The RewardsAgentOrigin is the hash of the string "external_validators_rewards"
/// TODO: Decide which agent origin we want to use. Currently for testing it's the zero hash
pub static RewardsAgentOrigin: H256 = H256::from_slice(&hex!(
"c505dfb2df107d106d08bd0f1a0acd10052ca9aa078629a4ccfd0c90c6e69b65"
));
// ╔══════════════════════ StorageHub Pallets ═══════════════════════╗
#[codec(index = 4)]
#[allow(non_upper_case_globals)]
/// 20 UNITs
pub static SlashAmountPerMaxFileSize: Balance = 20 * UNIT;
#[codec(index = 5)]
#[allow(non_upper_case_globals)]
/// 10k UNITs * [`MinChallengePeriod`] = 10k UNITs * 30 = 300k UNITs
///
/// This can be interpreted as "a Provider with 10k UNITs of stake would get the minimum challenge period".
pub static StakeToChallengePeriod: Balance =
10_000 * UNIT * Into::<u128>::into(MinChallengePeriod::get());
#[codec(index = 6)]
#[allow(non_upper_case_globals)]
/// The [`CheckpointChallengePeriod`] is set to be equal to the longest possible challenge period
/// (i.e. the [`StakeToChallengePeriod`] divided by the [`SpMinDeposit`]).
///
// 300k UNITs / 100 UNITs + 50 + 1 = ~3k ticks (i.e. ~5 hours with 6 seconds per tick)
pub static CheckpointChallengePeriod: BlockNumber = (StakeToChallengePeriod::get()
/ SpMinDeposit::get()).saturating_add(ChallengeTicksTolerance::get() as u128).saturating_add(1)
.try_into()
.expect(
"StakeToChallengePeriod / SpMinDeposit should be a number of ticks that can fit in BlockNumber numerical type",
);
#[codec(index = 7)]
#[allow(non_upper_case_globals)]
/// 30 ticks, or 3 minutes with 6 seconds per tick.
pub static MinChallengePeriod: BlockNumber = 30;
#[codec(index = 8)]
#[allow(non_upper_case_globals)]
/// Price decreases when system utilisation is below 30%.
pub static SystemUtilisationLowerThresholdPercentage: Perbill = Perbill::from_percent(30);
#[codec(index = 9)]
#[allow(non_upper_case_globals)]
/// Price increases when system utilisation is above 95%.
pub static SystemUtilisationUpperThresholdPercentage: Perbill = Perbill::from_percent(95);
#[codec(index = 10)]
#[allow(non_upper_case_globals)]
/// 50 [`NANOUNIT`]s is the price per GB of data, per tick.
///
/// With 6 seconds per tick, this means that over a month, the price of 1 GB is:
/// 50e-9 [`UNIT`]s * 10 ticks/min * 60 min/h * 24 h/day * 30 days/month = 21.6e-3 [`UNIT`]s
pub static MostlyStablePrice: Balance = 50 * NANO_UNIT;
#[codec(index = 11)]
#[allow(non_upper_case_globals)]
/// [`MostlyStablePrice`] * 10 = 500 [`NANOUNIT`]s
pub static MaxPrice: Balance = MostlyStablePrice::get() * 10;
#[codec(index = 12)]
#[allow(non_upper_case_globals)]
/// [`MostlyStablePrice`] / 5 = 10 [`NANOUNIT`]s
pub static MinPrice: Balance = MostlyStablePrice::get() / 5;
#[codec(index = 13)]
#[allow(non_upper_case_globals)]
/// u = [`UpperExponentFactor`]
/// system_utilisation = 1
///
/// [`MaxPrice`] = [`MostlyStablePrice`] + u * e ^ ( 1 - [`SystemUtilisationUpperThresholdPercentage`] )
///
/// 500 = 50 + u * (e ^ (1 - 0.95) - 1)
/// u = (500 - 50) / (e ^ (1 - 0.95) - 1) ≈ 8777
pub static UpperExponentFactor: u32 = 8777;
#[codec(index = 14)]
#[allow(non_upper_case_globals)]
/// l = [`LowerExponentFactor`]
/// system_utilisation = 0
///
/// [`MinPrice`] = [`MostlyStablePrice`] - u * e ^ ( [`SystemUtilisationLowerThresholdPercentage`] - 0 )
///
/// 10 = 50 - l * (e ^ (0.3 - 0) - 1)
/// l = (50 - 10) / (e ^ (0.3 - 0) - 1) ≈ 114
pub static LowerExponentFactor: u32 = 114;
#[codec(index = 15)]
#[allow(non_upper_case_globals)]
/// 0-size bucket fixed rate payment stream representing the price for 1 GB of data.
///
/// Base rate for a new fixed payment stream established between an MSP and a user.
pub static ZeroSizeBucketFixedRate: Balance = 50 * NANO_UNIT;
#[codec(index = 16)]
#[allow(non_upper_case_globals)]
/// Ideal utilisation rate of the system
pub static IdealUtilisationRate: Perbill = Perbill::from_percent(85);
#[codec(index = 17)]
#[allow(non_upper_case_globals)]
/// Decay rate of the power of two function that determines the percentage of funds that go to
/// the treasury for utilisation rates greater than the ideal.
pub static DecayRate: Perbill = Perbill::from_percent(5);
#[codec(index = 18)]
#[allow(non_upper_case_globals)]
/// The minimum treasury cut that can be taken from the amount charged from a payment stream.
pub static MinimumTreasuryCut: Perbill = Perbill::from_percent(1);
#[codec(index = 19)]
#[allow(non_upper_case_globals)]
/// The maximum treasury cut that can be taken from the amount charged from a payment stream.
pub static MaximumTreasuryCut: Perbill = Perbill::from_percent(5);
#[codec(index = 20)]
#[allow(non_upper_case_globals)]
/// The penalty a BSP must pay when they forcefully stop storing a file.
/// We set this to be half of the `SlashAmountPerMaxFileSize` with the rationale that
/// for a BSP that has lost this file, it should be more convenient to voluntarily
/// show up and pay this penalty in good faith, rather than risking being slashed for
/// being unable to submit a proof that should include this file.
pub static BspStopStoringFilePenalty: Balance = SlashAmountPerMaxFileSize::get() / 2;
/// Time-to-live for a provider to top up their deposit to cover a capacity deficit.
/// Set to 14_400 relay blocks = 1 day with 6 second timeslots.
#[codec(index = 21)]
#[allow(non_upper_case_globals)]
pub static ProviderTopUpTtl: BlockNumber = 14_400;
/// The following parameters are the replication targets for the different security levels
/// that a storage request (and thus the file it represents) can have.
///
/// These are associated with the probability that a malicious actor could hold the file hostage by controlling
/// all BSPs that volunteered and confirmed storing it.
/// The values were calculated from the probabilities derived using binomial distribution calculations,
/// where the total number of BSPs is set to 1000, the fraction of malicious BSPs is 1/3, and the target number of BSPs
/// is incremented until the probability of all selected BSPs being malicious falls below the required percentage.
///
/// The formula used is:
/// num_bsps = 1000
/// fraction_evil = 1/3
/// n_evil = int(num_bsps * fraction_evil) // = 333
/// target = range(1, num_bsps)
/// p_init = target / num_bsps
/// prob = binomial_cdf_at_least(n_evil, target, p_init)
///
/// This ensures that the replication targets were selected optimally to balance security and storage efficiency.
/// --------------------------------------------------------------------------------------------------------------------
/// The amount of BSPs that a basic security storage request should use as the replication target.
///
/// This must be the lowest amount of BSPs that guarantee that the probability that a malicious
/// actor controlling 1/3 of the BSPs can hold the file hostage by controlling all its
/// volunteered BSPs is ~1%.
#[codec(index = 22)]
#[allow(non_upper_case_globals)]
pub static BasicReplicationTarget: ReplicationTargetType = 7;
/// The amount of BSPs that a standard security storage request should use as the replication target.
///
/// This must be the lowest amount of BSPs that guarantee that the probability that a malicious
/// actor controlling 1/3 of the BSPs can hold the file hostage by controlling all its
/// volunteered BSPs is ~0.1%.
#[codec(index = 23)]
#[allow(non_upper_case_globals)]
pub static StandardReplicationTarget: ReplicationTargetType = 12;
/// The amount of BSPs that a high security storage request should use as the replication target.
///
/// This must be the lowest amount of BSPs that guarantee that the probability that a malicious
/// actor controlling 1/3 of the BSPs can hold the file hostage by controlling all its
/// volunteered BSPs is ~0.01%.
#[codec(index = 24)]
#[allow(non_upper_case_globals)]
pub static HighSecurityReplicationTarget: ReplicationTargetType = 17;
/// The amount of BSPs that a super high security storage request should use as the replication target.
///
/// This must be the lowest amount of BSPs that guarantee that the probability that a malicious
/// actor controlling 1/3 of the BSPs can hold the file hostage by controlling all its
/// volunteered BSPs is ~0.001%.
#[codec(index = 25)]
#[allow(non_upper_case_globals)]
pub static SuperHighSecurityReplicationTarget: ReplicationTargetType = 22;
/// The amount of BSPs that an ultra high security storage request should use as the replication target.
///
/// This must be the lowest amount of BSPs that guarantee that the probability that a malicious
/// actor controlling 1/3 of the BSPs can hold the file hostage by controlling all its
/// volunteered BSPs is ~0.0001%.
#[codec(index = 26)]
#[allow(non_upper_case_globals)]
pub static UltraHighSecurityReplicationTarget: ReplicationTargetType = 26;
/// The maximum amount of BSPs that a user can require a storage request to use as the replication target.
///
/// This is a safety measure to prevent users from issuing storage requests that are too large and would
/// require a large number of BSPs to store the file.
#[codec(index = 27)]
#[allow(non_upper_case_globals)]
pub static MaxReplicationTarget: ReplicationTargetType =
UltraHighSecurityReplicationTarget::get()
.saturating_mul(150)
.saturating_div(100);
/// The amount of ticks that have to pass for the threshold to volunteer for a specific storage request
/// to arrive at its maximum value.
///
/// This is big enough so volunteering for a storage request is not open to everyone inmediatly, preventing
/// a select few BSPs from taking all the requests, while small enough so that storage requests don't take
/// too long to be filled.
#[codec(index = 28)]
#[allow(non_upper_case_globals)]
pub static TickRangeToMaximumThreshold: BlockNumber = 3600; // 6 hours with a 6 second block time
/// The amount of ticks after which a storage request is considered expired and can be removed from storage.
///
/// It's a function of the TickRangeToMaximumThreshold since it does not make sense for a storage request to
/// expire before arriving at its maximum threshold for volunteering.
#[codec(index = 29)]
#[allow(non_upper_case_globals)]
pub static StorageRequestTtl: BlockNumber = TickRangeToMaximumThreshold::get()
.saturating_mul(110)
.saturating_div(100);
/// The minimum amount of ticks between a stop storing request from a BSP and that BSP being able to
/// confirm to stop storing that file key.
///
/// It's a function of the checkpoint challenge period since this makes it so BSPs can't avoid checkpoint
/// challenges by stopping storing a file key right before the challenge period ends in case they lost it.
#[codec(index = 30)]
#[allow(non_upper_case_globals)]
pub static MinWaitForStopStoring: BlockNumber = CheckpointChallengePeriod::get()
.saturating_mul(110)
.saturating_div(100);
#[codec(index = 31)]
#[allow(non_upper_case_globals)]
/// 20 ticks, or 2 minutes with 6 seconds per tick.
pub static MinSeedPeriod: BlockNumber = 20;
#[codec(index = 32)]
#[allow(non_upper_case_globals)]
/// 10k UNITs * [`MinSeedPeriod`] = 10k UNITs * 20 = 200k UNITs
///
/// This can be interpreted as "a Provider with 10k UNITs of stake would get the minimum seed period".
pub static StakeToSeedPeriod: Balance =
10_000 * UNIT * Into::<u128>::into(MinSeedPeriod::get());
#[codec(index = 33)]
#[allow(non_upper_case_globals)]
/// The amount of ticks to charge a user upfront when it tries to issue a new storage request.
/// This is done as a deterrent to avoid users spamming the network with huge files but never
/// actually planning to store them longterm.
///
/// 72k ticks = 5 days with 6 seconds per tick.
/// This means that a user must pay for 5 days of storage upfront, which gets transferred to the
/// treasury. Governance can then decide what to do with the accumulated funds.
///
/// With a stable price (defined as `MostlyStablePrice` in this file) of 50 NANOUNITs per gigabyte
/// per tick and a standard replication target (`StandardReplicationTarget`) of 12 BSPs, the upfront
/// cost for the user to issue a storage request for a 1 GB file would be:
/// 50 NANOUNITs per gigabyte per tick * 12 BSPs * 72k ticks * 1 GB = 0.0432 UNITs
pub static UpfrontTicksToPay: BlockNumber = 72_000;
// ╚══════════════════════ StorageHub Pallets ═══════════════════════╝
}
}
#[cfg(feature = "runtime-benchmarks")]
impl Default for RuntimeParameters {
fn default() -> Self {

View file

@ -0,0 +1,610 @@
use super::{
AccountId, Balance, Balances, BlockNumber, Hash, RuntimeEvent, RuntimeHoldReason, UNIT,
};
use crate::configs::runtime_params::dynamic_params::runtime_config;
use crate::{
BucketNfts, Nfts, PaymentStreams, ProofsDealer, Providers, Runtime, Signature, WeightToFee,
HOURS,
};
use core::marker::PhantomData;
use datahaven_runtime_common::time::{DAYS, MINUTES};
use frame_support::pallet_prelude::DispatchClass;
use frame_support::traits::AsEnsureOriginWithArg;
use frame_support::{
parameter_types,
traits::{ConstU128, ConstU32, ConstU64},
weights::Weight,
};
use frame_system::pallet_prelude::BlockNumberFor;
use frame_system::EnsureRoot;
use frame_system::EnsureSigned;
use num_bigint::BigUint;
use pallet_nfts::PalletFeatures;
use polkadot_runtime_common::prod_or_fast;
use shp_data_price_updater::{MostlyStablePriceIndexUpdater, MostlyStablePriceIndexUpdaterConfig};
use shp_file_key_verifier::FileKeyVerifier;
use shp_file_metadata::{ChunkId, FileMetadata};
use shp_forest_verifier::ForestVerifier;
use shp_treasury_funding::{
LinearThenPowerOfTwoTreasuryCutCalculator, LinearThenPowerOfTwoTreasuryCutCalculatorConfig,
};
use sp_core::Get;
use sp_core::Hasher;
use sp_core::H256;
use sp_runtime::traits::Convert;
use sp_runtime::traits::ConvertBack;
use sp_runtime::traits::Verify;
use sp_runtime::traits::Zero;
use sp_runtime::SaturatedConversion;
use sp_runtime::{traits::BlakeTwo256, Perbill};
use sp_std::convert::{From, Into};
use sp_std::vec;
use sp_trie::{LayoutV1, TrieConfiguration, TrieLayout};
/// Type representing the storage data units in StorageHub.
pub type StorageDataUnit = u64;
pub type StorageProofsMerkleTrieLayout = LayoutV1<BlakeTwo256>;
pub type Hashing = BlakeTwo256;
// TODO: remove this and replace with pallet treasury
pub struct TreasuryAccount;
impl Get<AccountId> for TreasuryAccount {
fn get() -> AccountId {
AccountId::from([0; 32])
}
}
/****** NFTs pallet ******/
parameter_types! {
pub const CollectionDeposit: Balance = 100 * UNIT;
pub const ItemDeposit: Balance = 1 * UNIT;
pub const MetadataDepositBase: Balance = 10 * UNIT;
pub const MetadataDepositPerByte: Balance = 1 * UNIT;
pub const ApprovalsLimit: u32 = 20;
pub const ItemAttributesApprovalsLimit: u32 = 20;
pub const MaxTips: u32 = 10;
pub const MaxDeadlineDuration: BlockNumber = 12 * 30 * DAYS;
pub const MaxAttributesPerCall: u32 = 10;
pub Features: PalletFeatures = PalletFeatures::all_enabled();
}
impl pallet_nfts::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
type CollectionId = u32;
type ItemId = u32;
type Currency = Balances;
type CreateOrigin = AsEnsureOriginWithArg<EnsureSigned<AccountId>>;
type ForceOrigin = frame_system::EnsureRoot<AccountId>;
type CollectionDeposit = CollectionDeposit;
type ItemDeposit = ItemDeposit;
type MetadataDepositBase = MetadataDepositBase;
type AttributeDepositBase = MetadataDepositBase;
type DepositPerByte = MetadataDepositPerByte;
type StringLimit = ConstU32<256>;
type KeyLimit = ConstU32<64>;
type ValueLimit = ConstU32<256>;
type ApprovalsLimit = ApprovalsLimit;
type ItemAttributesApprovalsLimit = ItemAttributesApprovalsLimit;
type MaxTips = MaxTips;
type MaxDeadlineDuration = MaxDeadlineDuration;
type MaxAttributesPerCall = MaxAttributesPerCall;
type Features = Features;
type OffchainSignature = Signature;
type OffchainPublic = <Signature as Verify>::Signer;
type WeightInfo = pallet_nfts::weights::SubstrateWeight<Runtime>;
type Locker = ();
}
/****** ****** ****** ******/
/****** Relay Randomness pallet ******/
impl pallet_randomness::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
type BabeDataGetter = BabeDataGetter;
type BabeBlockGetter = BlockNumberGetter;
type WeightInfo = ();
type BabeDataGetterBlockNumber = BlockNumber;
}
pub struct BabeDataGetter;
impl pallet_randomness::GetBabeData<u64, Hash> for BabeDataGetter {
fn get_epoch_index() -> u64 {
todo!("implement `get_epoch_index`");
}
fn get_epoch_randomness() -> Hash {
todo!("implement `get_epoch_randomness`");
}
fn get_parent_randomness() -> Hash {
todo!("implement `get_parent_randomness`");
}
}
pub struct BlockNumberGetter {}
impl sp_runtime::traits::BlockNumberProvider for BlockNumberGetter {
type BlockNumber = BlockNumber;
fn current_block_number() -> Self::BlockNumber {
frame_system::Pallet::<Runtime>::block_number()
}
}
/****** ****** ****** ******/
/****** Storage Providers pallet ******/
parameter_types! {
pub const SpMinDeposit: Balance = 100 * UNIT;
pub const BucketDeposit: Balance = 100 * UNIT;
pub const BspSignUpLockPeriod: BlockNumber = 90 * DAYS; // ~3 months
pub const MaxBlocksForRandomness: BlockNumber = prod_or_fast!(2 * HOURS, 2 * MINUTES);
// TODO: If the next line is uncommented (which should be eventually, replacing the line above), compilation breaks (most likely because of mismatched dependency issues)
// pub const MaxBlocksForRandomness: BlockNumber = prod_or_fast!(2 * runtime_constants::time::EPOCH_DURATION_IN_SLOTS, 2 * MINUTES);
}
impl pallet_storage_providers::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
type WeightInfo = pallet_storage_providers::weights::SubstrateWeight<Runtime>;
type ProvidersRandomness = pallet_randomness::RandomnessFromOneEpochAgo<Runtime>;
type PaymentStreams = PaymentStreams;
type ProofDealer = ProofsDealer;
type FileMetadataManager = FileMetadata<
{ shp_constants::H_LENGTH },
{ shp_constants::FILE_CHUNK_SIZE },
{ shp_constants::FILE_SIZE_TO_CHALLENGES },
>;
type NativeBalance = Balances;
type CrRandomness = MockCrRandomness;
type RuntimeHoldReason = RuntimeHoldReason;
type StorageDataUnit = StorageDataUnit;
type StorageDataUnitAndBalanceConvert = StorageDataUnitAndBalanceConverter;
type SpCount = u32;
type BucketCount = u128;
type MerklePatriciaRoot = Hash;
type MerkleTrieHashing = Hashing;
type ProviderId = Hash;
type ProviderIdHashing = Hashing;
type ValuePropId = Hash;
type ValuePropIdHashing = Hashing;
type ReadAccessGroupId = <Self as pallet_nfts::Config>::CollectionId;
type ProvidersProofSubmitters = ProofsDealer;
type ReputationWeightType = u32;
type StorageHubTickGetter = ProofsDealer;
type Treasury = TreasuryAccount;
type SpMinDeposit = SpMinDeposit;
type SpMinCapacity = ConstU64<2>;
type DepositPerData = ConstU128<2>;
type MaxFileSize = ConstU64<{ u64::MAX }>;
type MaxMultiAddressSize = ConstU32<100>;
type MaxMultiAddressAmount = ConstU32<5>;
type MaxProtocols = ConstU32<100>;
type BucketDeposit = BucketDeposit;
type BucketNameLimit = ConstU32<100>;
type MaxBlocksForRandomness = MaxBlocksForRandomness;
type MinBlocksBetweenCapacityChanges = ConstU32<10>;
type DefaultMerkleRoot = DefaultMerkleRoot<StorageProofsMerkleTrieLayout>;
type SlashAmountPerMaxFileSize = runtime_config::SlashAmountPerMaxFileSize;
type StartingReputationWeight = ConstU32<1>;
type BspSignUpLockPeriod = BspSignUpLockPeriod;
type MaxCommitmentSize = ConstU32<1000>;
type ZeroSizeBucketFixedRate = runtime_config::ZeroSizeBucketFixedRate;
type ProviderTopUpTtl = runtime_config::ProviderTopUpTtl;
type MaxExpiredItemsInBlock = ConstU32<100>;
}
pub struct StorageDataUnitAndBalanceConverter;
impl Convert<StorageDataUnit, Balance> for StorageDataUnitAndBalanceConverter {
fn convert(data_unit: StorageDataUnit) -> Balance {
data_unit.saturated_into()
}
}
impl ConvertBack<StorageDataUnit, Balance> for StorageDataUnitAndBalanceConverter {
fn convert_back(balance: Balance) -> StorageDataUnit {
balance.saturated_into()
}
}
pub type HasherOutT<T> = <<T as TrieLayout>::Hash as Hasher>::Out;
pub struct DefaultMerkleRoot<T>(PhantomData<T>);
impl<T: TrieConfiguration> Get<HasherOutT<T>> for DefaultMerkleRoot<T> {
fn get() -> HasherOutT<T> {
sp_trie::empty_trie_root::<T>()
}
}
/****** ****** ****** ******/
/****** Payment Streams pallet ******/
parameter_types! {
pub const PaymentStreamHoldReason: RuntimeHoldReason = RuntimeHoldReason::PaymentStreams(pallet_payment_streams::HoldReason::PaymentStreamDeposit);
pub const UserWithoutFundsCooldown: BlockNumber = 100;
}
impl pallet_payment_streams::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
type WeightInfo = pallet_payment_streams::weights::SubstrateWeight<Runtime>;
type NativeBalance = Balances;
type ProvidersPallet = Providers;
type RuntimeHoldReason = RuntimeHoldReason;
type UserWithoutFundsCooldown = UserWithoutFundsCooldown; // Amount of blocks that a user will have to wait before being able to clear the out of funds flag
type NewStreamDeposit = ConstU32<10>; // Amount of blocks that the deposit of a new stream should be able to pay for
type Units = StorageDataUnit; // Storage unit
type BlockNumberToBalance = BlockNumberToBalance;
type ProvidersProofSubmitters = ProofsDealer;
type TreasuryCutCalculator = LinearThenPowerOfTwoTreasuryCutCalculator<Runtime, Perbill>;
type TreasuryAccount = TreasuryAccount;
type MaxUsersToCharge = ConstU32<10>;
type BaseDeposit = ConstU128<10>;
}
// Converter from the BlockNumber type to the Balance type for math
pub struct BlockNumberToBalance;
impl Convert<BlockNumber, Balance> for BlockNumberToBalance {
fn convert(block_number: BlockNumber) -> Balance {
block_number.into() // In this converter we assume that the block number type is smaller in size than the balance type
}
}
impl LinearThenPowerOfTwoTreasuryCutCalculatorConfig<Perbill> for Runtime {
type Balance = Balance;
type ProvidedUnit = StorageDataUnit;
type IdealUtilisationRate = runtime_config::IdealUtilisationRate;
type DecayRate = runtime_config::DecayRate;
type MinimumCut = runtime_config::MinimumTreasuryCut;
type MaximumCut = runtime_config::MaximumTreasuryCut;
}
/****** ****** ****** ******/
/****** Proofs Dealer pallet ******/
const RANDOM_CHALLENGES_PER_BLOCK: u32 = 10;
const MAX_CUSTOM_CHALLENGES_PER_BLOCK: u32 = 10;
const TOTAL_MAX_CHALLENGES_PER_BLOCK: u32 =
RANDOM_CHALLENGES_PER_BLOCK + MAX_CUSTOM_CHALLENGES_PER_BLOCK;
parameter_types! {
pub const RandomChallengesPerBlock: u32 = RANDOM_CHALLENGES_PER_BLOCK;
pub const MaxCustomChallengesPerBlock: u32 = MAX_CUSTOM_CHALLENGES_PER_BLOCK;
pub const TotalMaxChallengesPerBlock: u32 = TOTAL_MAX_CHALLENGES_PER_BLOCK;
pub const TargetTicksStorageOfSubmitters: u32 = 3;
pub const ChallengeHistoryLength: BlockNumber = 100;
pub const ChallengesQueueLength: u32 = 100;
pub const ChallengesFee: Balance = 1 * UNIT;
pub const ChallengeTicksTolerance: u32 = 50;
}
impl pallet_proofs_dealer::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
type WeightInfo = pallet_proofs_dealer::weights::SubstrateWeight<Runtime>;
type ProvidersPallet = Providers;
type NativeBalance = Balances;
type MerkleTrieHash = Hash;
type MerkleTrieHashing = BlakeTwo256;
type ForestVerifier = ForestVerifier<StorageProofsMerkleTrieLayout, { BlakeTwo256::LENGTH }>;
type KeyVerifier = FileKeyVerifier<
StorageProofsMerkleTrieLayout,
{ shp_constants::H_LENGTH },
{ shp_constants::FILE_CHUNK_SIZE },
{ shp_constants::FILE_SIZE_TO_CHALLENGES },
>;
type StakeToBlockNumber = SaturatingBalanceToBlockNumber;
type RandomChallengesPerBlock = RandomChallengesPerBlock;
type MaxCustomChallengesPerBlock = MaxCustomChallengesPerBlock;
type MaxSubmittersPerTick = MaxSubmittersPerTick;
type TargetTicksStorageOfSubmitters = TargetTicksStorageOfSubmitters;
type ChallengeHistoryLength = ChallengeHistoryLength;
type ChallengesQueueLength = ChallengesQueueLength;
type CheckpointChallengePeriod = runtime_config::CheckpointChallengePeriod;
type ChallengesFee = ChallengesFee;
type Treasury = TreasuryAccount;
// TODO: Once the client logic to keep track of CR randomness deadlines and execute their submissions is implemented
// AND after the chain has been live for enough time to have enough providers to avoid the commit-reveal randomness being
// gameable, the randomness provider should be CrRandomness
type RandomnessProvider = pallet_randomness::ParentBlockRandomness<Runtime>;
type StakeToChallengePeriod = runtime_config::StakeToChallengePeriod;
type MinChallengePeriod = runtime_config::MinChallengePeriod;
type ChallengeTicksTolerance = ChallengeTicksTolerance;
type BlockFullnessPeriod = ChallengeTicksTolerance; // We purposely set this to `ChallengeTicksTolerance` so that spamming of the chain is evaluated for the same blocks as the tolerance BSPs are given.
type BlockFullnessHeadroom = BlockFullnessHeadroom;
type MinNotFullBlocksRatio = MinNotFullBlocksRatio;
type MaxSlashableProvidersPerTick = MaxSlashableProvidersPerTick;
}
// Converter from the Balance type to the BlockNumber type for math.
// It performs a saturated conversion, so that the result is always a valid BlockNumber.
pub struct SaturatingBalanceToBlockNumber;
impl Convert<Balance, BlockNumberFor<Runtime>> for SaturatingBalanceToBlockNumber {
fn convert(block_number: Balance) -> BlockNumberFor<Runtime> {
block_number.saturated_into()
}
}
pub struct MaxSubmittersPerTick;
impl Get<u32> for MaxSubmittersPerTick {
fn get() -> u32 {
let block_weights = <Runtime as frame_system::Config>::BlockWeights::get();
// Not being able to get the `max_total` weight for the Normal dispatch class is considered
// a critical bug. So we set it to be zero, essentially allowing zero submitters per tick.
// This value can be read from the constants of a node, but with the current configuration, this is:
//
// max_total: {
// ref_time: 1,500,000,000,000
// proof_size: 3,932,160
// }
let max_weight_for_class = block_weights
.get(DispatchClass::Normal)
.max_total
.unwrap_or(Zero::zero());
// Get the minimum weight a `submit_proof` extrinsic can have.
// This would be the case where the proof is just made up of a single file key proof, that is a
// response to all the random challenges. And there are no checkpoint challenges.
// With the current benchmarking, this is:
//
// TODO: UPDATE THIS WITH THE FINAL BENCHMARKING
// min_weight_for_submit_proof: {
// ref_time: 2,980,252,675
// proof_size: 16,056
// }
let min_weight_for_submit_proof =
<pallet_proofs_dealer::weights::SubstrateWeight<Runtime> as pallet_proofs_dealer::weights::WeightInfo>::submit_proof_no_checkpoint_challenges_key_proofs(1);
// Calculate the maximum number of submit proofs that is possible to have in a block/tick.
// With the current values, this would be:
//
// TODO: UPDATE THIS WITH THE FINAL BENCHMARKING
// 244 proof submissions per block (limited by `proof_size`)
let max_proof_submissions_per_tick = max_weight_for_class
.checked_div_per_component(&min_weight_for_submit_proof)
.unwrap_or(0);
// Saturating u64 to u32 should be enough.
max_proof_submissions_per_tick.saturated_into()
}
}
pub struct BlockFullnessHeadroom;
impl Get<Weight> for BlockFullnessHeadroom {
fn get() -> Weight {
// The block headroom is set to be the maximum benchmarked weight that a `submit_proof` extrinsic can have.
// That is, when the proof includes two file key proofs for every single random challenge, and for the maximum
// number of checkpoint challenges as well.
<pallet_proofs_dealer::weights::SubstrateWeight<Runtime> as pallet_proofs_dealer::weights::WeightInfo>::submit_proof_with_checkpoint_challenges_key_proofs(TOTAL_MAX_CHALLENGES_PER_BLOCK * 2)
}
}
pub struct MinNotFullBlocksRatio;
impl Get<Perbill> for MinNotFullBlocksRatio {
fn get() -> Perbill {
// This means that we tolerate at most 50% of misbehaving collators.
Perbill::from_percent(50)
}
}
pub struct MaxSlashableProvidersPerTick;
impl Get<u32> for MaxSlashableProvidersPerTick {
fn get() -> u32 {
// With the maximum number of slashable providers per tick being `N`, the absolute maximum
// weight that the `on_poll` hook can have, with the current benchmarking, is:
//
// TODO: UPDATE THIS WITH THE FINAL BENCHMARKING
// new_challenges_round_weight: {
// ref_time: 576,000,000 + N * 551,601,146
// proof_size: 8,523 + N * 3,158
// }
// new_checkpoint_challenge_round_max_weight: {
// ref_time: 587,205,208 + ChallengesQueueLength * 225,083 = 610,554,678
// proof_size: 4,787
// }
// check_spamming_condition_weight: {
// ref_time: 313,000,000
// proof_size: 6,012
// }
//
// For `N` = 1000, this would be:
// max_on_poll_weight: {
// ref_time: 313,000,000 + 610,554,678 + 576,000,000 + N * 551,601,146 ≈ 553,100,700,678
// proof_size: 6,012 + 4,787 + 8,523 + N * 3,158 ≈ 3,177,322
// }
//
// Consider that the maximum block weight is:
// maxBlock: {
// ref_time: 2,000,000,000,000
// proof_size: 5,242,880
// }
//
// This `on_poll` hook would consume roughly 1/4 of the block `ref_time` and 3/5 of the block `proof_size`.
// This is naturally a lot. But it would be a very unlikely scenario.
//
// This would be the case where all `N` Providers have synchronised their challenge periods
// and have the same deadline, plus, all of them missed their proof submissions.
// The normal scenario would be that NONE (or just a small number) of the Providers have
// missed their proof submissions.
let max_slashable_providers_per_tick = 1000;
max_slashable_providers_per_tick
}
}
/****** ****** ****** ******/
/****** File System pallet ******/
type ThresholdType = u32;
pub type ReplicationTargetType = u32;
parameter_types! {
pub const BaseStorageRequestCreationDeposit: Balance = 1 * UNIT;
pub const FileDeletionRequestCreationDeposit: Balance = 1 * UNIT;
pub const FileSystemStorageRequestCreationHoldReason: RuntimeHoldReason = RuntimeHoldReason::FileSystem(pallet_file_system::HoldReason::StorageRequestCreationHold);
pub const FileSystemFileDeletionRequestHoldReason: RuntimeHoldReason = RuntimeHoldReason::FileSystem(pallet_file_system::HoldReason::FileDeletionRequestHold);
}
impl pallet_file_system::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
type WeightInfo = pallet_file_system::weights::SubstrateWeight<Runtime>;
type Providers = Providers;
type ProofDealer = ProofsDealer;
type PaymentStreams = PaymentStreams;
// TODO: Replace the mocked CR randomness with the actual one when it's ready
// type CrRandomness = CrRandomness;
type CrRandomness = MockCrRandomness;
type UpdateStoragePrice = MostlyStablePriceIndexUpdater<Runtime>;
type UserSolvency = PaymentStreams;
type Fingerprint = Hash;
type ReplicationTargetType = ReplicationTargetType;
type ThresholdType = ThresholdType;
type ThresholdTypeToTickNumber = ThresholdTypeToBlockNumberConverter;
type HashToThresholdType = HashToThresholdTypeConverter;
type MerkleHashToRandomnessOutput = MerkleHashToRandomnessOutputConverter;
type ChunkIdToMerkleHash = ChunkIdToMerkleHashConverter;
type Currency = Balances;
type RuntimeHoldReason = RuntimeHoldReason;
type Nfts = Nfts;
type CollectionInspector = BucketNfts;
type BspStopStoringFilePenalty = runtime_config::BspStopStoringFilePenalty;
type TreasuryAccount = TreasuryAccount;
type MaxBatchConfirmStorageRequests = ConstU32<10>;
type MaxFilePathSize = ConstU32<512u32>;
type MaxPeerIdSize = ConstU32<100>;
type MaxNumberOfPeerIds = ConstU32<5>;
type MaxDataServerMultiAddresses = ConstU32<10>;
type MaxExpiredItemsInTick = ConstU32<100>;
type StorageRequestTtl = runtime_config::StorageRequestTtl;
type MoveBucketRequestTtl = ConstU32<40u32>;
type MaxUserPendingDeletionRequests = ConstU32<10u32>;
type MaxUserPendingMoveBucketRequests = ConstU32<10u32>;
type MinWaitForStopStoring = runtime_config::MinWaitForStopStoring;
type BaseStorageRequestCreationDeposit = BaseStorageRequestCreationDeposit;
type UpfrontTicksToPay = runtime_config::UpfrontTicksToPay;
type WeightToFee = WeightToFee;
type ReplicationTargetToBalance = ReplicationTargetToBalance;
type TickNumberToBalance = TickNumberToBalance;
type StorageDataUnitToBalance = StorageDataUnitToBalance;
type FileDeletionRequestDeposit = FileDeletionRequestCreationDeposit;
type BasicReplicationTarget = runtime_config::BasicReplicationTarget;
type StandardReplicationTarget = runtime_config::StandardReplicationTarget;
type HighSecurityReplicationTarget = runtime_config::HighSecurityReplicationTarget;
type SuperHighSecurityReplicationTarget = runtime_config::SuperHighSecurityReplicationTarget;
type UltraHighSecurityReplicationTarget = runtime_config::UltraHighSecurityReplicationTarget;
type MaxReplicationTarget = runtime_config::MaxReplicationTarget;
type TickRangeToMaximumThreshold = runtime_config::TickRangeToMaximumThreshold;
}
impl MostlyStablePriceIndexUpdaterConfig for Runtime {
type Price = Balance;
type StorageDataUnit = StorageDataUnit;
type LowerThreshold = runtime_config::SystemUtilisationLowerThresholdPercentage;
type UpperThreshold = runtime_config::SystemUtilisationUpperThresholdPercentage;
type MostlyStablePrice = runtime_config::MostlyStablePrice;
type MaxPrice = runtime_config::MaxPrice;
type MinPrice = runtime_config::MinPrice;
type UpperExponentFactor = runtime_config::UpperExponentFactor;
type LowerExponentFactor = runtime_config::LowerExponentFactor;
}
// Converter from the ThresholdType to the BlockNumber type and vice versa.
// It performs a saturated conversion, so that the result is always a valid BlockNumber.
pub struct ThresholdTypeToBlockNumberConverter;
impl Convert<ThresholdType, BlockNumberFor<Runtime>> for ThresholdTypeToBlockNumberConverter {
fn convert(threshold: ThresholdType) -> BlockNumberFor<Runtime> {
threshold.saturated_into()
}
}
impl ConvertBack<ThresholdType, BlockNumberFor<Runtime>> for ThresholdTypeToBlockNumberConverter {
fn convert_back(block_number: BlockNumberFor<Runtime>) -> ThresholdType {
block_number.into()
}
}
/// Converter from the [`Hash`] type to the [`ThresholdType`].
pub struct HashToThresholdTypeConverter;
impl Convert<<Runtime as frame_system::Config>::Hash, ThresholdType>
for HashToThresholdTypeConverter
{
fn convert(hash: <Runtime as frame_system::Config>::Hash) -> ThresholdType {
// Get the hash as bytes
let hash_bytes = hash.as_ref();
// Get the 4 least significant bytes of the hash and interpret them as an u32
let truncated_hash_bytes: [u8; 4] =
hash_bytes[28..].try_into().expect("Hash is 32 bytes; qed");
ThresholdType::from_be_bytes(truncated_hash_bytes)
}
}
// Converter from the MerkleHash (H256) type to the RandomnessOutput (H256) type.
pub struct MerkleHashToRandomnessOutputConverter;
impl Convert<H256, H256> for MerkleHashToRandomnessOutputConverter {
fn convert(hash: H256) -> H256 {
hash
}
}
// Converter from the ChunkId type to the MerkleHash (H256) type.
pub struct ChunkIdToMerkleHashConverter;
impl Convert<ChunkId, H256> for ChunkIdToMerkleHashConverter {
fn convert(chunk_id: ChunkId) -> H256 {
let chunk_id_biguint = BigUint::from(chunk_id.as_u64());
let mut bytes = chunk_id_biguint.to_bytes_be();
// Ensure the byte slice is exactly 32 bytes long by padding with leading zeros
if bytes.len() < 32 {
let mut padded_bytes = vec![0u8; 32 - bytes.len()];
padded_bytes.extend(bytes);
bytes = padded_bytes;
}
H256::from_slice(&bytes)
}
}
// Converter from the ReplicationTargetType type to the Balance type.
pub struct ReplicationTargetToBalance;
impl Convert<ReplicationTargetType, Balance> for ReplicationTargetToBalance {
fn convert(replication_target: ReplicationTargetType) -> Balance {
replication_target.into()
}
}
// Converter from the TickNumber type to the Balance type.
pub type TickNumber = BlockNumber;
pub struct TickNumberToBalance;
impl Convert<TickNumber, Balance> for TickNumberToBalance {
fn convert(tick_number: TickNumber) -> Balance {
tick_number.into()
}
}
// Converter from the StorageDataUnit type to the Balance type.
pub struct StorageDataUnitToBalance;
impl Convert<StorageDataUnit, Balance> for StorageDataUnitToBalance {
fn convert(storage_data_unit: StorageDataUnit) -> Balance {
storage_data_unit.into()
}
}
/****** ****** ****** ******/
/****** Bucket NFTs pallet ******/
impl pallet_bucket_nfts::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
type WeightInfo = pallet_bucket_nfts::weights::SubstrateWeight<Runtime>;
type Buckets = Providers;
}
/****** ****** ****** ******/
/****** Commit-Reveal Randomness pallet ******/
pub struct MockCrRandomness;
impl shp_traits::CommitRevealRandomnessInterface for MockCrRandomness {
type ProviderId = Hash;
fn initialise_randomness_cycle(
_who: &Self::ProviderId,
) -> frame_support::dispatch::DispatchResult {
Ok(())
}
fn stop_randomness_cycle(_who: &Self::ProviderId) -> frame_support::dispatch::DispatchResult {
Ok(())
}
}
/****** ****** ****** ******/

View file

@ -47,6 +47,14 @@ use sp_version::NativeVersion;
use sp_version::RuntimeVersion;
use xcm::VersionedLocation;
// Need by storage hub
use frame_support::weights::{
constants::ExtrinsicBaseWeight, WeightToFeeCoefficient, WeightToFeeCoefficients,
WeightToFeePolynomial,
};
use smallvec::smallvec;
use sp_runtime::Perbill;
pub use datahaven_runtime_common::{
time::EpochDurationInBlocks, AccountId, Address, Balance, BlockNumber, Hash, Header, Nonce,
Signature,
@ -132,8 +140,11 @@ pub const SUPPLY_FACTOR: Balance = 1;
// Unit = the base number of indivisible units for balances
pub const UNIT: Balance = 1_000_000_000_000;
pub const CENTS: Balance = UNIT / 100;
pub const MILLI_UNIT: Balance = 1_000_000_000;
pub const MICRO_UNIT: Balance = 1_000_000;
pub const NANO_UNIT: Balance = 1_000;
pub const PICO_UNIT: Balance = 1;
pub const STORAGE_BYTE_FEE: Balance = 100 * MICRO_UNIT * SUPPLY_FACTOR;
@ -212,8 +223,36 @@ where
}
}
/// Handles converting a weight scalar to a fee value, based on the scale and granularity of the
/// node's balance type.
///
/// This should typically create a mapping between the following ranges:
/// - `[0, MAXIMUM_BLOCK_WEIGHT]`
/// - `[Balance::min, Balance::max]`
///
/// Yet, it can be used for any other sort of change to weight-fee. Some examples being:
/// - Setting it to `0` will essentially disable the weight fee.
/// - Setting it to `1` will cause the literal `#[weight = x]` values to be charged.
pub struct WeightToFee;
impl WeightToFeePolynomial for WeightToFee {
type Balance = Balance;
fn polynomial() -> WeightToFeeCoefficients<Self::Balance> {
// in Rococo, extrinsic base weight (smallest non-zero weight) is mapped to 1 MILLIUNIT:
// in our template, we map to 1/10 of that, or 1/10 MILLIUNIT
let p = MILLI_UNIT / 10;
let q = 100 * Balance::from(ExtrinsicBaseWeight::get().ref_time());
smallvec![WeightToFeeCoefficient {
degree: 1,
negative: false,
coeff_frac: Perbill::from_rational(p % q, q),
coeff_integer: p / q,
}]
}
}
// Create the runtime by composing the FRAME pallets that were previously configured.
#[frame_support::runtime]
#[cfg(not(feature = "storage-hub"))]
mod runtime {
#[runtime::runtime]
#[runtime::derive(
@ -346,6 +385,193 @@ mod runtime {
// ╔══════════════════════ StorageHub Pallets ═══════════════════════╗
// Start with index 80
// #[runtime::pallet_index(80)]
// pub type Providers = pallet_storage_providers;
// #[runtime::pallet_index(81)]
// pub type FileSystem = pallet_file_system;
// #[runtime::pallet_index(82)]
// pub type ProofsDealer = pallet_proofs_dealer;
// #[runtime::pallet_index(83)]
// pub type Randomness = pallet_randomness;
// #[runtime::pallet_index(84)]
// pub type PaymentStreams = pallet_payment_streams;
// #[runtime::pallet_index(85)]
// pub type BucketNfts = pallet_bucket_nfts;
// #[runtime::pallet_index(90)]
// pub type Nfts = pallet_nfts;
// ╚══════════════════════ StorageHub Pallets ═══════════════════════╝
// ╔═══════════════════ DataHaven-specific Pallets ══════════════════╗
// Start with index 100
#[runtime::pallet_index(100)]
pub type OutboundCommitmentStore = pallet_outbound_commitment_store;
#[runtime::pallet_index(101)]
pub type ExternalValidatorsRewards = pallet_external_validators_rewards;
#[runtime::pallet_index(102)]
pub type DataHavenNativeTransfer = pallet_datahaven_native_transfer;
// ╚═══════════════════ DataHaven-specific Pallets ══════════════════╝
}
// Create the runtime by composing the FRAME pallets that were previously configured.
#[frame_support::runtime]
#[cfg(feature = "storage-hub")]
mod runtime {
#[runtime::runtime]
#[runtime::derive(
RuntimeCall,
RuntimeEvent,
RuntimeError,
RuntimeOrigin,
RuntimeFreezeReason,
RuntimeHoldReason,
RuntimeSlashReason,
RuntimeLockId,
RuntimeTask
)]
pub struct Runtime;
// ╔══════════════════ System and Consensus Pallets ═════════════════╗
#[runtime::pallet_index(0)]
pub type System = frame_system;
// Babe must be before session.
#[runtime::pallet_index(1)]
pub type Babe = pallet_babe;
#[runtime::pallet_index(2)]
pub type Timestamp = pallet_timestamp;
#[runtime::pallet_index(3)]
pub type Balances = pallet_balances;
// Consensus support.
// Authorship must be before session in order to note author in the correct session and era.
#[runtime::pallet_index(4)]
pub type Authorship = pallet_authorship;
#[runtime::pallet_index(5)]
pub type Offences = pallet_offences;
#[runtime::pallet_index(6)]
pub type Historical = pallet_session::historical;
// External Validators must be before Session.
#[runtime::pallet_index(7)]
pub type ExternalValidators = pallet_external_validators;
#[runtime::pallet_index(8)]
pub type Session = pallet_session;
#[runtime::pallet_index(9)]
pub type ImOnline = pallet_im_online;
#[runtime::pallet_index(10)]
pub type Grandpa = pallet_grandpa;
#[runtime::pallet_index(11)]
pub type TransactionPayment = pallet_transaction_payment;
#[runtime::pallet_index(12)]
pub type Beefy = pallet_beefy;
#[runtime::pallet_index(13)]
pub type Mmr = pallet_mmr;
#[runtime::pallet_index(14)]
pub type BeefyMmrLeaf = pallet_beefy_mmr;
// ╚═════════════════ System and Consensus Pallets ══════════════════╝
// ╔═════════════════ Polkadot SDK Utility Pallets ══════════════════╗
#[runtime::pallet_index(30)]
pub type Utility = pallet_utility;
#[runtime::pallet_index(31)]
pub type Scheduler = pallet_scheduler;
#[runtime::pallet_index(32)]
pub type Preimage = pallet_preimage;
#[runtime::pallet_index(33)]
pub type Identity = pallet_identity;
#[runtime::pallet_index(34)]
pub type Multisig = pallet_multisig;
#[runtime::pallet_index(35)]
pub type Parameters = pallet_parameters;
#[runtime::pallet_index(36)]
pub type Sudo = pallet_sudo;
// ╚═════════════════ Polkadot SDK Utility Pallets ══════════════════╝
// ╔════════════════════ Frontier (EVM) Pallets ═════════════════════╗
#[runtime::pallet_index(50)]
pub type Ethereum = pallet_ethereum;
#[runtime::pallet_index(51)]
pub type Evm = pallet_evm;
#[runtime::pallet_index(52)]
pub type EvmChainId = pallet_evm_chain_id;
// ╚════════════════════ Frontier (EVM) Pallets ═════════════════════╝
// ╔══════════════════════ Snowbridge Pallets ═══════════════════════╗
#[runtime::pallet_index(60)]
pub type EthereumBeaconClient = snowbridge_pallet_ethereum_client;
#[runtime::pallet_index(61)]
pub type EthereumInboundQueueV2 = snowbridge_pallet_inbound_queue_v2;
#[runtime::pallet_index(62)]
pub type EthereumOutboundQueueV2 = snowbridge_pallet_outbound_queue_v2;
#[runtime::pallet_index(63)]
pub type SnowbridgeSystem = snowbridge_pallet_system;
#[runtime::pallet_index(64)]
pub type SnowbridgeSystemV2 = snowbridge_pallet_system_v2;
// ╚══════════════════════ Snowbridge Pallets ═══════════════════════╝
// ╔════════════ Polkadot SDK Utility Pallets - Block 2 ═════════════╗
// The Message Queue pallet has to be after the Snowbridge Outbound
// Queue V2 pallet since the former processes messages in its
// `on_initialize` hook and the latter clears up messages in
// its `on_initialize` hook, so otherwise messages will be cleared
// up before they are processed.
#[runtime::pallet_index(70)]
pub type MessageQueue = pallet_message_queue;
// ╚════════════ Polkadot SDK Utility Pallets - Block 2 ═════════════╝
// ╔══════════════════════ StorageHub Pallets ═══════════════════════╗
// Start with index 80
#[runtime::pallet_index(80)]
pub type Providers = pallet_storage_providers;
#[runtime::pallet_index(81)]
pub type FileSystem = pallet_file_system;
#[runtime::pallet_index(82)]
pub type ProofsDealer = pallet_proofs_dealer;
#[runtime::pallet_index(83)]
pub type Randomness = pallet_randomness;
#[runtime::pallet_index(84)]
pub type PaymentStreams = pallet_payment_streams;
#[runtime::pallet_index(85)]
pub type BucketNfts = pallet_bucket_nfts;
#[runtime::pallet_index(90)]
pub type Nfts = pallet_nfts;
// ╚══════════════════════ StorageHub Pallets ═══════════════════════╝
// ╔═══════════════════ DataHaven-specific Pallets ══════════════════╗

View file

@ -118,6 +118,7 @@ snowbridge-pallet-system-v2 = { workspace = true }
snowbridge-outbound-queue-primitives = { workspace = true }
[features]
storage-hub = []
default = ["std"]
std = [
"codec/std",