use std::convert::TryFrom;
use std::ptr::copy_nonoverlapping;
use anyhow::{anyhow,
Error};
#[derive(Debug, PartialEq, Clone, Copy)]
pub enum RebaType
{
AddTxPool = 0,
CommitTx = 1
}
impl TryFrom<u8> for RebaType
{
type Error = Error;
fn try_from(b: u8) -> Result<Self, Self::Error>
{
match b
{
0 => Ok(RebaType::AddTxPool),
1 => Ok(RebaType::CommitTx),
_ => Err(anyhow!("Invalid RebaType value"))
}
}
}
#[derive(Debug, PartialEq)]
pub struct RebaData
{
reba_type: RebaType,
block_number: Option<u64>,
from: [u8; 20],
hash: [u8; 32],
bytes_size: usize
}
impl RebaData
{
pub fn reba_type(&self) -> RebaType
{
self.reba_type
}
pub fn block_number(&self) -> Option<u64>
{
self.block_number
}
pub fn from(&self) -> [u8; 20]
{
self.from
}
pub fn hash(&self) -> [u8; 32]
{
self.hash
}
pub fn bytes_size(&self) -> usize
{
self.bytes_size
}
}
impl TryFrom<&[u8]> for RebaData
{
type Error = Error;
fn try_from(bytes: &[u8]) -> Result<Self, Error>
{
let reba_type = RebaType::try_from(bytes[0])?;
let min_length = if reba_type == RebaType::CommitTx { 61 } else { 53 };
if bytes.len() < min_length
{
return Err(anyhow!("Byte slice is too small to contain RebaData"));
}
let block_number =
if reba_type == RebaType::CommitTx { Some(u64::from_le_bytes(bytes[1..9].try_into().unwrap())) } else { None };
let offset = if reba_type == RebaType::CommitTx { 9 } else { 1 };
let mut data = RebaData { reba_type,
block_number,
from: [0; 20],
hash: [0; 32],
bytes_size: min_length };
unsafe {
copy_nonoverlapping(bytes[offset..offset + 20].as_ptr(),
&mut data.from as *mut _ as *mut u8,
20);
copy_nonoverlapping(bytes[offset + 20..].as_ptr(),
&mut data.hash as *mut _ as *mut u8,
32);
}
Ok(data)
}
}
mod test
{
use super::*;
#[test]
fn test_from_bytes_add_tx_pool_no_block_number()
{
let bytes = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
let data = RebaData { reba_type: RebaType::AddTxPool,
block_number: None,
from: [0; 20],
hash: [0; 32],
bytes_size: 53 };
assert_eq!(RebaData::try_from(bytes.as_slice()).unwrap(), data);
}
#[test]
fn test_from_bytes_commit_tx_with_block_number()
{
let reba_type: u8 = RebaType::CommitTx as u8;
let block_number_bytes = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08];
let from = [1u8; 20];
let hash = [3u8; 32];
let mut data = Vec::new();
data.extend_from_slice(&[reba_type]);
data.extend_from_slice(&block_number_bytes);
data.extend_from_slice(&from);
data.extend_from_slice(&hash);
let block_number = u64::from_le_bytes(block_number_bytes);
let expected = RebaData { reba_type: RebaType::try_from(reba_type).unwrap(),
block_number: Some(block_number),
from,
hash,
bytes_size: data.len() };
let result = RebaData::try_from(data.as_slice()).unwrap();
assert_eq!(result, expected);
}
#[test]
fn test_from_bytes_invalid_reba_type()
{
let bytes = [2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
let res = RebaData::try_from(bytes.as_slice());
let e = res.unwrap_err();
assert_eq!(e.to_string(), "Invalid RebaType value");
}
}
#[test]
fn test_from_bytes_invalid_length()
{
let bytes = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
let res = RebaData::try_from(bytes.as_slice());
let e = res.unwrap_err();
assert_eq!(e.to_string(), "Byte slice is too small to contain RebaData");
}