Everybody Hates Balance

This post is produced as a result of an attempt to reverse engineer balance check and is a part of a bigger RM rethinking process. It describes the ideas behind the state structure, actions, and balance.

In this post I use a bunch of new terms and also old terms but in different ways than before/in other contexts. This is not an attempt to develop new terminology or a way to commit to new terminology.

State structure

As you know, our state[1] is kind of split into two parts:

  • a public log of resource tags: log(t) = {tag_1, tag_2, ..., tag_{n(t)}}
  • a virtual state made of resource pre-images [2][3].

The virtual state is divided into little realms based on the controlling identity: virtual\_state(t) = \{realm_{id_1}, realm_{id_2}, ..., realm_{id_{n(t)}}\}. Each realm contains resource pre-images that the controlling identity can create, consume, or make visible to other identities.

We never really need to talk about the whole virtual state. It only makes sense to talk about one or a few realms at a time, the latter is relevant when we want to create an inter-realm transaction.

Anyway, the reason we have such a dual state structure is that we want to enable coordination (that requires something that everyone agrees on - the public log) without the obligation to make the coordination matter public, that is why we have a bunch of realms instead. Note that here we are not talking about what should be public and what should be private, but about what is necessary for coordination and what isn’t.

Takeaway point:

  • coordination carcass - public log
  • flexible coordination tissues - realms

Transactions, transaction-wide constraints, local constraints

What is a transaction? State transition request. For simplicity, let’s assume that:

  • our transaction is inter-realm, so it involves multiple users that contribute to the same transaction
  • a coordinator assembles the final transaction from individual contributions
  • the transaction contains only a single atomic state change (not multiple independent changes aggregated into a single transaction)

How would such a transaction be built?

We have a bunch of constraints that have to be met to produce a valid transaction. The constraints depend on the resource kinds involved, users, some other properties, etc. Let’s just put them all into a single bucket of constraints for now.

In principle, we only care about verifying all these constraints transaction-wide, we don’t need to limit the constraints to smaller contexts within the transaction. So why do we produce some proofs locally? The reason is actually the same as for the state split: we want to separate the carcass from the tissues, to enable flexible visibility properties for identity-controlled contexts.

Given a bucket of constraints, let’s split the constraints into transaction-wide and local. Local constraints are proven locally and then aggregated. Transaction-wide constraints are proven by the coordinator.

Local constraints

The local constraints are proven by the realm-controlling identity (read: actions are proven by users). That allows the party to control who can view of the data checked by the constraints. It is a trade-off between computational power and visibility. What is local is not globally visible (which is nice) but requires local management (which is not necessarily cheap).

Transaction-wide constraints

The transaction-wide constraints are proven by the coordinator. But since transaction-wide constraints still constraint the inter-realm state transition, sacrifices have to be made. The involved identities either need to reveal some data they control (which might be okay) or use some kind of custom trick. Revealing some data gives up locality - that is sacrifice, especially if we want at the same time to preserve local properties for other constraints in the same transaction. Using a custom trick gives up generality (but might allow us to preserve locality) - that is a sacrifice too.

And here comes the balance! Balance is a transaction-wide constraint enabled by the trick of using a homomorphic commitment scheme. Because it uses a custom trick, it allows to preserve some locality properties.

Takeaway points

  • What we get from having a transaction split into actions is not execution contexts, it is locality. We see that in the current design we sometimes have these cross-action checks, which clearly violate the idea “one action = one execution context”. What we really have is “one action = one prover”.
  • Local constraints are a way to provide flexible visibility properties. Perhaps there is a way to construct a system in a way that allows for the constraints to merge if there is no intention to have flexible visibility?
  • Balance check is a transaction-wide check enabled by a trick. We don’t necessarily want a hardcoded balance check, but we do want to have a framework to talk about transaction-wide checks and a way to enable them that serves the system. This is a big question. Could it be that having too much flexibility will break the globaldesired properties of the system?

Next steps

  • Explore the possibility of having a transaction-wide constraint framework, so that we have “local constraints + transaction-wide constraints” instead of “local constraints + balance check”
  • Figure out how the balance system would fit in and how we can preserve the properties we want without being as limited as before
  • Explore how such a system could be made more liquid, with merging the local constraints that don’t need flexibility into global constraints. Can it be done in a better way than just delegating proving to the coordinator? Would it require some unpleasant circuit manipulations that make it practically infeasible?
  • Ensure that whatever I come up with still enables everything we had before, meaning intents/cross-action-checks, balance checks (when needed/wanted). How would it damage efficiency? Is there a way to not make it even less efficient?

  1. Here state is defined roughly as what exists in the system at time t. Not intended to be compatible with any other definition of the state we have ↩︎

  2. Resource pre-image = the data needed to assemble a resource. Can be seen as the resource structure where the hash fields are replaced by their pre-images. ↩︎

  3. It can actually include more than just resource pre-image. Stop nitpicking!!! ↩︎

2 Likes

This is super clear as an idea and perfectly matches Miller’s analogy to vast multiplayer games.

For the here and now, I am actually fine with the balance trick.

The intention here is not to say that the balance trick is bad, it isn’t and we want to have it. The intention was to compare what we want and what we get and assess if this is the optimal in the long-term way to do that

1 Like