Skip to content
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

Updates with more info #1118

Merged
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
106 changes: 76 additions & 30 deletions docs/architecture/transaction.md
Original file line number Diff line number Diff line change
@@ -1,56 +1,102 @@
# Transaction

`Transaction`s are a mechanism by which the state of the chain is updated. That is, all changes in account and note states result from executing `Transaction`s. In Miden, `Transaction`s can originate either from the users or from the network itself.
A `Transaction` in Miden is the state transition of a single account. A `Transaction` takes as input a single [account](accounts.md) and zero or more [notes](notes.md), and outputs the same account with an updated state, together with zero or more notes. `Transaction`s in Miden are executed as Miden VM programs, resulting in the generation of a zero-knowledge proof.

Miden aims for the following characteristics in `Transaction`s:

- **Parallel transaction execution**: Because a transaction is always performed against a single account, Miden obtains asynchronicity.
- **Private transaction execution**: Local execution of transactions enables preservation of sensitive data.

## What is the purpose of a transaction?
![Transaction diagram](../img/architecture/transaction/transaction-diagram.png)

In Miden, a `Transaction` represents the state transition of a single account. A `Transaction` takes a single [account](accounts.md) and zero or more [notes](notes.md), and none or one script (piece of code executed after all notes have been executed) as input, and outputs the same account with a potentially updated state, together with some potential newly created notes.
Compared to most blockchains, where a `Transaction` typically involves more than one account (e.g., sender and receiver) a `Transaction` in Miden involves a single account. To illustrate, Alice sends 5 ETH to Bob. In Miden, sending 5 ETH from Alice to Bob takes two `Transaction`s, one in which Alice creates a note containing 5 ETH and one in which Bob consumes that note and receives the 5 ETH. This model removes the need for a global lock on the blockchain state enabling Miden to process `Transaction`s in parallel.

![Transaction diagram](../img/architecture/transaction/transaction-diagram.png)
## What is the purpose of Miden's transaction model?

## Transaction core components
Miden's `Transaction` model aims for the following:

WIP
- **Parallel transaction execution**: Accounts can update their state independently from each other and in parallel.
- **Private transaction execution**: Client-side `Transaction` proving allows the network to verify `Transaction`s with zero knowledge.

## Transaction types

There are two types of transactions in Miden: **local transactions** and **network transactions**.
There are two types of `Transaction`s in Miden: **local transactions** and **network transactions** [not yet implemented].

![Local vs network transactions](../img/architecture/transaction/local-vs-network-transaction.png)
### Local transaction

users transition their account's state locally using the Miden VM and generate a `Transaction` proof that can be verified by the network, which we call **client-side proving**. The network then only has to verify the proof and to change the global parts of the state to apply the state transition.

### Local transactions
They are useful, because:

This is where clients executing the transactions also generate the proofs of their correct execution. So, no additional work needs to be performed by the network.
1. They enable privacy as neither the account state nor account code are needed to verify the zk-proof. Public inputs are only commitments and block information that are stored on-chain.
2. They are cheaper (i.e., lower in fees) as the execution of the state transition and the generation of the zk-proof are already made by the users. Hence **privacy is the cheaper option on Miden**.
3. They allow arbitrary complex computation to be done. The proof size doesn't grow linearly with the complexity of the computation. Hence there is no gas limit for client-side proving.

Local transactions are useful for several reasons:
### Network transaction

1. They are cheaper (i.e., lower fees) as zk-proofs are already generated by the clients.
2. They allow fairly complex computations because the proof size doesn't grow linearly with the complexity of the computation.
3. They enable privacy as neither the account state nor account code are needed to verify the zk-proof.
In **network transactions**, the Miden operator executes the `Transaction` and generates the proof. Miden uses network `Transaction`s for smart contracts with public shared state. This type of `Transaction` is quite similar to the ones in traditional blockchains (e.g., Ethereum).

### Network transactions
They are useful, because:

This is where the operator executes the transaction and generates the proofs.
1. For public shared state of smart contracts. Network `Transaction`s allow orchestrated state changes of public smart contracts without race conditions.
2. Smart contracts should be able to be executed autonomously, ensuring liveness. Local `Transaction`s require a user to execute and prove, but in some cases a smart contract should be able to execute when certain conditions are met.
3. Clients may not have sufficient resources to generate zk-proofs.

Network transactions are useful for two reasons:
![Local vs network transactions](../img/architecture/transaction/local-vs-network-transaction.png)

1. Clients may not have sufficient resources to generate zk-proofs.
2. Executing many transactions against the same public account by different clients is challenging, as the account state changes after every transaction. Due to this, the Miden node/operator acts as a "synchronizer" to execute transactions sequentially by feeding the output of the previous transaction into the input of the next.
The ability to facilitate both, local and network `Transaction`s, is what differentiates Miden from other blockchains. Local `Transaction` execution and proving can happen in parallel as for most `Transaction`s there is no need for public state changes. This increases the network's throughput tremendously and provides privacy. Network `Transaction`s on the other hand enable autonomous smart contracts and public shared state.

## Transaction lifecycle

In Miden, every `Transaction` is executed within the Miden VM. Throughout its lifetime, a `Transaction` progresses through various phases:

1. **Compilation:** All `Transaction` inputs (account, notes, script) are compiled into an executable Miden program.
2. **Execution:** The `Transaction` program is executed within the Miden VM, which produces outputs (updated account, notes).
3. **Proving:** The executed `Transaction` is proven by the Miden prover.
Every `Transaction` describes the process of an account changing its state. This process described as a Miden-VM program resulting in a zero-knowledge proof of correct execution.

![Transaction execution process](../img/architecture/transaction/transaction-execution-process.png)

### Prerequisites

To execute a `Transaction`, the executor must have complete knowledge of the account state and the notes used as `Transaction` inputs, specifically:

- Notes: A `Transaction` can only consume notes if the full note data is known. For private notes, the data can not be fetched from the blockchain and must be received otherwise.
- Foreign account data: Any foreign account data accessed during a `Transaction`, whether private or public, must be available beforehand. There is no need to know the full account storage, but the data necessary for the `Transaction`, e.g., the key/value pair that is read and the corresponding storage state root.
- Blockchain state: The current `BlockHeader` and `ChainMMR` used to authenticate input notes must be retrieved from the Miden operator before execution.

> **Info**
> - One of the main reasons for separating out the execution and proving steps is to allow _stateless provers_; i.e., the executed transaction has all the data it needs to re-execute and prove a transaction without database access. This supports easier proof-generation distribution.
> - Usually, notes that are consumed in a `Transaction` must be recorded on-chain in order for the `Transaction` to succeed. However, in Miden there is the concept of ephemeral notes that can be consumed in a `Transaction` before registered on-chain. This allows for the executor to consume notes before they reach the blockchain which is useful for sub-second orders.
> - There is no nullifier-check during a `Transaction`. Nullifiers are checked by the Miden operator during `Transaction` verification. So at the `Transaction` level, there is "double spending." If a note was already spent, i.e. there exists a nullifier for that note, the whole `Transaction` will fail when submitted to the network.

### Transaction execution flow

![Transaction execution process](../img/architecture/transaction/transaction-program.png)

1. **Prologue**: On-chain commitments are validated against provided data.
2. **Note processing**: Input notes are executed sequentially against the account, following a selected order.
- Notes must be consumed fully (all assets must be transferred to the account or to another note).
- The note script must be executed in full with the provided note inputs and `TransactionArguments`. `TransactionArguments` can be injected by the executor for each note at runtime.
3. **Transaction script execution (optional)**: A `Transaction` script, if present, is executed.
- This script can sign the `Transaction` or directly interact with the account without using notes.
4. **Epilogue**:
- The account state is updated.
- New notes are created (optional), transferring assets from the account to the newly created notes.
- Execution completes, resulting in an updated account state and a generated zk-proof.

## Example of consuming a note in a transaction

To illustrate, consider a **basic wallet account** that exposes a `receive_asset` function. When a note is consumed, it's script executes against the account, calling `receive_asset`. This transfers the assets contained in the note to the account.

### Note consumption conditions

Note creators can impose conditions on who can consume a note. These restrictions are enforced by the note script, which must be fully executed by the consuming account. For instance:

- A **P2ID** note verifies that the executing account's ID matches the expected account ID from the note inputs.
- A **Swap** note allows asset exchange based on predefined conditions. Example:
- The note's consumption condition is defined as "anyone can consume this note to take X units of asset A if they simultaneously create a note sending Y units of asset B back to the creator."
- If an executor wants to buy only a fraction `(X-m)` of asset A, they provide this amount via `TransactionArguments`.
- The note script then enforces the correct transfer:
- A new note is created returning `Y-((m*Y)/X)` of asset B to the sender.
- A second note is created, holding the remaining `(X-m)` of asset A for future consumption.

### Account Interaction via Transaction Script

Not all `Transaction`s require notes. For example, the owner of a faucet can mint new tokens using only a `Transaction` script, without interacting with external notes.

If the `Transaction` succeeds, a proof is generated of the correct `Transaction` execution. This proof together with the corresponding data needed for verification and updates on the global state can then be submitted and processed by the network.

> **More info**
> - One of the main reasons for separating execution and proving steps is to allow _stateless provers_; i.e., the executed `Transaction` has all the data it needs to re-execute and prove a `Transaction` without database access. This supports easier proof-generation distribution.
> - It is possible to set `Transaction` expiration heights and in doing so, to define a block height until a `Transaction` should be included into a block. If the `Transaction` is expired, the resulting account state change is not valid and the `Transaction` can not be verified anymore.
> - Note and `Transaction` scripts can read the state of foreign accounts during execution. This is called foreign procedure invocation. For example the price of an asset for the SWAP script might depend on a certain value stored in the oracle account.
Binary file modified docs/img/architecture/transaction/transaction-diagram.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading