use crate::error::Error;
use crate::message::{Message, Seed};
use crate::node::Address;
use crate::util;
use std::cmp::Ordering;
use std::time::{Duration, SystemTime};
use uuid::Uuid;
#[derive(Debug, Clone)]
pub struct Transaction {
pub uuid: Uuid,
created: SystemTime,
pub message: Message,
}
#[derive(Debug, PartialEq, Clone)]
pub struct Wire {
length: [u8; 2],
pub uuid: [u8; 16],
class: [u8; 4],
source: [u8; 32],
target: [u8; 32],
topic: [u8; 32],
nonce: [u8; 24],
body: Vec<u8>,
}
#[derive(Eq, PartialEq, Debug, Clone)]
pub enum Class {
Ping,
Pong,
Lookup,
Details,
Action,
Subscribe,
Unsubscribe,
Subscriber,
Unsubscriber,
Bootstrap,
}
impl Transaction {
pub fn new(message: Message) -> Self {
Self {
uuid: Uuid::new_v4(),
created: SystemTime::now(),
message,
}
}
pub fn build(uuid: Uuid, created: SystemTime, message: Message) -> Self {
Self {
uuid,
created,
message,
}
}
pub fn from_bytes(bytes: &[u8]) -> Result<Self, Error> {
let wire = match Wire::from_bytes(&bytes) {
Ok(data) => data,
Err(e) => {
return Err(e);
}
};
wire.convert()
}
pub fn to_wire(&self) -> Wire {
Wire {
length: self.len(),
uuid: *self.uuid.as_bytes(),
class: self.message.class.as_bytes(),
source: self.message.source.as_bytes(),
target: self.message.target.as_bytes(),
topic: self.message.topic.as_bytes(),
nonce: self.message.seed.as_bytes(),
body: self.message.body.clone().as_bytes(),
}
}
pub fn from_wire(wire: &Wire) -> Result<Transaction, Error> {
let bytes = wire.as_bytes();
Transaction::from_bytes(&bytes)
}
pub fn as_bytes(&self) -> Vec<u8> {
self.to_wire().as_bytes()
}
pub fn target(&self) -> Address {
self.message.target.clone()
}
pub fn source(&self) -> Address {
self.message.source.clone()
}
pub fn topic(&self) -> Address {
self.message.topic.clone()
}
pub fn class(&self) -> Class {
self.message.class.clone()
}
fn len(&self) -> [u8; 2] {
self.message.len()
}
pub fn redirect(&self, target: Address) -> Transaction {
let mut transaction = self.clone();
transaction.message.target = target;
return transaction;
}
pub fn export(&self) -> (Address, Vec<u8>) {
(self.source(), self.message.body.clone().as_bytes())
}
pub fn age(&self) -> Result<Duration, Error> {
match self.created.elapsed() {
Ok(time) => Ok(time),
Err(_) => Err(Error::System(String::from("transaction time is invalid"))),
}
}
}
impl Ord for Transaction {
fn cmp(&self, other: &Self) -> Ordering {
self.created.cmp(&other.created)
}
}
impl PartialOrd for Transaction {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(&other))
}
}
impl PartialEq for Transaction {
fn eq(&self, rhs: &Self) -> bool {
self.uuid == rhs.uuid
}
}
impl Eq for Transaction {}
impl Class {
fn from_bytes(raw: [u8; 4]) -> Result<Self, Error> {
match raw {
[0, 0, 0, 0] => Ok(Self::Bootstrap),
[0, 0, 0, 1] => Ok(Self::Ping),
[0, 0, 0, 2] => Ok(Self::Pong),
[0, 0, 1, 0] => Ok(Self::Lookup),
[0, 0, 1, 1] => Ok(Self::Details),
[0, 1, 0, 0] => Ok(Self::Subscribe),
[0, 1, 0, 1] => Ok(Self::Unsubscribe),
[0, 1, 0, 2] => Ok(Self::Subscriber),
[0, 1, 0, 3] => Ok(Self::Unsubscriber),
[1, 0, 0, 1] => Ok(Self::Action),
_ => Err(Error::Invalid(String::from("class serlaization invalid"))),
}
}
fn as_bytes(&self) -> [u8; 4] {
match self {
Self::Bootstrap => [0, 0, 0, 0],
Self::Ping => [0, 0, 0, 1],
Self::Pong => [0, 0, 0, 2],
Self::Lookup => [0, 0, 1, 0],
Self::Details => [0, 0, 1, 1],
Self::Subscribe => [0, 1, 0, 0],
Self::Unsubscribe => [0, 1, 0, 1],
Self::Subscriber => [0, 1, 0, 2],
Self::Unsubscriber => [0, 1, 0, 3],
Self::Action => [1, 0, 0, 1],
}
}
}
impl Wire {
pub fn bootstrap(body: Vec<u8>) -> Self {
Self {
length: util::compute_length(&body),
class: [0; 4],
source: [0; 32],
target: [0; 32],
topic: [0; 32],
uuid: [0; 16],
nonce: [0; 24],
body,
}
}
pub fn from_bytes(raw: &[u8]) -> Result<Self, Error> {
if raw.len() <= 81 {
return Err(Error::Invalid(String::from("invalid number of bytes")));
}
let mut length: [u8; 2] = [0; 2];
let mut class: [u8; 4] = [0; 4];
let mut source: [u8; 32] = [0; 32];
let mut target: [u8; 32] = [0; 32];
let mut topic: [u8; 32] = [0; 32];
let mut uuid: [u8; 16] = [0; 16];
let mut nonce: [u8; 24] = [0; 24];
let mut body: Vec<u8> = Vec::new();
for (i, j) in raw.iter().enumerate() {
if i <= 1 {
length[i] = *j;
}
else if i >= 2 && i <= 5 {
class[i - 2] = *j;
}
else if i >= 6 && i <= 37 {
source[i - 6] = *j;
}
else if i >= 38 && i <= 69 {
target[i - 38] = *j;
}
else if i >= 70 && i <= 101 {
topic[i - 70] = *j;
}
else if i >= 102 && i <= 117 {
uuid[i - 102] = *j;
}
else if i >= 118 && i <= 141 {
nonce[i - 118] = *j;
} else {
body.push(*j);
}
}
Ok(Self {
length,
class,
source,
target,
topic,
uuid,
nonce,
body,
})
}
pub fn as_bytes(&self) -> Vec<u8> {
let mut data: Vec<u8> = Vec::new();
data.append(&mut self.length.to_vec());
data.append(&mut self.class.to_vec());
data.append(&mut self.source.to_vec());
data.append(&mut self.target.to_vec());
data.append(&mut self.topic.to_vec());
data.append(&mut self.uuid.to_vec());
data.append(&mut self.nonce.to_vec());
data.append(&mut self.body.clone());
return data;
}
pub fn convert(self) -> Result<Transaction, Error> {
let class = Class::from_bytes(self.class)?;
let source = Address::from_bytes(self.source);
let target = Address::from_bytes(self.target);
let topic = Address::from_bytes(self.topic);
let seed = Seed::from_bytes(&self.nonce)?;
let uuid = Uuid::from_bytes(self.uuid);
let message = Message::create(class, source, target, topic, seed, self.body);
Ok(Transaction {
uuid,
created: SystemTime::now(),
message,
})
}
pub fn is_empty(&self) -> bool {
self.class == [0; 4]
&& self.nonce == [0; 24]
&& self.source == [0; 32]
&& self.target == [0; 32]
&& self.topic == [0; 32]
}
pub fn body(&self) -> &Vec<u8> {
&self.body
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_class_parse() {
assert_eq!(Class::from_bytes([0, 0, 0, 1]).unwrap(), Class::Ping);
}
#[test]
fn test_class_bytes() {
assert_eq!(
Class::from_bytes([0, 0, 0, 1]).unwrap().as_bytes(),
[0, 0, 0, 1]
);
}
#[test]
fn test_wire_from_bytes() {
let data = generate_test_data();
match Wire::from_bytes(&data) {
Ok(wire) => {
assert_eq!(wire.source, Address::generate("abc").as_bytes());
assert_eq!(wire.target, Address::generate("def").as_bytes());
assert_eq!(
wire.uuid,
Uuid::parse_str(&mut "27d626f0-1515-47d4-a366-0b75ce6950bf")
.unwrap()
.as_bytes()
.to_owned()
);
assert_eq!(wire.class, [0, 0, 0, 1]);
assert_eq!(wire.body, "test".to_string().as_bytes())
}
Err(_e) => {
panic!("Message Invalid!")
}
}
}
#[test]
fn test_wire_to_transaction() {
let data = generate_test_data();
let wire = Wire::from_bytes(&data).unwrap();
assert_eq!(wire.convert().unwrap().message.class, Class::Ping);
}
#[test]
fn test_wire_as_bytes() {
let data = generate_test_data();
let wire = Wire::from_bytes(&data).unwrap();
assert_eq!(wire.as_bytes(), data);
}
#[test]
fn test_transaction_new() {
let m = Message::create(
Class::Ping,
Address::generate("a"),
Address::generate("b"),
Address::generate("tpc"),
Seed::from_bytes(&[0; 24]).unwrap(),
Vec::new(),
);
assert_eq!(Transaction::new(m).to_wire().class, [0, 0, 0, 1]);
}
#[test]
fn test_transaction_age() {
let m = Message::create(
Class::Action,
Address::generate("a"),
Address::generate("b"),
Address::generate("tpc"),
Seed::from_bytes(&[0; 24]).unwrap(),
Vec::new(),
);
let t = Transaction::new(m);
let d = t.age().unwrap();
assert_eq!(d > Duration::from_secs(0), true);
}
#[test]
fn test_transaction_as_bytes() {
let data = generate_test_data();
let m = Message::create(
Class::Ping,
Address::generate("abc"),
Address::generate("def"),
Address::generate("tpc"),
Seed::from_bytes(&[0; 24]).unwrap(),
Vec::new(),
);
let t = Transaction::new(m).as_bytes();
assert_eq!(data[0], t[0]);
assert_eq!(data[22], t[22]);
assert_eq!(data[55], t[55]);
}
#[test]
fn test_transaction_from_bytes() {
let data = generate_test_data();
let t = Transaction::from_bytes(&data).unwrap();
assert_eq!(
t.message.source.as_bytes(),
Address::generate("abc").as_bytes()
);
}
#[test]
fn test_transaction_build() {
let uuid = Uuid::parse_str(&mut "27d626f0-1515-47d4-a366-0b75ce6950bf").unwrap();
let time = SystemTime::now();
let seed = Seed::from_bytes(&[0; 24]).unwrap();
let message = Message::create(
Class::Ping,
Address::generate("abc"),
Address::generate("def"),
Address::generate("tpc"),
seed,
"test".to_string().as_bytes().to_vec(),
);
let t = Transaction::build(uuid, time, message);
let d = Transaction::from_bytes(&generate_test_data()).unwrap();
assert_eq!(t.message, d.message);
}
#[test]
fn test_empty_wire() {
let bytes = [0; 142];
let wire = Wire::from_bytes(&bytes);
assert_eq!(wire.is_err(), false);
assert_eq!(wire.unwrap().is_empty(), true);
}
fn generate_test_data() -> Vec<u8> {
let mut data: Vec<u8> = Vec::new();
data.append(&mut [0, 8].to_vec());
data.append(&mut [0, 0, 0, 1].to_vec());
let source = Address::generate("abc").as_bytes().to_owned().to_vec();
data.append(&mut source.clone());
let target = Address::generate("def").as_bytes().to_owned().to_vec();
data.append(&mut target.clone());
let target = Address::generate("tpc").as_bytes().to_owned().to_vec();
data.append(&mut target.clone());
let uuid = Uuid::parse_str(&mut "27d626f0-1515-47d4-a366-0b75ce6950bf").unwrap();
data.append(&mut uuid.clone().as_bytes().to_vec());
data.append(&mut [0; 24].to_vec());
data.append(&mut "test".to_string().into_bytes());
return data;
}
}