use exonum::{
crypto,
helpers::{Height, ValidatorId},
merkledb::ObjectHash,
messages::{AnyTx, Verified},
runtime::{
ArtifactId, CommonError, ErrorMatch, InstanceId, RuntimeIdentifier, SnapshotExt,
SUPERVISOR_INSTANCE_ID,
},
};
use exonum_rust_runtime::{
api,
spec::{JustFactory, Spec},
RustRuntimeBuilder, ServiceFactory,
};
use exonum_supervisor::{
ArtifactError, CommonError as SupervisorCommonError, ConfigPropose, DeployRequest,
DeployResult, ServiceError, Supervisor, SupervisorInterface,
};
use exonum_testkit::{ApiKind, TestKit, TestKitApi, TestKitBuilder};
use crate::{
inc::{IncInterface, IncService, SERVICE_ID, SERVICE_NAME},
utils::{build_confirmation_transactions, CFG_CHANGE_HEIGHT},
};
const DEPLOY_HEIGHT: Height = CFG_CHANGE_HEIGHT;
const START_HEIGHT: Height = Height(DEPLOY_HEIGHT.0 * 2 + 1);
mod config;
mod config_api;
mod consensus_config;
mod deploy_failures;
mod inc;
mod migrations;
mod service_lifecycle;
mod supervisor_config;
mod utils;
fn default_artifact() -> ArtifactId {
IncService.artifact_id()
}
async fn assert_count(api: &TestKitApi, service_name: &'static str, expected_count: u64) {
let real_count: u64 = api
.public(ApiKind::Service(service_name))
.get("v1/counter")
.await
.unwrap();
assert_eq!(real_count, expected_count);
}
async fn assert_count_is_not_set(api: &TestKitApi, service_name: &'static str) {
let response: api::Result<u64> = api
.public(ApiKind::Service(service_name))
.get("v1/counter")
.await;
assert!(response.is_err());
}
#[allow(clippy::let_and_return)] fn artifact_exists(testkit: &TestKit, name: &str) -> bool {
let snapshot = testkit.snapshot();
let artifacts = snapshot.for_dispatcher().service_artifacts();
let artifact_exists = artifacts.keys().any(|artifact| artifact.name == name);
artifact_exists
}
fn service_instance_exists(testkit: &TestKit, name: &str) -> bool {
let snapshot = testkit.snapshot();
snapshot.for_dispatcher().get_instance(name).is_some()
}
fn find_instance_id(testkit: &TestKit, instance_name: &str) -> InstanceId {
let snapshot = testkit.snapshot();
snapshot
.for_dispatcher()
.get_instance(instance_name)
.expect("Can't find the instance")
.spec
.id
}
async fn deploy_artifact(api: &TestKitApi, request: DeployRequest) -> crypto::Hash {
let hash: crypto::Hash = api
.private(ApiKind::Service("supervisor"))
.query(&request)
.post("deploy-artifact")
.await
.unwrap();
hash
}
fn deploy_artifact_manually(
testkit: &mut TestKit,
request: &DeployRequest,
validator_id: ValidatorId,
) -> crypto::Hash {
let keypair = testkit.validator(validator_id).service_keypair();
let signed_request =
keypair.request_artifact_deploy(SUPERVISOR_INSTANCE_ID, request.to_owned());
let request_hash = signed_request.object_hash();
testkit.add_tx(signed_request);
request_hash
}
async fn start_service(api: &TestKitApi, request: ConfigPropose) -> crypto::Hash {
let hash: crypto::Hash = api
.private(ApiKind::Service("supervisor"))
.query(&request)
.post("propose-config")
.await
.unwrap();
hash
}
fn start_service_manually(
testkit: &mut TestKit,
request: &ConfigPropose,
validator_id: ValidatorId,
) -> crypto::Hash {
let keypair = testkit.validator(validator_id).service_keypair();
let signed_request = keypair.propose_config_change(SUPERVISOR_INSTANCE_ID, request.to_owned());
let request_hash = signed_request.object_hash();
testkit.add_tx(signed_request);
request_hash
}
fn deploy_confirmation(
testkit: &TestKit,
request: &DeployRequest,
validator_id: ValidatorId,
) -> Verified<AnyTx> {
let confirmation = DeployResult::ok(request.to_owned());
testkit
.validator(validator_id)
.service_keypair()
.report_deploy_result(SUPERVISOR_INSTANCE_ID, confirmation)
}
fn deploy_confirmation_hash(
testkit: &TestKit,
request: &DeployRequest,
validator_id: ValidatorId,
) -> crypto::Hash {
let confirmation_signed = deploy_confirmation(testkit, request, validator_id);
confirmation_signed.object_hash()
}
fn deploy_confirmation_hash_default(testkit: &TestKit, request: &DeployRequest) -> crypto::Hash {
deploy_confirmation_hash(testkit, request, ValidatorId(0))
}
fn deploy_request(artifact: ArtifactId, deadline_height: Height) -> DeployRequest {
DeployRequest::new(artifact, deadline_height)
}
fn start_service_request(
artifact: ArtifactId,
name: impl Into<String>,
deadline_height: Height,
) -> ConfigPropose {
ConfigPropose::new(0, deadline_height).start_service(artifact, name, Vec::default())
}
async fn deploy_default(testkit: &mut TestKit) {
let artifact = default_artifact();
let api = testkit.api();
assert!(!artifact_exists(testkit, &artifact.name));
let request = deploy_request(artifact.clone(), DEPLOY_HEIGHT);
let deploy_confirmation_hash = deploy_confirmation_hash_default(testkit, &request);
let hash = deploy_artifact(&api, request).await;
let block = testkit.create_block();
block[hash].status().unwrap();
assert!(testkit.is_tx_in_pool(&deploy_confirmation_hash));
testkit.create_blocks_until(DEPLOY_HEIGHT);
assert!(!testkit.is_tx_in_pool(&deploy_confirmation_hash));
assert!(artifact_exists(&testkit, &artifact.name));
}
async fn start_service_instance(testkit: &mut TestKit, instance_name: &str) -> InstanceId {
assert!(!service_instance_exists(testkit, instance_name));
let api = testkit.api();
let request = start_service_request(default_artifact(), instance_name, START_HEIGHT);
let hash = start_service(&api, request).await;
let block = testkit.create_block();
block[hash].status().unwrap();
testkit.create_blocks_until(START_HEIGHT);
assert!(service_instance_exists(testkit, instance_name));
find_instance_id(testkit, instance_name)
}
fn testkit_with_inc_service() -> TestKit {
TestKitBuilder::validator()
.with_logger()
.with(Supervisor::decentralized())
.with(JustFactory::new(IncService))
.build()
}
fn testkit_with_inc_service_and_n_validators(n: u16) -> TestKit {
TestKitBuilder::validator()
.with_logger()
.with(Supervisor::decentralized())
.with(JustFactory::new(IncService))
.with_validators(n)
.build()
}
fn testkit_with_inc_service_and_two_validators() -> TestKit {
testkit_with_inc_service_and_n_validators(2)
}
fn testkit_with_inc_service_auditor_validator() -> TestKit {
TestKitBuilder::auditor()
.with_logger()
.with(Supervisor::decentralized())
.with(JustFactory::new(IncService))
.with_validators(1)
.build()
}
fn testkit_with_inc_service_and_static_instance() -> TestKit {
TestKitBuilder::validator()
.with_logger()
.with(Supervisor::decentralized())
.with(Spec::new(IncService).with_default_instance())
.build()
}
fn available_services() -> RustRuntimeBuilder {
RustRuntimeBuilder::new()
.with_factory(IncService)
.with_factory(Supervisor)
}
#[tokio::test]
async fn test_static_service() {
let mut testkit = testkit_with_inc_service_and_static_instance();
let api = testkit.api();
assert_count_is_not_set(&api, SERVICE_NAME).await;
let keypair = crypto::KeyPair::random();
api.send(keypair.inc(SERVICE_ID, 0)).await;
testkit.create_block();
assert_count(&api, SERVICE_NAME, 1).await;
api.send(keypair.inc(SERVICE_ID, 1)).await;
testkit.create_block();
assert_count(&api, SERVICE_NAME, 2).await;
}
#[tokio::test]
async fn test_dynamic_service_normal_workflow() {
let mut testkit = testkit_with_inc_service();
deploy_default(&mut testkit).await;
let instance_name = "test_basics";
let instance_id = start_service_instance(&mut testkit, instance_name).await;
let api = testkit.api();
assert_count_is_not_set(&api, instance_name).await;
let keypair = crypto::KeyPair::random();
api.send(keypair.inc(instance_id, 0)).await;
testkit.create_block();
assert_count(&api, instance_name, 1).await;
api.send(keypair.inc(instance_id, 1)).await;
testkit.create_block();
assert_count(&api, instance_name, 2).await;
}
#[tokio::test]
async fn test_artifact_deploy_with_already_passed_deadline_height() {
let mut testkit = testkit_with_inc_service();
testkit.create_block();
let bad_deadline_height = testkit.height().previous();
let artifact = default_artifact();
let api = testkit.api();
let request = deploy_request(artifact.clone(), bad_deadline_height);
let deploy_confirmation_hash = deploy_confirmation_hash_default(&testkit, &request);
let hash = deploy_artifact(&api, request).await;
let block = testkit.create_block();
assert!(!artifact_exists(&testkit, &artifact.name));
assert!(!testkit.is_tx_in_pool(&deploy_confirmation_hash));
let expected_err = ErrorMatch::from_fail(&SupervisorCommonError::ActualFromIsPast)
.for_service(SUPERVISOR_INSTANCE_ID);
assert_eq!(*block[hash].status().unwrap_err(), expected_err);
}
#[tokio::test]
async fn test_start_service_instance_with_already_passed_deadline_height() {
let mut testkit = testkit_with_inc_service();
deploy_default(&mut testkit).await;
let api = testkit.api();
let artifact = default_artifact();
let instance_name = "inc_test";
let bad_deadline_height = testkit.height().previous();
let request = start_service_request(artifact, instance_name, bad_deadline_height);
let hash = start_service(&api, request).await;
let block = testkit.create_block();
let expected_err = ErrorMatch::from_fail(&SupervisorCommonError::ActualFromIsPast)
.with_description_containing("height for config proposal (2) is in the past")
.for_service(SUPERVISOR_INSTANCE_ID);
assert_eq!(*block[hash].status().unwrap_err(), expected_err);
}
#[tokio::test]
async fn test_try_run_unregistered_service_instance() {
let mut testkit = testkit_with_inc_service();
let api = testkit.api();
let instance_name = "wont_run";
let request = start_service_request(default_artifact(), instance_name.to_owned(), Height(1000));
let hash = start_service(&api, request).await;
let block = testkit.create_block();
let expected_err = ErrorMatch::from_fail(&ArtifactError::UnknownArtifact)
.for_service(SUPERVISOR_INSTANCE_ID)
.with_any_description();
assert_eq!(*block[hash].status().unwrap_err(), expected_err);
}
#[tokio::test]
async fn test_bad_artifact_name() {
let mut testkit = testkit_with_inc_service();
let api = testkit.api();
let bad_artifact = ArtifactId::from_raw_parts(
RuntimeIdentifier::Rust as _,
"does-not-exist".to_owned(),
"1.0.0".parse().unwrap(),
);
let request = deploy_request(bad_artifact.clone(), DEPLOY_HEIGHT);
let deploy_confirmation_hash = deploy_confirmation_hash_default(&testkit, &request);
let hash = deploy_artifact(&api, request).await;
let block = testkit.create_block();
block[hash].status().unwrap();
assert!(!testkit.is_tx_in_pool(&deploy_confirmation_hash));
testkit.create_block();
assert!(!artifact_exists(&testkit, &bad_artifact.name));
}
#[tokio::test]
async fn test_bad_runtime_id() {
let mut testkit = testkit_with_inc_service();
let api = testkit.api();
let bad_runtime_id = 10_000;
let mut artifact = IncService.artifact_id();
artifact.runtime_id = bad_runtime_id;
let request = deploy_request(artifact.clone(), DEPLOY_HEIGHT);
let deploy_confirmation_hash = deploy_confirmation_hash_default(&testkit, &request);
let hash = deploy_artifact(&api, request).await;
let block = testkit.create_block();
block[hash].status().unwrap();
assert!(!testkit.is_tx_in_pool(&deploy_confirmation_hash));
testkit.create_block();
assert!(!artifact_exists(&testkit, &artifact.name));
}
#[tokio::test]
async fn test_empty_service_instance_name() {
let mut testkit = testkit_with_inc_service();
deploy_default(&mut testkit).await;
let api = testkit.api();
let artifact = default_artifact();
let empty_instance_name = "";
let deadline_height = testkit.height().next();
let request = start_service_request(artifact, empty_instance_name, deadline_height);
let hash = start_service(&api, request).await;
let block = testkit.create_block();
let expected_err = ErrorMatch::from_fail(&ServiceError::InvalidInstanceName)
.with_description_containing("Service name is empty")
.for_service(SUPERVISOR_INSTANCE_ID);
assert_eq!(*block[hash].status().unwrap_err(), expected_err);
}
#[tokio::test]
async fn test_bad_service_instance_name() {
let mut testkit = testkit_with_inc_service();
deploy_default(&mut testkit).await;
let api = testkit.api();
let artifact = default_artifact();
let bad_instance_name = "\u{2764}";
let deadline_height = testkit.height().next();
let request = start_service_request(artifact, bad_instance_name, deadline_height);
let hash = start_service(&api, request).await;
let block = testkit.create_block();
let expected_msg = "Service name `\u{2764}` is invalid";
let expected_err = ErrorMatch::from_fail(&ServiceError::InvalidInstanceName)
.with_description_containing(expected_msg)
.for_service(SUPERVISOR_INSTANCE_ID);
assert_eq!(*block[hash].status().unwrap_err(), expected_err);
}
#[tokio::test]
async fn test_start_service_instance_twice() {
let instance_name = "inc";
let mut testkit = testkit_with_inc_service();
deploy_default(&mut testkit).await;
{
assert!(!service_instance_exists(&testkit, instance_name));
let api = testkit.api();
let deadline = testkit.height().next();
let request = start_service_request(default_artifact(), instance_name, deadline);
let hash = start_service(&api, request).await;
let block = testkit.create_block();
block[hash].status().unwrap();
assert!(service_instance_exists(&testkit, instance_name));
}
{
let api = testkit.api();
let deadline = testkit.height().next();
let request = start_service_request(default_artifact(), instance_name, deadline);
let hash = start_service(&api, request).await;
let block = testkit.create_block();
let expected_err = ErrorMatch::from_fail(&ServiceError::InstanceExists)
.for_service(SUPERVISOR_INSTANCE_ID)
.with_any_description();
assert_eq!(*block[hash].status().unwrap_err(), expected_err);
}
}
#[tokio::test]
async fn test_start_two_services_in_one_request() {
let instance_name_1 = "inc";
let instance_name_2 = "inc2";
let mut testkit = testkit_with_inc_service();
deploy_default(&mut testkit).await;
assert!(!service_instance_exists(&testkit, instance_name_1));
assert!(!service_instance_exists(&testkit, instance_name_2));
let artifact = default_artifact();
let deadline = testkit.height().next();
let request = ConfigPropose::new(0, deadline)
.start_service(artifact.clone(), instance_name_1, Vec::default())
.start_service(artifact, instance_name_2, Vec::default());
let api = testkit.api();
let hash = start_service(&api, request).await;
let block = testkit.create_block();
block[hash].status().unwrap();
assert!(service_instance_exists(&testkit, instance_name_1));
assert!(service_instance_exists(&testkit, instance_name_2));
}
#[tokio::test]
async fn test_restart_node_and_start_service_instance() {
let mut testkit = TestKitBuilder::validator()
.with_logger()
.with(Supervisor::decentralized())
.with(JustFactory::new(IncService))
.build();
deploy_default(&mut testkit).await;
let stopped_testkit = testkit.stop();
let mut testkit = stopped_testkit.resume(available_services());
assert!(artifact_exists(&testkit, &default_artifact().name));
let instance_name = "test_basics";
let keypair = crypto::KeyPair::random();
let instance_id = start_service_instance(&mut testkit, instance_name).await;
let api = testkit.api();
{
assert_count_is_not_set(&api, instance_name).await;
api.send(keypair.inc(instance_id, 0)).await;
testkit.create_block();
assert_count(&api, instance_name, 1).await;
api.send(keypair.inc(instance_id, 1)).await;
testkit.create_block();
assert_count(&api, instance_name, 2).await;
}
let stopped_testkit = testkit.stop();
let mut testkit = stopped_testkit.resume(available_services());
let api = testkit.api();
assert!(service_instance_exists(&testkit, instance_name));
{
assert_count(&api, instance_name, 2).await;
api.send(keypair.inc(instance_id, 2)).await;
testkit.create_block();
assert_count(&api, instance_name, 3).await;
}
}
#[tokio::test]
async fn test_restart_node_during_artifact_deployment_with_two_validators() {
let mut testkit = testkit_with_inc_service_and_two_validators();
let artifact = default_artifact();
let api = testkit.api();
assert!(!artifact_exists(&testkit, &artifact.name));
let request_deploy = deploy_request(artifact.clone(), DEPLOY_HEIGHT.next());
let deploy_confirmation_0 = deploy_confirmation(&testkit, &request_deploy, ValidatorId(0));
let deploy_confirmation_1 = deploy_confirmation(&testkit, &request_deploy, ValidatorId(1));
deploy_artifact(&api, request_deploy.clone()).await;
deploy_artifact_manually(&mut testkit, &request_deploy, ValidatorId(1));
let block = testkit.create_block();
block.iter().for_each(|tx| tx.status().unwrap());
assert!(testkit.is_tx_in_pool(&deploy_confirmation_0.object_hash()));
testkit.create_block();
let testkit = testkit.stop();
let mut testkit = testkit.resume(available_services());
testkit.add_tx(deploy_confirmation_1.clone());
assert!(testkit.is_tx_in_pool(&deploy_confirmation_1.object_hash()));
testkit.create_block();
assert!(!testkit.is_tx_in_pool(&deploy_confirmation_0.object_hash()));
assert!(!testkit.is_tx_in_pool(&deploy_confirmation_1.object_hash()));
assert!(artifact_exists(&testkit, &artifact.name));
}
#[tokio::test]
async fn test_two_validators() {
let mut testkit = testkit_with_inc_service_and_two_validators();
let artifact = default_artifact();
let api = testkit.api();
assert!(!artifact_exists(&testkit, &artifact.name));
let request_deploy = deploy_request(artifact.clone(), DEPLOY_HEIGHT);
let deploy_confirmation_0 = deploy_confirmation(&testkit, &request_deploy, ValidatorId(0));
let deploy_confirmation_1 = deploy_confirmation(&testkit, &request_deploy, ValidatorId(1));
deploy_artifact(&api, request_deploy.clone()).await;
deploy_artifact_manually(&mut testkit, &request_deploy, ValidatorId(1));
let block = testkit.create_block();
block.iter().for_each(|tx| tx.status().unwrap());
testkit.add_tx(deploy_confirmation_1.clone());
assert!(testkit.is_tx_in_pool(&deploy_confirmation_0.object_hash()));
assert!(testkit.is_tx_in_pool(&deploy_confirmation_1.object_hash()));
testkit.create_block();
assert!(!testkit.is_tx_in_pool(&deploy_confirmation_0.object_hash()));
assert!(!testkit.is_tx_in_pool(&deploy_confirmation_1.object_hash()));
let api = testkit.api(); assert!(artifact_exists(&testkit, &artifact.name));
let instance_name = "inc";
{
assert!(!service_instance_exists(&testkit, instance_name));
let deadline = DEPLOY_HEIGHT.next();
let request_start = start_service_request(default_artifact(), instance_name, deadline);
let propose_hash = request_start.object_hash();
start_service(&api, request_start).await;
testkit.create_block();
let signed_txs = build_confirmation_transactions(&testkit, propose_hash, ValidatorId(0));
testkit
.create_block_with_transactions(signed_txs)
.transactions[0]
.status()
.expect("Transaction with confirmations discarded.");
assert!(service_instance_exists(&testkit, instance_name));
}
let api = testkit.api(); let instance_id = find_instance_id(&testkit, instance_name);
{
assert_count_is_not_set(&api, instance_name).await;
let keypair = crypto::KeyPair::random();
api.send(keypair.inc(instance_id, 0)).await;
testkit.create_block();
assert_count(&api, instance_name, 1).await;
api.send(keypair.inc(instance_id, 1)).await;
testkit.create_block();
assert_count(&api, instance_name, 2).await;
}
}
#[tokio::test]
async fn test_multiple_validators_no_confirmation() {
let mut testkit = testkit_with_inc_service_and_two_validators();
let artifact = default_artifact();
let api = testkit.api();
assert!(!artifact_exists(&testkit, &artifact.name));
let request_deploy = deploy_request(artifact.clone(), DEPLOY_HEIGHT);
let deploy_confirmation_0 = deploy_confirmation(&testkit, &request_deploy, ValidatorId(0));
deploy_artifact(&api, request_deploy).await;
let block = testkit.create_block();
block.iter().for_each(|tx| tx.status().unwrap());
assert!(!testkit.is_tx_in_pool(&deploy_confirmation_0.object_hash()));
testkit.create_block();
assert!(!artifact_exists(&testkit, &artifact.name));
}
#[tokio::test]
async fn test_auditor_cant_send_requests() {
let mut testkit = testkit_with_inc_service_auditor_validator();
let artifact = default_artifact();
assert!(!artifact_exists(&testkit, &artifact.name));
let request_deploy = deploy_request(artifact, DEPLOY_HEIGHT);
let deploy_request_from_auditor = {
let confirmation = DeployResult::ok(request_deploy.clone());
testkit
.us()
.service_keypair()
.report_deploy_result(SUPERVISOR_INSTANCE_ID, confirmation)
};
testkit.add_tx(deploy_request_from_auditor.clone());
let deploy_artifact_validator_tx_hash =
deploy_artifact_manually(&mut testkit, &request_deploy, ValidatorId(0));
let block = testkit.create_block();
for tx in &block {
if tx.message().object_hash() == deploy_artifact_validator_tx_hash {
tx.status().unwrap();
} else if *tx.message() == deploy_request_from_auditor {
let expected_err = ErrorMatch::from_fail(&CommonError::UnauthorizedCaller)
.for_service(SUPERVISOR_INSTANCE_ID);
assert_eq!(*tx.status().unwrap_err(), expected_err);
} else {
panic!("Unexpected transaction in block: {:?}", tx);
}
}
}
#[tokio::test]
async fn test_auditor_normal_workflow() {
let mut testkit = testkit_with_inc_service_auditor_validator();
let artifact = default_artifact();
assert!(!artifact_exists(&testkit, &artifact.name));
let request_deploy = deploy_request(artifact.clone(), DEPLOY_HEIGHT);
let deploy_confirmation = deploy_confirmation(&testkit, &request_deploy, ValidatorId(0));
deploy_artifact_manually(&mut testkit, &request_deploy, ValidatorId(0));
let block = testkit.create_block();
block.iter().for_each(|tx| tx.status().unwrap());
testkit.add_tx(deploy_confirmation.clone());
assert!(testkit.is_tx_in_pool(&deploy_confirmation.object_hash()));
testkit.create_block();
assert!(!testkit.is_tx_in_pool(&deploy_confirmation.object_hash()));
assert!(artifact_exists(&testkit, &artifact.name));
let instance_name = "inc";
{
assert!(!service_instance_exists(&testkit, instance_name));
let deadline = DEPLOY_HEIGHT;
let request_start = start_service_request(default_artifact(), instance_name, deadline);
start_service_manually(&mut testkit, &request_start, ValidatorId(0));
let block = testkit.create_block();
block.iter().for_each(|tx| tx.status().unwrap());
assert!(service_instance_exists(&testkit, instance_name));
}
let api = testkit.api(); let instance_id = find_instance_id(&testkit, instance_name);
{
assert_count_is_not_set(&api, instance_name).await;
let keypair = crypto::KeyPair::random();
api.send(keypair.inc(instance_id, 0)).await;
testkit.create_block();
assert_count(&api, instance_name, 1).await;
api.send(keypair.inc(instance_id, 1)).await;
testkit.create_block();
assert_count(&api, instance_name, 2).await;
}
}
#[tokio::test]
async fn test_multiple_validators_deploy_confirm() {
let validators_count = 12;
let mut testkit = testkit_with_inc_service_and_n_validators(validators_count);
let artifact = default_artifact();
assert!(!artifact_exists(&testkit, &artifact.name));
let request_deploy = deploy_request(artifact.clone(), DEPLOY_HEIGHT);
for i in 0..validators_count {
deploy_artifact_manually(&mut testkit, &request_deploy, ValidatorId(i));
}
let block = testkit.create_block();
assert_eq!(block.len(), validators_count as usize);
block.iter().for_each(|tx| tx.status().unwrap());
let deploy_confirmations: Vec<Verified<AnyTx>> = (0..validators_count)
.map(|i| deploy_confirmation(&testkit, &request_deploy, ValidatorId(i)))
.collect();
testkit.create_block_with_transactions(deploy_confirmations);
assert!(artifact_exists(&testkit, &artifact.name));
}
#[test]
fn test_multiple_validators_deploy_confirm_byzantine_majority() {
let validators_count = 12;
let byzantine_majority = (validators_count * 2 / 3) + 1;
let mut testkit = testkit_with_inc_service_and_n_validators(validators_count);
let artifact = default_artifact();
assert!(!artifact_exists(&testkit, &artifact.name));
let request_deploy = deploy_request(artifact.clone(), DEPLOY_HEIGHT);
for i in 0..byzantine_majority {
deploy_artifact_manually(&mut testkit, &request_deploy, ValidatorId(i));
}
let block = testkit.create_block();
assert_eq!(block.len(), byzantine_majority as usize);
block.iter().for_each(|tx| tx.status().unwrap());
let deploy_confirmations: Vec<Verified<AnyTx>> = (0..validators_count)
.map(|i| deploy_confirmation(&testkit, &request_deploy, ValidatorId(i)))
.collect();
testkit.create_block_with_transactions(deploy_confirmations);
assert!(artifact_exists(&testkit, &artifact.name));
}
#[test]
fn test_multiple_validators_deploy_confirm_byzantine_minority() {
let validators_count = 12;
let byzantine_minority = validators_count * 2 / 3;
let mut testkit = testkit_with_inc_service_and_n_validators(validators_count);
let artifact = default_artifact();
assert!(!artifact_exists(&testkit, &artifact.name));
let request_deploy = deploy_request(artifact, DEPLOY_HEIGHT);
for i in 0..byzantine_minority {
deploy_artifact_manually(&mut testkit, &request_deploy, ValidatorId(i));
}
let block = testkit.create_block();
assert_eq!(block.len(), byzantine_minority as usize);
block.iter().for_each(|tx| tx.status().unwrap());
let confirmation = deploy_confirmation(&testkit, &request_deploy, ValidatorId(0));
let block = testkit.create_block_with_transaction(confirmation);
let expected_err = ErrorMatch::from_fail(&ArtifactError::DeployRequestNotRegistered)
.with_description_containing("Deploy of artifact `0:inc:1.0.0` is not registered")
.for_service(SUPERVISOR_INSTANCE_ID);
assert_eq!(*block[0].status().unwrap_err(), expected_err);
}
#[tokio::test]
async fn test_id_assignment() {
let max_builtin_id = SUPERVISOR_INSTANCE_ID;
let instance_name_1 = "inc";
let instance_name_2 = "inc2";
let mut testkit = testkit_with_inc_service();
deploy_default(&mut testkit).await;
let artifact = default_artifact();
let deadline = testkit.height().next();
let request = ConfigPropose::new(0, deadline)
.start_service(artifact.clone(), instance_name_1, Vec::default())
.start_service(artifact, instance_name_2, Vec::default());
let api = testkit.api();
start_service(&api, request).await;
testkit.create_block();
assert_eq!(
find_instance_id(&testkit, instance_name_1),
max_builtin_id + 1
);
assert_eq!(
find_instance_id(&testkit, instance_name_2),
max_builtin_id + 2
);
}
#[tokio::test]
async fn test_id_assignment_sparse() {
let max_builtin_id = 100;
let inc_service = Spec::new(IncService).with_instance(max_builtin_id, "inc", ());
let mut testkit = TestKitBuilder::validator()
.with_logger()
.with(Supervisor::decentralized())
.with(inc_service)
.build();
let artifact = default_artifact();
let deadline = testkit.height().next();
let instance_name = "inc2";
let request =
ConfigPropose::new(0, deadline).start_service(artifact, instance_name, Vec::default());
let api = testkit.api();
start_service(&api, request).await;
testkit.create_block();
assert_eq!(
find_instance_id(&testkit, instance_name),
max_builtin_id + 1
);
}