use alloc::sync::Arc;
use assert_matches::assert_matches;
use super::{PublicOutputNote, RawOutputNote, RawOutputNotes};
use crate::account::AccountId;
use crate::assembly::mast::{ExternalNodeBuilder, MastForest, MastForestContributor};
use crate::asset::FungibleAsset;
use crate::constants::NOTE_MAX_SIZE;
use crate::errors::{OutputNoteError, TransactionOutputError};
use crate::note::{
Note,
NoteAssets,
NoteMetadata,
NoteRecipient,
NoteScript,
NoteStorage,
NoteTag,
NoteType,
};
use crate::testing::account_id::{
ACCOUNT_ID_PRIVATE_FUNGIBLE_FAUCET,
ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET,
ACCOUNT_ID_SENDER,
};
use crate::utils::serde::Serializable;
use crate::{Felt, Word};
#[test]
fn test_duplicate_output_notes() -> anyhow::Result<()> {
let mock_note = Note::mock_noop(Word::empty());
let mock_note_id = mock_note.id();
let mock_note_clone = mock_note.clone();
let error = RawOutputNotes::new(vec![
RawOutputNote::Full(mock_note),
RawOutputNote::Full(mock_note_clone),
])
.expect_err("input notes creation should fail");
assert_matches!(error, TransactionOutputError::DuplicateOutputNote(note_id) if note_id == mock_note_id);
Ok(())
}
#[test]
fn output_note_size_hint_matches_serialized_length() -> anyhow::Result<()> {
let sender_id = ACCOUNT_ID_SENDER.try_into().unwrap();
let faucet_id_1 = AccountId::try_from(ACCOUNT_ID_PRIVATE_FUNGIBLE_FAUCET).unwrap();
let faucet_id_2 = AccountId::try_from(ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET).unwrap();
let asset_1 = FungibleAsset::new(faucet_id_1, 100)?.into();
let asset_2 = FungibleAsset::new(faucet_id_2, 200)?.into();
let assets = NoteAssets::new(vec![asset_1, asset_2])?;
let metadata = NoteMetadata::new(sender_id, NoteType::Private)
.with_tag(NoteTag::with_account_target(sender_id));
let storage = NoteStorage::new(vec![Felt::new(1), Felt::new(2)])?;
let serial_num = Word::empty();
let script = NoteScript::mock();
let recipient = NoteRecipient::new(serial_num, script, storage);
let note = Note::new(assets, metadata, recipient);
let output_note = RawOutputNote::Full(note);
let bytes = output_note.to_bytes();
assert_eq!(bytes.len(), output_note.get_size_hint());
Ok(())
}
#[test]
fn oversized_public_note_triggers_size_limit_error() -> anyhow::Result<()> {
let sender_id = ACCOUNT_ID_SENDER.try_into().unwrap();
let mut mast = MastForest::new();
let mut root_id = None;
for i in 0..7_000_u16 {
let digest = Word::new([Felt::from(i + 1), Felt::ZERO, Felt::ZERO, Felt::ZERO]);
let id = ExternalNodeBuilder::new(digest)
.add_to_forest(&mut mast)
.expect("adding external node should not fail");
root_id = Some(id);
}
let root_id = root_id.unwrap();
mast.make_root(root_id);
let script = NoteScript::from_parts(Arc::new(mast), root_id);
let serial_num = Word::empty();
let storage = NoteStorage::new(alloc::vec::Vec::new())?;
let faucet_id = AccountId::try_from(ACCOUNT_ID_PRIVATE_FUNGIBLE_FAUCET).unwrap();
let asset = FungibleAsset::new(faucet_id, 100)?.into();
let assets = NoteAssets::new(vec![asset])?;
let metadata = NoteMetadata::new(sender_id, NoteType::Public)
.with_tag(NoteTag::with_account_target(sender_id));
let recipient = NoteRecipient::new(serial_num, script, storage);
let oversized_note = Note::new(assets, metadata, recipient);
let computed_note_size = oversized_note.get_size_hint();
assert!(
computed_note_size > NOTE_MAX_SIZE as usize,
"Expected note size ({computed_note_size}) to exceed NOTE_MAX_SIZE ({NOTE_MAX_SIZE})"
);
let result = PublicOutputNote::new(oversized_note.clone());
assert_matches!(
result,
Err(OutputNoteError::NoteSizeLimitExceeded { note_id: _, note_size })
if note_size > NOTE_MAX_SIZE as usize
);
let output_note = RawOutputNote::Full(oversized_note);
let result = output_note.into_output_note();
assert_matches!(
result,
Err(OutputNoteError::NoteSizeLimitExceeded { note_id: _, note_size })
if note_size > NOTE_MAX_SIZE as usize
);
Ok(())
}