use bit_vec::BitVec;
use chrono::{DateTime, Utc};
use exonum::{
blockchain::Block,
crypto::{Hash, PublicKey},
helpers::{Height, Round, ValidatorId},
impl_exonum_msg_try_from_signed,
merkledb::{BinaryValue, HashTag},
messages::{AnyTx, Precommit, SignedMessage},
};
use exonum_derive::{BinaryValue, ObjectHash};
use exonum_proto::ProtobufConvert;
use std::convert::TryFrom;
use crate::proto::consensus;
#[derive(Clone, PartialEq, Eq, Ord, PartialOrd, Debug, ProtobufConvert)]
#[protobuf_convert(source = "consensus::Connect")]
pub struct Connect {
pub host: String,
pub time: DateTime<Utc>,
pub user_agent: String,
}
impl Connect {
pub fn new(
host: impl Into<String>,
time: DateTime<Utc>,
user_agent: impl Into<String>,
) -> Self {
Self {
host: host.into(),
time,
user_agent: user_agent.into(),
}
}
pub fn pub_addr(&self) -> &str {
&self.host
}
pub fn time(&self) -> DateTime<Utc> {
self.time
}
pub fn user_agent(&self) -> &str {
&self.user_agent
}
}
#[derive(Clone, PartialEq, Eq, Ord, PartialOrd, Debug, ProtobufConvert)]
#[protobuf_convert(source = "consensus::Status")]
pub struct Status {
pub epoch: Height,
pub blockchain_height: Height,
pub last_hash: Hash,
pub pool_size: u64,
}
impl Status {
pub fn new(epoch: Height, blockchain_height: Height, last_hash: Hash, pool_size: u64) -> Self {
Self {
epoch,
blockchain_height,
last_hash,
pool_size,
}
}
}
#[derive(Clone, PartialEq, Eq, Ord, PartialOrd, Debug)]
#[derive(ProtobufConvert, BinaryValue, ObjectHash)]
#[protobuf_convert(source = "consensus::Propose")]
pub struct Propose {
pub validator: ValidatorId,
pub epoch: Height,
pub round: Round,
pub prev_hash: Hash,
pub transactions: Vec<Hash>,
pub skip: bool,
}
impl Propose {
pub fn new(
validator: ValidatorId,
epoch: Height,
round: Round,
prev_hash: Hash,
transactions: impl IntoIterator<Item = Hash>,
) -> Self {
Self {
validator,
epoch,
round,
prev_hash,
transactions: transactions.into_iter().collect(),
skip: false,
}
}
pub fn skip(validator: ValidatorId, epoch: Height, round: Round, prev_hash: Hash) -> Self {
Self {
validator,
epoch,
round,
prev_hash,
transactions: vec![],
skip: true,
}
}
}
#[derive(Clone, PartialEq, Eq, Ord, PartialOrd, Debug, ProtobufConvert)]
#[protobuf_convert(source = "consensus::Prevote")]
pub struct Prevote {
pub validator: ValidatorId,
pub epoch: Height,
pub round: Round,
pub propose_hash: Hash,
pub locked_round: Round,
}
impl Prevote {
pub fn new(
validator: ValidatorId,
epoch: Height,
round: Round,
propose_hash: Hash,
locked_round: Round,
) -> Self {
Self {
validator,
epoch,
round,
propose_hash,
locked_round,
}
}
}
#[derive(Clone, PartialEq, Eq, Ord, PartialOrd, Debug, ProtobufConvert)]
#[protobuf_convert(source = "consensus::BlockResponse")]
pub struct BlockResponse {
pub to: PublicKey,
pub block: Block,
pub precommits: Vec<Vec<u8>>,
pub transactions: Vec<Hash>,
}
impl BlockResponse {
pub fn new(
to: PublicKey,
block: Block,
precommits: impl IntoIterator<Item = Vec<u8>>,
transactions: impl IntoIterator<Item = Hash>,
) -> Self {
Self {
to,
block,
precommits: precommits.into_iter().collect(),
transactions: transactions.into_iter().collect(),
}
}
pub fn verify_tx_hash(&self) -> bool {
self.block.tx_hash == HashTag::hash_list(&self.transactions)
}
}
#[derive(Clone, PartialEq, Eq, Ord, PartialOrd, Debug, ProtobufConvert)]
#[protobuf_convert(source = "consensus::TransactionsResponse")]
pub struct TransactionsResponse {
pub to: PublicKey,
pub transactions: Vec<Vec<u8>>,
}
impl TransactionsResponse {
pub fn new(to: PublicKey, transactions: impl IntoIterator<Item = Vec<u8>>) -> Self {
Self {
to,
transactions: transactions.into_iter().collect(),
}
}
}
#[derive(Clone, PartialEq, Eq, Ord, PartialOrd, Debug, ProtobufConvert)]
#[protobuf_convert(source = "consensus::ProposeRequest")]
pub struct ProposeRequest {
pub to: PublicKey,
pub epoch: Height,
pub propose_hash: Hash,
}
impl ProposeRequest {
pub fn new(to: PublicKey, epoch: Height, propose_hash: Hash) -> Self {
Self {
to,
epoch,
propose_hash,
}
}
}
#[derive(Clone, PartialEq, Eq, Ord, PartialOrd, Debug, ProtobufConvert)]
#[protobuf_convert(source = "consensus::TransactionsRequest")]
pub struct TransactionsRequest {
pub to: PublicKey,
pub txs: Vec<Hash>,
}
impl TransactionsRequest {
pub fn new(to: PublicKey, txs: impl IntoIterator<Item = Hash>) -> Self {
Self {
to,
txs: txs.into_iter().collect(),
}
}
}
#[derive(Clone, PartialEq, Eq, Ord, PartialOrd, Debug, ProtobufConvert)]
#[protobuf_convert(source = "consensus::PoolTransactionsRequest")]
pub struct PoolTransactionsRequest {
pub to: PublicKey,
}
impl PoolTransactionsRequest {
pub fn new(to: PublicKey) -> Self {
Self { to }
}
}
#[derive(Clone, PartialEq, Eq, Ord, PartialOrd, Debug, ProtobufConvert)]
#[protobuf_convert(source = "consensus::PrevotesRequest")]
pub struct PrevotesRequest {
pub to: PublicKey,
pub epoch: Height,
pub round: Round,
pub propose_hash: Hash,
pub validators: BitVec,
}
impl PrevotesRequest {
pub fn new(
to: PublicKey,
epoch: Height,
round: Round,
propose_hash: Hash,
validators: BitVec,
) -> Self {
Self {
to,
epoch,
round,
propose_hash,
validators,
}
}
}
#[derive(Clone, PartialEq, Eq, Ord, PartialOrd, Debug, ProtobufConvert)]
#[protobuf_convert(source = "consensus::PeersRequest")]
pub struct PeersRequest {
pub to: PublicKey,
}
impl PeersRequest {
pub fn new(to: PublicKey) -> Self {
Self { to }
}
}
#[derive(Clone, PartialEq, Eq, Ord, PartialOrd, Debug, ProtobufConvert)]
#[protobuf_convert(source = "consensus::BlockRequest")]
pub struct BlockRequest {
pub to: PublicKey,
pub height: Height,
pub epoch: Height,
}
impl BlockRequest {
pub fn new(to: PublicKey, height: Height) -> Self {
Self {
to,
height,
epoch: Height(0),
}
}
pub fn with_epoch(to: PublicKey, height: Height, epoch: Height) -> Self {
debug_assert!(epoch > Height(0));
Self { to, height, epoch }
}
pub fn epoch(&self) -> Option<Height> {
if self.epoch == Height(0) {
None
} else {
Some(self.epoch)
}
}
}
#[derive(Clone, PartialEq, Eq, Ord, PartialOrd, Debug)]
#[derive(ProtobufConvert, BinaryValue, ObjectHash)]
#[protobuf_convert(
source = "consensus::ExonumMessage",
rename(case = "snake_case"),
impl_from_trait
)]
#[allow(clippy::large_enum_variant)]
pub enum ExonumMessage {
AnyTx(AnyTx),
Connect(Connect),
Status(Status),
Propose(Propose),
Prevote(Prevote),
Precommit(Precommit),
TransactionsResponse(TransactionsResponse),
BlockResponse(BlockResponse),
ProposeRequest(ProposeRequest),
TransactionsRequest(TransactionsRequest),
PrevotesRequest(PrevotesRequest),
PeersRequest(PeersRequest),
BlockRequest(BlockRequest),
PoolTransactionsRequest(PoolTransactionsRequest),
}
impl TryFrom<SignedMessage> for ExonumMessage {
type Error = anyhow::Error;
fn try_from(value: SignedMessage) -> Result<Self, Self::Error> {
Self::from_bytes(value.payload.into())
}
}
impl TryFrom<&SignedMessage> for ExonumMessage {
type Error = anyhow::Error;
fn try_from(value: &SignedMessage) -> Result<Self, Self::Error> {
let bytes = std::borrow::Cow::Borrowed(value.payload.as_ref());
Self::from_bytes(bytes)
}
}
impl_exonum_msg_try_from_signed! {
ExonumMessage => Connect, Status,
Propose, Prevote, TransactionsResponse,
BlockResponse, ProposeRequest, TransactionsRequest,
PrevotesRequest, PeersRequest, BlockRequest, PoolTransactionsRequest
}