Problem definition
I was going through the different requirements that should be satisfied properly, in particular the ones that regard the staking
module.
While doing so, I've noticed there are currently two ways of getting the following set of information:
- Update validator information
- Calculate validator voting power percentage
- Calculate validator self delegation ratio
- Update the total staked tokens
Solutions
1. Read the per-block state
The first thing that came into my mind was:
Ok, to do so we can simply query the x/staking
LCD endpoints and get the returned values. This way we will be able to get all the info we need.
This can be achieved using the following endpoints:
/staking/validators/{address}
/staking/validators/{address}/delegations
The problem with this approach is that if you use those endpoints without any additional parameter, they will always returns the latest block state.
To solve this, you can use the ?height=
parameter at the end of the endpoint, specifying the block height. However, this only works for all heights if the fullnode on which the LCD relies has been started in archive
mode and thus stores all the blocks information. Otherwise, if the fullnode has not started in such mode, the following error will be returned when querying too-old blocks states:
{
"error": "invalid request: failed to load state at height <height>; version does not exist (latest height: <latest_height>)"
}
Also, the downside of this approach is that you need to perform a lot of LCD queries (namely, 2 per validator, per block) to get all the data you need.
2. Rely on messages parsing
The alternative to the above explained approach is to handle data set changes the same way that DJuno does: parsing different messages.
Following this approach, we will store the data only when it is effectively changed, instead that on a per-block basis.
The problem with this way of doing is that continuously changing data (i.e. validators/delegators rewards) would never be updated.
In order to solve this problem, what we can do is subscribe to events using the Tendermint websocket. Such events should include everything that we need. As an example, here you can see the x/distribution
events including the rewards ones.
Conclusions
I'd like to know @kwunyeung what you think is best.
I personally think that approach number 2 would create a much more efficient code since there are no doubled queries of sort.