S&P Global Completes Acquisition of OpenZeppelin Read the announcement

Miden Transaction Kernel Security Review

  • OpenZeppelin Security

Findings
Total issues: 8 (0 resolved)
Critical: 0 (0 resolved) · High: 1 (0 resolved) · Medium: 2 (0 resolved) · Low: 2 (0 resolved)

Notes & Additional Information
3 notes raised (0 resolved)

Scope

OpenZeppelin performed a review of the 0xMiden/miden-base repository at commit 5c4edb4.

In scope were the following files:

crates/miden-lib/asm/kernels/transaction
├── api.masm
├── lib
│   ├── account_delta.masm
│   ├── account.masm
│   ├── asset_vault.masm
│   ├── asset.masm
│   ├── constants.masm
│   ├── epilogue.masm
│   ├── faucet.masm
│   ├── input_note.masm
│   ├── link_map.masm
│   ├── memory.masm
│   ├── note.masm
│   ├── output_note.masm
│   ├── prologue.masm
│   └── tx.masm
├── main.masm
├── tx_script_main.masm

Note that the review did not cover every single line of code within the listed scope. Instead, it focused on examining the overall execution flow of the transaction kernel program.

System Overview

The transaction kernel is one of the kernels that are used to instantiate the Miden VM, with different kernels providing different functionality. In particular, the transaction kernel implements the logic required to execute a transaction on the Miden blockchain and exposes a set of syscalls that user programs can invoke to interact with the kernel.

In Miden, a transaction represents the state transition of a single account (the “native” account). It takes as input one account and zero or more notes, and produces the same account with an updated state, along with zero or more output notes. Since transactions in Miden are executed as Miden VM programs, their execution results in the generation of a zero-knowledge proof.

Transaction execution in Miden is conceptually divided into four phases: the Prologue, Note Processing, Transaction Script Processing, and the Epilogue.

Prologue

The Prologue establishes the execution context for the transaction. During this phase, all public and private inputs required for execution are loaded, authenticated, and made available in memory. This includes commitments to the reference block, the native account, the consumed notes, as well as the kernel definition itself. By fully executing in the root context, the Prologue ensures that all subsequent phases operate over a single, persistent view of memory.

In addition to input validation, the Prologue initializes core execution state. The native account’s state and procedures are loaded, and its initial state is snapshotted for later comparison. Input notes are also loaded and registered for execution, and their associated assets are accumulated into a transaction-level view that is used to enforce asset conservation. Optional transaction scripts and authentication parameters are recorded at this stage, completing the setup required for user-driven execution.

Note Processing

In the Note Processing phase, the kernel executes each input note in sequence. Each note runs in its own isolated user context, allowing note logic to be evaluated independently while preventing unintended persistence of local execution state. This design enables notes to express arbitrary behavior - such as transferring assets, interacting with account storage, or creating new notes, while relying on the kernel to manage shared state explicitly.

Any effects that must persist across note executions are written to the root context, ensuring that the cumulative outcome of all notes is visible to later phases. By the end of this phase, all note-driven effects have been applied to the transaction state, and the kernel has a complete view of how note execution has modified the account and asset landscape.

Transaction Script Processing

After all the notes have been processed, the kernel optionally executes a transaction-level script. This script represents logic that applies to the transaction as a whole instead of individual notes. It is executed in a fresh user context and may depend on the effects produced by prior note execution.

Transaction scripts are typically used to perform final account-level actions, such as signing the transaction and incrementing the account’s nonce, minting tokens from a faucet, creating notes, or modifying account storage. If no transaction script is provided, this phase is skipped, and the execution directly proceeds to the Epilogue phase.

Epilogue

The Epilogue finalizes the transaction and produces its externally visible results. In this phase, the kernel enforces account-specific authorization rules by invoking the native account’s authentication procedure and ensures that required state transitions, such as nonce updates, have occurred.

The kernel then consolidates all asset movements performed during execution and enforces transaction-wide invariants, most notably the preservation of assets across inputs and outputs. Commitments are computed for all outputs, including newly created notes and the account state delta, capturing the net effect of the transaction. Transaction fees are calculated based on execution cost and applied to the account.

Finally, the kernel emits a compact set of outputs that fully characterize the transaction’s effects. These outputs allow external verifiers to authenticate the execution and apply the resulting state updates to the blockchain.

Security Model and Trust Assumptions

For the correct functioning of the transaction kernel, it is essential that the data written to the root context’s memory is valid and consistent. This is enforced in two ways:

  • Authenticating the nondeterministic inputs loaded from the Advice Provider against the transaction’s public inputs.
  • Executing user programs in separate execution contexts and allowing interaction with the root context only through controlled syscalls (defined as procedures in the api.masm file).

In addition, the security of the transaction kernel depends on several other components and systems that are assumed to be correctly implemented. These include:

  • the compilation of MASM into Miden VM instructions
  • the underlying Miden VM, which must be properly constrained
  • the block kernel, which must correctly implement deferred validations, such as delayed note authentication

High Severity

Epilogue May Execute Under a Foreign Account Context

The finalize_transaction function in the Epilogue ensures that the context is switched back to the native account after processing notes and the transaction script. However, the subsequent call to the auth procedure may switch the active context to a foreign account, which would lead to incorrect behavior during the remaining Epilogue execution. In particular:

  • The asset preservation check would no longer reflect changes to the native account balance and would instead consider the foreign account’s assets.
  • Transaction fees would be deducted from the foreign (active) account instead of the native account.
  • The final account commitment would be computed for the foreign account instead of the native account. If an incorrect commitment is stored on-chain, it could prevent future transactions from the affected account, as the correct data would no longer produce the stored commitment.

Consider ensuring that the active account is reset to the native account after executing the authentication procedure.

Medium Severity

Authentication Procedure Can Be Reentered

The authentication procedure of an account must only be called once by the transaction kernel. This invariant is enforced in the Epilogue within execute_auth_procedure. However, execution of the auth procedure may result in a reentrant call to itself, breaking the invariant enforced by the kernel. This could lead to unexpected behavior, depending on the specific implementation of the authentication procedure.

Consider introducing additional safeguards to ensure that user programs cannot invoke the authentication procedure reentrantly.

Note and Transaction Scripts May Be Executed Under a Foreign Account Context

Note and transaction scripts can use the tx_start_foreign_context and tx_end_foreign_context syscalls to start and end a foreign account context. When the first note is executed, the active account is set to the native account. However, after a note is executed, it is not enforced that the active account is still the native account before processing the next note or the transaction script.

As a result, scripts assuming that the active account is the native account by default may exhibit unexpected behavior. While many state updates, such as account updates and output note creation, are restricted to the native account context, other updates (e.g., modifying the transaction's block expiration) can be performed from a foreign context. Consequently, a note script that updates the block expiration based on the native account’s state may be vulnerable to manipulation.

Consider enforcing that the active account is the native account before processing note and transaction scripts.

Low Severity

Underspecified MMR Behavior

The authenticate_note procedure in prologue.masm authenticates the MMR leaf associated with the block in which an input note was created. However, the behavior is not specified for the case where the note was created in the future relative to the reference block. In particular, the documentation of the mmr::get procedure does not clarify whether it fails or returns a special value when the requested leaf is not present in the MMR.

Consider documenting the expected behavior for this scenario to improve the readability and maintainability of the codebase.

Misleading Documentation

Throughout the codebase, multiple instances of misleading documentation were identified:

Consider updating the documentation accordingly to improve the clarity and maintainability of the codebase.

Notes & Additional Information

Typographical Errors

Throughout the codebase, multiple typographical errors were identified:

  • In line 617 of prologue.masm, the phrase “in the” is repeated.
  • In line 694 of prologue.masm, the extra “the” should be removed.
  • In line 137 of epilogue.masm, “greater then” should be corrected to “greater than”.

Consider fixing the above-listed typographical errors to improve the overall readability of the codebase.

Code Clarity

Throughout the codebase, multiple opportunities for improving code clarity were identified:

  • The set_nullifier_commitment procedure in memory.masm should be renamed to set_input_notes_commitment to better reflect its behavior and align with the corresponding getter name.
  • Setter functions interact inconsistently with the Operand Stack. Some setters drop the values they store, while others leave them on the stack, making it harder to reason about the stack state after a setter call.
  • The ARRAY storage slot type and getter defined in constants.masm are currently unused by the transaction kernel and could be removed to avoid confusion.

Consider addressing the above points to improve the overall clarity and maintainability of the codebase.

Redundant Duplication and Drop Sequence

In the process_block_data procedure of prologue.masm file, in line #217, the dup and drop instructions are unnecessary. This is because the memory::set_note_root procedure neither consumes a value from the stack nor does it push a value to the stack.

Consider removing the redundant instructions to increase code readability and maintainability.

Conclusion

The Miden transaction kernel is a critical component for enforcing correct state transitions within the zero-knowledge environment of the Miden blockchain. All transactions in Miden are executed inside the Miden VM, with the transaction kernel responsible for validating and processing each transaction.

The review focused on examining the overall execution flow of the transaction kernel program, as well as ensuring that nondeterministic inputs were properly authenticated. During the review, three primary issues were identified, mainly related to insufficient validation of the active account context after performing dynamic calls to user programs. Additionally, several opportunities to improve code and documentation clarity and consistency were noted. Overall, the codebase was found to be well structured and documented, which increased its auditability.

The Miden team is commended for their readiness to provide detailed technical explanations, clarify design decisions, and share comprehensive documentation. These efforts greatly facilitated the assessment and demonstrated a strong commitment to delivering a secure and reliable blockchain protocol.

Appendix

Issue Classification

OpenZeppelin classifies smart contract vulnerabilities on a 5-level scale:

  • Critical
  • High
  • Medium
  • Low
  • Note/Information

Critical Severity

This classification is applied when the issue’s impact is catastrophic, threatening extensive damage to the client's reputation and/or causing severe financial loss to the client or users. The likelihood of exploitation can be high, warranting a swift response. Critical issues typically involve significant risks such as the permanent loss or locking of a large volume of users' sensitive assets or the failure of core system functionalities without viable mitigations. These issues demand immediate attention due to their potential to compromise system integrity or user trust significantly.

High Severity

These issues are characterized by the potential to substantially impact the client’s reputation and/or result in considerable financial losses. The likelihood of exploitation is significant, warranting a swift response. Such issues might include temporary loss or locking of a significant number of users' sensitive assets or disruptions to critical system functionalities, albeit with potential, yet limited, mitigations available. The emphasis is on the significant but not always catastrophic effects on system operation or asset security, necessitating prompt and effective remediation.

Medium Severity

Issues classified as being of medium severity can lead to a noticeable negative impact on the client's reputation and/or moderate financial losses. Such issues, if left unattended, have a moderate likelihood of being exploited or may cause unwanted side effects in the system. These issues are typically confined to a smaller subset of users' sensitive assets or might involve deviations from the specified system design that, while not directly financial in nature, compromise system integrity or user experience. The focus here is on issues that pose a real but contained risk, warranting timely attention to prevent escalation.

Low Severity

Low-severity issues are those that have a low impact on the client's operations and/or reputation. These issues may represent minor risks or inefficiencies to the client's specific business model. They are identified as areas for improvement that, while not urgent, could enhance the security and quality of the codebase if addressed.

Notes & Additional Information Severity

This category is reserved for issues that, despite having a minimal impact, are still important to resolve. Addressing these issues contributes to the overall security posture and code quality improvement but does not require immediate action. It reflects a commitment to maintaining high standards and continuous improvement, even in areas that do not pose immediate risks.