corevm-host 0.1.28

Types that are common across CoreVM service, builder, monitor, tooling
Documentation
use alloc::vec::Vec;
use bytes::{Buf, Bytes};
use codec::{Compact, Decode, Encode};
use ed25519_consensus::{Signature, VerificationKey};

const SIGNATURE_LEN: usize = 64;

/// Input chunk.
///
/// Contains data for each stream.
#[derive(Encode, Debug, Default, Clone)]
pub struct InputChunk {
	pub video: Bytes,
	pub audio: Bytes,
	// TODO @ivan Ideally console and host messages should be chunked and signed separately
	// because they're consumed independently from video/audio.
	pub console: Bytes,
	/// Messages exchanged between the service and the host (builder/monitor etc.).
	pub host_messages: Vec<Bytes>,
}

impl InputChunk {
	/// Decode signed input chunk.
	///
	/// Checks the signature at the end of the input against the provided key before doing the usual
	/// decoding.
	pub fn decode_signed(mut input: Bytes, key: &VerificationKey) -> Result<Self, codec::Error> {
		let signature_offset = input.len().checked_sub(SIGNATURE_LEN).ok_or("No signature")?;
		let signature = input.split_off(signature_offset);
		let signature: Signature = (&signature[..]).try_into().expect("Signature size is constant");
		key.verify(&signature, &input[..]).map_err(|_| "Bad signature")?;
		let mut input = BytesInput(input);
		let video = input.decode_bytes()?;
		let audio = input.decode_bytes()?;
		let console = input.decode_bytes()?;
		let Compact(len) = Compact::<u32>::decode(&mut input)?;
		let mut host_messages = Vec::new();
		for _ in 0..len {
			host_messages.push(input.decode_bytes()?);
		}
		if !input.0.is_empty() {
			return Err("Invalid InputChunk".into());
		}
		Ok(Self { video, audio, console, host_messages })
	}

	/// Decode multiple signed chunks from the extrinsic.
	pub fn decode_multiple_signed(
		extrinsic: Bytes,
		key: &VerificationKey,
	) -> Result<Vec<Self>, codec::Error> {
		let mut chunks = Vec::new();
		let mut input = BytesInput(extrinsic);
		while !input.0.is_empty() {
			let raw_chunk = input.decode_bytes()?;
			let chunk = Self::decode_signed(raw_chunk, key)?;
			chunks.push(chunk);
		}
		Ok(chunks)
	}
}

struct BytesInput(Bytes);

impl BytesInput {
	/// Decode [`Bytes`] from the input without copying.
	fn decode_bytes(&mut self) -> Result<Bytes, codec::Error> {
		let Compact(len) = Compact::<u32>::decode(self)?;
		if len > self.0.len() as u32 {
			return Err("Invalid bytes".into());
		}
		let bytes = self.0.split_to(len as usize);
		Ok(bytes)
	}
}

impl codec::Input for BytesInput {
	fn remaining_len(&mut self) -> Result<Option<usize>, codec::Error> {
		Ok(Some(self.0.len()))
	}

	fn read(&mut self, buf: &mut [u8]) -> Result<(), codec::Error> {
		let n = buf.len();
		if n > self.0.len() {
			return Err("Buffer overflow".into());
		}
		buf.copy_from_slice(&self.0[..n]);
		self.0.advance(n);
		Ok(())
	}
}