Volition: Hybrid Data Availability Solution

I appreciate the detail in the explanation.

I’m not a programmer (although I understand some of the DA context).

From my position as a regular user of the network, I can only express my feelings.

I cannot deny that reading the entire context generates some uncertainty and perhaps fear about the complexity required to implement this hybrid concept.

There are two main aspects for me:

UX in relation to the appropriate security guarantees for each use case (regular user).
The regular user will face a new way of approaching their actions on the network.

It is clear that 90% will choose the most economical route, even if it means having lower security guarantees.

I’m not so sure how much they are interested in understanding and accepting the final trade-offs as regular users.

Perhaps we will need general education? Or is it not the goal to reach that extreme?

Maybe we can consider it as a trade-off that doesn’t necessarily have to be fully understood?

I wonder if users of Inmutable X, Sorare, or Myria truly understand the entire context of security guarantees. I think not.

Derived from this, I believe a user interface that is sufficiently intuitive is required to clearly and FRIENDLY present the trade-offs of the two possible paths, L1DA/L2DA.
However, it should not reach the point of generating fear in the user.

Please note that my perspective is from the point of view of a non-technical user (which is what I am).

So, under this context, I would only ask that the interfaces presented for choosing L1DA/L2DA be quite user-friendly, clear, and provide basic (not excessive) information that allows me to make a quick decision but with the necessary understanding.

I hope I have understood and expressed coherent ideas with the technical publication you just described.

Thank you for the feedback!

Bottom line up front: I think that your concerns are valid and should be in front of all core and dapps developers. In the end, the way to measure it will be in a longer time on a special Testnet to test how developers benefit from the Volition in reality.

As for the regular user experience: I’d split it into two parts - the interaction w/ the network in general (how Volition will affect its account contract, paid fees) and the interaction with specific dapps. The first one will be specified explicitly in the transaction and in the wallets. The second part is dependent on the specific dapps and what it chooses to show to the users.

In general, I think that two measures to be taken may allow reducing the risk:

  1. Make every default behavior use L1DA.
  2. Clearly mark (in block explorers and wallets, for example) contracts that are using L1DA purely.

Will there be some way how existing contracts can migrate their storage to Volition?

In an ideal scenario, I’d like to just replace_class with the same storage vars, but just mark them with #[manual_da] though I understand fiddling with storage is not straightforward.

Are there any solutions to this?

Would it be possible to have some storage for which the address domain points to some “temporary storage” effectively acting as transient storage?

No, sequencer won’t execute txs without having the relevant DA_mode data.

There are no plans to migrate storage to L2DA at the network level; it should be implemented within the contract level. For example, if you want to exclusively use L2DA mode instead of the existing L1DA, you can devise a logic such as ‘try to fetch the data from L2DA, and if it doesn’t exist, fetch it from L1DA and write it under the same key in L2DA.’

But would be easy/possible to make this DA_mode available?

Maybe I didn’t understand your question, can you elaborate more about the DA mode that you are suggesting?

The first version of volition in Starknet is planned to contain only two DA mode, there is an intention to expand it in the future.

Basically having a DA that would store stuff until the end of the transaction.
Once the transaction is done, it would drop the “stored” data entirely.

This would effectively achieve the same purpose as transient storage:

Great post. Thank you for the clear description. Two questions relating to this quote:

  • Assuming this would be compatible with Adamantium style DA as well?

  • Also, could you decide to write to multiple cheap DA options at once rather than choosing just one, e.g., L2DA + Multiple Adamantium DA providers + Other non-native DA provider. In most cases you may not want to do this, but I can think of one or two exceptions.

Thank you for your comment!

Assuming this would be compatible with Adamantium 1 style DA as well?

The first version of Volition for Starknet will include L1DA and L2DA. The intention is to expand it to other Data Availability solutions, but there is no specific design yet for supporting a particular DA solution.

Also, could you decide to write to multiple cheap DA options at once rather than choosing just one, e.g., L2DA + Multiple Adamantium DA providers + Other non-native DA provider. In most cases you may not want to do this, but I can think of one or two exceptions.

Of course, it is possible to implement it at the contract level without the need for a new feature in Cairo. As Starknet currently has no specific design for more than two DA modes, there is no intention at the moment to expand Cairo to support features for more than one non-L1DA mode.

Regarding the L2 DA crisis: I should be able to use volition only for index data, i.e. data that can be reconstructed from L1 data (e.g. Enumerable extension of the 721 specification), without incurring the risk of L2 DA crisis permanently breaking my contracts. If the data I store in volition is just an index, anyone can reconstruct the L2 data of the contract from L1 data, so I shouldn’t be taking on the same L2 DA risk as everyone else by using it in such a way.

My suggestion is to change the volition design to store a commitment for an L2 DA trie, separately for each account, in the L1 data. If one contract uses L2 DA in a way that is safe, it can always recover from any L2 DA issues. This will be more expensive but also more secure.

Some other comments below:

  • The Cairo API: why not just have a read_from and write_to methods that take the DA mode as a first argument, alongside the existing read and write? Seems more explicit and easier than having to add #[manual_da] and having the same method name with different arguments.
  • My preferred implementation of an ERC20 would by default query L2 balances first, and only query L1 balances if L2 balance is insufficient to cover a transfer. On transfer, recipient should always receive L1 balances so the sender is not deciding to risk the recipient’s payment. There should also be a method to move tokens from L1DA to L2DA. The recipient contract (account or protocol) can choose to expose a method that allows the sender to move tokens in some amount to DA.

Ideally the cheaper volition token is standardized. Here is my proposed interface:

#[starknet::interface]
trait VolitionToken<TContractState> {
  // the existing methods are unchanged but now check L2 DA balances first
  fn transfer(ref self: TContractState, to: ContractAddress, amount: u256) -> bool;
  fn balanceOf(self: @TContractState, account: ContractAddress) -> u256;

  // Returns the balance of this token stored in one particular DA
  fn balance_of_da(self: @TContractState, data_availability_mode: u8, account: ContractAddress) -> u256;

  // transfers from a specific da balance only
  fn transfer_from_da_balance(ref self: TContractState, data_availability_mode: u8, to: ContractAddress, amount: u128);
  fn move_between_da(ref self: TContractState, from_da_mode: u8, to_da_mode: u8, amount: u128);
}

Of course, approve/allowance is omitted from this specification, since it is not required with native AA.

I think the goals of transient storage should be achieved with a separate syscall. Using L2 DA for this doesn’t help that much because you still have to prove the 0 value of the storage address at the beginning of the transaction.

Regarding the L2DA crisis – the expectation is that malicious node operators would have their STRK token stake slashed in full then, right?

We have planned on running our own full node to guarantee the security of our users L2 data, in the expectation that in the event of a DA crisis, we could provide the data our users need.

If I understand the crisis situation correctly, we would only be able to supply old data, but reconstruct the current L2DA state by re-executing transactions (which we would have saved on our full node), proving that they lead to a certain L2DA storage state.

Am I then right to understand that any L2DA crisis would be temporary? Or is the expectation that they would prove txs, but not broadcast them to the rest of the network, meaning we wouldn’t be able to reconstruct the L2 state – at all? And keep operating this majority honest-on-L1DA-but-withholding-information network forever if they wish, since there’s no way to prove that the malicious majority is indeed withholding data?

Thank you for your response! I agree that the proposed DA mode seems to have interesting potential, especially as a middle ground between L2DA and L1DA in the future.

However, I think that your proposed design shouldn’t serve as the immediate L2DA solution for two main reasons:

  • Going this route means an inherent cost of 1.5k gas per transaction (changing the nonce of the calling contract), which means that achieving a significant reduction in cost for simple transactions won’t be possible.
  • The security benefit is limited; while you can continue working with contracts that have not been changed during a malicious majority attack, a reasonable few-hours control by a malicious majority will alter most of the common/“important” contracts, and the data needed to reconstruct them won’t be available.

Hey!

The L2DA crisis occurs when we have a malicious majority of validators. In this case, they could sign on a block without exposing the data to full nodes, and the block will still be valid on L1 as 2/3 of the stake signed on the block.

It’s not a probable event, but if that happens, the L2DA might not be able to reconstruct, even by L2 full nodes.

Unless I am misunderstanding, the security benefit is not that you can continue working with contracts during a malicious majority attack. It is that your contract cannot ever get into a completely irrecoverable state. If a contract uses volition only for index data, the index data for that contract can always be reconstructed from the state that is posted to L1. This is a significant win: it means you can store even more index data to optimize computation elsewhere. The 1.5k gas will be insignificant compared to how much you can save by storing more index data.

I think DA modes should be introduced to most-safe to least-safe. Obviously L1 DA is safest. Next would be L2 DA per-contract. Then would be global L2 DA. I don’t find the global L2 DA as useful because a malicious majority could put my contracts in an irrecoverable state, even though I only stored index data in volition, which is not an acceptable tradeoff for gas efficiency.

Evyatar’s point was that in a catastrophic-enough event, enough key L2-relying contracts would have been hit, which leads to most L2-relying contracts becoming unusable.

It is true that if you only stored index data you’re less impacted, but the main benefit from alternative DA modes isn’t using index data to save computation. Computation is cheap on SN and will most likely become even cheaper, already today L1 DA costs are > 90% of the fee on most transactions. L2 DA targets at reducing costs for common use cases (e.g. simple transfers).

I understand the point. The issue I have is not that it is temporarily unusable–I understand this would be the case for both the global and account specific version. It’s that the data would be permanently unavailable for all contracts with the global version; that the only way I can see out the bad situation is to revert the state of the chain to a state with public data; and that I have no way to code my contract to use this global L2 DA mode s.t. it cannot be bricked by loss of data availability.

On coding my contract to be tolerant to loss of DA: you could try allowing a way the contracts can be called to not use the indexes. This will be prohibitively expensive in my case for the functions that read indexes. In addition, since you cannot write to indexes during loss of DA, you cannot actually use the indexes since they are not updated by the no-index code path. It seems impossible to write a contract to be tolerant to loss of DA without introducing an oracle for loss of DA (or more generally just introducing upgradeability)

I would not recommend anyone to use volition with global L2 DA for index data in custodial contracts such as AMMs, lending markets.

I understand your concerns. I believe that in the future, adding another DA mode that publishes all contract state roots on L1 could help reduce costs while maintaining security for contracts that store index data.

The suggested DA mode has the potential to significantly decrease fee costs for users, but it does require careful and secure implementation of contracts. We believe that with a secure contract implementation, the benefits of significantly reduced costs can be obtained while risking only a limited amount of resources.

In hindsight, I agree that starting with global L2 DA mode is better (definitely better to start with, maybe better long term).

If Ekubo uses DA only for index data, we will be competing with AMMs that use it for everything that can offer order of magnitude cheaper interactions. So we’d still have to offer both the volition-only version and the L1-DA version to support all the use cases.