use exonum::{
blockchain::config::GenesisConfigBuilder,
crypto,
helpers::ValidatorId,
keys::Keys,
merkledb::TemporaryDB,
runtime::{RuntimeInstance, WellKnownRuntime},
};
#[cfg(feature = "exonum-node")]
use exonum_node::NodePlugin;
use exonum_rust_runtime::{spec::Deploy, RustRuntime, RustRuntimeBuilder};
use futures::channel::mpsc;
use std::net::SocketAddr;
use crate::{ApiNotifierChannel, TestKit, TestNetwork};
#[derive(Debug)]
pub struct TestKitBuilder {
our_validator_id: Option<ValidatorId>,
test_network: Option<TestNetwork>,
logger: bool,
rust_runtime: RustRuntimeBuilder,
api_notifier_channel: ApiNotifierChannel,
additional_runtimes: Vec<RuntimeInstance>,
#[cfg(feature = "exonum-node")]
plugins: Vec<Box<dyn NodePlugin>>,
genesis_config: GenesisConfigBuilder,
}
impl TestKitBuilder {
pub fn validator() -> Self {
Self::new(Some(ValidatorId(0)))
}
pub fn auditor() -> Self {
Self::new(None)
}
pub fn with_keys(mut self, keys: impl IntoIterator<Item = Keys>) -> Self {
assert!(
self.test_network.is_none(),
"Number of validators is already specified"
);
self.test_network = Some(TestNetwork::with_our_role_from_keys(
self.our_validator_id,
keys,
));
self
}
pub fn with_validators(mut self, validator_count: u16) -> Self {
assert!(
self.test_network.is_none(),
"Number of validators is already specified"
);
self.test_network = Some(TestNetwork::with_our_role(
self.our_validator_id,
validator_count,
));
self
}
pub fn with(mut self, spec: impl Deploy) -> Self {
spec.deploy(&mut self.genesis_config, &mut self.rust_runtime);
self
}
#[cfg(feature = "exonum-node")]
pub fn with_plugin(mut self, plugin: impl NodePlugin + 'static) -> Self {
self.plugins.push(Box::new(plugin));
self
}
pub fn with_logger(mut self) -> Self {
self.logger = true;
self
}
pub fn with_additional_runtime(mut self, runtime: impl WellKnownRuntime) -> Self {
let instance: RuntimeInstance = runtime.into();
if instance.id == RustRuntime::ID
|| self.additional_runtimes.iter().any(|r| r.id == instance.id)
{
panic!(
"TestkitBuilder already contains runtime with id {}",
instance.id
);
}
self.additional_runtimes.push(instance);
self
}
pub fn build(mut self) -> TestKit {
if self.logger {
exonum::helpers::init_logger().ok();
}
crypto::init();
let our_validator_id = self.our_validator_id;
let network = self
.test_network
.unwrap_or_else(|| TestNetwork::with_our_role(our_validator_id, 1));
let rust_runtime = self.rust_runtime.build(self.api_notifier_channel.0.clone());
self.additional_runtimes.push(rust_runtime.into());
let mut genesis_config = self.genesis_config.build();
genesis_config.consensus_config = network.consensus_config();
#[cfg(feature = "exonum-node")]
{
let mut testkit = TestKit::assemble(
TemporaryDB::new(),
network,
Some(genesis_config),
self.additional_runtimes,
self.api_notifier_channel,
);
testkit.set_plugins(self.plugins);
testkit
}
#[cfg(not(feature = "exonum-node"))]
{
TestKit::assemble(
TemporaryDB::new(),
network,
Some(genesis_config),
self.additional_runtimes,
self.api_notifier_channel,
)
}
}
pub async fn serve(self, public_api_address: SocketAddr, private_api_address: SocketAddr) {
let testkit = self.build();
testkit.run(public_api_address, private_api_address).await
}
fn new(validator_id: Option<ValidatorId>) -> Self {
let api_notifier_channel = mpsc::channel(16);
Self {
test_network: None,
our_validator_id: validator_id,
logger: false,
rust_runtime: RustRuntimeBuilder::new(),
api_notifier_channel,
additional_runtimes: vec![],
#[cfg(feature = "exonum-node")]
plugins: vec![],
genesis_config: GenesisConfigBuilder::default(),
}
}
}