Skip to content

Commit

Permalink
note: implement note storage modes
Browse files Browse the repository at this point in the history
Notes can be stored offchain, on-chain, or encrypted. This is an
orthogonal concept to how the notes are executed. This commit adds a new
metadata field to encode the storage type.
  • Loading branch information
hackaugusto committed Mar 25, 2024
1 parent 2922568 commit df61515
Show file tree
Hide file tree
Showing 23 changed files with 354 additions and 113 deletions.
9 changes: 5 additions & 4 deletions miden-lib/asm/kernels/transaction/api.masm
Original file line number Diff line number Diff line change
Expand Up @@ -421,21 +421,22 @@ end

#! Creates a new note and returns a pointer to the memory address at which the note is stored.
#!
#! Inputs: [ASSET, tag, RECIPIENT]
#! Inputs: [ASSET, tag, note_type, RECIPIENT]
#! Outputs: [ptr, 0, 0, 0, 0, 0, 0, 0, 0]
#!
#! ASSET is the asset to be included in the note.
#! tag is the tag to be included in the note.
#! note_type is the note storage type
#! RECIPIENT is the recipient of the note.
#! ptr is the pointer to the memory address at which the note is stored.
export.create_note
# authenticate that the procedure invocation originates from the account context
exec.authenticate_account_origin
# => [ASSET, tag, RECIPIENT]
# => [ASSET, tag, note_type, RECIPIENT]

# create the note
# create the offchain note
exec.tx::create_note
# => [ptr, 0, 0, 0, 0, 0, 0, 0, 0]
# => [ptr, 0, 0, 0, 0, 0, 0, 0, 0, 0]
end

#! Returns a commitment to the account vault the transaction is being executed against.
Expand Down
7 changes: 7 additions & 0 deletions miden-lib/asm/miden/contracts/faucets/basic_fungible.masm
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ use.miden::faucet
use.miden::tx
use.miden::contracts::auth::basic

# CONSTANTS
# =================================================================================================
const.PUBLIC_NOTE=2

# ERRORS
# =================================================================================================

Expand Down Expand Up @@ -68,6 +72,9 @@ export.distribute
exec.faucet::mint
# => [ASSET, tag, RECIPIENT, ...]

push.PUBLIC_NOTE movdn.5
# => [ASSET, tag, note_type, RECIPIENT, ...]

# create a note containing the asset
exec.tx::create_note
# => [note_ptr, ZERO, ZERO, ...]
Expand Down
8 changes: 7 additions & 1 deletion miden-lib/asm/miden/contracts/wallets/basic.masm
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
use.miden::account
use.miden::tx

# CONSTANTS
# =================================================================================================
const.PUBLIC_NOTE=2

#! Adds the provided asset to the current account.
#!
#! Inputs: [ASSET]
Expand Down Expand Up @@ -43,7 +47,9 @@ export.send_asset.1
push.0 swap loc_store.0 padw push.0.0.0 swapdw loc_load.0
# => [ASSET, tag, RECIPIENT, ZERO, ZERO, ...]


push.PUBLIC_NOTE movdn.5
# => [ASSET, tag, note_type, RECIPIENT, ...]
#
exec.tx::create_note
# => [note_ptr, ZERO, ZERO, ...]
end
27 changes: 21 additions & 6 deletions miden-lib/asm/miden/kernels/tx/tx.masm
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,23 @@ use.miden::kernels::tx::constants
use.miden::kernels::tx::memory
use.miden::kernels::tx::note

# CONSTANTS
# =================================================================================================

# Constants for different note types
const.OFFCHAIN_NOTE=0
const.ENCRYPTED_NOTE=1
const.PUBLIC_NOTE=2

# ERRORS
# =================================================================================================

# Output notes exceeded the maximum limit
const.ERR_TX_OUTPUT_NOTES_OVERFLOW=0x00020020

# Invalid note type
const.ERR_INVALID_NOTE_TYPE=0x00020044

#! Returns the block hash of the last known block at the time of transaction execution.
#!
#! Inputs: []
Expand Down Expand Up @@ -64,7 +75,7 @@ end

#! Creates a new note and returns a pointer to the memory address at which the note is stored.
#!
#! Inputs: [ASSET, tag, RECIPIENT]
#! Inputs: [ASSET, tag, note_type, RECIPIENT]
#! Outputs: [ptr, 0, 0, 0, 0, 0, 0, 0, 0]
#!
#! ASSET is the asset to be included in the note.
Expand All @@ -74,19 +85,23 @@ end
export.create_note
# validate the asset
exec.asset::validate_asset
# => [ASSET, tag, RECIPIENT]
# => [ASSET, tag, note_type, RECIPIENT]

# validate the note type
dup.5 push.OFFCHAIN_NOTE eq dup.6 push.ENCRYPTED_NOTE eq or dup.6 push.PUBLIC_NOTE eq or assert.err=ERR_INVALID_NOTE_TYPE
# => [ASSET, tag, note_type, RECIPIENT]

# get the index for the next note to be created and increment counter
exec.increment_num_created_notes
# => [note_idx, ASSET, tag, RECIPIENT]
# => [note_idx, ASSET, tag, note_type, RECIPIENT]

# get a pointer to the memory address at which the note will be stored
exec.memory::get_created_note_ptr
# => [note_ptr, ASSET, tag, RECIPIENT]
# => [note_ptr, ASSET, tag, note_type, RECIPIENT]

# populate the metadata
movup.5 exec.account::get_id push.0.0
# => [0, 0, acct_id, tag, note_ptr, ASSET, RECIPIENT]
movup.5 exec.account::get_id movup.7 push.0
# => [0, note_type, acct_id, tag, note_ptr, ASSET, RECIPIENT]

# set the metadata for the new created note
dup.4 exec.memory::set_created_note_metadata
Expand Down
8 changes: 5 additions & 3 deletions miden-lib/asm/miden/tx.masm
Original file line number Diff line number Diff line change
Expand Up @@ -58,17 +58,19 @@ end

#! Creates a new note and returns a pointer to the memory address at which the note is stored.
#!
#! Inputs: [ASSET, tag, RECIPIENT]
#! Inputs: [ASSET, tag, note_type, RECIPIENT]
#! Outputs: [ptr]
#!
#! ASSET is the asset to be included in the note.
#! tag is the tag to be included in the note.
#! note_type is the storage type of the note
#! RECIPIENT is the recipient of the note.
#! ptr is the pointer to the memory address at which the note is stored.
export.create_note
syscall.create_note
# => [ptr, 0, 0, 0, 0, 0, 0, 0, 0]
# => [ptr, 0, 0, 0, 0, 0, 0, 0, 0, 0]

movdn.8 dropw dropw
# clear the padding from the kernel response
movdn.4 dropw movdn.4 dropw swap drop
# => [ptr]
end
23 changes: 19 additions & 4 deletions miden-lib/src/notes/mod.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
use alloc::vec::Vec;

use miden_objects::{
accounts::AccountId, assets::Asset, crypto::rand::FeltRng, notes::Note, Felt, NoteError, Word,
accounts::AccountId,
assets::Asset,
crypto::rand::FeltRng,
notes::{Note, NoteType},
Felt, NoteError, Word,
};

use self::utils::build_note_script;
Expand All @@ -25,6 +29,7 @@ pub fn create_p2id_note<R: FeltRng>(
sender: AccountId,
target: AccountId,
assets: Vec<Asset>,
note_type: NoteType,
mut rng: R,
) -> Result<Note, NoteError> {
let bytes = include_bytes!(concat!(env!("OUT_DIR"), "/assets/note_scripts/P2ID.masb"));
Expand All @@ -34,7 +39,7 @@ pub fn create_p2id_note<R: FeltRng>(
let tag: Felt = target.into();
let serial_num = rng.draw_word();

Note::new(note_script, &inputs, &assets, serial_num, sender, tag)
Note::new(note_script, &inputs, &assets, serial_num, sender, note_type, tag)
}

/// Generates a P2IDR note - pay to id with recall after a certain block height.
Expand All @@ -53,6 +58,7 @@ pub fn create_p2idr_note<R: FeltRng>(
sender: AccountId,
target: AccountId,
assets: Vec<Asset>,
note_type: NoteType,
recall_height: u32,
mut rng: R,
) -> Result<Note, NoteError> {
Expand All @@ -63,7 +69,7 @@ pub fn create_p2idr_note<R: FeltRng>(
let tag: Felt = target.into();
let serial_num = rng.draw_word();

Note::new(note_script.clone(), &inputs, &assets, serial_num, sender, tag)
Note::new(note_script.clone(), &inputs, &assets, serial_num, sender, note_type, tag)
}

/// Generates a SWAP note - swap of assets between two accounts.
Expand All @@ -78,6 +84,7 @@ pub fn create_swap_note<R: FeltRng>(
sender: AccountId,
offered_asset: Asset,
requested_asset: Asset,
note_type: NoteType,
mut rng: R,
) -> Result<(Note, Word), NoteError> {
let bytes = include_bytes!(concat!(env!("OUT_DIR"), "/assets/note_scripts/SWAP.masb"));
Expand All @@ -102,7 +109,15 @@ pub fn create_swap_note<R: FeltRng>(
let tag: Felt = Felt::new(0);
let serial_num = rng.draw_word();

let note = Note::new(note_script.clone(), &inputs, &[offered_asset], serial_num, sender, tag)?;
let note = Note::new(
note_script.clone(),
&inputs,
&[offered_asset],
serial_num,
sender,
note_type,
tag,
)?;

Ok((note, payback_serial_num))
}
58 changes: 50 additions & 8 deletions miden-lib/src/tests/test_tx.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
use alloc::vec::Vec;

use miden_objects::{
notes::Note,
notes::{Note, NoteType},
transaction::{OutputNote, OutputNotes},
FieldElement,
};
use mock::{
mock::{
Expand All @@ -21,7 +22,8 @@ use super::{
};
use crate::transaction::memory::{
CREATED_NOTE_ASSETS_OFFSET, CREATED_NOTE_METADATA_OFFSET, CREATED_NOTE_NUM_ASSETS_OFFSET,
CREATED_NOTE_RECIPIENT_OFFSET, CREATED_NOTE_SECTION_OFFSET, NUM_CREATED_NOTES_PTR,
CREATED_NOTE_RECIPIENT_OFFSET, CREATED_NOTE_SECTION_OFFSET, NOTE_MEM_SIZE,
NUM_CREATED_NOTES_PTR,
};

#[test]
Expand All @@ -43,15 +45,17 @@ fn test_create_note() {
exec.prologue::prepare_transaction
push.{recipient}
push.{PUBLIC_NOTE}
push.{tag}
push.{asset}
exec.tx::create_note
end
",
recipient = prepare_word(&recipient),
PUBLIC_NOTE = NoteType::Public as u8,
tag = tag,
asset = prepare_word(&asset)
asset = prepare_word(&asset),
);

let transaction = prepare_transaction(tx_inputs, None, &code, None);
Expand All @@ -72,7 +76,7 @@ fn test_create_note() {
// assert the metadata is stored at the correct memory location.
assert_eq!(
read_root_mem_value(&process, CREATED_NOTE_SECTION_OFFSET + CREATED_NOTE_METADATA_OFFSET),
[tag, Felt::from(account_id), ZERO, ZERO]
[tag, Felt::from(account_id), NoteType::Public.into(), ZERO]
);

// assert the number of assets is stored at the correct memory location.
Expand Down Expand Up @@ -109,6 +113,7 @@ fn test_create_note_too_many_notes() {
exec.memory::set_num_created_notes
push.{recipient}
push.{PUBLIC_NOTE}
push.{tag}
push.{asset}
Expand All @@ -117,7 +122,8 @@ fn test_create_note_too_many_notes() {
",
recipient = prepare_word(&recipient),
tag = tag,
asset = prepare_word(&asset)
asset = prepare_word(&asset),
PUBLIC_NOTE = NoteType::Public as u8,
);

let process =
Expand Down Expand Up @@ -147,6 +153,7 @@ fn test_get_output_notes_hash() {
&[input_asset_1],
output_serial_no_1,
tx_inputs.account().id(),
NoteType::Public,
output_tag_1,
)
.unwrap();
Expand All @@ -160,6 +167,7 @@ fn test_get_output_notes_hash() {
&[input_asset_2],
output_serial_no_2,
tx_inputs.account().id(),
NoteType::Public,
output_tag_2,
)
.unwrap();
Expand All @@ -178,27 +186,38 @@ fn test_get_output_notes_hash() {
use.miden::tx
begin
# => [BH, acct_id, IAH, NC]
exec.prologue::prepare_transaction
# => []
# create output note 1
push.{recipient_1}
push.{PUBLIC_NOTE}
push.{tag_1}
push.{asset_1}
exec.tx::create_note
# => [note_ptr]
drop
# => []
# create output note 2
push.{recipient_2}
push.{PUBLIC_NOTE}
push.{tag_2}
push.{asset_2}
exec.tx::create_note
# => [note_ptr]
drop
# => []
# compute the output notes hash
exec.tx::get_output_notes_hash
push.{expected} assert_eqw
# => [COMM]
end
",
PUBLIC_NOTE = NoteType::Public as u8,
recipient_1 = prepare_word(&output_note_1.recipient()),
tag_1 = output_note_1.metadata().tag(),
asset_1 = prepare_word(&Word::from(
Expand All @@ -209,11 +228,34 @@ fn test_get_output_notes_hash() {
asset_2 = prepare_word(&Word::from(
**output_note_2.assets().iter().take(1).collect::<Vec<_>>().first().unwrap()
)),
expected = prepare_word(&expected_output_notes_hash)
);

let transaction = prepare_transaction(tx_inputs, None, &code, None);
let _process = run_tx(&transaction).unwrap();
let process = run_tx(&transaction).unwrap();

assert_eq!(
process.get_mem_value(ContextId::root(), NUM_CREATED_NOTES_PTR),
Some([Felt::new(2), Felt::ZERO, Felt::ZERO, Felt::ZERO]),
"The test creates two notes",
);
assert_eq!(
process.get_mem_value(
ContextId::root(),
CREATED_NOTE_SECTION_OFFSET + CREATED_NOTE_METADATA_OFFSET
),
Some(output_note_1.metadata().into()),
"Validate the output note 1 metadata",
);
assert_eq!(
process.get_mem_value(
ContextId::root(),
CREATED_NOTE_SECTION_OFFSET + CREATED_NOTE_METADATA_OFFSET + NOTE_MEM_SIZE
),
Some(output_note_2.metadata().into()),
"Validate the output note 1 metadata",
);

assert_eq!(process.get_stack_word(0), *expected_output_notes_hash);
}

// HELPER FUNCTIONS
Expand Down
Loading

0 comments on commit df61515

Please sign in to comment.