use alloc::collections::BTreeSet;
use alloc::string::ToString;
use alloc::vec::Vec;
use core::fmt::Debug;
use crate::constants::NOTE_MAX_SIZE;
use crate::errors::{OutputNoteError, TransactionOutputError};
use crate::note::{
Note,
NoteAssets,
NoteHeader,
NoteId,
NoteMetadata,
NoteRecipient,
PartialNote,
compute_note_commitment,
};
use crate::utils::serde::{
ByteReader,
ByteWriter,
Deserializable,
DeserializationError,
Serializable,
};
use crate::{Felt, Hasher, MAX_OUTPUT_NOTES_PER_TX, Word};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct OutputNoteCollection<N> {
notes: Vec<N>,
commitment: Word,
}
impl<N> OutputNoteCollection<N>
where
for<'a> &'a NoteHeader: From<&'a N>,
for<'a> NoteId: From<&'a N>,
{
pub fn new(notes: Vec<N>) -> Result<Self, TransactionOutputError> {
if notes.len() > MAX_OUTPUT_NOTES_PER_TX {
return Err(TransactionOutputError::TooManyOutputNotes(notes.len()));
}
let mut seen_notes = BTreeSet::new();
for note in notes.iter() {
let note_id = NoteId::from(note);
if !seen_notes.insert(note_id) {
return Err(TransactionOutputError::DuplicateOutputNote(note_id));
}
}
let commitment = Self::compute_commitment(notes.iter().map(<&NoteHeader>::from));
Ok(Self { notes, commitment })
}
pub fn commitment(&self) -> Word {
self.commitment
}
pub fn num_notes(&self) -> usize {
self.notes.len()
}
pub fn is_empty(&self) -> bool {
self.notes.is_empty()
}
pub fn get_note(&self, idx: usize) -> &N {
&self.notes[idx]
}
pub fn iter(&self) -> impl Iterator<Item = &N> {
self.notes.iter()
}
pub(crate) fn compute_commitment<'header>(
notes: impl ExactSizeIterator<Item = &'header NoteHeader>,
) -> Word {
if notes.len() == 0 {
return Word::empty();
}
let mut elements: Vec<Felt> = Vec::with_capacity(notes.len() * 8);
for note_header in notes {
elements.extend_from_slice(note_header.id().as_elements());
elements.extend_from_slice(note_header.metadata().to_commitment().as_elements());
}
Hasher::hash_elements(&elements)
}
}
impl<N> IntoIterator for OutputNoteCollection<N> {
type Item = N;
type IntoIter = alloc::vec::IntoIter<N>;
fn into_iter(self) -> Self::IntoIter {
self.notes.into_iter()
}
}
impl<N: Serializable> Serializable for OutputNoteCollection<N> {
fn write_into<W: ByteWriter>(&self, target: &mut W) {
assert!(self.notes.len() <= u16::MAX.into());
target.write_u16(self.notes.len() as u16);
target.write_many(&self.notes);
}
}
impl<N> Deserializable for OutputNoteCollection<N>
where
N: Deserializable,
for<'a> &'a NoteHeader: From<&'a N>,
for<'a> NoteId: From<&'a N>,
{
fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
let num_notes = source.read_u16()?;
let notes = source.read_many_iter::<N>(num_notes.into())?.collect::<Result<_, _>>()?;
Self::new(notes).map_err(|err| DeserializationError::InvalidValue(err.to_string()))
}
}
pub type RawOutputNotes = OutputNoteCollection<RawOutputNote>;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum RawOutputNote {
Full(Note),
Partial(PartialNote),
}
impl RawOutputNote {
const FULL: u8 = 0;
const PARTIAL: u8 = 1;
pub fn assets(&self) -> &NoteAssets {
match self {
Self::Full(note) => note.assets(),
Self::Partial(note) => note.assets(),
}
}
pub fn id(&self) -> NoteId {
match self {
Self::Full(note) => note.id(),
Self::Partial(note) => note.id(),
}
}
pub fn recipient(&self) -> Option<&NoteRecipient> {
match self {
Self::Full(note) => Some(note.recipient()),
Self::Partial(_) => None,
}
}
pub fn recipient_digest(&self) -> Word {
match self {
RawOutputNote::Full(note) => note.recipient().digest(),
RawOutputNote::Partial(note) => note.recipient_digest(),
}
}
pub fn metadata(&self) -> &NoteMetadata {
match self {
Self::Full(note) => note.metadata(),
Self::Partial(note) => note.metadata(),
}
}
pub fn into_output_note(self) -> Result<OutputNote, OutputNoteError> {
match self {
Self::Full(note) if note.metadata().is_private() => {
let note_id = note.id();
let (_, metadata, _) = note.into_parts();
let note_header = NoteHeader::new(note_id, metadata);
Ok(OutputNote::Private(PrivateNoteHeader::new(note_header)?))
},
Self::Full(note) => Ok(OutputNote::Public(PublicOutputNote::new(note)?)),
Self::Partial(note) => {
let (_, header) = note.into_parts();
Ok(OutputNote::Private(PrivateNoteHeader::new(header)?))
},
}
}
pub fn header(&self) -> &NoteHeader {
match self {
Self::Full(note) => note.header(),
Self::Partial(note) => note.header(),
}
}
pub fn commitment(&self) -> Word {
compute_note_commitment(self.id(), self.metadata())
}
}
impl From<&RawOutputNote> for NoteId {
fn from(note: &RawOutputNote) -> Self {
note.id()
}
}
impl<'note> From<&'note RawOutputNote> for &'note NoteHeader {
fn from(note: &'note RawOutputNote) -> Self {
note.header()
}
}
impl Serializable for RawOutputNote {
fn write_into<W: ByteWriter>(&self, target: &mut W) {
match self {
Self::Full(note) => {
target.write(Self::FULL);
target.write(note);
},
Self::Partial(note) => {
target.write(Self::PARTIAL);
target.write(note);
},
}
}
fn get_size_hint(&self) -> usize {
let tag_size = 0u8.get_size_hint();
match self {
Self::Full(note) => tag_size + note.get_size_hint(),
Self::Partial(note) => tag_size + note.get_size_hint(),
}
}
}
impl Deserializable for RawOutputNote {
fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
match source.read_u8()? {
Self::FULL => Ok(Self::Full(Note::read_from(source)?)),
Self::PARTIAL => Ok(Self::Partial(PartialNote::read_from(source)?)),
v => Err(DeserializationError::InvalidValue(format!("invalid output note type: {v}"))),
}
}
}
pub type OutputNotes = OutputNoteCollection<OutputNote>;
#[allow(clippy::large_enum_variant)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum OutputNote {
Public(PublicOutputNote),
Private(PrivateNoteHeader),
}
impl OutputNote {
const PUBLIC: u8 = 0;
const PRIVATE: u8 = 1;
pub fn id(&self) -> NoteId {
match self {
Self::Public(note) => note.id(),
Self::Private(header) => header.id(),
}
}
pub fn metadata(&self) -> &NoteMetadata {
match self {
Self::Public(note) => note.metadata(),
Self::Private(header) => header.metadata(),
}
}
pub fn assets(&self) -> Option<&NoteAssets> {
match self {
Self::Public(note) => Some(note.assets()),
Self::Private(_) => None,
}
}
pub fn to_commitment(&self) -> Word {
compute_note_commitment(self.id(), self.metadata())
}
pub fn recipient(&self) -> Option<&NoteRecipient> {
match self {
Self::Public(note) => Some(note.recipient()),
Self::Private(_) => None,
}
}
}
impl<'note> From<&'note OutputNote> for &'note NoteHeader {
fn from(value: &'note OutputNote) -> Self {
match value {
OutputNote::Public(note) => note.header(),
OutputNote::Private(header) => &header.0,
}
}
}
impl From<&OutputNote> for NoteId {
fn from(value: &OutputNote) -> Self {
value.id()
}
}
impl Serializable for OutputNote {
fn write_into<W: ByteWriter>(&self, target: &mut W) {
match self {
Self::Public(note) => {
target.write(Self::PUBLIC);
target.write(note);
},
Self::Private(header) => {
target.write(Self::PRIVATE);
target.write(header);
},
}
}
fn get_size_hint(&self) -> usize {
let tag_size = 0u8.get_size_hint();
match self {
Self::Public(note) => tag_size + note.get_size_hint(),
Self::Private(header) => tag_size + header.get_size_hint(),
}
}
}
impl Deserializable for OutputNote {
fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
match source.read_u8()? {
Self::PUBLIC => Ok(Self::Public(PublicOutputNote::read_from(source)?)),
Self::PRIVATE => Ok(Self::Private(PrivateNoteHeader::read_from(source)?)),
v => Err(DeserializationError::InvalidValue(format!(
"invalid proven output note type: {v}"
))),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PublicOutputNote(Note);
impl PublicOutputNote {
pub fn new(mut note: Note) -> Result<Self, OutputNoteError> {
if note.metadata().is_private() {
return Err(OutputNoteError::NoteIsPrivate(note.id()));
}
note.minify_script();
let note_size = note.get_size_hint();
if note_size > NOTE_MAX_SIZE as usize {
return Err(OutputNoteError::NoteSizeLimitExceeded { note_id: note.id(), note_size });
}
Ok(Self(note))
}
pub fn id(&self) -> NoteId {
self.0.id()
}
pub fn metadata(&self) -> &NoteMetadata {
self.0.metadata()
}
pub fn assets(&self) -> &NoteAssets {
self.0.assets()
}
pub fn recipient(&self) -> &NoteRecipient {
self.0.recipient()
}
pub fn header(&self) -> &NoteHeader {
self.0.header()
}
pub fn as_note(&self) -> &Note {
&self.0
}
pub fn into_note(self) -> Note {
self.0
}
}
impl Serializable for PublicOutputNote {
fn write_into<W: ByteWriter>(&self, target: &mut W) {
self.0.write_into(target);
}
fn get_size_hint(&self) -> usize {
self.0.get_size_hint()
}
}
impl Deserializable for PublicOutputNote {
fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
let note = Note::read_from(source)?;
Self::new(note).map_err(|err| DeserializationError::InvalidValue(err.to_string()))
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PrivateNoteHeader(NoteHeader);
impl PrivateNoteHeader {
pub fn new(header: NoteHeader) -> Result<Self, OutputNoteError> {
if !header.metadata().is_private() {
return Err(OutputNoteError::NoteIsPublic(header.id()));
}
Ok(Self(header))
}
pub fn id(&self) -> NoteId {
self.0.id()
}
pub fn metadata(&self) -> &NoteMetadata {
self.0.metadata()
}
pub fn into_metadata(self) -> NoteMetadata {
self.0.into_metadata()
}
pub fn commitment(&self) -> Word {
self.0.to_commitment()
}
pub fn as_header(&self) -> &NoteHeader {
&self.0
}
pub fn into_header(self) -> NoteHeader {
self.0
}
}
impl Serializable for PrivateNoteHeader {
fn write_into<W: ByteWriter>(&self, target: &mut W) {
self.0.write_into(target);
}
fn get_size_hint(&self) -> usize {
self.0.get_size_hint()
}
}
impl Deserializable for PrivateNoteHeader {
fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
let header = NoteHeader::read_from(source)?;
Self::new(header).map_err(|err| DeserializationError::InvalidValue(err.to_string()))
}
}