use std::{
convert::TryInto,
io::{self, Read, Write},
};
use crate::{
config::Config,
hash::double_sha512,
io::{LenBm, ReadFrom, SizedReadFrom, TooLongError, WriteTo},
message::{InvHash, Message},
object,
packet::Command,
pow,
time::Time,
};
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub struct Object {
nonce: pow::Nonce,
header: object::Header,
object_payload: Vec<u8>,
}
impl Object {
pub const MAX_OBJECT_PAYLOAD_LENGTH: usize = 1 << 18;
pub fn new(
nonce: pow::Nonce,
header: object::Header,
object_payload: Vec<u8>,
) -> Result<Self, TooLongError> {
if object_payload.len() > Self::MAX_OBJECT_PAYLOAD_LENGTH {
return Err(TooLongError::new(
Self::MAX_OBJECT_PAYLOAD_LENGTH,
object_payload.len(),
));
}
Ok(Self {
nonce,
header,
object_payload,
})
}
pub fn nonce(&self) -> pow::Nonce {
self.nonce
}
pub fn header(&self) -> &object::Header {
&self.header
}
pub fn object_payload(&self) -> &[u8] {
&self.object_payload
}
pub fn inv_hash(&self) -> InvHash {
let mut bytes: Vec<u8> = Vec::new();
self.write_to(&mut bytes).unwrap();
InvHash(double_sha512(bytes)[0..32].try_into().unwrap())
}
pub fn validate_pow_custom(
&self,
config: &Config,
nonce_trials_per_byte: pow::NonceTrialsPerByte,
extra_bytes: pow::PayloadLengthExtraBytes,
now: Time,
) -> Result<(), pow::ValidateError> {
let mut bytes =
Vec::with_capacity(self.header.len_bm() as usize + self.object_payload.len());
self.header.write_to(&mut bytes).unwrap();
self.object_payload.write_to(&mut bytes).unwrap();
let initial_hash = pow::initial_hash(&bytes);
let target = pow::target(
config,
bytes.len(),
self.header.expires_time(),
now,
nonce_trials_per_byte,
extra_bytes,
);
pow::validate(initial_hash, target, self.nonce)
}
pub fn validate_pow(&self, config: &Config) -> Result<(), pow::ValidateError> {
self.validate_pow_custom(
config,
config.nonce_trials_per_byte(),
config.payload_length_extra_bytes(),
Time::now(),
)
}
}
impl WriteTo for Object {
fn write_to(&self, w: &mut dyn Write) -> io::Result<()> {
self.nonce.write_to(w)?;
self.header.write_to(w)?;
self.object_payload.write_to(w)?;
Ok(())
}
}
impl SizedReadFrom for Object {
fn sized_read_from(r: &mut dyn Read, len: usize) -> io::Result<Self>
where
Self: Sized,
{
let nonce = pow::Nonce::read_from(r)?;
let header = object::Header::read_from(r)?;
let object_payload_len = len - nonce.len_bm() - header.len_bm();
if object_payload_len > Self::MAX_OBJECT_PAYLOAD_LENGTH {
return Err(io::Error::new(
io::ErrorKind::Other,
TooLongError::new(Self::MAX_OBJECT_PAYLOAD_LENGTH, object_payload_len),
));
}
let object_payload = Vec::<u8>::sized_read_from(r, object_payload_len)?;
Ok(Self {
nonce,
header,
object_payload,
})
}
}
impl Message for Object {
const COMMAND: Command = Command::OBJECT;
}
#[test]
fn test_object_write_to() {
let nonce = pow::Nonce::new(0xfedc_ba98_7654_3210);
let header = object::Header::new(
0x0123_4567_89ab_cdef.into(),
2.into(),
3u64.into(),
1u32.into(),
);
let object_payload = [0xff; 23].to_vec();
let test = Object::new(nonce, header, object_payload).unwrap();
let mut bytes = Vec::new();
test.write_to(&mut bytes).unwrap();
let expected = [
0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10, 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0x00, 0x00, 0x00, 0x02, 3, 1, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, ];
assert_eq!(bytes, expected.to_vec());
}
#[test]
fn test_object_sized_read_from() {
use std::io::Cursor;
let mut bytes = Cursor::new(
[
0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10, 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0x00, 0x00, 0x00, 0x02, 3, 1, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, ]
.to_vec(),
);
let test = Object::sized_read_from(&mut bytes, 45).unwrap();
let nonce = pow::Nonce::new(0xfedc_ba98_7654_3210);
let header = object::Header::new(
0x0123_4567_89ab_cdef.into(),
2.into(),
3u64.into(),
1u32.into(),
);
let object_payload = [0xff; 23].to_vec();
let expected = Object::new(nonce, header, object_payload).unwrap();
assert_eq!(test, expected);
}