use anyhow::format_err;
use exonum::{crypto::Hash, helpers::byzantine_quorum};
use exonum_merkledb::access::Access;
use exonum_proto::ProtobufConvert;
use serde_derive::{Deserialize, Serialize};
use std::{fmt, str::FromStr};
use super::{multisig::MultisigIndex, proto, DeployRequest, MigrationRequest};
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
#[non_exhaustive]
pub enum Mode {
Simple,
Decentralized,
}
impl ProtobufConvert for Mode {
type ProtoStruct = proto::SupervisorMode;
fn to_pb(&self) -> Self::ProtoStruct {
match self {
Self::Simple => proto::SupervisorMode::SIMPLE,
Self::Decentralized => proto::SupervisorMode::DECENTRALIZED,
}
}
fn from_pb(pb: Self::ProtoStruct) -> anyhow::Result<Self> {
let result = match pb {
proto::SupervisorMode::SIMPLE => Self::Simple,
proto::SupervisorMode::DECENTRALIZED => Self::Decentralized,
};
Ok(result)
}
}
impl Mode {
pub fn deploy_approved<T: Access>(
self,
deploy: &DeployRequest,
deploy_requests: &MultisigIndex<T, DeployRequest>,
validators: usize,
) -> bool {
match self {
Self::Simple => {
deploy_requests.confirmations(deploy) >= 1
}
Self::Decentralized => {
let confirmations = deploy_requests.confirmations(deploy);
confirmations >= byzantine_quorum(validators)
}
}
}
pub fn config_approved<T: Access>(
self,
config_hash: &Hash,
config_confirms: &MultisigIndex<T, Hash>,
validators: usize,
) -> bool {
match self {
Self::Simple => {
config_confirms.confirmations(config_hash) >= 1
}
Self::Decentralized => {
let confirmations = config_confirms.confirmations(config_hash);
confirmations >= byzantine_quorum(validators)
}
}
}
pub fn migration_approved<T: Access>(
self,
request: &MigrationRequest,
migration_requests: &MultisigIndex<T, MigrationRequest>,
validators: usize,
) -> bool {
match self {
Self::Simple => {
migration_requests.confirmations(request) >= 1
}
Self::Decentralized => {
let confirmations = migration_requests.confirmations(request);
confirmations >= byzantine_quorum(validators)
}
}
}
}
impl fmt::Display for Mode {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str(match self {
Self::Simple => "simple",
Self::Decentralized => "decentralized",
})
}
}
impl FromStr for Mode {
type Err = anyhow::Error;
fn from_str(input: &str) -> Result<Self, Self::Err> {
match input {
"simple" => Ok(Self::Simple),
"decentralized" => Ok(Self::Decentralized),
_ => Err(format_err!(
"Invalid supervisor mode: {}. Should be 'simple' or 'decentralized'",
input
)),
}
}
}
#[cfg(test)]
mod tests {
use super::Mode;
use std::str::FromStr;
#[test]
fn simple_mode_from_str() {
let input = "simple";
let mode = Mode::from_str(input).unwrap();
assert_eq!(mode, Mode::Simple);
}
#[test]
fn decentralized_mode_from_str() {
let input = "decentralized";
let mode = Mode::from_str(input).unwrap();
assert_eq!(mode, Mode::Decentralized);
}
#[test]
fn invalid_mode_from_str() {
let input = "invalid_mode";
let err = Mode::from_str(input).unwrap_err();
assert!(err.to_string().contains("Invalid supervisor mode"));
}
}