Replies: 3 comments 8 replies
-
Just posting a few random thoughts below:
|
Beta Was this translation helpful? Give feedback.
-
A bit more explanation of how transaction execution works. As I mentioned in the original article, transaction execution consists of several steps. These steps (grouped slightly differently) are described below:
Using the above convention, we can put together a few call diagrams for various transactions. For example, a transaction which sends money out of a wallet may look like so. This transaction does not consume any notes but has tx script which calls the A call diagram for a transaction which receives funds into a wallet may look like so: As opposed to the previous transaction, this transaction consumes a single note ( A transaction could also consume several notes at the same time. A call diagram for such a transaction may look like so: Or a transaction can consume notes and produce notes a the same time. The transaction below does one of each. Note that this transaction is not atomic: consumed and produced notes are not related to each other. Thus, this transaction could have be easily broken up into two independent transactions. To make a transaction atomic, we'd have to have a single note which invokes both |
Beta Was this translation helpful? Give feedback.
-
In ZeroPool we use an account-based model close to this one. So, the transaction has one account input, one account output, and arbitrary note input and output. The advantage in comparison with fully UTXO-based models (like sapling) that's we publish only one nullifier per account. The disadvantage is that we can spend notes only in chronological order. We reach it by storing the interval of spent notes inside the account and incrementing this interval in any transaction. For private transactions, the anonymity set is corresponding to one Merkle tree. @bobbinth are you planning to build fully private transactions (with hidden both amounts and transaction graph) or just hidden amounts? In the 1st case, there are some issues with scalability, because all notes and accounts should be put inside one Merkle tree. We are using Poseidon hash, it is not so fast. Also, the logic of nullifiers (one nullifier per account like in ZeroPool or one nullifier per note like ZCash or something else) seems to be not generalizable by arbitrary scripts. Is the main purpose of using the account-notes-based model in Miden privacy or scalability? |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
I'd like to start discussing how the rollup itself could look like because for the design decisions beyond Miden VM v0.2 we'd need to have a pretty good understanding of what we are building towards.
To this end, I'll try to describe some of the thoughts I've accumulated over time. These are not meant as comprehensive descriptions. Some details may be omitted for brevity, while other details may be omitted because I have not thought them through yet. In all cases, these are just preliminary thoughts, subject to drastic changes.
First, here are high-level design goals I have for Polygon Miden:
Not all of these goals will be address in this note, and some may be addressed only superficially. A more detailed and expanded look into these is left for future discussions.
The architecture
The architecture described below is loosely modeled after the actor model. In this model there are actors who communicate with each other via message passing. In our context an actor is an account, and messages are notes. Both accounts and notes can hold value (or assets). All of these are described below.
Assets
An asset is a way to represent a unit of value. I am not going to go too deeply into describing how assets work, but at the high level there are two types of assets: fungible and non-fungible. All assets have a unique identifier (e.g., something like a 24-byte value). Fungible assets also have an amount, while non-fungible assets have a serial number.
An example of a fungible asset could be ETH. In this case, an asset could be
1 ETH
, where 1 is the amount and ETH stands in for an asset ID. An example of a non-fungible asset could be a collectable where the ID defines a collection and a serial number specifies an item in that collection (there could also be other ways to define an NFT).Accounts
An account is an entity which holds assets and defines rules of how these assets can be transferred. The diagram below illustrates basic components of an account.
In the above:
Functions exposed by the account have the following properties:
Notes
A note is a way of transferring assets between accounts. A note consists of a vault and a script as shown in the diagram below.
A note's vault is basically the same as an account's vault. However, unlike an account, a note has a single executable script. This script is also a root of a Miden program MAST. A script is always executed in the context of a single account, and thus, may invoke account's functions. For example, a simple script could look like this:
The above will push value
123
onto the stack and then call a function with MAST root0x123456
(assuming such function exists in the account). A few other things to note about note scripts:0x123456
. However, it is possible to tie scripts to accounts explicitly. For example, if we wanted to make sure a script can be executed against an account with ID0x9876
, we could do something like this:Executing this script will have give the same result as executing the earlier simpler script, but this script cannot be executed against any other account.
Transaction model
Asset transfers between accounts are done by executing transactions. A transaction is always executed against a single account (it may be possible to relax this condition, but this would require more thinking). It can consume zero or more notes, and it may produce zero or more notes as shown in the diagram below.
In addition to specifying inputs and outputs, a transaction must also include a single executable script. This script has a well-defined structure which must do the following:
The last point ensures that a transaction does not create or destroy any assets.
Under this model transferring assets between accounts requires two transactions as shown in the diagram below.
The first transaction invokes a function on
account_a
(e.g., "send" function) which creates a new note and also updates the internal state ofaccount_a
. The second transaction consumes the note which invokes a function onaccount_b
(e.g., "receive" function), which also updates the internal state ofaccount_b
.It is important to note that both transactions can be executed asynchronously: first
transaction1
is executed, and then, some time later,transaction2
can be executed. This opens up a few interesting possibilities:account_b
two may wait until there are many notes sent to them and process all incoming notes in a single transaction.account_b
does not consume the note after the specified time, the funds can be returned. This mechanism could be used to make sure funds sent to non-existent accounts are not lost.note1
(and for this they need to know the assets to be transferred and the root of the note's script) - they don't need any information on who will eventually consume the note. From the recipients perspective, they just need to consumenote1
. They don't need to know who created it.transaction1
was executed and submit it to the network. The network can verify the proof without the need for executing the transaction itself. Same can be done fortransaction2
. Moreover, we can mix and match - e.g.,transaction1
can be executed locally, buttransaction2
can be executed by the network, or vice-versa.Wallet example
Let's consider a simple wallet example. An account implementing such a wallet would define two external functions:
send
andreceive
.The
send
function would be responsible for sending funds out of the account. It could take the following inputs:The function would verify that the certificate is valid, that the account has the specified asset, and then would remove the asset from the account's vault and create a note as
[asset, recipient]
. The note's script could be something similar to what was sketched out earlier (e.g., it could tie the note to a specific account ID).The
receive
function would be responsible for accepting assets into an account. It would would be fairly simple: take an asset as an input and add it to the account's vault.One thing to note is that having these
send
andreceive
functions should be sufficient to support atomic swaps (e.g., a note which invokes bothsend
andreceive
functions as a part of its script). This could enable a lot of interesting use cases. For example, this can be used to simulate a network-wide decentralized exchange without needing any 3rd party smart contracts. However, this is outside of the scope of this note (and requires a bit more additional thinking).Evaluation
Assuming the above architecture works, what does it give us?
And just to re-iterate what was said in the intro, the above description glosses over a lot of details. For example:
These and many other topics are left for future discussions.
Beta Was this translation helpful? Give feedback.
All reactions