#![allow(unknown_lints)]
extern crate protobuf;
extern crate rand;
extern crate zmq;
use protobuf::Message as M;
use protobuf::RepeatedField;
use std;
use std::borrow::Borrow;
use std::collections::HashMap;
use std::error::Error as StdError;
use messages::events::Event;
use messages::events::Event_Attribute;
use messages::processor::TpProcessRequest;
use messages::state_context::*;
use messages::validator::Message_MessageType;
use messaging::stream::MessageSender;
use messaging::stream::ReceiveError;
use messaging::stream::SendError;
use messaging::zmq_stream::ZmqMessageSender;
use super::generate_correlation_id;
#[derive(Debug)]
pub enum ApplyError {
InvalidTransaction(String),
InternalError(String),
}
impl std::error::Error for ApplyError {
fn description(&self) -> &str {
match *self {
ApplyError::InvalidTransaction(ref msg) => msg,
ApplyError::InternalError(ref msg) => msg,
}
}
fn cause(&self) -> Option<&std::error::Error> {
match *self {
ApplyError::InvalidTransaction(_) => None,
ApplyError::InternalError(_) => None,
}
}
}
impl std::fmt::Display for ApplyError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match *self {
ApplyError::InvalidTransaction(ref s) => write!(f, "InvalidTransaction: {}", s),
ApplyError::InternalError(ref s) => write!(f, "InternalError: {}", s),
}
}
}
#[derive(Debug)]
pub enum ContextError {
AuthorizationError(String),
ResponseAttributeError(String),
TransactionReceiptError(String),
SerializationError(Box<StdError>),
SendError(Box<StdError>),
ReceiveError(Box<StdError>),
}
impl std::error::Error for ContextError {
fn description(&self) -> &str {
match *self {
ContextError::AuthorizationError(ref msg) => msg,
ContextError::ResponseAttributeError(ref msg) => msg,
ContextError::TransactionReceiptError(ref msg) => msg,
ContextError::SerializationError(ref err) => err.description(),
ContextError::SendError(ref err) => err.description(),
ContextError::ReceiveError(ref err) => err.description(),
}
}
fn cause(&self) -> Option<&std::error::Error> {
match *self {
ContextError::AuthorizationError(_) => None,
ContextError::ResponseAttributeError(_) => None,
ContextError::TransactionReceiptError(_) => None,
ContextError::SerializationError(ref err) => Some(err.borrow()),
ContextError::SendError(ref err) => Some(err.borrow()),
ContextError::ReceiveError(ref err) => Some(err.borrow()),
}
}
}
impl std::fmt::Display for ContextError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match *self {
ContextError::AuthorizationError(ref s) => write!(f, "AuthorizationError: {}", s),
ContextError::ResponseAttributeError(ref s) => {
write!(f, "ResponseAttributeError: {}", s)
}
ContextError::TransactionReceiptError(ref s) => {
write!(f, "TransactionReceiptError: {}", s)
}
ContextError::SerializationError(ref err) => {
write!(f, "SerializationError: {}", err.description())
}
ContextError::SendError(ref err) => write!(f, "SendError: {}", err.description()),
ContextError::ReceiveError(ref err) => write!(f, "ReceiveError: {}", err.description()),
}
}
}
impl From<ContextError> for ApplyError {
fn from(context_error: ContextError) -> Self {
match context_error {
ContextError::TransactionReceiptError(..) => {
ApplyError::InternalError(format!("{}", context_error))
}
_ => ApplyError::InvalidTransaction(format!("{}", context_error)),
}
}
}
impl From<protobuf::ProtobufError> for ContextError {
fn from(e: protobuf::ProtobufError) -> Self {
ContextError::SerializationError(Box::new(e))
}
}
impl From<SendError> for ContextError {
fn from(e: SendError) -> Self {
ContextError::SendError(Box::new(e))
}
}
impl From<ReceiveError> for ContextError {
fn from(e: ReceiveError) -> Self {
ContextError::ReceiveError(Box::new(e))
}
}
#[derive(Clone)]
pub struct TransactionContext {
context_id: String,
sender: ZmqMessageSender,
}
impl TransactionContext {
pub fn new(context_id: &str, sender: ZmqMessageSender) -> TransactionContext {
TransactionContext {
context_id: String::from(context_id),
sender,
}
}
#[allow(clippy::needless_pass_by_value)]
pub fn get_state(&mut self, addresses: Vec<String>) -> Result<Option<Vec<u8>>, ContextError> {
let mut request = TpStateGetRequest::new();
request.set_context_id(self.context_id.clone());
request.set_addresses(RepeatedField::from_vec(addresses.to_vec()));
let serialized = request.write_to_bytes()?;
let x: &[u8] = &serialized;
let mut future = self.sender.send(
Message_MessageType::TP_STATE_GET_REQUEST,
&generate_correlation_id(),
x,
)?;
let response: TpStateGetResponse = protobuf::parse_from_bytes(future.get()?.get_content())?;
match response.get_status() {
TpStateGetResponse_Status::OK => {
let entry = match response.get_entries().first() {
Some(x) => x,
None => {
return Err(ContextError::ResponseAttributeError(String::from(
"TpStateGetResponse is missing entries.",
)));
}
};
match entry.get_data().len() {
0 => Ok(None),
_ => Ok(Some(Vec::from(entry.get_data()))),
}
}
TpStateGetResponse_Status::AUTHORIZATION_ERROR => {
Err(ContextError::AuthorizationError(format!(
"Tried to get unauthorized address: {:?}",
addresses
)))
}
TpStateGetResponse_Status::STATUS_UNSET => Err(ContextError::ResponseAttributeError(
String::from("Status was not set for TpStateGetResponse"),
)),
}
}
#[allow(clippy::needless_pass_by_value)]
pub fn set_state(&mut self, entries: HashMap<String, Vec<u8>>) -> Result<(), ContextError> {
let state_entries: Vec<TpStateEntry> = entries
.iter()
.map(|(address, payload)| {
let mut entry = TpStateEntry::new();
entry.set_address(address.to_string());
entry.set_data(payload.to_vec());
entry
})
.collect();
let mut request = TpStateSetRequest::new();
request.set_context_id(self.context_id.clone());
request.set_entries(RepeatedField::from_vec(state_entries.to_vec()));
let serialized = request.write_to_bytes()?;
let x: &[u8] = &serialized;
let mut future = self.sender.send(
Message_MessageType::TP_STATE_SET_REQUEST,
&generate_correlation_id(),
x,
)?;
let response: TpStateSetResponse = protobuf::parse_from_bytes(future.get()?.get_content())?;
match response.get_status() {
TpStateSetResponse_Status::OK => Ok(()),
TpStateSetResponse_Status::AUTHORIZATION_ERROR => {
Err(ContextError::AuthorizationError(format!(
"Tried to set unauthorized address: {:?}",
state_entries
)))
}
TpStateSetResponse_Status::STATUS_UNSET => Err(ContextError::ResponseAttributeError(
String::from("Status was not set for TpStateSetResponse"),
)),
}
}
#[allow(clippy::needless_pass_by_value)]
pub fn delete_state(
&mut self,
addresses: Vec<String>,
) -> Result<Option<Vec<String>>, ContextError> {
let mut request = TpStateDeleteRequest::new();
request.set_context_id(self.context_id.clone());
request.set_addresses(RepeatedField::from_vec(addresses.clone()));
let serialized = request.write_to_bytes()?;
let x: &[u8] = &serialized;
let mut future = self.sender.send(
Message_MessageType::TP_STATE_DELETE_REQUEST,
&generate_correlation_id(),
x,
)?;
let response: TpStateDeleteResponse =
protobuf::parse_from_bytes(future.get()?.get_content())?;
match response.get_status() {
TpStateDeleteResponse_Status::OK => Ok(Some(Vec::from(response.get_addresses()))),
TpStateDeleteResponse_Status::AUTHORIZATION_ERROR => {
Err(ContextError::AuthorizationError(format!(
"Tried to delete unauthorized address: {:?}",
addresses
)))
}
TpStateDeleteResponse_Status::STATUS_UNSET => {
Err(ContextError::ResponseAttributeError(String::from(
"Status was not set for TpStateDeleteResponse",
)))
}
}
}
pub fn add_receipt_data(&mut self, data: &[u8]) -> Result<(), ContextError> {
let mut request = TpReceiptAddDataRequest::new();
request.set_context_id(self.context_id.clone());
request.set_data(Vec::from(data));
let serialized = request.write_to_bytes()?;
let x: &[u8] = &serialized;
let mut future = self.sender.send(
Message_MessageType::TP_RECEIPT_ADD_DATA_REQUEST,
&generate_correlation_id(),
x,
)?;
let response: TpReceiptAddDataResponse =
protobuf::parse_from_bytes(future.get()?.get_content())?;
match response.get_status() {
TpReceiptAddDataResponse_Status::OK => Ok(()),
TpReceiptAddDataResponse_Status::ERROR => Err(ContextError::TransactionReceiptError(
format!("Failed to add receipt data {:?}", data),
)),
TpReceiptAddDataResponse_Status::STATUS_UNSET => {
Err(ContextError::ResponseAttributeError(String::from(
"Status was not set for TpReceiptAddDataResponse",
)))
}
}
}
pub fn add_event(
&mut self,
event_type: String,
attributes: Vec<(String, String)>,
data: &[u8],
) -> Result<(), ContextError> {
let mut event = Event::new();
event.set_event_type(event_type);
let mut attributes_vec = Vec::new();
for (key, value) in attributes {
let mut attribute = Event_Attribute::new();
attribute.set_key(key);
attribute.set_value(value);
attributes_vec.push(attribute);
}
event.set_attributes(RepeatedField::from_vec(attributes_vec));
event.set_data(Vec::from(data));
let mut request = TpEventAddRequest::new();
request.set_context_id(self.context_id.clone());
request.set_event(event.clone());
let serialized = request.write_to_bytes()?;
let x: &[u8] = &serialized;
let mut future = self.sender.send(
Message_MessageType::TP_EVENT_ADD_REQUEST,
&generate_correlation_id(),
x,
)?;
let response: TpEventAddResponse = protobuf::parse_from_bytes(future.get()?.get_content())?;
match response.get_status() {
TpEventAddResponse_Status::OK => Ok(()),
TpEventAddResponse_Status::ERROR => Err(ContextError::TransactionReceiptError(
format!("Failed to add event {:?}", event),
)),
TpEventAddResponse_Status::STATUS_UNSET => Err(ContextError::ResponseAttributeError(
String::from("Status was not set for TpEventAddRespons"),
)),
}
}
}
pub trait TransactionHandler {
fn family_name(&self) -> String;
fn family_versions(&self) -> Vec<String>;
fn namespaces(&self) -> Vec<String>;
fn apply(
&self,
request: &TpProcessRequest,
context: &mut TransactionContext,
) -> Result<(), ApplyError>;
}