use serde::{Deserialize, Serialize};
use serde_json;
use exonum::{
blockchain::{ConsensusConfig, GenesisConfig, StoredConfiguration, ValidatorKeys},
crypto::{self, CryptoHash, PublicKey, SecretKey},
helpers::{Height, Round, ValidatorId},
messages::{Message, Precommit, Propose, Signed},
};
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct TestNetwork {
us: TestNode,
validators: Vec<TestNode>,
}
impl TestNetwork {
pub fn new(validator_count: u16) -> Self {
Self::with_our_role(Some(ValidatorId(0)), validator_count)
}
pub fn with_our_role(us: Option<ValidatorId>, validator_count: u16) -> Self {
assert!(
validator_count > 0,
"At least one validator should be present in the network."
);
let validators = (0..validator_count)
.map(ValidatorId)
.map(TestNode::new_validator)
.collect::<Vec<_>>();
let us = if let Some(ValidatorId(id)) = us {
validators[id as usize].clone()
} else {
TestNode::new_auditor()
};
TestNetwork { validators, us }
}
pub fn us(&self) -> &TestNode {
&self.us
}
pub fn validators(&self) -> &[TestNode] {
&self.validators
}
pub fn genesis_config(&self) -> GenesisConfig {
GenesisConfig::new(self.validators.iter().map(TestNode::public_keys))
}
pub fn update<I: IntoIterator<Item = TestNode>>(&mut self, mut us: TestNode, validators: I) {
let validators = validators
.into_iter()
.enumerate()
.map(|(id, mut validator)| {
let validator_id = ValidatorId(id as u16);
validator.change_role(Some(validator_id));
if us.public_keys().consensus_key == validator.public_keys().consensus_key {
us.change_role(Some(validator_id));
}
validator
})
.collect::<Vec<_>>();
self.validators = validators;
self.us.clone_from(&us);
}
pub fn update_configuration(&mut self, config: TestNetworkConfiguration) {
self.update(config.us, config.validators);
}
pub fn service_public_key_of(&self, id: ValidatorId) -> Option<&PublicKey> {
self.validators()
.get(id.0 as usize)
.map(|x| &x.service_public_key)
}
pub fn consensus_public_key_of(&self, id: ValidatorId) -> Option<&PublicKey> {
self.validators()
.get(id.0 as usize)
.map(|x| &x.consensus_public_key)
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct TestNode {
consensus_secret_key: SecretKey,
consensus_public_key: PublicKey,
service_secret_key: SecretKey,
service_public_key: PublicKey,
validator_id: Option<ValidatorId>,
}
impl TestNode {
pub fn new_auditor() -> Self {
let (consensus_public_key, consensus_secret_key) = crypto::gen_keypair();
let (service_public_key, service_secret_key) = crypto::gen_keypair();
TestNode {
consensus_secret_key,
consensus_public_key,
service_secret_key,
service_public_key,
validator_id: None,
}
}
pub fn new_validator(validator_id: ValidatorId) -> Self {
let (consensus_public_key, consensus_secret_key) = crypto::gen_keypair();
let (service_public_key, service_secret_key) = crypto::gen_keypair();
TestNode {
consensus_secret_key,
consensus_public_key,
service_secret_key,
service_public_key,
validator_id: Some(validator_id),
}
}
pub fn from_parts(
consensus_keypair: (PublicKey, SecretKey),
service_keypair: (PublicKey, SecretKey),
validator_id: Option<ValidatorId>,
) -> TestNode {
TestNode {
consensus_public_key: consensus_keypair.0,
consensus_secret_key: consensus_keypair.1,
service_public_key: service_keypair.0,
service_secret_key: service_keypair.1,
validator_id,
}
}
pub fn create_propose(
&self,
height: Height,
last_hash: &crypto::Hash,
tx_hashes: &[crypto::Hash],
) -> Signed<Propose> {
Message::concrete(
Propose::new(
self.validator_id
.expect("An attempt to create propose from a non-validator node."),
height,
Round::first(),
last_hash,
tx_hashes,
),
self.consensus_public_key,
&self.consensus_secret_key,
)
}
pub fn create_precommit(
&self,
propose: &Propose,
block_hash: &crypto::Hash,
) -> Signed<Precommit> {
use std::time::SystemTime;
Message::concrete(
Precommit::new(
self.validator_id
.expect("An attempt to create propose from a non-validator node."),
propose.height(),
propose.round(),
&propose.hash(),
block_hash,
SystemTime::now().into(),
),
self.consensus_public_key,
&self.consensus_secret_key,
)
}
pub fn public_keys(&self) -> ValidatorKeys {
ValidatorKeys {
consensus_key: self.consensus_public_key,
service_key: self.service_public_key,
}
}
pub fn validator_id(&self) -> Option<ValidatorId> {
self.validator_id
}
pub fn change_role(&mut self, role: Option<ValidatorId>) {
self.validator_id = role;
}
pub fn service_keypair(&self) -> (&PublicKey, &SecretKey) {
(&self.service_public_key, &self.service_secret_key)
}
}
impl From<TestNode> for ValidatorKeys {
fn from(node: TestNode) -> Self {
node.public_keys()
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct TestNetworkConfiguration {
us: TestNode,
validators: Vec<TestNode>,
stored_configuration: StoredConfiguration,
}
impl TestNetworkConfiguration {
pub(crate) fn new(
network: &TestNetwork,
mut stored_configuration: StoredConfiguration,
) -> Self {
let prev_hash = CryptoHash::hash(&stored_configuration);
stored_configuration.previous_cfg_hash = prev_hash;
TestNetworkConfiguration {
us: network.us().clone(),
validators: network.validators().into(),
stored_configuration,
}
}
pub fn us(&self) -> &TestNode {
&self.us
}
pub fn set_us(&mut self, us: TestNode) {
self.us = us;
self.update_our_role();
}
pub fn validators(&self) -> &[TestNode] {
self.validators.as_ref()
}
pub fn consensus_configuration(&self) -> &ConsensusConfig {
&self.stored_configuration.consensus
}
pub fn actual_from(&self) -> Height {
self.stored_configuration.actual_from
}
pub fn set_actual_from(&mut self, actual_from: Height) {
self.stored_configuration.actual_from = actual_from;
}
pub fn set_consensus_configuration(&mut self, consensus: ConsensusConfig) {
self.stored_configuration.consensus = consensus;
}
pub fn set_validators<I>(&mut self, validators: I)
where
I: IntoIterator<Item = TestNode>,
{
self.validators = validators
.into_iter()
.enumerate()
.map(|(idx, mut node)| {
node.change_role(Some(ValidatorId(idx as u16)));
node
})
.collect();
self.stored_configuration.validator_keys = self
.validators
.iter()
.cloned()
.map(ValidatorKeys::from)
.collect();
self.update_our_role();
}
pub fn service_config<D>(&self, id: &str) -> D
where
for<'de> D: Deserialize<'de>,
{
let value = self
.stored_configuration
.services
.get(id)
.expect("Unable to find configuration for service");
serde_json::from_value(value.clone()).unwrap()
}
pub fn set_service_config<D>(&mut self, id: &str, config: D)
where
D: Serialize,
{
let value = serde_json::to_value(config).unwrap();
self.stored_configuration.services.insert(id.into(), value);
}
pub fn stored_configuration(&self) -> &StoredConfiguration {
&self.stored_configuration
}
fn update_our_role(&mut self) {
let validator_id = self
.validators
.iter()
.position(|x| x.public_keys().service_key == self.us.service_public_key)
.map(|x| ValidatorId(x as u16));
self.us.validator_id = validator_id;
}
}