From 896f5d390ab01fb1567ea88f210fba0df3853c63 Mon Sep 17 00:00:00 2001 From: "Alisher A. Khassanov" Date: Mon, 11 Dec 2023 14:25:29 +0600 Subject: [PATCH] Add genesis config for DDC pallets (#176) --- Cargo.lock | 1 + docs/genesis-state.md | 39 +++++ node/service/chain-specs/example.json | 232 ++++++++++++++++++++++++++ node/service/src/chain_spec.rs | 1 + pallets/ddc-customers/src/lib.rs | 36 +++- pallets/ddc-customers/src/mock.rs | 2 +- pallets/ddc-nodes/Cargo.toml | 2 + pallets/ddc-nodes/src/cdn_node.rs | 4 + pallets/ddc-nodes/src/lib.rs | 25 +++ pallets/ddc-nodes/src/storage_node.rs | 4 + 10 files changed, 341 insertions(+), 5 deletions(-) create mode 100644 docs/genesis-state.md create mode 100644 node/service/chain-specs/example.json diff --git a/Cargo.lock b/Cargo.lock index fe193760e..68d46882e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4805,6 +4805,7 @@ dependencies = [ "pallet-timestamp", "parity-scale-codec", "scale-info", + "serde", "sp-core", "sp-io", "sp-runtime", diff --git a/docs/genesis-state.md b/docs/genesis-state.md new file mode 100644 index 000000000..587683b62 --- /dev/null +++ b/docs/genesis-state.md @@ -0,0 +1,39 @@ +# State preset + +Sometimes we use the blockchain as a part of a test environment. +Those tests typically want a certain state of the blockchain. +One option is to make a script which sends a number of transactions to put the blockchain into the desired state. +But those scripts may become large and execution may take significant time. +Another option is to make a chain specification, a JSON file with the desired genesis state of the blockchain. +And launch the chain with the desired state from the first block. + +## Build a chain spec + +Chain specification is a JSON file which contains network parameters with the the initial runtime and a genesis state of the database for each runtime module. +There are two forms of chain specification - a human-readable _plain_ form where each runtime module is represented by it's name. +And a _raw_ form with key-value pairs which will be written to the database. + +1. Create a plain form chain spec. + + ```console + ./target/release/cere build-spec --chain=dev --disable-default-bootnode > plain.json + ``` + +1. Set genesis state for each module. + There is an example in `node/service/example.json`, you can copy everything except the `code` field value from it to the `plain.json`. + +1. Create a raw form chain spec. + + ```console + ./target/release/cere build-spec --chain=plain.json --disable-default-bootnode --raw > raw.json + ``` + +## Launch the chain + +```console +./target/release/cere --chain=raw.json --tmp --alice --unsafe-rpc-external --unsafe-ws-external --rpc-cors=all +``` + +## See also + +[docs.substrate.io: Create a custom chain specification](https://docs.substrate.io/tutorials/build-a-blockchain/add-trusted-nodes/#create-a-custom-chain-specification) diff --git a/node/service/chain-specs/example.json b/node/service/chain-specs/example.json new file mode 100644 index 000000000..7e3b5c97c --- /dev/null +++ b/node/service/chain-specs/example.json @@ -0,0 +1,232 @@ +{ + "name": "Development", + "id": "cere_dev", + "chainType": "Development", + "bootNodes": [], + "telemetryEndpoints": null, + "protocolId": "cere", + "properties": { + "ss58Format": 54, + "tokenDecimals": 10, + "tokenSymbol": "CERE" + }, + "forkBlocks": null, + "badBlocks": null, + "lightSyncState": null, + "codeSubstitutes": {}, + "genesis": { + "runtime": { + "system": { + "code": "0x00" + }, + "babe": { + "authorities": [], + "epochConfig": { + "c": [ + 1, + 4 + ], + "allowed_slots": "PrimaryAndSecondaryPlainSlots" + } + }, + "indices": { + "indices": [] + }, + "balances": { + "balances": [ + [ + "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY", + 10000000000000 + ], + [ + "5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty", + 10000000000000 + ], + [ + "5GNJqTPyNqANBkUVMN1LPPrxXnFouWXoe2wNSmmEoLctxiZY", + 10000000000000 + ], + [ + "5HpG9w8EBLe5XCrbczpwq5TSXvedjrBGCwqxK1iQ7qUsSWFc", + 10000000000000 + ], + [ + "5FLSigC9HGRKVhB9FiEo4Y3koPsNmBmLJbpXg2mp1hXcS59Y", + 10000000000000 + ] + ] + }, + "transactionPayment": { + "multiplier": "10000000000" + }, + "staking": { + "validatorCount": 1, + "minimumValidatorCount": 1, + "invulnerables": [ + "5GNJqTPyNqANBkUVMN1LPPrxXnFouWXoe2wNSmmEoLctxiZY" + ], + "forceEra": "NotForcing", + "slashRewardFraction": 100000000, + "canceledPayout": 0, + "stakers": [ + [ + "5GNJqTPyNqANBkUVMN1LPPrxXnFouWXoe2wNSmmEoLctxiZY", + "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY", + 1000000000000, + "Validator" + ] + ], + "minNominatorBond": 0, + "minValidatorBond": 0, + "maxValidatorCount": null, + "maxNominatorCount": null + }, + "session": { + "keys": [ + [ + "5GNJqTPyNqANBkUVMN1LPPrxXnFouWXoe2wNSmmEoLctxiZY", + "5GNJqTPyNqANBkUVMN1LPPrxXnFouWXoe2wNSmmEoLctxiZY", + { + "grandpa": "5FA9nQDVg267DEd8m1ZypXLBnvN7SFxYwV7ndqSYGiN9TTpu", + "babe": "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY", + "im_online": "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY", + "authority_discovery": "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY" + } + ] + ] + }, + "democracy": { + "phantom": null + }, + "council": { + "phantom": null, + "members": [] + }, + "technicalCommittee": { + "phantom": null, + "members": [ + "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY", + "5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty" + ] + }, + "elections": { + "members": [ + [ + "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY", + 10000000000 + ], + [ + "5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty", + 10000000000 + ] + ] + }, + "technicalMembership": { + "members": [], + "phantom": null + }, + "grandpa": { + "authorities": [] + }, + "treasury": null, + "sudo": { + "key": "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY" + }, + "imOnline": { + "keys": [] + }, + "authorityDiscovery": { + "keys": [] + }, + "society": { + "pot": 0, + "members": [ + "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY", + "5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty" + ], + "maxMembers": 999 + }, + "vesting": { + "vesting": [] + }, + "nominationPools": { + "minJoinBond": 0, + "minCreateBond": 0, + "maxPools": 16, + "maxMembersPerPool": 32, + "maxMembers": 512 + }, + "ddcStaking": { + "cdns": [], + "storages": [] + }, + "ddcCustomers": { + "buckets": [ + [ + "0x0000000000000000000000000000000000000001", + "5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty", + 10000000000 + ] + ] + }, + "ddcNodes": { + "storageNodes": [ + { + "pub_key": "5FLSigC9HGRKVhB9FiEo4Y3koPsNmBmLJbpXg2mp1hXcS59Y", + "provider_id": "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY", + "cluster_id": "0x0000000000000000000000000000000000000001", + "props": { + "host": [], + "http_port": 8080, + "grpc_port": 8081, + "p2p_port": 8082 + } + } + ], + "cdnNodes": [] + }, + "ddcClusters": { + "clusters": [ + { + "cluster_id": "0x0000000000000000000000000000000000000001", + "manager_id": "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY", + "reserve_id": "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY", + "props": { + "node_provider_auth_contract": null + } + } + ], + "clustersGovParams": [ + [ + "0x0000000000000000000000000000000000000001", + { + "treasury_share": 0, + "validators_share": 0, + "cluster_reserve_share": 0, + "cdn_bond_size": 0, + "cdn_chill_delay": 0, + "cdn_unbonding_delay": 0, + "storage_bond_size": 0, + "storage_chill_delay": 0, + "storage_unbonding_delay": 0, + "unit_per_mb_stored": 0, + "unit_per_mb_streamed": 0, + "unit_per_put_request": 0, + "unit_per_get_request": 0 + } + ] + ], + "clustersNodes": [ + [ + "0x0000000000000000000000000000000000000001", + [ + { + "StoragePubKey": "5FLSigC9HGRKVhB9FiEo4Y3koPsNmBmLJbpXg2mp1hXcS59Y" + } + ] + ] + ] + } + } + } +} \ No newline at end of file diff --git a/node/service/src/chain_spec.rs b/node/service/src/chain_spec.rs index f17f9343f..12859a2c6 100644 --- a/node/service/src/chain_spec.rs +++ b/node/service/src/chain_spec.rs @@ -234,6 +234,7 @@ pub fn cere_dev_genesis( ddc_customers: Default::default(), nomination_pools: Default::default(), ddc_clusters: Default::default(), + ddc_nodes: Default::default(), } } diff --git a/pallets/ddc-customers/src/lib.rs b/pallets/ddc-customers/src/lib.rs index fcefa7036..5ccc4e0b8 100644 --- a/pallets/ddc-customers/src/lib.rs +++ b/pallets/ddc-customers/src/lib.rs @@ -262,23 +262,51 @@ pub mod pallet { } #[pallet::genesis_config] - pub struct GenesisConfig; + pub struct GenesisConfig { + pub buckets: Vec<(ClusterId, T::AccountId, BalanceOf)>, + } #[cfg(feature = "std")] - impl Default for GenesisConfig { + impl Default for GenesisConfig { fn default() -> Self { - Self + GenesisConfig { buckets: Default::default() } } } #[pallet::genesis_build] - impl GenesisBuild for GenesisConfig { + impl GenesisBuild for GenesisConfig { fn build(&self) { let account_id = >::account_id(); let min = ::Currency::minimum_balance(); if ::Currency::free_balance(&account_id) < min { let _ = ::Currency::make_free_balance_be(&account_id, min); } + + for &(ref cluster_id, ref owner_id, ref deposit) in &self.buckets { + let cur_bucket_id = >::get() + .checked_add(1) + .ok_or(Error::::ArithmeticOverflow) + .unwrap(); + >::set(cur_bucket_id); + + let bucket = Bucket { + bucket_id: cur_bucket_id, + owner_id: owner_id.clone(), + cluster_id: *cluster_id, + }; + >::insert(cur_bucket_id, bucket); + + let ledger = AccountsLedger::, T> { + owner: owner_id.clone(), + total: *deposit, + active: *deposit, + unlocking: Default::default(), + }; + >::insert(&ledger.owner, &ledger); + + ::Currency::deposit_into_existing(&account_id, *deposit) + .unwrap(); + } } } diff --git a/pallets/ddc-customers/src/mock.rs b/pallets/ddc-customers/src/mock.rs index 3b62bc757..0137a5e93 100644 --- a/pallets/ddc-customers/src/mock.rs +++ b/pallets/ddc-customers/src/mock.rs @@ -41,7 +41,7 @@ construct_runtime!( System: frame_system::{Pallet, Call, Config, Storage, Event}, Timestamp: pallet_timestamp::{Pallet, Call, Storage, Inherent}, Balances: pallet_balances::{Pallet, Call, Storage, Config, Event}, - DdcCustomers: pallet_ddc_customers::{Pallet, Call, Storage, Config, Event}, + DdcCustomers: pallet_ddc_customers::{Pallet, Call, Storage, Config, Event}, } ); diff --git a/pallets/ddc-nodes/Cargo.toml b/pallets/ddc-nodes/Cargo.toml index 395c613c6..9369fd55e 100644 --- a/pallets/ddc-nodes/Cargo.toml +++ b/pallets/ddc-nodes/Cargo.toml @@ -11,6 +11,7 @@ frame-benchmarking = { default-features = false, git = "https://github.com/parit frame-support = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.31" } frame-system = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.31" } scale-info = { version = "2.1.2", default-features = false, features = ["derive"] } +serde = { version = "1.0.136", default-features = false, features = ["derive"], optional = true } sp-runtime = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.31" } sp-std = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.31" } @@ -34,6 +35,7 @@ std = [ "sp-runtime/std", "sp-std/std", "sp-core/std", + "serde", ] runtime-benchmarks = [ "ddc-primitives/runtime-benchmarks", diff --git a/pallets/ddc-nodes/src/cdn_node.rs b/pallets/ddc-nodes/src/cdn_node.rs index edde9de68..7ec266f54 100644 --- a/pallets/ddc-nodes/src/cdn_node.rs +++ b/pallets/ddc-nodes/src/cdn_node.rs @@ -2,6 +2,8 @@ use codec::{Decode, Encode}; use ddc_primitives::{CDNNodePubKey, ClusterId, NodeParams, NodePubKey, NodeType}; use frame_support::{parameter_types, BoundedVec}; use scale_info::TypeInfo; +#[cfg(feature = "std")] +use serde::{Deserialize, Serialize}; use sp_runtime::RuntimeDebug; use sp_std::prelude::*; @@ -12,6 +14,7 @@ parameter_types! { pub MaxHostLen: u8 = 255; } +#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] #[derive(Clone, Encode, Decode, RuntimeDebug, TypeInfo, PartialEq)] #[scale_info(skip_type_params(T))] pub struct CDNNode { @@ -21,6 +24,7 @@ pub struct CDNNode { pub props: CDNNodeProps, } +#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] #[derive(Clone, Encode, Decode, RuntimeDebug, TypeInfo, PartialEq)] pub struct CDNNodeProps { pub host: BoundedVec, diff --git a/pallets/ddc-nodes/src/lib.rs b/pallets/ddc-nodes/src/lib.rs index a6ce51fb7..b0ab6e6b2 100644 --- a/pallets/ddc-nodes/src/lib.rs +++ b/pallets/ddc-nodes/src/lib.rs @@ -92,6 +92,31 @@ pub mod pallet { #[pallet::getter(fn cdn_nodes)] pub type CDNNodes = StorageMap<_, Blake2_128Concat, CDNNodePubKey, CDNNode>; + #[pallet::genesis_config] + pub struct GenesisConfig { + pub storage_nodes: Vec>, + pub cdn_nodes: Vec>, + } + + #[cfg(feature = "std")] + impl Default for GenesisConfig { + fn default() -> Self { + GenesisConfig { storage_nodes: Default::default(), cdn_nodes: Default::default() } + } + } + + #[pallet::genesis_build] + impl GenesisBuild for GenesisConfig { + fn build(&self) { + for storage_node in &self.storage_nodes { + >::insert(storage_node.pub_key.clone(), storage_node); + } + for cdn_node in &self.cdn_nodes { + >::insert(cdn_node.pub_key.clone(), cdn_node); + } + } + } + #[pallet::call] impl Pallet { #[pallet::weight(T::WeightInfo::create_node())] diff --git a/pallets/ddc-nodes/src/storage_node.rs b/pallets/ddc-nodes/src/storage_node.rs index d299bfe31..3a6798c71 100644 --- a/pallets/ddc-nodes/src/storage_node.rs +++ b/pallets/ddc-nodes/src/storage_node.rs @@ -2,6 +2,8 @@ use codec::{Decode, Encode}; use ddc_primitives::{ClusterId, NodeParams, NodePubKey, NodeType, StorageNodePubKey}; use frame_support::{parameter_types, BoundedVec}; use scale_info::TypeInfo; +#[cfg(feature = "std")] +use serde::{Deserialize, Serialize}; use sp_runtime::RuntimeDebug; use crate::node::{NodeError, NodeProps, NodeTrait}; @@ -11,6 +13,7 @@ parameter_types! { pub MaxHostLen: u8 = 255; } +#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] #[derive(Clone, Encode, Decode, RuntimeDebug, TypeInfo, PartialEq)] #[scale_info(skip_type_params(T))] pub struct StorageNode { @@ -20,6 +23,7 @@ pub struct StorageNode { pub props: StorageNodeProps, } +#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] #[derive(Clone, Encode, Decode, RuntimeDebug, TypeInfo, PartialEq)] pub struct StorageNodeProps { pub host: BoundedVec,