Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • nodes/rust/duniter-v2s
  • llaq/lc-core-substrate
  • pini-gh/duniter-v2s
  • vincentux/duniter-v2s
  • mildred/duniter-v2s
  • d0p1/duniter-v2s
  • bgallois/duniter-v2s
  • Nicolas80/duniter-v2s
8 results
Show changes
Commits on Source (16)
Showing
with 543 additions and 139 deletions
......@@ -7,6 +7,7 @@ stages:
workflow:
rules:
- changes:
- docker/Dockerfile
- node/**/*
- pallets/**/*
- runtime/**/*
......@@ -138,6 +139,14 @@ tests_debug:
- cargo cucumber -i balance*
- cargo cucumber -i monetary*
- cargo cucumber -i transfer*
after_script:
- cd target/debug/deps/
- rm cucumber_tests-*.d
- mv cucumber_tests* ../../../build/duniter-cucumber
artifacts:
paths:
- build/
expire_in: 3 day
tests_release:
extends: .env
......@@ -153,6 +162,14 @@ tests_release:
- cargo cucumber -i balance*
- cargo cucumber -i monetary*
- cargo cucumber -i transfer*
after_script:
- cd target/debug/deps/
- rm cucumber_tests-*.d
- mv cucumber_tests* ../../../build/duniter-cucumber
artifacts:
paths:
- build/
expire_in: 3 day
dependencies:
- build_release
......
......@@ -1007,6 +1007,16 @@ dependencies = [
"cipher",
]
[[package]]
name = "ctrlc"
version = "3.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b37feaa84e6861e00a1f5e5aa8da3ee56d605c9992d33e082786754828e20865"
dependencies = [
"nix",
"winapi 0.3.9",
]
[[package]]
name = "cuckoofilter"
version = "0.5.0"
......@@ -1324,6 +1334,8 @@ name = "duniter-end2end-tests"
version = "3.0.0"
dependencies = [
"async-trait",
"clap",
"ctrlc",
"cucumber",
"env_logger",
"notify",
......@@ -3071,9 +3083,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
[[package]]
name = "libc"
version = "0.2.112"
version = "0.2.126"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b03d17f364a3a042d5e5d46b053bbbf82c92c9430c592dd4c064dc6ee997125"
checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836"
[[package]]
name = "libloading"
......@@ -4054,6 +4066,17 @@ version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab250442c86f1850815b5d268639dff018c0627022bc1940eb2d642ca1ce12f0"
[[package]]
name = "nix"
version = "0.24.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f17df307904acd05aa8e32e97bb20f2a0df1728bbc2d771ae8f9a90463441e9"
dependencies = [
"bitflags",
"cfg-if 1.0.0",
"libc",
]
[[package]]
name = "nodrop"
version = "0.1.14"
......@@ -8977,9 +9000,12 @@ dependencies = [
"anyhow",
"clap",
"frame-metadata",
"hex",
"memmap2 0.5.0",
"parity-scale-codec",
"run_script",
"scale-info",
"serde_json",
"version-compare",
"version_check",
]
......
......@@ -27,6 +27,14 @@ RUN test -x build/duniter || \
mv target/release/duniter build/ \
)
# Create fake duniter-cucumber if is not exist
# The goal is to avoid error later, this binary is optional
RUN test -x build/duniter-cucumber || \
( \
mkdir -p build && \
touch build/duniter-cucumber \
)
# ------------------------------------------------------------------------------
# Final Stage
# ------------------------------------------------------------------------------
......@@ -40,7 +48,7 @@ LABEL description="Crypto-currency software (based on Substrate framework) to op
RUN adduser --home /var/lib/duniter duniter
# Configuration
# rpc, rpc-ws, p2p, telemetry
# rpc, rpc-ws, p2p, telemetry
EXPOSE 9933 9944 30333 9615
VOLUME /var/lib/duniter
ENTRYPOINT ["docker-entrypoint"]
......@@ -48,4 +56,5 @@ USER duniter
# Intall
COPY --from=build /root/build/duniter /usr/local/bin/duniter
COPY --from=build /root/build/duniter-cucumber /usr/local/bin/duniter-cucumber
COPY docker/docker-entrypoint /usr/local/bin/
# Compile the runtime with srtool
```docker
docker run \
-i \
--rm \
-e PACKAGE=gdev-runtime \
-e RUNTIME_DIR=runtime/gdev \
-v $PWD:/build \
paritytech/srtool:1.60.0 build --app --json -cM
```
Then, the runtime wasm bytecode is generated in this location:
```
runtime/gdev/target/srtool/release/wbuild/gdev-runtime/gdev_runtime.compact.compressed.wasm
```
......@@ -10,6 +10,8 @@ version = '3.0.0'
[dev-dependencies]
async-trait = "0.1"
clap = { version = "3.0", features = ["derive"] }
ctrlc = "3.2.2"
cucumber = "0.11"
env_logger = "0.9.0"
notify = "4.0"
......
......@@ -58,21 +58,26 @@ Feature: My awesome feature
- eve
- ferdie
### Currency amounts
### genesis state
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`).
Each scenario bootstraps its own blockchain with its own genesis state.
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
that user. Note that this created money is not included in the monetary mass used to revalue the UD
amount.
The genesis configuration must then be defined in a json file located at
`/cucumber-genesis/confName.json`.
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
......
Feature: Balance transfer
Scenario: After 10 blocks, the monetary mass should be 30 ĞD
Then Monetary mass should be 0.00 ĞD
Then Current UD amount should be 10.00 ĞD
When 10 blocks later
Scenario: After 10 blocks, the monetary mass should be 60 ĞD
Then Monetary mass should be 30.00 ĞD
Then Current UD amount should be 10.00 ĞD
When 10 blocks later
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
@genesis.default
Feature: Balance transfer all
Scenario: If alice sends all her ĞDs to Dave, Dave will get 10 ĞD
Given alice have 10 ĞD
Scenario: If alice sends all her ĞDs to Dave, Dave will get 8 ĞD
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 {}
use serde_json::Value;
use sp_keyring::AccountKeyring;
use std::io::prelude::*;
use std::path::PathBuf;
use std::process::Command;
use std::str::FromStr;
use subxt::rpc::{rpc_params, ClientT, SubscriptionClientT};
......@@ -38,29 +39,39 @@ pub type TransactionProgress<'client> =
pub const SUDO_ACCOUNT: AccountKeyring = AccountKeyring::Alice;
pub struct Process(std::process::Child);
impl Drop for Process {
fn drop(&mut self) {
impl Process {
pub fn kill(&mut self) {
self.0.kill().expect("node already down");
}
}
const DUNITER_DOCKER_PATH: &str = "/usr/local/bin/duniter";
const DUNITER_LOCAL_PATH: &str = "../target/debug/duniter";
struct FullNode {
process: Process,
p2p_port: u16,
ws_port: u16,
}
pub async fn spawn_node() -> (Api, Client, Process) {
let duniter_binary_path = std::env::var("DUNITER_BINARY_PATH")
.unwrap_or_else(|_| "../target/debug/duniter".to_owned());
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").unwrap_or_else(|_| {
if std::path::Path::new(DUNITER_DOCKER_PATH).exists() {
DUNITER_DOCKER_PATH.to_owned()
} else {
DUNITER_LOCAL_PATH.to_owned()
}
});
let FullNode {
process,
p2p_port: _,
ws_port,
} = spawn_full_node(
&duniter_binary_path,
&["--dev", "--execution=Native", "--sealing=manual"],
&duniter_binary_path,
maybe_genesis_conf_file,
);
let client = ClientBuilder::new()
.set_url(format!("ws://127.0.0.1:{}", ws_port))
......@@ -110,12 +121,27 @@ pub async fn create_block_with_extrinsic(
.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 rpc_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 = std::fs::File::create(&log_file_path).expect("fail to create log file");
// Command
let process = Process(
Command::new(duniter_binary_path)
.args(
......@@ -133,6 +159,7 @@ fn spawn_full_node(duniter_binary_path: &str, args: &[&str]) -> FullNode {
.iter()
.chain(args),
)
.envs(envs)
.stdout(std::process::Stdio::null())
.stderr(log_file)
.spawn()
......
......@@ -21,13 +21,42 @@ use common::*;
use cucumber::{given, then, when, World, WorldInit};
use sp_keyring::AccountKeyring;
use std::convert::Infallible;
use std::path::PathBuf;
use std::str::FromStr;
use std::sync::{
atomic::{AtomicBool, Ordering},
Arc,
};
#[derive(WorldInit)]
pub struct DuniterWorld {
api: Api,
client: Client,
_process: Process,
pub struct DuniterWorld(Option<DuniterWorldInner>);
impl DuniterWorld {
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 {
......@@ -42,12 +71,27 @@ impl World for DuniterWorld {
type Error = Infallible;
async fn new() -> std::result::Result<Self, Infallible> {
let (api, client, _process) = spawn_node().await;
Ok(DuniterWorld {
Ok(DuniterWorld(None))
}
}
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,
client,
_process,
})
process,
}
}
fn kill(&mut self) {
self.process.kill();
}
}
......@@ -69,7 +113,7 @@ async fn who_have(world: &mut DuniterWorld, who: String, amount: u64, unit: Stri
if is_ud {
let current_ud_amount = world
.api
.api()
.storage()
.universal_dividend()
.current_ud(None)
......@@ -78,7 +122,7 @@ async fn who_have(world: &mut DuniterWorld, who: String, amount: u64, unit: Stri
}
// 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(())
}
......@@ -86,7 +130,7 @@ async fn who_have(world: &mut DuniterWorld, who: String, amount: u64, unit: Stri
#[when(regex = r"(\d+) blocks? later")]
async fn n_blocks_later(world: &mut DuniterWorld, n: usize) -> Result<()> {
for _ in 0..n {
common::create_empty_block(&world.client).await?;
common::create_empty_block(world.client()).await?;
}
Ok(())
}
......@@ -105,9 +149,9 @@ async fn transfer(
let (amount, is_ud) = parse_amount(amount, &unit);
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 {
common::balances::transfer(&world.api, &world.client, from, amount, to).await
common::balances::transfer(world.api(), world.client(), from, amount, to).await
}
}
......@@ -117,7 +161,7 @@ async fn send_all_to(world: &mut DuniterWorld, from: String, to: String) -> Resu
let from = AccountKeyring::from_str(&from).expect("unknown from");
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")]
......@@ -128,7 +172,7 @@ async fn should_have(world: &mut DuniterWorld, who: String, amount: u64) -> Resu
.to_account_id();
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);
Ok(())
}
......@@ -141,7 +185,7 @@ async fn current_ud_amount_should_be(
) -> Result<()> {
let expected = (amount * 100) + cents;
let actual = world
.api
.api()
.storage()
.universal_dividend()
.current_ud(None)
......@@ -154,7 +198,7 @@ async fn current_ud_amount_should_be(
async fn monetary_mass_should_be(world: &mut DuniterWorld, amount: u64, cents: u64) -> Result<()> {
let expected = (amount * 100) + cents;
let actual = world
.api
.api()
.storage()
.universal_dividend()
.monetary_mass(None)
......@@ -163,13 +207,76 @@ async fn monetary_mass_should_be(world: &mut DuniterWorld, amount: u64, cents: u
Ok(())
}
#[derive(clap::Args)]
struct CustomOpts {
/// Keep running
#[clap(short, long)]
keep_running: bool,
}
const DOCKER_FEATURES_PATH: &str = "/var/lib/duniter/cucumber-features";
const LOCAL_FEATURES_PATH: &str = "cucumber-features";
#[tokio::main(flavor = "current_thread")]
async fn main() {
//env_logger::init();
let features_path = if std::path::Path::new(DOCKER_FEATURES_PATH).exists() {
DOCKER_FEATURES_PATH
} else if std::path::Path::new(LOCAL_FEATURES_PATH).exists() {
LOCAL_FEATURES_PATH
} else {
panic!("cucumber-features folder not found");
};
let opts = cucumber::cli::Opts::<_, _, _, CustomOpts>::parsed();
let keep_running = opts.custom.keep_running;
// Handle crtl+C
let running = Arc::new(AtomicBool::new(true));
let running_clone = running.clone();
ctrlc::set_handler(move || {
running_clone.store(false, Ordering::SeqCst);
})
.expect("Error setting Ctrl-C handler");
DuniterWorld::cucumber()
//.fail_on_skipped()
.max_concurrent_scenarios(4)
.run_and_exit("cucumber-features")
.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(move |_feature, _rule, _scenario, maybe_world| {
if keep_running {
while running.load(Ordering::SeqCst) {}
}
if let Some(world) = maybe_world {
world.kill();
}
Box::pin(std::future::ready(()))
})
.with_cli(opts)
.run_and_exit(features_path)
.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 {
pub fn development_chain_spec() -> Result<ChainSpec, String> {
let wasm_binary = WASM_BINARY.ok_or_else(|| "Development wasm not available".to_string())?;
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,
))
if std::env::var("DUNITER_GENESIS_CONFIG").is_ok() {
super::gen_genesis_data::generate_genesis_data(
|genesis_data| {
ChainSpec::from_genesis(
// Name
"Development",
// ID
"gdev",
sc_service::ChainType::Development,
move || genesis_data_to_gdev_genesis_conf(genesis_data.clone(), wasm_binary),
// 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,
)
},
Some(get_authority_keys_from_seed("Alice").encode()),
Some(super::gen_genesis_data::ParamsAppliedAtGenesis {
genesis_certs_expire_on: 100_000,
genesis_smith_certs_expire_on: 100_000,
genesis_memberships_expire_on: 100_000,
genesis_memberships_renewable_on: 50,
genesis_smith_memberships_expire_on: 100_000,
genesis_smith_memberships_renewable_on: 50,
}),
)
} else {
Ok(ChainSpec::from_genesis(
// Name
"Development",
// ID
"gdev",
ChainType::Development,
move || {
gen_genesis_for_local_chain(
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> {
......@@ -139,6 +183,7 @@ pub fn gen_live_conf() -> Result<ChainSpec, String> {
None,
)
},
None,
Some(super::gen_genesis_data::ParamsAppliedAtGenesis {
genesis_certs_expire_on: 100_000,
genesis_smith_certs_expire_on: 100_000,
......@@ -164,7 +209,7 @@ pub fn local_testnet_config(
"gdev_local",
ChainType::Local,
move || {
gen_genesis_conf(
gen_genesis_for_local_chain(
wasm_binary,
// Initial authorities len
initial_authorities_len,
......@@ -200,7 +245,7 @@ pub fn local_testnet_config(
))
}
fn gen_genesis_conf(
fn gen_genesis_for_local_chain(
wasm_binary: &[u8],
initial_authorities_len: usize,
initial_smiths_len: usize,
......
......@@ -85,6 +85,7 @@ struct SmithData {
pub fn generate_genesis_data<CS, P, SK, F>(
f: F,
maybe_force_authority: Option<Vec<u8>>,
params_applied_at_genesis: Option<ParamsAppliedAtGenesis>,
) -> Result<CS, String>
where
......@@ -234,6 +235,7 @@ where
// SMITHS SUB-WOT //
let mut initial_authorities = BTreeMap::new();
let mut online_authorities_counter = 0;
let mut session_keys_map = BTreeMap::new();
let mut smiths_memberships = BTreeMap::new();
let mut smiths_certs_by_issuer = BTreeMap::new();
......@@ -253,15 +255,27 @@ where
}
// Initial authorities
initial_authorities.insert(
*idty_index,
(identity.pubkey.clone(), smith_data.session_keys.is_some()),
);
if maybe_force_authority.is_some() {
if smith_data.session_keys.is_some() {
return Err("session_keys field forbidden".to_owned());
}
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
let session_keys_bytes = if let Some(ref session_keys) = smith_data.session_keys {
online_authorities_counter += 1;
hex::decode(&session_keys[2..])
.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 {
// Create fake session keys (must be unique and deterministic)
let mut fake_session_keys_bytes = Vec::with_capacity(128);
......@@ -297,6 +311,10 @@ where
);
}
if maybe_force_authority.is_none() && online_authorities_counter == 0 {
return Err("The session_keys field must be filled in for at least one smith.".to_owned());
}
let genesis_data = GenesisData {
accounts,
certs_by_issuer,
......
......@@ -67,7 +67,7 @@ pub fn development_chain_spec() -> Result<ChainSpec, String> {
"gtest_dev",
ChainType::Development,
move || {
gen_genesis_conf(
gen_genesis_for_local_chain(
wasm_binary,
// Initial authorities
1,
......@@ -114,7 +114,7 @@ pub fn local_testnet_config(
"gtest_local",
ChainType::Local,
move || {
gen_genesis_conf(
gen_genesis_for_local_chain(
wasm_binary,
// Initial authorities len
initial_authorities_len,
......@@ -148,7 +148,7 @@ pub fn local_testnet_config(
))
}
fn gen_genesis_conf(
fn gen_genesis_for_local_chain(
wasm_binary: &[u8],
initial_authorities_len: usize,
initial_identities_len: usize,
......
......@@ -378,8 +378,8 @@ pub mod pallet {
payload.using_encoded(|bytes| payload_sig.verify(bytes, &payload.owner_key)),
Error::<T>::InvalidRevocationProof
);
if let Some(idty_index) = <IdentityIndexOf<T>>::take(payload.owner_key) {
Self::do_remove_identity(idty_index);
if let Some(idty_index) = <IdentityIndexOf<T>>::take(&payload.owner_key) {
Self::do_remove_identity(idty_index, Some(&payload.owner_key));
Ok(().into())
} else {
Err(Error::<T>::IdtyNotFound.into())
......@@ -394,7 +394,7 @@ pub mod pallet {
) -> DispatchResultWithPostInfo {
ensure_root(origin)?;
Self::do_remove_identity(idty_index);
Self::do_remove_identity(idty_index, None);
if let Some(idty_name) = idty_name {
<IdentitiesNames<T>>::remove(idty_name);
}
......@@ -499,9 +499,15 @@ pub mod pallet {
// INTERNAL FUNCTIONS //
impl<T: Config> Pallet<T> {
pub(super) fn do_remove_identity(idty_index: T::IdtyIndex) -> Weight {
pub(super) fn do_remove_identity(
idty_index: T::IdtyIndex,
maybe_owner_key: Option<&T::AccountId>,
) -> Weight {
if let Some(idty_val) = Identities::<T>::take(idty_index) {
T::RemoveIdentityConsumers::remove_idty_consumers(idty_index);
if let Some(owner_key) = maybe_owner_key {
IdentityIndexOf::<T>::remove(owner_key);
}
frame_system::Pallet::<T>::dec_sufficients(&idty_val.owner_key);
Self::deposit_event(Event::IdtyRemoved { idty_index });
T::OnIdtyChange::on_idty_change(idty_index, IdtyEvent::Removed);
......@@ -523,7 +529,8 @@ pub mod pallet {
for (idty_index, idty_status) in IdentitiesRemovableOn::<T>::take(block_number) {
if let Ok(idty_val) = <Identities<T>>::try_get(idty_index) {
if idty_val.removable_on == block_number && idty_val.status == idty_status {
total_weight += Self::do_remove_identity(idty_index)
total_weight +=
Self::do_remove_identity(idty_index, Some(&idty_val.owner_key))
}
}
}
......
......@@ -74,6 +74,50 @@ fn test_create_identity_ok() {
});
}
#[test]
fn test_create_identity_but_not_confirm_it() {
new_test_ext(IdentityConfig {
identities: vec![alice()],
})
.execute_with(|| {
// We need to initialize at least one block before any call
run_to_block(1);
// Alice should be able te create an identity
assert_ok!(Identity::create_identity(Origin::signed(1), 2));
// The identity shoud expire in blocs #3
run_to_block(3);
let events = System::events();
assert_eq!(events.len(), 1);
assert_eq!(
events[0],
EventRecord {
phase: Phase::Initialization,
event: Event::Identity(crate::Event::IdtyRemoved { idty_index: 2 }),
topics: vec![],
}
);
// We shoud be able to recreate the identity
run_to_block(4);
assert_ok!(Identity::create_identity(Origin::signed(1), 2));
let events = System::events();
assert_eq!(events.len(), 1);
assert_eq!(
events[0],
EventRecord {
phase: Phase::Initialization,
event: Event::Identity(crate::Event::IdtyCreated {
idty_index: 3,
owner_key: 2,
}),
topics: vec![],
}
);
});
}
#[test]
fn test_idty_creation_period() {
new_test_ext(IdentityConfig {
......
{
"first_ud": 1000,
"first_ud_reeval": 200,
"first_ud_reeval": 100800,
"identities": {
"Elois0": {
"balance": 1000,
"certs": ["Elois1", "Elois2", "Elois3", "Elois4"],
"pubkey": "5Co5AWcBiz4HaAEpu8BxLdghnCob89rFGU5yQ65WP3t2jsyB"
},
"Elois1": {
"balance": 1000,
"certs": ["Elois2", "Elois3"],
"certs": ["Elois0", "Elois2", "Elois3", "Elois4"],
"pubkey": "5H95G8bwuf4yyNNNa83hDhj7wpYaSRhZiGezZ9TDbyqNdAhY"
},
"Elois2": {
"balance": 1000,
"certs": ["Elois1", "Elois3"],
"certs": ["Elois0", "Elois1", "Elois3", "Elois4"],
"pubkey": "5GFEEx7kqvP4QEPCAXkALeYCG7m8DiA5LQ4YzW62j7FytQyg"
},
"Elois3": {
"balance": 1000,
"certs": ["Elois1", "Elois2"],
"certs": ["Elois0", "Elois1", "Elois2", "Elois4"],
"pubkey": "5HNFikZ2smaTvWSfxHBRXpPVkCVeiKs6oBjGBn95RCs1RPRB"
},
"Elois4": {
"balance": 1000,
"certs": ["Elois0", "Elois1", "Elois2", "Elois3"],
"pubkey": "5HNMRSuZkaw9UvTmkEQSLSiYoR8ZwyYDBnfuH6Sasgj5yFou"
}
},
"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": 100000,
"idty_confirm_period": 40,
"idty_creation_period": 50,
"membership_period": 100000,
"membership_renewable_period": 50,
"pending_membership_period": 500,
"ud_creation_period": 10,
"ud_first_reeval": 0,
"ud_reeval_period": 50,
"ud_reeval_period_in_blocks": 500,
"smith_cert_period": 15,
"smith_cert_max_by_issuer": 8,
"babe_epoch_duration": 600,
"cert_period": 14400,
"cert_max_by_issuer": 100,
"cert_min_received_cert_to_issue_cert": 5,
"cert_renewable_period": 172800,
"cert_validity_period": 2102400,
"idty_confirm_period": 14400,
"idty_creation_period": 14400,
"membership_period": 1051200,
"membership_renewable_period": 172800,
"pending_membership_period": 172800,
"ud_creation_period": 14400,
"ud_reeval_period": 100800,
"smith_cert_period": 14400,
"smith_cert_max_by_issuer": 12,
"smith_cert_min_received_cert_to_issue_cert": 2,
"smith_cert_renewable_period": 50,
"smith_cert_validity_period": 100000,
"smith_membership_period": 100000,
"smith_membership_renewable_period": 50,
"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
"smith_cert_renewable_period": 172800,
"smith_cert_validity_period": 2102400,
"smith_membership_period": 1051200,
"smith_membership_renewable_period": 172800,
"smith_pending_membership_period": 172800,
"smiths_wot_first_cert_issuable_on": 14400,
"smiths_wot_min_cert_for_membership": 3,
"wot_first_cert_issuable_on": 0,
"wot_min_cert_for_create_idty_right": 5,
"wot_min_cert_for_membership": 5
},
"smiths": {
"Elois0": {
"certs": ["Elois1", "Elois2", "Elois3", "Elois4"],
"session_keys": "0xb67db3acc0ec68327e9e258aa5247a0f504616142ecb445c2540b9bdb223b5bea0beb8d9dea455ad5591961c868e59e0760e6e819a85c89ea7cbb527a0944241a0beb8d9dea455ad5591961c868e59e0760e6e819a85c89ea7cbb527a0944241a0beb8d9dea455ad5591961c868e59e0760e6e819a85c89ea7cbb527a0944241"
},
"Elois1": {
"certs": ["Elois2", "Elois3"],
"session_keys": "0x90ea1e9059fd30860fc14370b62a3675a720763913e0aa41fa01b268c33afd3b64c9301d94f7a043ac6714c8a6301541e56e833c1b13170c19f9785fa469407864c9301d94f7a043ac6714c8a6301541e56e833c1b13170c19f9785fa469407864c9301d94f7a043ac6714c8a6301541e56e833c1b13170c19f9785fa4694078"
"certs": ["Elois0", "Elois2", "Elois3", "Elois4"]
},
"Elois2": {
"certs": ["Elois1", "Elois3"]
"certs": ["Elois0", "Elois1", "Elois3", "Elois4"]
},
"Elois3": {
"certs": ["Elois1", "Elois2"]
"certs": ["Elois0", "Elois1", "Elois2", "Elois4"]
},
"Elois4": {
"certs": ["Elois0", "Elois1", "Elois2", "Elois3"]
}
},
"sudo_key": "5H95G8bwuf4yyNNNa83hDhj7wpYaSRhZiGezZ9TDbyqNdAhY"
"sudo_key": "5Co5AWcBiz4HaAEpu8BxLdghnCob89rFGU5yQ65WP3t2jsyB"
}
No preview for this file type
......@@ -262,7 +262,7 @@ macro_rules! pallets_config {
impl pallet_atomic_swap::Config for Runtime {
type Event = Event;
type SwapAction = pallet_atomic_swap::BalanceSwapAction<AccountId, Balances>;
type ProofLimit = frame_support::traits::ConstU32<255>;
type ProofLimit = frame_support::traits::ConstU32<1_024>;
}
impl pallet_provide_randomness::Config for Runtime {
......
......@@ -22,7 +22,7 @@ SPEC_DIR="${4:-resources}"
echo "CURRENCY=$CURRENCY"
# constants
DUNITER_IMAGE_TAG="sha-54010fa1"
DUNITER_IMAGE_TAG="sha-68fd7b3a"
# Clean and (re-)create working forders
rm -rf $WORK_DIR
......