livepeer / research

Organization of the current open research tracks within the Livepeer Project
MIT License
7 stars 2 forks source link

Subregistry commitments #20

Open yondonfu opened 5 years ago

yondonfu commented 5 years ago

Based off of discussion with @ericxtang and @j0sh

Suppose Os can be grouped into bundles based on a category such as geographic location. From a global registry of Os we can derive a number of subregistries for each of these categories. Each of these subregistries would have a fixed number of slots for Os. If we define a global orchestratorMaxFloat as mentioned in #18 then we can derive the max float for the entire subregistry as subregistryMaxFloat = max subregistry size * orchestratorMaxFloat.

A B can commit to subregistries (the word "subscribe" has also been used, but I'll stick with the terminology from the recipient set commitment construction for now) in order to work with Os in the subregistries. B does not have to worry about the actual membership changes in the subregistry as long as the subregistry has a fixed number of slots - if this is the case then subregistryMaxFloat will always remain the same given a constant global orchestratorMaxFloat.

B's reserve is sliced into allocations for each committed subregistry. Then each committed subregistry allocation is further sliced into allocations for each O in the subregistry.

yondonfu commented 5 years ago

One way to implement a reserve for a set of committed subregistries is to have a contract keep track of a reserve per subregistry. I'll refer to this as the "multi-reserve approach" For example, if B commits to us-east and us-west, then the contract would keep track of the reserve for us-east and us-west separately. Then, when B wants to withdraw the reserve for us-east (perhaps because it no longer wants to use this subregistry), it will initiate an unlock period of N blocks and after this period completes it will be able to withdraw the us-east reserve without affecting any interactions B is having with members of the us-west subregistry.

Another way to implement a reserve for a set of committed subregistries is to have a contract keep track of a single reserve which is sliced into allocations for each committed subregistry. I'll refer to this as the "single reserve approach". Below is a sketch of each of the operations involving the reserve:

Commitment

  1. B selects its desired subregistries identified by contract addresses (we assume that a subregistry is implemented as its own contract)
  2. B constructs a Merkle sum tree with each leaf being (subregistryAddr, subregistryMaxFloat)
  3. B funds its required reserve which is equal to the root sum of the tree
  4. B submits the Merkle root on-chain

Payment authentication

  1. Suppose that B selects an O in the subregistry us-east
  2. B sends a Merkle proof that us-east was included in B's on-chain commitment
  3. O verifies the Merkle proof and also checks that it is for a subregistry that it is actually a member of. A valid proof also demonstrates that B's reserve is sufficient to cover subregistryMaxFloat for us-east

Reserve liquidation

  1. Suppose that B has exhausted its deposit, but sent O (a member of subregistry us-east) a set of winning tickets with a cumulative face value of X < orchestratorMaxFloat
  2. O submits a Merkle proof that us-east was included in B's on-chain commitment along with the winning tickets
  3. The TicketBroker contract checks that O is in the subregistry us-east
  4. X of B's reserve is liquidated and sent to O

This construction would be simpler if we disallowed partial reserve withdrawals - if B wants to withdraw its reserve, it has to withdraw its reserve for all of its committed subregistries. This comes at the cost of worse UX. Below is one way to do partial reserve withdrawals:

  1. Suppose that B would like to withdraw part of its reserve because it no longer wants to work with a particular subregistry us-east
  2. B submits a Merkle proof that us-east was included in B’s on-chain commitment
  3. The TicketBroker freezes the subregistryMaxFloat amount for us-east for N blocks by storing the hash of the Merkle proof as withdrawalID and the withdraw block for the amount
  4. After N blocks, B uses withdrawalID to withdraw subregistryMaxFloat amount for us-east. The TicketBroker marks withdrawalID as completed

If this type of partial reserve withdrawal is allowed, then the payment authentication protocol would need to be modified to have O also check with the TicketBroker that a given Merkle proof was not already used to initiate a partial reserve withdrawal.

The upside of the single reserve approach is improved on-chain efficiency (less tx costs due to minimal storage operations) for committing to multiple subregistries. The downsides are increased protocol complexity especially with partial reserve withdrawals and possible increased latency in the end workflow if O has to not only verify a Merkle proof and also check if the Merkle proof was used for a withdrawal [1].

[1] This downside might be mitigated if O is caching data. Ex. O can cache the Merkle root for B's reserve and cache Merkle proofs used by B to withdraw as they are submitted on-chain. Then, O would just need to execute local database reads for verifying the Merkle proof and checking if the Merkle proof was used for withdrawal instead of sending network requests to do contract reads.