use crate::core::block::{Block, BlockHeader, Error, UntrustedBlockHeader};
use crate::core::hash::{DefaultHashable, Hashed};
use crate::core::id::ShortIdentifiable;
use crate::core::{Output, ShortId, TxKernel};
use crate::ser::{self, read_multi, Readable, Reader, VerifySortedAndUnique, Writeable, Writer};
use rand::{thread_rng, Rng};
#[derive(Debug, Clone)]
pub struct CompactBlockBody {
pub out_full: Vec<Output>,
pub kern_full: Vec<TxKernel>,
pub kern_ids: Vec<ShortId>,
}
impl CompactBlockBody {
fn init(
out_full: Vec<Output>,
kern_full: Vec<TxKernel>,
kern_ids: Vec<ShortId>,
verify_sorted: bool,
) -> Result<Self, Error> {
let body = CompactBlockBody {
out_full,
kern_full,
kern_ids,
};
if verify_sorted {
body.verify_sorted()?;
Ok(body)
} else {
let mut body = body;
body.sort();
Ok(body)
}
}
fn sort(&mut self) {
self.out_full.sort_unstable();
self.kern_full.sort_unstable();
self.kern_ids.sort_unstable();
}
fn validate_read(&self) -> Result<(), Error> {
self.verify_sorted()?;
Ok(())
}
fn verify_sorted(&self) -> Result<(), Error> {
self.out_full.verify_sorted_and_unique()?;
self.kern_full.verify_sorted_and_unique()?;
self.kern_ids.verify_sorted_and_unique()?;
Ok(())
}
}
impl Readable for CompactBlockBody {
fn read<R: Reader>(reader: &mut R) -> Result<CompactBlockBody, ser::Error> {
let (out_full_len, kern_full_len, kern_id_len) =
ser_multiread!(reader, read_u64, read_u64, read_u64);
let out_full = read_multi(reader, out_full_len)?;
let kern_full = read_multi(reader, kern_full_len)?;
let kern_ids = read_multi(reader, kern_id_len)?;
let body = CompactBlockBody::init(out_full, kern_full, kern_ids, true)
.map_err(|_| ser::Error::CorruptedData)?;
Ok(body)
}
}
impl Writeable for CompactBlockBody {
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), ser::Error> {
ser_multiwrite!(
writer,
[write_u64, self.out_full.len() as u64],
[write_u64, self.kern_full.len() as u64],
[write_u64, self.kern_ids.len() as u64]
);
self.out_full.write(writer)?;
self.kern_full.write(writer)?;
self.kern_ids.write(writer)?;
Ok(())
}
}
impl Into<CompactBlockBody> for CompactBlock {
fn into(self) -> CompactBlockBody {
self.body
}
}
#[derive(Debug, Clone)]
pub struct CompactBlock {
pub header: BlockHeader,
pub nonce: u64,
body: CompactBlockBody,
}
impl DefaultHashable for CompactBlock {}
impl CompactBlock {
fn validate_read(&self) -> Result<(), Error> {
self.body.validate_read()?;
Ok(())
}
pub fn kern_ids(&self) -> &[ShortId] {
&self.body.kern_ids
}
pub fn kern_full(&self) -> &[TxKernel] {
&self.body.kern_full
}
pub fn out_full(&self) -> &[Output] {
&self.body.out_full
}
}
impl From<Block> for CompactBlock {
fn from(block: Block) -> Self {
let header = block.header.clone();
let nonce = thread_rng().gen();
let out_full = block
.outputs()
.iter()
.filter(|x| x.is_coinbase())
.cloned()
.collect::<Vec<_>>();
let mut kern_full = vec![];
let mut kern_ids = vec![];
for k in block.kernels() {
if k.is_coinbase() {
kern_full.push(k.clone());
} else {
kern_ids.push(k.short_id(&header.hash(), nonce));
}
}
let body = CompactBlockBody::init(out_full, kern_full, kern_ids, false)
.expect("sorting, not verifying");
CompactBlock {
header,
nonce,
body,
}
}
}
impl Writeable for CompactBlock {
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), ser::Error> {
self.header.write(writer)?;
if writer.serialization_mode() != ser::SerializationMode::Hash {
writer.write_u64(self.nonce)?;
self.body.write(writer)?;
}
Ok(())
}
}
impl Readable for CompactBlock {
fn read<R: Reader>(reader: &mut R) -> Result<CompactBlock, ser::Error> {
let header = BlockHeader::read(reader)?;
let nonce = reader.read_u64()?;
let body = CompactBlockBody::read(reader)?;
Ok(CompactBlock {
header,
nonce,
body,
})
}
}
impl From<UntrustedCompactBlock> for CompactBlock {
fn from(ucb: UntrustedCompactBlock) -> Self {
ucb.0
}
}
pub struct UntrustedCompactBlock(CompactBlock);
impl Readable for UntrustedCompactBlock {
fn read<R: Reader>(reader: &mut R) -> Result<UntrustedCompactBlock, ser::Error> {
let header = UntrustedBlockHeader::read(reader)?;
let nonce = reader.read_u64()?;
let body = CompactBlockBody::read(reader)?;
let cb = CompactBlock {
header: header.into(),
nonce,
body,
};
cb.validate_read().map_err(|_| ser::Error::CorruptedData)?;
Ok(UntrustedCompactBlock(cb))
}
}