-
Notifications
You must be signed in to change notification settings - Fork 145
feat: migrate transaction scripts to Library #3173
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: next
Are you sure you want to change the base?
Changes from 2 commits
af44cea
c8e13d9
698f06f
61aad5c
70aba3c
a5751fb
17e21bc
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -11,6 +11,7 @@ use miden_mast_package::Package; | |
|
|
||
| use super::{Felt, Hasher, Word}; | ||
| use crate::account::auth::{PublicKeyCommitment, Signature}; | ||
| use crate::assembly::Library; | ||
| use crate::errors::TransactionScriptError; | ||
| use crate::note::{NoteId, NoteRecipient}; | ||
| use crate::utils::serde::{ | ||
|
|
@@ -318,6 +319,9 @@ impl Deserializable for TransactionScriptRoot { | |
| // TRANSACTION SCRIPT | ||
| // ================================================================================================ | ||
|
|
||
| /// The attribute name used to mark the entrypoint procedure in a transaction script library. | ||
| const TRANSACTION_SCRIPT_ATTRIBUTE: &str = "transaction_script"; | ||
|
|
||
| /// Transaction script. | ||
| /// | ||
| /// A transaction script is a program that is executed in a transaction after all input notes | ||
|
|
@@ -336,6 +340,8 @@ impl TransactionScript { | |
| // -------------------------------------------------------------------------------------------- | ||
|
|
||
| /// Returns a new [TransactionScript] instantiated with the provided code. | ||
| // TODO: we can remove this `Program` based constructor once the compiler integrates the | ||
| // `@transaction_script` attribute (https://github.com/0xMiden/compiler/issues/1190). | ||
| pub fn new(code: Program) -> Self { | ||
| Self::from_parts(code.mast_forest().clone(), code.entrypoint()) | ||
| } | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's do this in a follow up right after this PR. And we should do the same for note scripts. cc @greenhat for visibility. |
||
|
|
@@ -364,6 +370,37 @@ impl TransactionScript { | |
| Ok(TransactionScript::new(program)) | ||
| } | ||
|
|
||
| /// Returns a new [TransactionScript] instantiated from the provided library. | ||
| /// | ||
| /// The library must contain exactly one procedure with the `@transaction_script` attribute, | ||
| /// which will be used as the entrypoint. | ||
| /// | ||
| /// # Errors | ||
| /// Returns an error if: | ||
| /// - The library does not contain a procedure with the `@transaction_script` attribute. | ||
| /// - The library contains multiple procedures with the `@transaction_script` attribute. | ||
| pub fn from_library(library: &Library) -> Result<Self, TransactionScriptError> { | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. To align a bit better with (see also my other comment about |
||
| let mut entrypoint = None; | ||
|
|
||
| for export in library.exports() { | ||
| if let Some(proc_export) = export.as_procedure() | ||
| && proc_export.attributes.has(TRANSACTION_SCRIPT_ATTRIBUTE) | ||
| { | ||
| if entrypoint.is_some() { | ||
| return Err(TransactionScriptError::MultipleProceduresWithAttribute); | ||
|
TomasArrachea marked this conversation as resolved.
|
||
| } | ||
| entrypoint = Some(proc_export.node); | ||
| } | ||
| } | ||
|
|
||
| let entrypoint = entrypoint.ok_or(TransactionScriptError::NoProcedureWithAttribute)?; | ||
|
|
||
| Ok(Self { | ||
| mast: library.mast_forest().clone(), | ||
| entrypoint, | ||
| }) | ||
| } | ||
|
|
||
| // PUBLIC ACCESSORS | ||
| // -------------------------------------------------------------------------------------------- | ||
|
|
||
|
|
@@ -461,4 +498,48 @@ mod tests { | |
| let stored = mast.advice_map().get(&key).expect("entry should be present"); | ||
| assert_eq!(stored.as_ref(), value.as_slice()); | ||
| } | ||
|
|
||
| #[test] | ||
| fn test_transaction_script_from_library() { | ||
| use assert_matches::assert_matches; | ||
|
|
||
| use super::TransactionScript; | ||
| use crate::assembly::Assembler; | ||
| use crate::errors::TransactionScriptError; | ||
| use crate::utils::serde::{Deserializable, Serializable}; | ||
|
|
||
| let source = " | ||
| @transaction_script | ||
| pub proc main | ||
| push.1 drop | ||
| end | ||
| "; | ||
| let library = Assembler::default().assemble_library([source]).unwrap(); | ||
|
|
||
| let script = TransactionScript::from_library(&library).unwrap(); | ||
|
|
||
| // the script must round-trip through serialization unchanged | ||
| let bytes = script.to_bytes(); | ||
| let decoded = TransactionScript::read_from_bytes(&bytes).unwrap(); | ||
| assert_eq!(script, decoded); | ||
|
|
||
| // a library without the attribute is rejected | ||
| let no_attr = Assembler::default() | ||
| .assemble_library(["pub proc main push.1 drop end"]) | ||
| .unwrap(); | ||
| assert_matches!( | ||
| TransactionScript::from_library(&no_attr), | ||
| Err(TransactionScriptError::NoProcedureWithAttribute) | ||
| ); | ||
|
|
||
| // a library with multiple tagged procedures is rejected | ||
| let multiple = Assembler::default() | ||
| .assemble_library(["@transaction_script pub proc main_a push.1 drop end | ||
| @transaction_script pub proc main_b push.2 drop end"]) | ||
| .unwrap(); | ||
| assert_matches!( | ||
| TransactionScript::from_library(&multiple), | ||
| Err(TransactionScriptError::MultipleProceduresWithAttribute) | ||
| ); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -22,7 +22,8 @@ use miden::protocol::tx | |
| #! - delta is 0 or not a u32 in the range 1..=0xFFFF (ERR_TX_INVALID_EXPIRATION_DELTA). | ||
| #! | ||
| #! Invocation: call | ||
| begin | ||
| @transaction_script | ||
| pub proc main | ||
|
Comment on lines
+25
to
+26
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not for this PR, but we should move this out into a package (similar to how we do with notes).
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, this is part of #3111 |
||
| exec.tx::update_expiration_block_delta | ||
| # => [pad(16)] | ||
| end | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
At this point
NoteScriptandTransactionScriptshare the majority of the struct's implementation code, with the only difference beingTRANSACTION_SCRIPT_ATTRIBUTEvs.NOTE_SCRIPT_ATTRIBUTE. We might want to pull this functionality into something more re-usable.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this would make sense. Created #3189 for it, so we avoid growing this PR.