#[cfg(test)]
mod tests;
use alloc::string::ToString;
use alloc::vec::Vec;
use crate::crypto::SequentialCommit;
use crate::errors::NoteError;
use crate::utils::serde::{
ByteReader,
ByteWriter,
Deserializable,
DeserializationError,
Serializable,
};
use crate::{Felt, Hasher, Word};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct NoteAttachment {
attachment_scheme: NoteAttachmentScheme,
content: NoteAttachmentContent,
}
impl NoteAttachment {
pub const MAX_NUM_WORDS: u16 = 256;
pub fn new(attachment_scheme: NoteAttachmentScheme, content: NoteAttachmentContent) -> Self {
Self { attachment_scheme, content }
}
pub fn with_word(attachment_scheme: NoteAttachmentScheme, word: Word) -> Self {
Self {
attachment_scheme,
content: NoteAttachmentContent::new(vec![word]).expect("single word is always valid"),
}
}
pub fn with_words(
attachment_scheme: NoteAttachmentScheme,
words: Vec<Word>,
) -> Result<Self, NoteError> {
NoteAttachmentContent::new(words).map(|content| Self { attachment_scheme, content })
}
pub fn attachment_scheme(&self) -> NoteAttachmentScheme {
self.attachment_scheme
}
pub fn content(&self) -> &NoteAttachmentContent {
&self.content
}
pub fn to_commitment(&self) -> Word {
self.content().to_commitment()
}
pub fn as_elements(&self) -> &[Felt] {
self.content.as_elements()
}
pub fn to_elements(&self) -> Vec<Felt> {
self.content().to_elements()
}
pub fn num_words(&self) -> u16 {
self.content.num_words()
}
}
impl Serializable for NoteAttachment {
fn write_into<W: ByteWriter>(&self, target: &mut W) {
self.attachment_scheme().write_into(target);
self.content().write_into(target);
}
fn get_size_hint(&self) -> usize {
self.attachment_scheme().get_size_hint() + self.content().get_size_hint()
}
}
impl Deserializable for NoteAttachment {
fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
let attachment_scheme = NoteAttachmentScheme::read_from(source)?;
let content = NoteAttachmentContent::read_from(source)?;
Ok(Self::new(attachment_scheme, content))
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct NoteAttachmentContent {
words: Vec<Word>,
commitment: Word,
}
impl NoteAttachmentContent {
pub fn new(words: Vec<Word>) -> Result<Self, NoteError> {
if words.is_empty() {
return Err(NoteError::NoteAttachmentContentEmpty);
}
if words.len() > NoteAttachment::MAX_NUM_WORDS as usize {
return Err(NoteError::NoteAttachmentContentTooManyWords(words.len()));
}
let elements = Word::words_as_elements(&words).to_vec();
let commitment = Hasher::hash_elements(&elements);
Ok(Self { words, commitment })
}
pub fn as_words(&self) -> &[Word] {
&self.words
}
pub fn num_words(&self) -> u16 {
u16::try_from(self.words.len()).expect("num words should fit in u16")
}
pub fn as_elements(&self) -> &[Felt] {
Word::words_as_elements(&self.words)
}
pub fn to_elements(&self) -> Vec<Felt> {
<Self as SequentialCommit>::to_elements(self)
}
pub fn to_commitment(&self) -> Word {
<Self as SequentialCommit>::to_commitment(self)
}
}
impl Serializable for NoteAttachmentContent {
fn write_into<W: ByteWriter>(&self, target: &mut W) {
let num_words_minus_1 =
u8::try_from(self.num_words().checked_sub(1).expect("num_words should be at least 1"))
.expect("num_words - 1 should fit in u8");
num_words_minus_1.write_into(target);
target.write_many(self.as_words());
}
fn get_size_hint(&self) -> usize {
core::mem::size_of::<u8>() + usize::from(self.num_words()) * Word::empty().get_size_hint()
}
}
impl Deserializable for NoteAttachmentContent {
fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
let num_words_minus_1 = u8::read_from(source)?;
let num_words = u16::from(num_words_minus_1) + 1;
let words: Vec<Word> =
source.read_many_iter(num_words as usize)?.collect::<Result<_, _>>()?;
Self::new(words).map_err(|err| DeserializationError::InvalidValue(err.to_string()))
}
}
impl SequentialCommit for NoteAttachmentContent {
type Commitment = Word;
fn to_elements(&self) -> Vec<Felt> {
Word::words_as_elements(&self.words).to_vec()
}
fn to_commitment(&self) -> Self::Commitment {
self.commitment
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct NoteAttachmentScheme(u16);
impl NoteAttachmentScheme {
const RESERVED: u16 = 0;
const NONE: u16 = 1;
pub const MAX: NoteAttachmentScheme = NoteAttachmentScheme(65534);
pub fn new(attachment_scheme: u16) -> Result<Self, NoteError> {
if attachment_scheme == Self::RESERVED {
return Err(NoteError::NoteAttachmentSchemeZeroReserved);
}
if attachment_scheme > Self::MAX.as_u16() {
return Err(NoteError::NoteAttachmentSchemeExceeded(attachment_scheme as u32));
}
Ok(Self(attachment_scheme))
}
pub const fn new_const(attachment_scheme: u16) -> Self {
assert!(attachment_scheme != Self::RESERVED, "attachment scheme must not be 0");
assert!(attachment_scheme <= Self::MAX.as_u16(), "attachment scheme exceeds maximum");
Self(attachment_scheme)
}
pub const fn none() -> Self {
Self(Self::NONE)
}
pub const fn is_none(&self) -> bool {
self.0 == Self::NONE
}
pub const fn as_u16(&self) -> u16 {
self.0
}
}
impl TryFrom<u16> for NoteAttachmentScheme {
type Error = NoteError;
fn try_from(value: u16) -> Result<Self, Self::Error> {
Self::new(value)
}
}
impl Default for NoteAttachmentScheme {
fn default() -> Self {
Self::none()
}
}
impl core::fmt::Display for NoteAttachmentScheme {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.write_fmt(format_args!("{}", self.0))
}
}
impl Serializable for NoteAttachmentScheme {
fn write_into<W: ByteWriter>(&self, target: &mut W) {
self.as_u16().write_into(target);
}
fn get_size_hint(&self) -> usize {
core::mem::size_of::<u16>()
}
}
impl Deserializable for NoteAttachmentScheme {
fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
let value = u16::read_from(source)?;
Self::try_from(value).map_err(|err| DeserializationError::InvalidValue(err.to_string()))
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct NoteAttachmentHeader {
scheme: Option<NoteAttachmentScheme>,
}
impl NoteAttachmentHeader {
pub fn new(scheme: NoteAttachmentScheme) -> Self {
Self { scheme: Some(scheme) }
}
pub fn new_maybe(scheme: Option<NoteAttachmentScheme>) -> Self {
Self { scheme }
}
pub const fn absent() -> Self {
Self { scheme: None }
}
pub const fn scheme(&self) -> Option<NoteAttachmentScheme> {
self.scheme
}
pub(super) fn as_u16(&self) -> u16 {
match self.scheme {
None => 0,
Some(scheme) => scheme.as_u16(),
}
}
pub const fn is_absent(&self) -> bool {
self.scheme.is_none()
}
}
impl Default for NoteAttachmentHeader {
fn default() -> Self {
Self::absent()
}
}
impl From<NoteAttachmentScheme> for NoteAttachmentHeader {
fn from(scheme: NoteAttachmentScheme) -> Self {
NoteAttachmentHeader::new(scheme)
}
}
impl Serializable for NoteAttachmentHeader {
fn write_into<W: ByteWriter>(&self, target: &mut W) {
self.scheme.write_into(target);
}
fn get_size_hint(&self) -> usize {
self.scheme.get_size_hint()
}
}
impl Deserializable for NoteAttachmentHeader {
fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
let scheme = Option::<NoteAttachmentScheme>::read_from(source)?;
Ok(Self::new_maybe(scheme))
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct NoteAttachments {
attachments: Vec<NoteAttachment>,
}
impl NoteAttachments {
pub const MAX_COUNT: usize = 4;
pub const MAX_NUM_WORDS: u16 = 512;
pub fn empty() -> Self {
Self { attachments: Vec::new() }
}
pub fn new(attachments: Vec<NoteAttachment>) -> Result<Self, NoteError> {
if attachments.len() > Self::MAX_COUNT {
return Err(NoteError::TooManyAttachments(attachments.len()));
}
let total_num_words = attachments
.iter()
.map(|attachment| attachment.num_words() as usize)
.sum::<usize>();
if total_num_words > Self::MAX_NUM_WORDS as usize {
return Err(NoteError::NoteAttachmentsTooManyWords(total_num_words));
}
Ok(Self { attachments })
}
pub fn get(&self, index: usize) -> Option<&NoteAttachment> {
self.attachments.get(index)
}
pub fn find(&self, scheme: NoteAttachmentScheme) -> Option<&NoteAttachment> {
self.attachments
.iter()
.find(|attachment| attachment.attachment_scheme == scheme)
}
pub fn num_attachments(&self) -> u8 {
u8::try_from(self.attachments.len())
.expect("constructor should ensure num attachment fits in u8")
}
pub fn is_empty(&self) -> bool {
self.attachments.is_empty()
}
pub fn iter(&self) -> impl Iterator<Item = &NoteAttachment> {
self.attachments.iter()
}
pub fn commitments(&self) -> Vec<Word> {
self.attachments
.iter()
.map(|attachment| attachment.content().to_commitment())
.collect()
}
pub fn to_commitment(&self) -> Word {
<Self as SequentialCommit>::to_commitment(self)
}
pub fn to_headers(&self) -> [NoteAttachmentHeader; Self::MAX_COUNT] {
let mut headers = [NoteAttachmentHeader::absent(); Self::MAX_COUNT];
for (i, attachment) in self.attachments.iter().enumerate() {
headers[i] = NoteAttachmentHeader::new(attachment.attachment_scheme());
}
headers
}
pub fn into_vec(self) -> Vec<NoteAttachment> {
self.attachments
}
}
impl Default for NoteAttachments {
fn default() -> Self {
Self::empty()
}
}
impl SequentialCommit for NoteAttachments {
type Commitment = Word;
fn to_elements(&self) -> Vec<Felt> {
let mut elements = Vec::new();
for commitment in self.attachments.iter().map(NoteAttachment::to_commitment) {
elements.extend_from_slice(commitment.as_elements());
}
elements
}
}
impl From<NoteAttachment> for NoteAttachments {
fn from(attachment: NoteAttachment) -> Self {
Self::new(vec![attachment]).expect("one attachment does not exceed the max of four")
}
}
impl Serializable for NoteAttachments {
fn write_into<W: ByteWriter>(&self, target: &mut W) {
self.num_attachments().write_into(target);
target.write_many(&self.attachments);
}
fn get_size_hint(&self) -> usize {
self.num_attachments().get_size_hint()
+ self.iter().map(NoteAttachment::get_size_hint).sum::<usize>()
}
}
impl Deserializable for NoteAttachments {
fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
let num_attachments = u8::read_from(source)? as usize;
let attachments = source
.read_many_iter::<NoteAttachment>(num_attachments)?
.collect::<Result<Vec<_>, _>>()?;
Self::new(attachments).map_err(|err| DeserializationError::InvalidValue(err.to_string()))
}
}