Skip to content

Commit

Permalink
feat: add vesting account handling (#232)
Browse files Browse the repository at this point in the history
<!-- < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < ☺
v                               ✰  Thanks for creating a PR! ✰    
v    Before smashing the submit button please review the checkboxes.
v    If a checkbox is n/a - please still include it but + a little note why
☺ > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > >  -->

## Description
This PR replaces #158 
close #138

## Checklist
- [x] Targeted PR against correct branch.
- [x] Linked to Github issue with discussion and accepted design OR link to spec that describes this work.
- [x] Wrote unit tests.  
- [x] Re-reviewed `Files changed` in the Github PR explorer.
  • Loading branch information
huichiaotsou authored Oct 20, 2021
1 parent b81e781 commit f36063f
Show file tree
Hide file tree
Showing 5 changed files with 160 additions and 8 deletions.
86 changes: 86 additions & 0 deletions database/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,14 @@ package database

import (
"fmt"
"time"

"github.com/cosmos/cosmos-sdk/x/auth/vesting/exported"
vestingtypes "github.com/cosmos/cosmos-sdk/x/auth/vesting/types"
dbtypes "github.com/forbole/bdjuno/v2/database/types"
dbutils "github.com/forbole/bdjuno/v2/database/utils"
"github.com/gogo/protobuf/proto"
"github.com/lib/pq"

"github.com/forbole/bdjuno/v2/types"
)
Expand Down Expand Up @@ -52,6 +58,86 @@ func (db *Db) saveAccounts(paramsNumber int, accounts []types.Account) error {
return nil
}

// SaveVestingAccounts saves the given vesting accounts inside the database
func (db *Db) SaveVestingAccounts(vestingAccounts []exported.VestingAccount) error {
if len(vestingAccounts) == 0 {
return nil
}

for _, account := range vestingAccounts {
switch vestingAccount := account.(type) {
case *vestingtypes.ContinuousVestingAccount, *vestingtypes.DelayedVestingAccount:
_, err := db.storeVestingAccount(account)
if err != nil {
return err
}

case *vestingtypes.PeriodicVestingAccount:
vestingAccountRowID, err := db.storeVestingAccount(account)
if err != nil {
return err
}
err = db.storeVestingPeriods(vestingAccountRowID, vestingAccount.VestingPeriods)
if err != nil {
return err
}
}
}

return nil
}

func (db *Db) storeVestingAccount(account exported.VestingAccount) (int, error) {
stmt := `
INSERT INTO vesting_account (type, address, original_vesting, end_time, start_time)
VALUES ($1, $2, $3, $4, $5)
ON CONFLICT (address) DO UPDATE
SET original_vesting = excluded.original_vesting,
end_time = excluded.end_time,
start_time = excluded.start_time
RETURNING id `

var vestingAccountRowID int
err := db.Sql.QueryRow(stmt,
proto.MessageName(account),
account.GetAddress().String(),
pq.Array(dbtypes.NewDbCoins(account.GetOriginalVesting())),
time.Unix(account.GetEndTime(), 0),
time.Unix(account.GetStartTime(), 0),
).Scan(&vestingAccountRowID)

if err != nil {
return vestingAccountRowID, fmt.Errorf("error while saving Vesting Account of type %v: %s", proto.MessageName(account), err)
}

return vestingAccountRowID, nil
}

// storeVestingPeriods handles storing the vesting periods of PeriodicVestingAccount type
func (db *Db) storeVestingPeriods(id int, vestingPeriods []vestingtypes.Period) error {
stmt := `
INSERT INTO vesting_period (vesting_account_id, period_order, length, amount)
VALUES `

var params []interface{}
for i, period := range vestingPeriods {
ai := i * 4
stmt += fmt.Sprintf("($%d,$%d,$%d,$%d),", ai+1, ai+2, ai+3, ai+4)

order := i
amount := pq.Array(dbtypes.NewDbCoins(period.Amount))
params = append(params, id, order, period.Length, amount)
}
stmt = stmt[:len(stmt)-1]

_, err := db.Sql.Exec(stmt, params...)
if err != nil {
return fmt.Errorf("error while saving vesting periods: %s", err)
}

return nil
}

// GetAccounts returns all the accounts that are currently stored inside the database.
func (db *Db) GetAccounts() ([]string, error) {
var rows []string
Expand Down
30 changes: 30 additions & 0 deletions database/schema/01-auth.sql
Original file line number Diff line number Diff line change
@@ -1,4 +1,34 @@
CREATE TABLE account
(
address TEXT NOT NULL PRIMARY KEY
);

/* ---- Moved from bank.sql for vesting account usage ---- */
CREATE TYPE COIN AS
(
denom TEXT,
amount TEXT
);

/* ---- AUTH/ VESTING ACCOUNT ---- */
CREATE TABLE vesting_account
(
id SERIAL PRIMARY KEY NOT NULL,
type TEXT NOT NULL,
address TEXT NOT NULL REFERENCES account (address),
original_vesting COIN[] NOT NULL DEFAULT '{}',
end_time TIMESTAMP WITHOUT TIME ZONE NOT NULL,
start_time TIMESTAMP WITHOUT TIME ZONE
);
/* ---- start_time can be empty on DelayedVestingAccount ---- */

CREATE UNIQUE INDEX vesting_account_address_idx ON vesting_account (address);


CREATE TABLE vesting_period
(
vesting_account_id BIGINT NOT NULL REFERENCES vesting_account (id),
period_order BIGINT NOT NULL,
length BIGINT NOT NULL,
amount COIN[] NOT NULL DEFAULT '{}'
);
6 changes: 0 additions & 6 deletions database/schema/02-bank.sql
Original file line number Diff line number Diff line change
@@ -1,9 +1,3 @@
CREATE TYPE COIN AS
(
denom TEXT,
amount TEXT
);

/* ---- SUPPLY ---- */

CREATE TABLE supply
Expand Down
35 changes: 35 additions & 0 deletions modules/auth/auth_vesting_accounts.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package auth

import (
"encoding/json"

"github.com/cosmos/cosmos-sdk/codec"
authttypes "github.com/cosmos/cosmos-sdk/x/auth/types"
"github.com/cosmos/cosmos-sdk/x/auth/vesting/exported"
)

// GetGenesisVestingAccounts parses the given appState and returns the genesis vesting accounts
func GetGenesisVestingAccounts(appState map[string]json.RawMessage, cdc codec.Marshaler) ([]exported.VestingAccount, error) {
var authState authttypes.GenesisState
if err := cdc.UnmarshalJSON(appState[authttypes.ModuleName], &authState); err != nil {
return nil, err
}

// Build vestingAccounts Array
vestingAccounts := []exported.VestingAccount{}
for _, account := range authState.Accounts {
var accountI authttypes.AccountI
err := cdc.UnpackAny(account, &accountI)
if err != nil {
return nil, err
}

vestingAccount, ok := accountI.(exported.VestingAccount)
if !ok {
continue
}
vestingAccounts = append(vestingAccounts, vestingAccount)
}

return vestingAccounts, nil
}
11 changes: 9 additions & 2 deletions modules/auth/handle_genesis.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,23 @@ import (
// HandleGenesis implements modules.GenesisModule
func (m *Module) HandleGenesis(_ *tmtypes.GenesisDoc, appState map[string]json.RawMessage) error {
log.Debug().Str("module", "auth").Msg("parsing genesis")

accounts, err := GetGenesisAccounts(appState, m.cdc)
if err != nil {
return fmt.Errorf("error while getting genesis accounts: %s", err)
}

err = m.db.SaveAccounts(accounts)
if err != nil {
return fmt.Errorf("error while storing genesis accounts: %s", err)
}

vestingAccounts, err := GetGenesisVestingAccounts(appState, m.cdc)
if err != nil {
return fmt.Errorf("error while getting genesis vesting accounts: %s", err)
}
err = m.db.SaveVestingAccounts(vestingAccounts)
if err != nil {
return fmt.Errorf("error while storing genesis vesting accounts: %s", err)
}

return nil
}

0 comments on commit f36063f

Please sign in to comment.