Genesis 配置定义了诸如帐户、余额、自定义托盘的 Genesis 等事物的初始状态。接下来一起看看使用 2 个简单的存储项目配置pallet的Genesis。
在pallet中实施Genesis
为了我们的目的,假设已经有了 <SingleValue<T>>
和 <AccountMap<T>>
。
例如:
#[pallet::storage]
#[pallet::getter(fn something)]
pub type SingleValue<T: Config> = StorageValue<_, T::Balance>;
// A map that has enumerable entries.
#[pallet::storage]
#[pallet::getter(fn accounts)]
pub type AccountMap<T: Config> = StorageMap<_, Blake2_128Concat, T::AccountId, T::Balance>;
GenesisConfig
代码应该紧随storage items后面。
-
使用
#[pallet::genesis_config]
属性, 为pallet编写GenesisConfig
struct 。#[pallet::genesis_config] pub struct GenesisConfig<T: Config> { pub single_value: T::Balance, pub account_map: Vec<(T::AccountId, T::Balance)>, }
-
为
GenesisConfig
struct设置默认值.#[cfg(feature = "std")] impl<T: Config> Default for GenesisConfig<T> { fn default() -> Self { Self { single_value: Default::default(), account_map: Default::default() } } }
-
使用
#[pallet::genesis_build]
属性, 实现GenesisBuild
trait。#[pallet::genesis_build] impl<T: Config> GenesisBuild<T> for GenesisConfig<T> { fn build(&self) { <SingleValue<T>>::put(&self.single_value); for (a, b) in &self.account_map { <AccountMap<T>>::insert(a, b); } } }
这允许执行一些逻辑来定义
GenesisConfig
结构如何将一些内容放入到storage中。
设置节点的链规范
接下来要做的是指定我们想要放进链的Genesis state中的值。 假设 pallet 叫做 pallet_something
, 声明 PalletSomething
在runtime的construct_runtime!
宏中。
-
在
node/chain_spec.rs
, 创建一个T::Balance
类型的值储存在<SingleValue<T>>
中。const VALUE: Balance = 235813;
注意,在
chain_spec.rs
的顶部导入了node_template_runtime::Balance
-
创建一个vector并初始化账户信息。
let accounts_to_map: Vec<AccountId> = vec![ get_account_id_from_seed::<sr25519::Public>("Alice"), get_account_id_from_seed::<sr25519::Public>("Bob"), get_account_id_from_seed::<sr25519::Public>("Charlie"), ];
-
在
testnet_genesis
中完成GenesisConfig
。通常我们在
runtime/src/lib.rs
里的construct_runtime!
中,使用pallet名称的小写拼写。例如声明为CamelCase
的pallet,通常在testnet_genesis
函数中称其为camel_case
。在我们的例子中:
pallet_something: PalletSomethingConfig { single_value: VALUE, account_map: accounts_to_map.iter().cloned().map(|x| (x, VALUE)).collect(), }
在上边的代码中,我们把
accounts_to_map
中每个账户和VALUE
映射到一起。这和Balances pallet的GenesisConfig
实现非常相似。
实例
- Node template
chain_spec.rs
use node_template_runtime::{
AccountId, AuraConfig, BalancesConfig, GenesisConfig, GrandpaConfig, Signature, SudoConfig,
SystemConfig, WASM_BINARY,
};
use sc_service::ChainType;
use sp_consensus_aura::sr25519::AuthorityId as AuraId;
use sp_core::{
sr25519, Pair, Public};
use sp_finality_grandpa::AuthorityId as GrandpaId;
use sp_runtime::traits::{
IdentifyAccount, Verify};
// The URL for the telemetry server.
// const STAGING_TELEMETRY_URL: &str = "wss://telemetry.polkadot.io/submit/";
/// Specialized `ChainSpec`. This is a specialization of the general Substrate ChainSpec type.
pub type ChainSpec = sc_service::GenericChainSpec<GenesisConfig>;
/// Generate a crypto pair from seed.
pub fn get_from_seed<TPublic: Public>(seed: &str) -> <TPublic::Pair as Pair>::Public {
TPublic::Pair::from_string(&format!("//{}", seed), None)
.expect("static values are valid; qed")
.public()
}
type AccountPublic = <Signature as Verify>::Signer;
/// Generate an account ID from seed.
pub fn get_account_id_from_seed<TPublic: Public>(seed: &str) -> AccountId
where
AccountPublic: From<<TPublic::Pair as Pair>::Public>,
{
AccountPublic::from(get_from_seed::<TPublic>(seed)).into_account()
}
/// Generate an Aura authority key.
pub fn authority_keys_from_seed(s: &str) -> (AuraId, GrandpaId) {
(get_from_seed::<AuraId>(s), get_from_seed::<GrandpaId>(s))
}
pub fn development_config() -> Result<ChainSpec, String> {
let wasm_binary = WASM_BINARY.ok_or_else(|| "Development wasm not available".to_string())?;
Ok(ChainSpec::from_genesis(
// Name
"Development",
// ID
"dev",
ChainType::Development,
move || {
testnet_genesis(
wasm_binary,
// Initial PoA authorities
vec![authority_keys_from_seed("Alice")],
// Sudo account
get_account_id_from_seed::<sr25519::Public>("Alice"),
// Pre-funded accounts
vec![
get_account_id_from_seed::<sr25519::Public>("Alice"),
get_account_id_from_seed::<sr25519::Public>("Bob"),
get_account_id_from_seed::<sr25519::Public>("Alice//stash"),
get_account_id_from_seed::<sr25519::Public>("Bob//stash"),
],
true,
)
},
// Bootnodes
vec![],
// Telemetry
None,
// Protocol ID
None,
None,
// Properties
None,
// Extensions
None,
))
}
pub fn local_testnet_config() -> Result<ChainSpec, String> {
let wasm_binary = WASM_BINARY.ok_or_else(|| "Development wasm not available".to_string())?;
Ok(ChainSpec::from_genesis(
// Name
"Local Testnet",
// ID
"local_testnet",
ChainType::Local,
move || {
testnet_genesis(
wasm_binary,
// Initial PoA authorities
vec![authority_keys_from_seed("Alice"), authority_keys_from_seed("Bob")],
// Sudo account
get_account_id_from_seed::<sr25519::Public>("Alice"),
// Pre-funded accounts
vec![
get_account_id_from_seed::<sr25519::Public>("Alice"),
get_account_id_from_seed::<sr25519::Public>("Bob"),
get_account_id_from_seed::<sr25519::Public>("Charlie"),
get_account_id_from_seed::<sr25519::Public>("Dave"),
get_account_id_from_seed::<sr25519::Public>("Eve"),
get_account_id_from_seed::<sr25519::Public>("Ferdie"),
get_account_id_from_seed::<sr25519::Public>("Alice//stash"),
get_account_id_from_seed::<sr25519::Public>("Bob//stash"),
get_account_id_from_seed::<sr25519::Public>("Charlie//stash"),
get_account_id_from_seed::<sr25519::Public>("Dave//stash"),
get_account_id_from_seed::<sr25519::Public>("Eve//stash"),
get_account_id_from_seed::<sr25519::Public>("Ferdie//stash"),
],
true,
)
},
// Bootnodes
vec![],
// Telemetry
None,
// Protocol ID
None,
// Properties
None,
None,
// Extensions
None,
))
}
/// Configure initial storage state for FRAME modules.
fn testnet_genesis(
wasm_binary: &[u8],
initial_authorities: Vec<(AuraId, GrandpaId)>,
root_key: AccountId,
endowed_accounts: Vec<AccountId>,
_enable_println: bool,
) -> GenesisConfig {
GenesisConfig {
system: SystemConfig {
// Add Wasm runtime to storage.
code: wasm_binary.to_vec(),
},
balances: BalancesConfig {
// Configure endowed accounts with initial balance of 1 << 60.
balances: endowed_accounts.iter().cloned().map(|k| (k, 1 << 60)).collect(),
},
aura: AuraConfig {
authorities: initial_authorities.iter().map(|x| (x.0.clone())).collect(),
},
grandpa: GrandpaConfig {
authorities: initial_authorities.iter().map(|x| (x.1.clone(), 1)).collect(),
},
sudo: SudoConfig {
// Assign network admin rights.
key: Some(root_key),
},
transaction_payment: Default::default(),
}
}