Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(snapshot): analysis code #103

Merged
merged 2 commits into from
Feb 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions snapshots/analysis/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
data
accounts.json
68 changes: 68 additions & 0 deletions snapshots/analysis/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# Snapshot analysis

Tool to validate governance data from a snapshot and make it usable to for a
blockchain genesis.

```
Usage: go run . [tally|accounts] PATH
```

Where PATH is a directory containing the following files:
- [`votes.json`][1]
- [`delegations.json`][2]
- [`active_validators.json`][3]
- [`prop.json`][4]
- [`auth_genesis.json`][6]

> [!NOTE]
> The links to these files corresponds to data extracted for the proposal 848,
> the extraction method is detailed [here](../README.md).
> The prop 848 is taken as an example, but any other proposal can be used to
> build the same result.

## Verification

Considering all these files downloaded in the `data/prop848` directory, you can
compute the tally and compare it to the prop `FinalTallyResult` field.

```
$ go run . tally data/prop848

173,165 votes
180 validators
1,061,423 delegations for 765,656 delegators
Computed total voting power 177,825,601,877,018
Yes percent: 0.517062127500689774
--- TALLY RESULT ---
+-----------+------------+------------+------------+------------+-------------+
| | YES | NO | NOWITHVETO | ABSTAIN | TOTAL |
+-----------+------------+------------+------------+------------+-------------+
| computed | 73,165,203 | 56,667,011 | 11,669,549 | 36,323,836 | 177,825,601 |
| from prop | 73,165,203 | 56,667,011 | 11,669,549 | 36,323,836 | 177,825,601 |
| diff | 0 | 0 | 0 | 0 | 0 |
+-----------+------------+------------+------------+------------+-------------+
```

which shows that the tally calculated from these files is exactly the same as
the tally from the prop stored in the blockchain data.

## List of accounts

Then, to build the genesis, we need the list of accounts with their liquid and
staked balances, their delegations and their votes. This is doable thanks to
the `accounts` command:

```
$ go run . accounts data/prop848
```

This command creates a [`accounts.json`][5] file, which can be used to build
the `bank` module genesis, once the account balances are updated according to
their vote on prop 848.

[1]: https://atomone.fra1.digitaloceanspaces.com/cosmoshub-4/prop848/votes.json
[2]: https://atomone.fra1.digitaloceanspaces.com/cosmoshub-4/prop848/delegations.json
[3]: https://atomone.fra1.digitaloceanspaces.com/cosmoshub-4/prop848/active_validators.json
[4]: https://atomone.fra1.digitaloceanspaces.com/cosmoshub-4/prop848/prop.json
[5]: https://atomone.fra1.digitaloceanspaces.com/cosmoshub-4/prop848/accounts.json
[6]: https://atomone.fra1.digitaloceanspaces.com/cosmoshub-4/prop848/auth_genesis.json
85 changes: 85 additions & 0 deletions snapshots/analysis/accounts.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package main

import (
sdk "github.com/cosmos/cosmos-sdk/types"
govtypes "github.com/cosmos/cosmos-sdk/x/gov/types"
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
)

type Account struct {
Address string
Type string
LiquidAmount sdk.Dec
StakedAmount sdk.Dec
Vote govtypes.WeightedVoteOptions
Delegations []Delegation
}

type Delegation struct {
Amount sdk.Dec
ValidatorAddress string
Vote govtypes.WeightedVoteOptions
}

// getAccounts returns the list of all account with their vote and
// power, from direct or indirect votes.
func getAccounts(
delegsByAddr map[string][]stakingtypes.Delegation,
votesByAddr map[string]govtypes.WeightedVoteOptions,
valsByAddr map[string]govtypes.ValidatorGovInfo,
balancesByAddr map[string]sdk.Coin,
accountTypesPerAddr map[string]string,
) []Account {
accountsByAddr := make(map[string]Account, len(delegsByAddr))
// Feed delegations
for addr, delegs := range delegsByAddr {
account := Account{
Address: addr,
Type: accountTypesPerAddr[addr],
LiquidAmount: sdk.ZeroDec(),
StakedAmount: sdk.ZeroDec(),
Vote: votesByAddr[addr],
}
for _, deleg := range delegs {
// Find validator
val, ok := valsByAddr[deleg.ValidatorAddress]
if !ok {
// Validator isn't in active set or jailed, ignore
continue
}

// Compute delegation voting power
delegVotingPower := deleg.GetShares().MulInt(val.BondedTokens).Quo(val.DelegatorShares)
account.StakedAmount = account.StakedAmount.Add(delegVotingPower)

// Populate delegations with validator votes
account.Delegations = append(account.Delegations, Delegation{
ValidatorAddress: val.Address.String(),
Amount: delegVotingPower,
Vote: val.Vote,
})
}
accountsByAddr[addr] = account
}
// Feed balances
for addr, balance := range balancesByAddr {
acc, ok := accountsByAddr[addr]
if ok {
acc.LiquidAmount = balance.Amount.ToDec()
accountsByAddr[addr] = acc
} else {
accountsByAddr[addr] = Account{
Address: addr,
Type: accountTypesPerAddr[addr],
LiquidAmount: balance.Amount.ToDec(),
StakedAmount: sdk.ZeroDec(),
}
}
}
// Map to slice
var accounts []Account
for _, a := range accountsByAddr {
accounts = append(accounts, a)
}
return accounts
}
Loading