use chrono::{DateTime, Utc};
use exonum::{
blockchain::{Block, Schema, TxLocation},
crypto::Hash,
merkledb::{access::Access, ListProof},
runtime::{ExecutionStatus, InstanceId, MethodId},
};
use serde_derive::{Deserialize, Serialize};
use std::fmt;
use super::TransactionHex;
use crate::median_precommits_time;
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
#[serde(tag = "type", content = "payload", rename_all = "snake_case")]
pub enum IncomingMessage {
SetSubscriptions(Vec<SubscriptionType>),
Transaction(TransactionHex),
}
#[derive(Debug, PartialEq, Eq, Hash, Clone, PartialOrd, Ord)]
#[derive(Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum SubscriptionType {
None,
Blocks,
Transactions {
filter: Option<TransactionFilter>,
},
}
#[derive(Debug, PartialEq, Eq, Hash, Clone, PartialOrd, Ord)]
#[derive(Serialize, Deserialize)]
pub struct TransactionFilter {
pub instance_id: InstanceId,
pub method_id: Option<MethodId>,
}
impl TransactionFilter {
pub fn new(instance_id: InstanceId, method_id: Option<MethodId>) -> Self {
Self {
instance_id,
method_id,
}
}
}
#[serde(tag = "result", rename_all = "snake_case")]
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub enum Response<T> {
Success {
response: T,
},
Error {
description: String,
},
}
impl<T> Response<T> {
pub fn success(value: T) -> Self {
Response::Success { response: value }
}
pub fn error(description: impl fmt::Display) -> Self {
Response::Error {
description: description.to_string(),
}
}
pub fn into_result(self) -> Result<T, String> {
match self {
Response::Success { response } => Ok(response),
Response::Error { description } => Err(description),
}
}
}
impl<T> From<Result<T, String>> for Response<T> {
fn from(res: Result<T, String>) -> Self {
match res {
Ok(value) => Self::success(value),
Err(description) => Response::Error { description },
}
}
}
#[derive(Debug, Serialize, Deserialize)]
pub struct CommittedTransactionSummary {
pub tx_hash: Hash,
pub instance_id: InstanceId,
pub method_id: MethodId,
pub status: ExecutionStatus,
pub location: TxLocation,
pub location_proof: ListProof<Hash>,
pub time: DateTime<Utc>,
}
impl CommittedTransactionSummary {
pub fn new(schema: &Schema<impl Access>, tx_hash: &Hash) -> Option<Self> {
let tx = schema.transactions().get(tx_hash)?;
let tx = tx.payload();
let instance_id = tx.call_info.instance_id;
let method_id = tx.call_info.method_id;
let location = schema.transactions_locations().get(tx_hash)?;
let tx_result = schema.transaction_result(location)?;
let location_proof = schema
.block_transactions(location.block_height())
.get_proof(location.position_in_block().into());
let time = median_precommits_time(
&schema
.block_and_precommits(location.block_height())
.unwrap()
.precommits,
);
Some(Self {
tx_hash: *tx_hash,
instance_id,
method_id,
status: ExecutionStatus(tx_result),
location,
location_proof,
time,
})
}
}
#[derive(Debug, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum Notification {
Block(Block),
Transaction(CommittedTransactionSummary),
}