exonum-supervisor 1.0.0

Exonum supervisor service.
Documentation
// Copyright 2020 The Exonum Team
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use exonum::{helpers::ValidatorId, merkledb::ObjectHash};
use exonum_testkit::TestKitBuilder;

use crate::utils::*;
use exonum_supervisor::Supervisor;

#[test]
fn test_add_nodes_to_validators() {
    let mut testkit = TestKitBuilder::auditor()
        .with_validators(1)
        .with(Supervisor::decentralized())
        .build();

    let new_node_keys = testkit.network_mut().add_node().public_keys();
    let new_consensus_config = {
        let mut cfg = testkit.consensus_config();
        // Add our node.
        cfg.validator_keys.push(testkit.us().public_keys());
        // Add new node.
        cfg.validator_keys.push(new_node_keys);
        cfg
    };

    let new_config_propose = ConfigProposeBuilder::new(CFG_CHANGE_HEIGHT)
        .extend_consensus_config_propose(new_consensus_config.clone())
        .build();
    let signed_proposal =
        sign_config_propose_transaction(&testkit, new_config_propose, ValidatorId(0));
    testkit.create_block_with_transaction(signed_proposal);

    testkit.create_blocks_until(CFG_CHANGE_HEIGHT);

    assert_eq!(config_propose_entry(&testkit), None);
    assert_eq!(testkit.network().us().validator_id(), Some(ValidatorId(1)));
    assert_eq!(testkit.consensus_config(), new_consensus_config);
}

#[test]
fn test_exclude_us_from_validators() {
    let mut testkit = testkit_with_supervisor(4);
    let initiator_id = testkit.network().us().validator_id().unwrap();

    let new_consensus_config = {
        let mut cfg = testkit.consensus_config();
        // Remove us from validators
        cfg.validator_keys.remove(0);
        cfg
    };

    let old_validators = testkit.network().validators();

    let config_proposal = ConfigProposeBuilder::new(CFG_CHANGE_HEIGHT)
        .extend_consensus_config_propose(new_consensus_config.clone())
        .build();
    let proposal_hash = config_proposal.object_hash();
    testkit.create_block_with_transaction(sign_config_propose_transaction(
        &testkit,
        config_proposal,
        initiator_id,
    ));

    let signed_txs = build_confirmation_transactions(&testkit, proposal_hash, initiator_id);
    testkit.create_block_with_transactions(signed_txs);

    testkit.create_blocks_until(CFG_CHANGE_HEIGHT);

    let new_validators = testkit.network().validators();

    assert_eq!(config_propose_entry(&testkit), None);
    assert_eq!(testkit.consensus_config(), new_consensus_config);
    assert_eq!(testkit.network().us().validator_id(), None);
    assert_ne!(old_validators, new_validators);
}

#[test]
fn test_exclude_other_from_validators() {
    let mut testkit = testkit_with_supervisor(4);
    let initiator_id = testkit.network().us().validator_id().unwrap();

    let new_consensus_config = {
        let mut cfg = testkit.consensus_config();
        // Remove one of node from validators
        cfg.validator_keys.remove(1);
        cfg
    };

    let config_proposal = ConfigProposeBuilder::new(CFG_CHANGE_HEIGHT)
        .extend_consensus_config_propose(new_consensus_config.clone())
        .build();
    let proposal_hash = config_proposal.object_hash();

    testkit.create_block_with_transaction(sign_config_propose_transaction(
        &testkit,
        config_proposal,
        initiator_id,
    ));

    let signed_txs = build_confirmation_transactions(&testkit, proposal_hash, initiator_id);
    testkit.create_block_with_transactions(signed_txs);

    testkit.create_blocks_until(CFG_CHANGE_HEIGHT);

    assert_eq!(config_propose_entry(&testkit), None);
    assert_eq!(testkit.consensus_config(), new_consensus_config);
    assert_eq!(testkit.network().us().validator_id(), Some(initiator_id));
}

#[test]
fn test_change_our_validator_id() {
    let mut testkit = testkit_with_supervisor(4);
    let initiator_id = testkit.network().us().validator_id().unwrap();

    let new_consensus_config = {
        let mut cfg = testkit.consensus_config();
        // Change our validator id with another
        cfg.validator_keys.swap(0, 1);
        cfg
    };

    let config_proposal = ConfigProposeBuilder::new(CFG_CHANGE_HEIGHT)
        .extend_consensus_config_propose(new_consensus_config.clone())
        .build();
    let proposal_hash = config_proposal.object_hash();

    testkit.create_block_with_transaction(sign_config_propose_transaction(
        &testkit,
        config_proposal,
        initiator_id,
    ));

    let signed_txs = build_confirmation_transactions(&testkit, proposal_hash, initiator_id);
    testkit.create_block_with_transactions(signed_txs);

    testkit.create_blocks_until(CFG_CHANGE_HEIGHT);

    assert_eq!(config_propose_entry(&testkit), None);
    assert_eq!(testkit.network().us().validator_id(), Some(ValidatorId(1)));
    assert_eq!(&testkit.network().validators()[1], testkit.network().us());
    assert_eq!(testkit.consensus_config(), new_consensus_config);
}