Skip to content
Snippets Groups Projects
Commit f3fac52c authored by Éloïs's avatar Éloïs
Browse files

WIP: feat(tests): allow custom genesis for each cucumber scenario

parent 701913b9
No related branches found
No related tags found
1 merge request!50Custom genesis for end2end tests & cucumber binary in docker
...@@ -58,21 +58,26 @@ Feature: My awesome feature ...@@ -58,21 +58,26 @@ Feature: My awesome feature
- eve - eve
- ferdie - ferdie
### Currency amounts ### genesis state
Amounts must be expressed as an integer of `ĞD` or `UD`, decimal numbers are not supported. Each scenario bootstraps its own blockchain with its own genesis state.
If you need more precision, you can express amounts in cents of ĞD (write `cĞD`), or in thousandths
of UD (write `mUD`). By default, all scenarios use the same configuration for the genesis, which is located in the file
`/cucumber-genesis/default.json`.
#### Given You can define a custom genesis state for each scenario with the tag `@genesis.confName`.
You can give any currency balance to each of the test users, so money will be created ex-nihilo for The genesis configuration must then be defined in a json file located at
that user. Note that this created money is not included in the monetary mass used to revalue the UD `/cucumber-genesis/confName.json`.
amount.
Usage: `{user} have {amount} {unit}` You can also define a custom genesis at the feature level, all the scenarios of this feature will
then inherit the genesis configuration.
Example: `alice have 10 ĞD` ### Currency amounts
Amounts must be expressed as an integer of `ĞD` or `UD`, decimal numbers are not supported.
If you need more precision, you can express amounts in cents of ĞD (write `cĞD`), or in thousandths
of UD (write `mUD`).
#### When #### When
......
Feature: Balance transfer Feature: Balance transfer
Scenario: After 10 blocks, the monetary mass should be 30 ĞD Scenario: After 10 blocks, the monetary mass should be 60 ĞD
Then Monetary mass should be 0.00 ĞD
Then Current UD amount should be 10.00 ĞD
When 10 blocks later
Then Monetary mass should be 30.00 ĞD Then Monetary mass should be 30.00 ĞD
Then Current UD amount should be 10.00 ĞD
When 10 blocks later When 10 blocks later
Then Monetary mass should be 60.00 ĞD Then Monetary mass should be 60.00 ĞD
When 10 blocks later
Then Monetary mass should be 90.00 ĞD
Then Current UD amount should be 10.00 ĞD Then Current UD amount should be 10.00 ĞD
@genesis.default
Feature: Balance transfer all Feature: Balance transfer all
Scenario: If alice sends all her ĞDs to Dave, Dave will get 10 ĞD Scenario: If alice sends all her ĞDs to Dave, Dave will get 8 ĞD
Given alice have 10 ĞD
When alice sends all her ĞDs to dave When alice sends all her ĞDs to dave
Then dave should have 10 ĞD """
Alice is a smith member, as such she is not allowed to empty her account completely,
if she tries to do so, the existence deposit (2 ĞD) must remain.
"""
Then alice should have 2 ĞD
Then dave should have 8 ĞD
{
"first_ud": 1000,
"first_ud_reeval": 100,
"identities": {
"Alice": {
"balance": 1000,
"certs": ["Bob", "Charlie"],
"pubkey": "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY"
},
"Bob": {
"balance": 1000,
"certs": ["Alice", "Charlie"],
"pubkey": "5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty"
},
"Charlie": {
"balance": 1000,
"certs": ["Alice", "Bob"],
"pubkey": "5FLSigC9HGRKVhB9FiEo4Y3koPsNmBmLJbpXg2mp1hXcS59Y"
}
},
"parameters": {
"babe_epoch_duration": 30,
"cert_period": 15,
"cert_max_by_issuer": 10,
"cert_min_received_cert_to_issue_cert": 2,
"cert_renewable_period": 50,
"cert_validity_period": 1000,
"idty_confirm_period": 40,
"idty_creation_period": 50,
"membership_period": 1000,
"membership_renewable_period": 50,
"pending_membership_period": 500,
"ud_creation_period": 10,
"ud_reeval_period": 100,
"smith_cert_period": 15,
"smith_cert_max_by_issuer": 8,
"smith_cert_min_received_cert_to_issue_cert": 2,
"smith_cert_renewable_period": 50,
"smith_cert_validity_period": 1000,
"smith_membership_period": 1000,
"smith_membership_renewable_period": 20,
"smith_pending_membership_period": 500,
"smiths_wot_first_cert_issuable_on": 20,
"smiths_wot_min_cert_for_membership": 2,
"wot_first_cert_issuable_on": 20,
"wot_min_cert_for_create_idty_right": 2,
"wot_min_cert_for_membership": 2
},
"smiths": {
"Alice": {
"certs": ["Bob", "Charlie"]
},
"Bob": {
"certs": ["Alice", "Charlie"]
},
"Charlie": {
"certs": ["Alice", "Bob"]
}
},
"sudo_key": "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY"
}
...@@ -24,6 +24,7 @@ pub mod node_runtime {} ...@@ -24,6 +24,7 @@ pub mod node_runtime {}
use serde_json::Value; use serde_json::Value;
use sp_keyring::AccountKeyring; use sp_keyring::AccountKeyring;
use std::io::prelude::*; use std::io::prelude::*;
use std::path::PathBuf;
use std::process::Command; use std::process::Command;
use std::str::FromStr; use std::str::FromStr;
use subxt::rpc::{rpc_params, ClientT, SubscriptionClientT}; use subxt::rpc::{rpc_params, ClientT, SubscriptionClientT};
...@@ -38,9 +39,8 @@ pub type TransactionProgress<'client> = ...@@ -38,9 +39,8 @@ pub type TransactionProgress<'client> =
pub const SUDO_ACCOUNT: AccountKeyring = AccountKeyring::Alice; pub const SUDO_ACCOUNT: AccountKeyring = AccountKeyring::Alice;
pub struct Process(std::process::Child); pub struct Process(std::process::Child);
impl Process {
impl Drop for Process { pub fn kill(&mut self) {
fn drop(&mut self) {
self.0.kill().expect("node already down"); self.0.kill().expect("node already down");
} }
} }
...@@ -51,7 +51,8 @@ struct FullNode { ...@@ -51,7 +51,8 @@ struct FullNode {
ws_port: u16, ws_port: u16,
} }
pub async fn spawn_node() -> (Api, Client, Process) { pub async fn spawn_node(maybe_genesis_conf_file: Option<PathBuf>) -> (Api, Client, Process) {
println!("maybe_genesis_conf_file={:?}", maybe_genesis_conf_file);
let duniter_binary_path = std::env::var("DUNITER_BINARY_PATH") let duniter_binary_path = std::env::var("DUNITER_BINARY_PATH")
.unwrap_or_else(|_| "../target/debug/duniter".to_owned()); .unwrap_or_else(|_| "../target/debug/duniter".to_owned());
let FullNode { let FullNode {
...@@ -59,8 +60,9 @@ pub async fn spawn_node() -> (Api, Client, Process) { ...@@ -59,8 +60,9 @@ pub async fn spawn_node() -> (Api, Client, Process) {
p2p_port: _, p2p_port: _,
ws_port, ws_port,
} = spawn_full_node( } = spawn_full_node(
&duniter_binary_path,
&["--dev", "--execution=Native", "--sealing=manual"], &["--dev", "--execution=Native", "--sealing=manual"],
&duniter_binary_path,
maybe_genesis_conf_file,
); );
let client = ClientBuilder::new() let client = ClientBuilder::new()
.set_url(format!("ws://127.0.0.1:{}", ws_port)) .set_url(format!("ws://127.0.0.1:{}", ws_port))
...@@ -110,12 +112,27 @@ pub async fn create_block_with_extrinsic( ...@@ -110,12 +112,27 @@ pub async fn create_block_with_extrinsic(
.map_err(Into::into) .map_err(Into::into)
} }
fn spawn_full_node(duniter_binary_path: &str, args: &[&str]) -> FullNode { fn spawn_full_node(
args: &[&str],
duniter_binary_path: &str,
maybe_genesis_conf_file: Option<PathBuf>,
) -> FullNode {
// Ports
let p2p_port = portpicker::pick_unused_port().expect("No ports free"); let p2p_port = portpicker::pick_unused_port().expect("No ports free");
let rpc_port = portpicker::pick_unused_port().expect("No ports free"); let rpc_port = portpicker::pick_unused_port().expect("No ports free");
let ws_port = portpicker::pick_unused_port().expect("No ports free"); let ws_port = portpicker::pick_unused_port().expect("No ports free");
// Env vars
let mut envs = Vec::new();
if let Some(genesis_conf_file) = maybe_genesis_conf_file {
envs.push(("DUNITER_GENESIS_CONFIG", genesis_conf_file));
}
// Logs
let log_file_path = format!("duniter-v2s-{}.log", ws_port); let log_file_path = format!("duniter-v2s-{}.log", ws_port);
let log_file = std::fs::File::create(&log_file_path).expect("fail to create log file"); let log_file = std::fs::File::create(&log_file_path).expect("fail to create log file");
// Command
let process = Process( let process = Process(
Command::new(duniter_binary_path) Command::new(duniter_binary_path)
.args( .args(
...@@ -133,6 +150,7 @@ fn spawn_full_node(duniter_binary_path: &str, args: &[&str]) -> FullNode { ...@@ -133,6 +150,7 @@ fn spawn_full_node(duniter_binary_path: &str, args: &[&str]) -> FullNode {
.iter() .iter()
.chain(args), .chain(args),
) )
.envs(envs)
.stdout(std::process::Stdio::null()) .stdout(std::process::Stdio::null())
.stderr(log_file) .stderr(log_file)
.spawn() .spawn()
......
...@@ -21,13 +21,38 @@ use common::*; ...@@ -21,13 +21,38 @@ use common::*;
use cucumber::{given, then, when, World, WorldInit}; use cucumber::{given, then, when, World, WorldInit};
use sp_keyring::AccountKeyring; use sp_keyring::AccountKeyring;
use std::convert::Infallible; use std::convert::Infallible;
use std::path::PathBuf;
use std::str::FromStr; use std::str::FromStr;
#[derive(WorldInit)] #[derive(WorldInit)]
pub struct DuniterWorld { pub struct DuniterWorld(Option<DuniterWorldInner>);
api: Api,
client: Client, impl DuniterWorld {
_process: Process, async fn init(&mut self, maybe_genesis_conf_file: Option<PathBuf>) {
if let Some(ref mut inner) = self.0 {
inner.kill();
}
self.0 = Some(DuniterWorldInner::new(maybe_genesis_conf_file).await);
}
fn api(&self) -> &Api {
if let Some(ref inner) = self.0 {
&inner.api
} else {
panic!("uninit")
}
}
fn client(&self) -> &Client {
if let Some(ref inner) = self.0 {
&inner.client
} else {
panic!("uninit")
}
}
fn kill(&mut self) {
if let Some(ref mut inner) = self.0 {
inner.kill();
}
}
} }
impl std::fmt::Debug for DuniterWorld { impl std::fmt::Debug for DuniterWorld {
...@@ -42,12 +67,27 @@ impl World for DuniterWorld { ...@@ -42,12 +67,27 @@ impl World for DuniterWorld {
type Error = Infallible; type Error = Infallible;
async fn new() -> std::result::Result<Self, Infallible> { async fn new() -> std::result::Result<Self, Infallible> {
let (api, client, _process) = spawn_node().await; Ok(DuniterWorld(None))
Ok(DuniterWorld { }
}
struct DuniterWorldInner {
api: Api,
client: Client,
process: Process,
}
impl DuniterWorldInner {
async fn new(maybe_genesis_conf_file: Option<PathBuf>) -> Self {
let (api, client, process) = spawn_node(maybe_genesis_conf_file).await;
DuniterWorldInner {
api, api,
client, client,
_process, process,
}) }
}
fn kill(&mut self) {
self.process.kill();
} }
} }
...@@ -69,7 +109,7 @@ async fn who_have(world: &mut DuniterWorld, who: String, amount: u64, unit: Stri ...@@ -69,7 +109,7 @@ async fn who_have(world: &mut DuniterWorld, who: String, amount: u64, unit: Stri
if is_ud { if is_ud {
let current_ud_amount = world let current_ud_amount = world
.api .api()
.storage() .storage()
.universal_dividend() .universal_dividend()
.current_ud(None) .current_ud(None)
...@@ -78,7 +118,7 @@ async fn who_have(world: &mut DuniterWorld, who: String, amount: u64, unit: Stri ...@@ -78,7 +118,7 @@ async fn who_have(world: &mut DuniterWorld, who: String, amount: u64, unit: Stri
} }
// Create {amount} ĞD for {who} // Create {amount} ĞD for {who}
common::balances::set_balance(&world.api, &world.client, who, amount).await?; common::balances::set_balance(&world.api(), &world.client(), who, amount).await?;
Ok(()) Ok(())
} }
...@@ -86,7 +126,7 @@ async fn who_have(world: &mut DuniterWorld, who: String, amount: u64, unit: Stri ...@@ -86,7 +126,7 @@ async fn who_have(world: &mut DuniterWorld, who: String, amount: u64, unit: Stri
#[when(regex = r"(\d+) blocks? later")] #[when(regex = r"(\d+) blocks? later")]
async fn n_blocks_later(world: &mut DuniterWorld, n: usize) -> Result<()> { async fn n_blocks_later(world: &mut DuniterWorld, n: usize) -> Result<()> {
for _ in 0..n { for _ in 0..n {
common::create_empty_block(&world.client).await?; common::create_empty_block(&world.client()).await?;
} }
Ok(()) Ok(())
} }
...@@ -105,9 +145,9 @@ async fn transfer( ...@@ -105,9 +145,9 @@ async fn transfer(
let (amount, is_ud) = parse_amount(amount, &unit); let (amount, is_ud) = parse_amount(amount, &unit);
if is_ud { if is_ud {
common::balances::transfer_ud(&world.api, &world.client, from, amount, to).await common::balances::transfer_ud(&world.api(), &world.client(), from, amount, to).await
} else { } else {
common::balances::transfer(&world.api, &world.client, from, amount, to).await common::balances::transfer(&world.api(), &world.client(), from, amount, to).await
} }
} }
...@@ -117,7 +157,7 @@ async fn send_all_to(world: &mut DuniterWorld, from: String, to: String) -> Resu ...@@ -117,7 +157,7 @@ async fn send_all_to(world: &mut DuniterWorld, from: String, to: String) -> Resu
let from = AccountKeyring::from_str(&from).expect("unknown from"); let from = AccountKeyring::from_str(&from).expect("unknown from");
let to = AccountKeyring::from_str(&to).expect("unknown to"); let to = AccountKeyring::from_str(&to).expect("unknown to");
common::balances::transfer_all(&world.api, &world.client, from, to).await common::balances::transfer_all(&world.api(), &world.client(), from, to).await
} }
#[then(regex = r"([a-zA-Z]+) should have (\d+) ĞD")] #[then(regex = r"([a-zA-Z]+) should have (\d+) ĞD")]
...@@ -128,7 +168,7 @@ async fn should_have(world: &mut DuniterWorld, who: String, amount: u64) -> Resu ...@@ -128,7 +168,7 @@ async fn should_have(world: &mut DuniterWorld, who: String, amount: u64) -> Resu
.to_account_id(); .to_account_id();
let amount = amount * 100; let amount = amount * 100;
let who_account = world.api.storage().system().account(who, None).await?; let who_account = world.api().storage().system().account(who, None).await?;
assert_eq!(who_account.data.free, amount); assert_eq!(who_account.data.free, amount);
Ok(()) Ok(())
} }
...@@ -141,7 +181,7 @@ async fn current_ud_amount_should_be( ...@@ -141,7 +181,7 @@ async fn current_ud_amount_should_be(
) -> Result<()> { ) -> Result<()> {
let expected = (amount * 100) + cents; let expected = (amount * 100) + cents;
let actual = world let actual = world
.api .api()
.storage() .storage()
.universal_dividend() .universal_dividend()
.current_ud(None) .current_ud(None)
...@@ -154,7 +194,7 @@ async fn current_ud_amount_should_be( ...@@ -154,7 +194,7 @@ async fn current_ud_amount_should_be(
async fn monetary_mass_should_be(world: &mut DuniterWorld, amount: u64, cents: u64) -> Result<()> { async fn monetary_mass_should_be(world: &mut DuniterWorld, amount: u64, cents: u64) -> Result<()> {
let expected = (amount * 100) + cents; let expected = (amount * 100) + cents;
let actual = world let actual = world
.api .api()
.storage() .storage()
.universal_dividend() .universal_dividend()
.monetary_mass(None) .monetary_mass(None)
...@@ -170,6 +210,35 @@ async fn main() { ...@@ -170,6 +210,35 @@ async fn main() {
DuniterWorld::cucumber() DuniterWorld::cucumber()
//.fail_on_skipped() //.fail_on_skipped()
.max_concurrent_scenarios(4) .max_concurrent_scenarios(4)
.before(|feature, _rule, scenario, world| {
let mut genesis_conf_file_path = PathBuf::new();
genesis_conf_file_path.push("cucumber-genesis");
genesis_conf_file_path.push(&format!(
"{}.json",
genesis_conf_name(&feature.tags, &scenario.tags)
));
Box::pin(world.init(Some(genesis_conf_file_path)))
})
.after(|_feature, _rule, _scenario, maybe_world| {
if let Some(world) = maybe_world {
world.kill();
}
Box::pin(std::future::ready(()))
})
.run_and_exit("cucumber-features") .run_and_exit("cucumber-features")
.await; .await;
} }
fn genesis_conf_name(feature_tags: &[String], scenario_tags: &[String]) -> String {
for tag in scenario_tags {
if let Some(("genesis", conf_name)) = tag.split_once(".") {
return conf_name.to_owned();
}
}
for tag in feature_tags {
if let Some(("genesis", conf_name)) = tag.split_once(".") {
return conf_name.to_owned();
}
}
"default".to_owned()
}
...@@ -62,47 +62,91 @@ pub fn get_authority_keys_from_seed(s: &str) -> AuthorityKeys { ...@@ -62,47 +62,91 @@ pub fn get_authority_keys_from_seed(s: &str) -> AuthorityKeys {
pub fn development_chain_spec() -> Result<ChainSpec, String> { pub fn development_chain_spec() -> Result<ChainSpec, String> {
let wasm_binary = WASM_BINARY.ok_or_else(|| "Development wasm not available".to_string())?; let wasm_binary = WASM_BINARY.ok_or_else(|| "Development wasm not available".to_string())?;
Ok(ChainSpec::from_genesis( if std::env::var("DUNITER_GENESIS_CONFIG").is_ok() {
// Name super::gen_genesis_data::generate_genesis_data(
"Development", |genesis_data| {
// ID ChainSpec::from_genesis(
"gdev", // Name
ChainType::Development, "Development",
move || { // ID
gen_genesis_conf( "gdev",
wasm_binary, sc_service::ChainType::Development,
// Initial authorities len move || genesis_data_to_gdev_genesis_conf(genesis_data.clone(), wasm_binary),
1, // Bootnodes
// Initial smiths members len vec![],
3, // Telemetry
// Inital identities len None,
4, // Protocol ID
// Sudo account None,
get_account_id_from_seed::<sr25519::Public>("Alice"), //Fork ID
true, None,
) // Properties
}, Some(
// Bootnodes serde_json::json!({
vec![], "tokenDecimals": TOKEN_DECIMALS,
// Telemetry "tokenSymbol": TOKEN_SYMBOL,
None, })
// Protocol ID .as_object()
None, .expect("must be a map")
//Fork ID .clone(),
None, ),
// Properties // Extensions
Some( None,
serde_json::json!({ )
"tokenDecimals": TOKEN_DECIMALS, },
"tokenSymbol": TOKEN_SYMBOL, Some(get_authority_keys_from_seed("Alice").encode()),
}) Some(super::gen_genesis_data::ParamsAppliedAtGenesis {
.as_object() genesis_certs_expire_on: 100_000,
.expect("must be a map") genesis_smith_certs_expire_on: 100_000,
.clone(), genesis_memberships_expire_on: 100_000,
), genesis_memberships_renewable_on: 50,
// Extensions genesis_smith_memberships_expire_on: 100_000,
None, genesis_smith_memberships_renewable_on: 50,
)) }),
)
} else {
Ok(ChainSpec::from_genesis(
// Name
"Development",
// ID
"gdev",
ChainType::Development,
move || {
gen_genesis_conf(
wasm_binary,
// Initial authorities len
1,
// Initial smiths members len
3,
// Inital identities len
4,
// Sudo account
get_account_id_from_seed::<sr25519::Public>("Alice"),
true,
)
},
// Bootnodes
vec![],
// Telemetry
None,
// Protocol ID
None,
//Fork ID
None,
// Properties
Some(
serde_json::json!({
"tokenDecimals": TOKEN_DECIMALS,
"tokenSymbol": TOKEN_SYMBOL,
})
.as_object()
.expect("must be a map")
.clone(),
),
// Extensions
None,
))
}
} }
pub fn gen_live_conf() -> Result<ChainSpec, String> { pub fn gen_live_conf() -> Result<ChainSpec, String> {
...@@ -139,6 +183,7 @@ pub fn gen_live_conf() -> Result<ChainSpec, String> { ...@@ -139,6 +183,7 @@ pub fn gen_live_conf() -> Result<ChainSpec, String> {
None, None,
) )
}, },
None,
Some(super::gen_genesis_data::ParamsAppliedAtGenesis { Some(super::gen_genesis_data::ParamsAppliedAtGenesis {
genesis_certs_expire_on: 100_000, genesis_certs_expire_on: 100_000,
genesis_smith_certs_expire_on: 100_000, genesis_smith_certs_expire_on: 100_000,
......
...@@ -85,6 +85,7 @@ struct SmithData { ...@@ -85,6 +85,7 @@ struct SmithData {
pub fn generate_genesis_data<CS, P, SK, F>( pub fn generate_genesis_data<CS, P, SK, F>(
f: F, f: F,
maybe_force_authority: Option<Vec<u8>>,
params_applied_at_genesis: Option<ParamsAppliedAtGenesis>, params_applied_at_genesis: Option<ParamsAppliedAtGenesis>,
) -> Result<CS, String> ) -> Result<CS, String>
where where
...@@ -234,6 +235,7 @@ where ...@@ -234,6 +235,7 @@ where
// SMITHS SUB-WOT // // SMITHS SUB-WOT //
let mut initial_authorities = BTreeMap::new(); let mut initial_authorities = BTreeMap::new();
let mut online_authorities_counter = 0;
let mut session_keys_map = BTreeMap::new(); let mut session_keys_map = BTreeMap::new();
let mut smiths_memberships = BTreeMap::new(); let mut smiths_memberships = BTreeMap::new();
let mut smiths_certs_by_issuer = BTreeMap::new(); let mut smiths_certs_by_issuer = BTreeMap::new();
...@@ -253,15 +255,27 @@ where ...@@ -253,15 +255,27 @@ where
} }
// Initial authorities // Initial authorities
initial_authorities.insert( if maybe_force_authority.is_some() {
*idty_index, if smith_data.session_keys.is_some() {
(identity.pubkey.clone(), smith_data.session_keys.is_some()), return Err(format!("session_keys field forbidden",));
); }
if *idty_index == 1 {
initial_authorities.insert(1, (identity.pubkey.clone(), true));
}
} else {
initial_authorities.insert(
*idty_index,
(identity.pubkey.clone(), smith_data.session_keys.is_some()),
);
}
// Session keys // Session keys
let session_keys_bytes = if let Some(ref session_keys) = smith_data.session_keys { let session_keys_bytes = if let Some(ref session_keys) = smith_data.session_keys {
online_authorities_counter += 1;
hex::decode(&session_keys[2..]) hex::decode(&session_keys[2..])
.map_err(|_| format!("invalid session keys for idty {}", &idty_name))? .map_err(|_| format!("invalid session keys for idty {}", &idty_name))?
} else if let (1, Some(ref session_keys_bytes)) = (*idty_index, &maybe_force_authority) {
session_keys_bytes.clone()
} else { } else {
// Create fake session keys (must be unique and deterministic) // Create fake session keys (must be unique and deterministic)
let mut fake_session_keys_bytes = Vec::with_capacity(128); let mut fake_session_keys_bytes = Vec::with_capacity(128);
...@@ -297,6 +311,12 @@ where ...@@ -297,6 +311,12 @@ where
); );
} }
if maybe_force_authority.is_none() && online_authorities_counter == 0 {
return Err(format!(
"The session_keys field must be filled in for at least one smith.",
));
}
let genesis_data = GenesisData { let genesis_data = GenesisData {
accounts, accounts,
certs_by_issuer, certs_by_issuer,
......
...@@ -32,8 +32,7 @@ ...@@ -32,8 +32,7 @@
"pending_membership_period": 500, "pending_membership_period": 500,
"ud_creation_period": 10, "ud_creation_period": 10,
"ud_first_reeval": 0, "ud_first_reeval": 0,
"ud_reeval_period": 50, "ud_reeval_period": 200,
"ud_reeval_period_in_blocks": 500,
"smith_cert_period": 15, "smith_cert_period": 15,
"smith_cert_max_by_issuer": 8, "smith_cert_max_by_issuer": 8,
"smith_cert_min_received_cert_to_issue_cert": 2, "smith_cert_min_received_cert_to_issue_cert": 2,
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment