livepeer / livepeer/protocol

Create a "viewer" contract that can fetch the entire on-chain transcoder pool

Open
#388 6 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
JavaScript
Stars
155
Forks
50
PR merge metrics
No merged PRs in 30d

Description

Currently, all clients interacting with the BondingManager need to submit N RPC requests in order to fetch the current on-chain transcoder pool ([example](https://github.com/livepeer/go-livepeer/blob/96ca7f8aa5ff4ec2d91f949f76292f332fd431bc/eth/client.go#L701) from go-livepeer) where N is the size of the on-chain transcoder pool. Furthermore, oftentimes, clients need other on-chain data about a transcoder (i.e. active stake, total stake, service URI) in addition to the transcoder's address. At the moment, a client needs to send separate RPC requests to fetch this on-chain data. Reducing the # of RPC requests required for these situations would help clients that depend on rate limited ETH RPC providers (i.e. Infura) and would also reduce the execution time required to fetch relevant on-chain data about the transcoder pool [1].

One way to reduce the # of RPC requests in these situations could be to deploy a "viewer" contract. This contract would read data from the BondingManager and could batch together function calls that would otherwise need to be executed on the BondingManager individually into a single function call. Clients would then interact with this viewer contract instead of directly interacting with the BondingManager at least for the on-chain data that can be fetched via the viewer contract. To address the situations described above, the viewer contract could expose a function that loops through the transcoder pool and returns all relevant on-chain data for each of the pool addresses.

Here is an example of what the viewer contract might look like:

```diff
commit 05746368b85a22f3332cdc89d3bde9fb43fa7710
Author: Yondon Fu
Date: Wed Jul 8 17:21:21 2020 -0400

Viewer WIP

diff --git a/contracts/Viewer.sol b/contracts/Viewer.sol
new file mode 100644
index 0000000..be7a680
--- /dev/null
+++ b/contracts/Viewer.sol
@@ -0,0 +1,86 @@
+pragma solidity ^0.5.11;
+
+
+contract Viewer {
+ struct Transcoder {
+ uint256 lastRewardRound;
+ uint256 rewardCut;
+ uint256 feeShare;
+ uint256 lastActiveStakeUpdateRound;
+ uint256 activationRound;
+ uint256 deactivationRound;
+ uint256 activeStake;
+ uint256 totalStake;
+ string serviceURI;
+ }
+
+ function getTranscoder(
+ IBondingManager _bondingManager,
+ IServiceRegistry _serviceRegistry,
+ IRoundsManager _roundsManager,
+ address _addr
+ )
+ public
+ view
+ returns (Transcoder memory)
+ {
+ (
+ uint256 lastRewardRound,
+ uint256 rewardCut,
+ uint256 feeShare,
+ uint256 lastActiveStakeUpdateRound,
+ uint256 activationRound,
+ uint256 deactivationRound
+ ) = _bondingManager.getTranscoder(_addr);
+
+ (
+ ,
+ ,
+ uint256 activeStake,
+ ,
+ ,
+ ,
+ ,
+ ,
+ ,
+ ) = _bondingManager.getTranscoderEarningsPoolForRound(_addr, _roundsManager.currentRound());
+
+ return Transcoder({
+ lastRewardRound: lastRewardRound,
+ rewardCut: rewardCut,
+ feeShare: feeShare,
+ lastActiveStakeUpdateRound: lastActiveStakeUpdateRound,
+ activationRound: activationRound,
+ deactivationRound: deactivationRound,
+ activeStake: activeStake,
+ totalStake: _bondingManager.transcoderTotalStake(addr),
+ serviceURI: _serviceRegistry.getServiceURI(addr)
+ });
+ }
+
+ function getTranscoderPool(
+ IBondingManager _bondingManager,
+ IServiceRegistry _serviceRegistry,
+ IRoundsManager _roundsManager
+ )
+ public
+ view
+ returns (Transcoder[] memory)
+ {
+ uint256 poolSize = _bondingManager.getTranscoderPoolSize();
+ Transcoder[] memory res = new Transcoder[](poolSize);
+
+ address addr = address(0);
+ for (uint256 = 0; i < poolSize; i++) {
+ if (i == 0) {
+ addr = _bondingManager.getFirstTranscoderInPool();
+ } else {
+ addr = _bondingManager.getNextTranscoderInPool(addr);
+ }
+
+ res[i] = getTranscoder(_bondingManager, _serviceRegistry, _roundsManager, addr);
+ }
+
+ return res;
+ }
+}
\ No newline at end of file
```

An additional function that could be useful could be one that accepts a list of addresses (instead of using the addresses in the transcoder pool) and returns all relevant on-chain data for each address. This function could be used by a client that needs to fetch the stake for multiple addresses at a regular interval (i.e. at the beginning of a round).

[1] Batching operations into a single function call will reduce the # of RPC requests, but it will increase the amount of steps executed in the EVM. My guess is that the overhead from EVM step execution will be less than the execution time saved by not submitting multiple RPC requests, but we should validate this. We should also benchmark the gas cost of the functions exposed by the viewer contract to make sure that it is below the gas cap for `eth_call` imposed by certain RPC providers such as Infura.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the proposed contracts/Viewer.sol example and review the BondingManager, service registry, and rounds manager calls it uses. Define the pool and address-list viewer behavior, then validate the RPC reduction, EVM execution overhead, and eth_call gas cost against provider limits.

Written by the indexing model from the issue text.

Assessment

Tech stack
solidity
Domain
blockchain
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.