0xPolygonMiden / examples

Examples of using Polygon Miden
MIT License
17 stars 18 forks source link

Product requirements for Miden rollup developer playground #200

Open Dominik1999 opened 1 month ago

Dominik1999 commented 1 month ago

Goal

We want a second playground for Miden rollup developers to quickly test note scripts and account code. The current playground is about the Miden Virtual Machine.

Start simple and iterate

Initially, we only want to execute a note script against an account and see its result. Gradually, we will iterate on the developer playground and add more features.

The layout

The layout is super simple. We need four boxes and one button.

image

bobbinth commented 1 month ago

This box will show the account info. Here the user can check how the account changed after the note script was executed against it. Here we will show this information, but more beautiful. One idea to save space is not to show the full hashes / commitments but make them collapsable.

I was thinking something similar: we'd have a component like an "account explorer" which can be used to expand various parts of an account (e.g., view vault, storage, code). We could start out pretty simple - e.g., list all storage slots, for maps, allow expanding and just list key-value pairs - and we can make it more sophisticated later on.

Upper left box: The box is a code editor. Users will write their note scripts in it. The code is in Miden assembly. It will look similar to what we have in the VM playground. We need two dropdowns, one for the font size and one to select examples.

For notes, we also need to provide note inputs, arguments, and metadata. In my mind, we have a "note composer" component which includes these things.

It would also be cool to be able to upload our note files to populate this data - but this can be done later on.

Separately, we also need a way for the user to provide the transaction script.

Dominik1999 commented 1 month ago

Excellent points, I forgot the inputs and the transaction script.

Maybe we can start without the transaction script and use the default internally (just signing)? What do you think?

bobbinth commented 1 month ago

Maybe we can start without the transaction script and use the default internally (just signing)? What do you think?

If we want to start with a simplified implementation, I would probably start with a transaction script and add notes later on.

Dominik1999 commented 1 month ago

I think, notes are important for the first iteration. I added three tabs to it now (Notes | Inputs | Tx), then the user can switch between them. Let's see how usable it will be and test it with some users.

bobbinth commented 1 month ago

I think, notes are important for the first iteration. I added three tabs to it now (Notes | Inputs | Tx), then the user can switch between them. Let's see how usable it will be and test it with some users.

We can have transactions without notes (i.e., a transaction that creates new notes), but having transactions without tx scripts would be a bit more difficult (e.g., we won't be able to use the standard wallet account).

Dominik1999 commented 1 month ago

As requested:

it would be helpful for us is to understand how a user would interact with this playground. Additionally, I'd like to know how the different scripts (such as notes, input, and transaction) work together and what dependencies they have on the account code tab.

You could drop a user scenario here

The different scripts - background

In the Rollup playground, we want users to execute transactions transparently in the browser. A Miden transaction always involves an account (~smart contract), which can consume notes during the transaction. The transaction program executes all note scripts sequentially, and then it executes the transaction script.

Screenshot 2024-09-30 at 09 51 12

Note scripts

Notes consist of assets and an executable script. If an account wants to consume a note to get the asset, it has to execute the note script. Every note script can have input. Let's look at the P2ID (pay-to-id) script as an example:

A P2ID note can carry 10 POL tokens. The tokens should only go to a specific receiver, the one with the correct account ID. So, only if the correct account (having the correct account ID) executes the note script can the account incorporate the ten tokens. However, the script itself doesn't define the account ID; the note creator needs to specify the target ID via the inputs.

Transaction scripts

After all note scripts are executed, the last script executed during a transaction is the Transaction Script. Whereas the note scripts + inputs are defined by the note creators (not the executing account that wants to consume the notes), the executing account can define the transaction script. At the moment, we primarily use it to sign the transaction. It looks like:

begin
    call.::miden::contracts::auth::basic::auth_tx_rpo_falcon512
end 

The above transaction script calls the authentication procedure. In theory, also transaction scripts can have inputs.

Expected user interaction

We expect for the first iteration of the playground, that a user creates an account interface and a note script that calls functions from this interface.

For example, our basic wallet exposes 4 functions, see here. Note scripts can call those procedures, see here.

So we expect that users,

Does that make sense?

Dominik1999 commented 4 days ago

For the Output fields we have three categories of fields. Some fields can be hardcoded, others must be provided from the backend.

Hardcoded fields

Already computed in the backend

For the first iteration, we will return an AccountDelta from the backend that looks like

AccountDelta { storage: AccountStorageDelta { slots: {}, maps: {} }, vault: AccountVaultDelta { fungible: FungibleAssetDelta({AccountId(2305843009213693983): 100}), non_fungible: NonFungibleAssetDelta({}) }, nonce: Some(2) }

That means, with every transaction execution, we can get an update on

There can be several updates in the AccountStorage and AccountVault.

Furthermore from the backend, we provide

for now we return a long String:

    let account_code_commitment = executed_transaction.final_account().code_commitment().to_hex();
    let account_storage_commitment = executed_transaction.final_account().storage_root().to_hex();
    let account_vault_commitment = executed_transaction.final_account().vault_root().to_hex();
    let account_hash = executed_transaction.final_account().hash().to_hex();

    Ok(format!("AccountDelta: {:?}, AccountCodeCommitment: {:?}, AccountStorageCommitment: {:?}, AccountVaultCommitment: {:?}, AccountHash: {:?},",
    executed_transaction.account_delta(),
    account_code_commitment,
    account_storage_commitment,
    account_vault_commitment,
    account_hash))

But happy to change that to a more structured object.