Controllers from first principles

Background

(feel free to skip if you’re familiar with the context)

Recall the CAP theorem, which states that any distributed database system can only pick two of:

  • Consistency (reads return latest write)
  • Availability (reads return)
  • Partition tolerance (progress continues even when arbitrary messages are dropped)

In a Venn diagram of possible choices:

Consider the interchain ecosystem (different L1s, rollups, Cosmos zones, etc.). Let’s refer to each of these components as domains, where different domains can have both heterogeneous security and heterogeneous clocks (so shards would also count, if they run in parallel).

Which class do these domains fall into?

  • Most domains are CP (consistent and partition tolerant). BFT consensus (e.g. Tendermint, so all Cosmos chains), Near, decentralised sequencer proposals based on BFT systems, etc. are all CP; under network partitions no progress is made.
  • Nakamoto consensus chains such as Bitcoin are AP (available and partition tolerant); under network partitions they lose consistency.
  • Hybrid chains with Nakamoto-esque (longest chain) block production and a separate finality gadget - e.g. Ethereum, Polkadot - are AP from the perspective of block production, but CP from the perspective of the finality gadget, and from the perspective of the rest of the system they are typically treated as CP (otherwise consistency would be lost if blocks were accepted before being finalized).

So far, nothing new. However, these domains are interconnected, with message-passing provided by bridges, IBC, ZK light clients, etc. - so we can also consider the whole interconnected set as one database system, and ask the same question of the interchain as such - which class does it fall into?

If we understand the chains as processes, I think the interchain is basically an AP system - individual chains will return reads even when they haven’t heard from others in awhile, parts of the system will continue making progress under partitions, etc. Current bridges send messages authenticated in some fashion, but they typically don’t do much in the way of detecting or preventing consistency violations (at most, equivocation can be detected, e.g. with Tendermint light client misbehaviour evidence). Operating this way, we give up consistency - chains might disagree with each other on what another chain said - and there’s no easy way to even check whether a subset of the system is consistent at any point in logical time.

Suppose that we had some way of merging history from one chain into another. Then, the interchain would be an AP system where individual reads can choose to be consistent with respect to certain chains, by giving up availability with respect to those chains (so, instead, CP “on demand”). For example, as a client I could wait for a confirmation from the Cosmos Hub, Ethereum, and Solana that some state transition on some other chain had been witnessed and included in such-and-such an order before considering it valid - for all users who do this, their reads are guaranteed to be consistent w.r.t. any other users who also do so (even if the other chain’s sequencer/consensus etc. equivocates), as long as the Cosmos Hub, Ethereum, and Solana chains refuse to accept any linearity violations in their locally verified history (to which they re-attest by signing blocks).

One difficulty of implementing this safely is dealing with recovery from a linearity violation elsewhere. Suppose that chain A signs two conflicting blocks and sends one to chain B, the other to chain C. Now, if chains B and C have each naively accepted the first block sent to them by A, they can no longer merge history from each other - in a permissionless context where anyone can create and merge history, this becomes a huge DoS vector and renders this design unworkable.

In order to safely merge history, we need to do a few things:

  • Craft proofs in some fashion that makes merging history a constant-cost operation from the perspective of the recipient, regardless of the length of the history
  • Separate out individual dependencies (instead of dealing only on a block level) so that Byzantine users or chains cannot block others’ progress by manufacturing equivocations
  • Specify how conflicts should be resolved, such that B and C (perhaps with some other signature) can make progress after the equivocation event and remain consistent with each other

To satisfy these requirements, as a proposal for part of a standardised protocol architecture, Anoma introduces the concept of resource controllers.

Introduction

The main idea of resource controllers is to allow for resources (including data and logic) to move across trust and concurrency domains, preserving appropriate integrity guarantees, with no additional work required by the application developer. Controllers establish an order of resolution between consensus providers involved in the history of a particular resource, such that conflicts later in the controller chain can be resolved by appeal to earlier controllers, all the way back to the original resource issuer if necessary. In the context of an execution logic capable of privacy-preserving verification (such as Taiga), controllers allow for the privacy-preserving properties to be retained across the domain boundary (i.e., private bridging). In the context of an execution logic capable of succinct verification (such as Taiga - both privacy-preserving and succinct), merging history is a constant-cost operation independent of the length of the history.

Controllers generalize the intuition behind cross-chain token denomination tracking in the IBC protocol (details), which is greatly simplified with a standardised resource model and identity type. Unlike chains in IBC, controllers in this standardised resource logic can provide global double spend detection (with garbage collection under a tunable synchrony assumption), and the controller model allow for recovery by the original issuer under exceptional circumstances (i.e. manually deciding to gain availability at the cost of consistency).

Intuition

In this model, each resource has a DAG of controllers, ending in a terminal node (last controller), where the signature of this last controller is required to execute a partial transaction. A controller is not necessarily the owner of a resource, but they are the current custodian, of whom a liveness assumption is made for interactions involving the resource in question.

The controller DAG can be appended to and reduced (from the end) at any time. Intuitively, appending controller A is a cross-domain transfer to A, and removing a controller at the end of the DAG is unwinding by one step the previous cross-domain transfer path. A DAG is used instead of a list in order to allow resources to have multiple ancestors from different controllers.

Transaction execution logic changes

Controllers take the basic resource structure and extend it with a list of external identities:

data Resource = Resource {
    logic :: Set Resource -> Set Resource -> Extradata -> Bool,
    data_static :: ByteString,
    data_dynamic :: ByteString,
    quantity :: Integer,
    controllers :: TerminalDAG ExternalIdentity
}

The controllers field is also included in the static data (for the purposes of denomination derivation).

Note: Possibly, controllers could just be put in the static data, and this logic standardised at a slightly higher level.

The partial transaction validity function is also changed such that the following extra condition must hold:

  • In order for the partial transaction to be considered valid, a valid signature from the last controller in the list over the partial transaction (or a block containing it) must be provided in extradata.

The resource logic should also include the following rule:

  • A resource with controller DAG cs may be spent to a copy of the same resource, but with any number of (arbitrary) controllers appended to the DAG. These controllers do not need to sign.
  • A resource with controller DAG cs <> cs', where both cs and cs' are sub-DAGs of arbitrary length, may be spent to a resource with controller DAG cs, iff. signatures over the partial transaction are provided by all controllers in cs'. (i.e., any later sub-DAG which is strictly after the earlier counterpart sub-DAG may be pruned if all controllers in the later sub-DAG sign).

Integrity assumptions

In order to provide integrity in this system, a controller c must commit to not signing any two partial transactions which double-spend (reveal the same nullifiers). This does not require a total order, but it does require that controllers track the nullifiers in their signature history.

Cross-domain usage flow

Let’s say that a user Alice wants to issue a resource, move it to a controller C, and send it to Bob. Bob then transfers it to controller D, and sends it to Charlie.

  1. Alice creates the resource (ex nihilo), with herself as the initial controller.
  2. Alice crafts a transaction spending this resource to a copy with C as an additional controller and Bob as the owner. She signs it.
  3. Alice relays this transaction to controller C. C performs the following steps (probably with the assistance of proofs provided by Alice):
    a. Checks partial transaction validity (as above)
    b. Checks valid resource history all the way back to the time of creation by Alice
    c. Checks no-conflicts (that C hasn’t signed over any other partial transaction which revealed any nullifiers in this history)
    d. Then, C executes the transaction and appends the nullifiers to its known nullifier set
  4. Bob crafts a transaction spending this resource to a copy with D as an additional controller and Charlie as the owner. Bob relays this transaction first to controller C, for a signature from C. C performs the checks as above, then signs it. Bob then relays this transaction, along with C’s signature, to controller D, which performs the same steps as C above.
  5. Charlie can now spend the resource (on controller D).

Note: it may be prudent to explicitly separate the step of C learning about Alice’s valid history of which C was previously unaware, and perform this “eagerly” as opposed to “lazily”.

Note: if chains do not have compatible history formats, they can instead accept resources under a promise of computational integrity. Concern: application-dependent invariants.

At first glance this may seem expensive, since each recipient of a resource must verify the history since inception. This can be cut to a constant cost, however, with recursive ZKPs and incremental proof composition. In order to adhere to the integrity assumption, potential controllers must store a commitment to all nullifiers over which they have ever signed, but data availability (storage) for the actual nullifiers can be sharded (in which case the transaction author must fetch the nullifiers in question, and prove to the consensus provider into whose history they wish to merge that there are no conflicts).

Note: Partitioning the nullifier space is also possible (with some slight privacy loss).

Partition recovery

A resource may allow that a signature from a controller earlier in the DAG, without a signature from the last controller, is sufficient to execute a transaction (perhaps under certain additional circumstances, e.g. an attestation from other parties that the last controller is not live). This will mean that the resource in question will not be able to be sent back from the last controller to any parties who learn about the nullifier reveal of the initial (revocation) spend first, and that further history built on top of a potential partition spend will not be mergeable. Effectively, this constitutes an (intentional) choice of the controller to break their redemption promise for controllers later in the chain of a particular resource. Consistency is lost (we’re opting to give up consistency for availability here), so controllers should be extremely careful about doing this.

Private bridging

Assuming succinct recursive ZKPs, private bridging emerges naturally here merely through the use of a privacy-preserving transaction execution logic and nullifier-based double spend prevention. Note that - unlike IBC - in a fully privacy-preserving world, what balances of which assets were held with which controllers at any given point in logical time would be not in general public information. Some further research to determine security implications of this would be prudent. Auditing options such as keeping track of token movements with homomorphically encrypted counters and periodically threshold-decrypting should be reasonably straightforward (this may be useful, for example, to check that the amount of stake securing a particular domain is sufficient).

2 Likes

Can all of these checks be completed by C verifying a single ZKP? And how long would it take Alice or a solver to generate these proofs today?

  1. Yes.
  2. I’m not sure, this depends on a lot of implementation details. Hopefully we will know soon!
1 Like

Musings on Fungibility

Problem: Even if Anoma provides perfect fungibility within its own ecosystem through protocol adaptors and endorsement mechanisms, some of these assets like Ether already trade on existing chains with existing bridges and representations. Any assets that come into Anoma “pre-wrapped” will not be fungible with each other.

Example Scenario

Let’s say @apriori spawns a Patrick controller. I decided to create one hundred PATRICK token resources. I then send equal amounts (twenty resources - could also be one resource with twenty tokens attached, but just for the example assume the former) of the resources to Chris, Adrian, Maurice and Jeremy controllers. The token distribution across the different controllers is the following.

  • Patrick - 20
  • Chris - 20
  • Adrian - 20
  • Maurice - 20
  • Jeremy - 20

All of these resources are fungible across the different controllers, meaning that I can assume that the exchange rate of PATRICK-XAN will trade at the same rate on each controller (assuming arbitrageurs exist for these PATRICK-XAN [AMM or DLOB])

Now lets say I have ETH. The Ethereum controller has issued a certain amount of ETH ~ 120M tokens give or take. However, Anoma is only aware of 10M ETH that @apriori has deposited in the Ethereum controller PA. Now let us say I send 2M ETH to four additional controllers which are;

  • Patrick - 2M
  • Chris - 2M
  • Arbitrum - 2M
  • Base - 2M

@apriori then deposits some ETH from Arbitrum EVM that he already has into the protocol Adaptor on Arbitrum and sends some to all the controllers above. @apriori does the same exercise for Base EVM as well. @apriori now wants to swap ETH for XAN on each controller.

In principle, all the ETH-XAN pairs trading on each controller should be priced the same. However here is where I have a question. Patrick and Chris are running some version of HPB for consensus and the full Anoma node stack. They spun these controllers up only recently since the controller party just happened. Lets think of them as chains for the sake of this example. Arbitrum and Base are connected to Anoma via the protocol adaptor and have been for some time. Let’s assume ETH issued through an Ethereum bridge to Arbitrum and Base trades at a discount to main-net ETH not 1:1 because the asset issuer is different as noted above. You then have different denominations of ETH as resources. You’d have different flavors depending on which PA contract there is, even though the ETH is technically redeemable on main-net for 1:1.

  • Ethereum ETH on ETH controller
  • Ethereum ETH on Chris controller
  • Ethereum ETH on Patrick controller
  • Ethereum ETH on Arbitrum controller
  • Ethereum ETH on Base controller

  • Arbitrum ETH on Arbitrum
  • Base ETH on Arbitrum
  • Base ETH on Base
  • Arbitrum ETH on Base
  • Arbitrum ETH on Chris controller
  • Arbitrum ETH on Patrick controller
  • Base ETH on Chris controller
  • Base ETH on Patrick controller

So in our example you would have:

  • Chris Controller - 2M Ethereum ETH, 2M Arbitrum ETH, 2M Base ETH
  • Patrick Controller - 2M Ethereum ETH, 2M Arbitrum ETH, 2M Base ETH
  • Arbitrum Controller - 2M Ethereum ETH, 2M Base ETH, 2M Arb ETH
  • Base Controller - 2M Ethereum ETH, 2M Base ETH, 2M Arb ETH

Could our system help with reconciling fungibility?

Maybe?

If Ethereum controller just had one canonical bridge contract across all chains then the ETH in Anoma would be fungible across all chains. But it does not and some ETH that lives on these rollups will be deposited into the Anoma protocol adaptor when deployed to those rollups.

The only ways I can see to reconcile this would be;

  • Only have one entrance to Anoma in Ethereum land. This won’t work because anyone can deploy a protocol adaptor.
  • We might be able to convince all the Ethereum validators to endorse any resource denomination of ETH with a signature that any ETH withdrawn from the Anoma PA on main net is redeemable 1:1. This likely wouldn’t work, though as its a coordination or social problem.
  • I think Metastability Mechanism absorbing the difference subsidized through XAN is the only viable way to fix the issue. For example, if you are trying to in one intent go from ArbETH to withdraw to main net the protocol adaptor would mint say 1.0001 ETH and burn the 1.0 ETH making up the difference of .0001 with an equivalent XAN amount held by the MSM.
  • Likewise MSM could subsidize any solver making different denominations of ETH intent matching for users where if ETH is not 1 ETH the solvers just presents a proof to the MSM which then gives the solver the difference which they can rebate to users and keep any surplus

Am I thinking about this correctly? I would appreciate your input @cwgoes.

1 Like

Great question, we haven’t really worked all this out yet.

First, I want to note that Anoma does not exactly make resources fungible across different controllers. Provenance is tracked explicitly (similar to IBC) – XAN on Patrick-controller and XAN on Chris-controller are two different assets. What the controllers system improves upon (relative to IBC) is automatic simplification of the provenance graph using attestations.

For example, consider the following userflow: Alice starts with XAN on Anoma-controller, transfers it to Chris-controller, and then transfers it to Bob-controller. With IBC, Alice now has “Chris/Bob XAN”, which is not fungible with “Bob XAN”, for example, and the only way for Alice to get just “XAN” back is to go through Chris. The controllers system allows this provenance graph to be simplified (with an attestation from Patrick-controller) in the background, such that Alice’s “Chris/Bob XAN” can be simplified to “Bob XAN”, which then is fungible with “Bob XAN” which originally arrived through another route (e.g. Charlie-controller) and was subsequently also simplified. If we expect this simplification mechanism to be generally in use, most assets would be held in single-hop variants – “Bob XAN”, “Charlie XAN”, “Patrick XAN”, etc. – which can be sent back to the XAN issuing controller directly at any time, but are not fungible with each other (nor could they ever be, as they are controlled by different controllers – no atomic transactions are possible).

So, absent other mechanisms, one would still expect separate markets for these XAN variants to exist on each controller. Generalized intents should also help here, in that users can express (in a single intent) that they’re willing to swap e.g. XAN on any controller which they hold it for ETH at such-and-such a price. A more complex intent could be used to implement an AMM curve instead of a fixed price. Settlement of any given transaction still happens on exactly one controller, but – properly standardized – multi-controller intents like this should mostly defragment liquidity – roughly, the whole system should behave like a giant orderbook, just with some extra constraints (since users will trust different controllers). There would still be arbitrage opportunities sometimes, and market makers will still want to periodically rebalance their assets across controllers, but one can imagine a world where user demand is predictable enough that the efficiency loss due to multiple controllers (vs a single “infinitely scalable, totally trustworthy” one) is minimal.

So far, we’ve just been talking about tokens and resources operating “natively” within Anoma. Let’s now talk about Ethereum chains (and rollups), which would support Anoma via the protocol adapter (one protocol adapter per chain/rollup/etc.). It’s worth noting that the protocol adapter in principle can support the controllers system, although we haven’t implemented (or fully designed) that bit yet – the protocol adapter could verify attestations from other controllers, and a proof that a certain transaction has been processed by a protocol adapter could be treated as an attestation elsewhere (at least where the chains/rollups involved have some kind of light client).

We don’t yet know what the gas costs will be, but this can all eventually be proof-aggregated, so let’s assume that in the mid-long term this happens. In this world, chains with protocol adapters are full members of the controller system, not second-class citizens. However, they may still have other bridges of which the controller system is “unaware”. Concretely, now, supposing that the Ethereum mainnet and Arbitrum (as examples) have protocol adapters, we’d have two kinds of ETH on Arbitrum:

  • Arbitrum-ETH through the controller system (i.e. a user consumes an ETH-resource on Ethereum and creates an ETH-resource which can be consumed on Arbitrum) – this would behave identical to “native” Anoma tokens as described above.
  • Arbitrum-ETH through the existing bridge (i.e. a user uses the existing bridge contract) – this is not legible to the controllers system, so it just behaves like a different token (e.g. the controllers system can never simplify the provenance graph, although generalized intent-orders for multi-assets are still possible as described above).

My somewhat contrarian thesis is that – although we could try to design more fancy ways to reduce the UX costs of existing Arbitrum-ETH – maybe we should instead just lobby Arbitrum users to adopt the controllers system for bridging instead. They don’t need to make any additional trust assumptions, they could continue to use all existing Arbitrum applications without extra transactions, and they get full benefits of the controllers system (e.g. they can transfer directly to another rollup without going back through the Ethereum main chain).

I do think that your suggestion for the MSM (or a related sub-mechanism) to provide liquidity across different controller pairs is reasonable, and may make sense regardless – in a sense, this is just a governance-controlled liquidity provider / market maker, which is something many chains/assets with high user overlap likely would want. That would be complementary to whatever we do here (generally, it just fills the role of a market maker, perhaps one which is subsidized or preferentially treated).

1 Like