Skip to content

Commit

Permalink
feat: remove block height foreign key from propoval vote and deposit,…
Browse files Browse the repository at this point in the history
… add timestamp column (forbole#489)

block height foreign key on `proposal_vote` and `proposal_deposit` tables is getting `parse genesis-file` errors because the block table could be empty at the time of parsing genesis. It results in the error like `pq: insert or update on table "proposal_deposit" violates foreign key constraint "proposal_deposit_height_fkey"`

Add `timestamp` column so frontend can access this data.

---

*All items are required. Please add a note to the item if the item is not applicable and
please add links to any relevant follow up issues.*

I have...

- [x] included the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title
- [ ] added `!` to the type prefix if API or client breaking change
- [x] targeted the correct branch
- [ ] provided a link to the relevant issue or specification
- [x] added a changelog entry to `CHANGELOG.md`
- [ ] included comments for [documenting Go code](https://blog.golang.org/godoc)
- [ ] updated the relevant documentation or specification
- [x] reviewed "Files changed" and left comments if necessary
- [x] confirmed all CI checks have passed

*All items are required. Please add a note if the item is not applicable and please add
your handle next to the items reviewed if you only reviewed selected items.*

I have...

- [ ] confirmed the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title
- [ ] confirmed `!` in the type prefix if API or client breaking change
- [ ] confirmed all author checklist items have been addressed
- [ ] reviewed API design and naming
- [ ] reviewed documentation is accurate
- [ ] reviewed tests and test coverage
- [ ] manually tested (if applicable)
  • Loading branch information
huichiaotsou authored and ankurdotb committed Nov 1, 2022
1 parent 99783a0 commit 00631f7
Show file tree
Hide file tree
Showing 9 changed files with 97 additions and 44 deletions.
15 changes: 9 additions & 6 deletions database/gov.go
Original file line number Diff line number Diff line change
Expand Up @@ -247,22 +247,24 @@ func (db *Db) SaveDeposits(deposits []types.Deposit) error {
return nil
}

query := `INSERT INTO proposal_deposit (proposal_id, depositor_address, amount, height) VALUES `
query := `INSERT INTO proposal_deposit (proposal_id, depositor_address, amount, timestamp, height) VALUES `
var param []interface{}

for i, deposit := range deposits {
vi := i * 4
query += fmt.Sprintf("($%d,$%d,$%d,$%d),", vi+1, vi+2, vi+3, vi+4)
vi := i * 5
query += fmt.Sprintf("($%d,$%d,$%d,$%d,$%d),", vi+1, vi+2, vi+3, vi+4, vi+5)
param = append(param, deposit.ProposalID,
deposit.Depositor,
pq.Array(dbtypes.NewDbCoins(deposit.Amount)),
deposit.Timestamp,
deposit.Height,
)
}
query = query[:len(query)-1] // Remove trailing ","
query += `
ON CONFLICT ON CONSTRAINT unique_deposit DO UPDATE
SET amount = excluded.amount,
timestamp = excluded.timestamp,
height = excluded.height
WHERE proposal_deposit.height <= excluded.height`
_, err := db.Sql.Exec(query, param...)
Expand All @@ -278,10 +280,11 @@ WHERE proposal_deposit.height <= excluded.height`
// SaveVote allows to save for the given height and the message vote
func (db *Db) SaveVote(vote types.Vote) error {
query := `
INSERT INTO proposal_vote (proposal_id, voter_address, option, height)
VALUES ($1, $2, $3, $4)
INSERT INTO proposal_vote (proposal_id, voter_address, option, timestamp, height)
VALUES ($1, $2, $3, $4, $5)
ON CONFLICT ON CONSTRAINT unique_vote DO UPDATE
SET option = excluded.option,
timestamp = excluded.timestamp,
height = excluded.height
WHERE proposal_vote.height <= excluded.height`

Expand All @@ -291,7 +294,7 @@ WHERE proposal_vote.height <= excluded.height`
return fmt.Errorf("error while storing voter account: %s", err)
}

_, err = db.Sql.Exec(query, vote.ProposalID, vote.Voter, vote.Option.String(), vote.Height)
_, err = db.Sql.Exec(query, vote.ProposalID, vote.Voter, vote.Option.String(), vote.Timestamp, vote.Height)
if err != nil {
return fmt.Errorf("error while storing vote: %s", err)
}
Expand Down
44 changes: 25 additions & 19 deletions database/gov_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -348,19 +348,23 @@ func (suite *DbTestSuite) TestBigDipperDb_SaveDeposits() {
depositor3 := suite.getAccount("cosmos1gyds87lg3m52hex9yqta2mtwzw89pfukx3jl7g")
amount3 := sdk.NewCoins(sdk.NewCoin("desmos", sdk.NewInt(50000)))

timestamp1 := time.Date(2020, 1, 1, 15, 00, 00, 000, time.UTC)
timestamp2 := time.Date(2020, 1, 1, 16, 00, 00, 000, time.UTC)
timestamp3 := time.Date(2020, 1, 1, 17, 00, 00, 000, time.UTC)

deposit := []types.Deposit{
types.NewDeposit(proposal.ProposalID, depositor.String(), amount, 10),
types.NewDeposit(proposal.ProposalID, depositor2.String(), amount2, 10),
types.NewDeposit(proposal.ProposalID, depositor3.String(), amount3, 10),
types.NewDeposit(proposal.ProposalID, depositor.String(), amount, timestamp1, 10),
types.NewDeposit(proposal.ProposalID, depositor2.String(), amount2, timestamp2, 10),
types.NewDeposit(proposal.ProposalID, depositor3.String(), amount3, timestamp3, 10),
}

err := suite.database.SaveDeposits(deposit)
suite.Require().NoError(err)

expected := []dbtypes.DepositRow{
dbtypes.NewDepositRow(1, depositor.String(), dbtypes.NewDbCoins(amount), 10),
dbtypes.NewDepositRow(1, depositor2.String(), dbtypes.NewDbCoins(amount2), 10),
dbtypes.NewDepositRow(1, depositor3.String(), dbtypes.NewDbCoins(amount3), 10),
dbtypes.NewDepositRow(1, depositor.String(), dbtypes.NewDbCoins(amount), timestamp1, 10),
dbtypes.NewDepositRow(1, depositor2.String(), dbtypes.NewDbCoins(amount2), timestamp2, 10),
dbtypes.NewDepositRow(1, depositor3.String(), dbtypes.NewDbCoins(amount3), timestamp3, 10),
}
var result []dbtypes.DepositRow
err = suite.database.Sqlx.Select(&result, `SELECT * FROM proposal_deposit`)
Expand All @@ -377,19 +381,19 @@ func (suite *DbTestSuite) TestBigDipperDb_SaveDeposits() {
amount3 = sdk.NewCoins(sdk.NewCoin("desmos", sdk.NewInt(30)))

deposit = []types.Deposit{
types.NewDeposit(proposal.ProposalID, depositor.String(), amount, 9),
types.NewDeposit(proposal.ProposalID, depositor2.String(), amount2, 10),
types.NewDeposit(proposal.ProposalID, depositor3.String(), amount3, 11),
types.NewDeposit(proposal.ProposalID, depositor.String(), amount, timestamp1, 9),
types.NewDeposit(proposal.ProposalID, depositor2.String(), amount2, timestamp2, 10),
types.NewDeposit(proposal.ProposalID, depositor3.String(), amount3, timestamp3, 11),
}

err = suite.database.SaveDeposits(deposit)
suite.Require().NoError(err)

expected = []dbtypes.DepositRow{
dbtypes.NewDepositRow(1, depositor.String(), dbtypes.NewDbCoins(
sdk.NewCoins(sdk.NewCoin("desmos", sdk.NewInt(10000)))), 10),
dbtypes.NewDepositRow(1, depositor2.String(), dbtypes.NewDbCoins(amount2), 10),
dbtypes.NewDepositRow(1, depositor3.String(), dbtypes.NewDbCoins(amount3), 11),
sdk.NewCoins(sdk.NewCoin("desmos", sdk.NewInt(10000)))), timestamp1, 10),
dbtypes.NewDepositRow(1, depositor2.String(), dbtypes.NewDbCoins(amount2), timestamp2, 10),
dbtypes.NewDepositRow(1, depositor3.String(), dbtypes.NewDbCoins(amount3), timestamp3, 11),
}

result = []dbtypes.DepositRow{}
Expand All @@ -410,11 +414,13 @@ func (suite *DbTestSuite) TestBigDipperDb_SaveVote() {
proposal := suite.getProposalRow(1)
voter := suite.getAccount("cosmos1z4hfrxvlgl4s8u4n5ngjcw8kdqrcv43599amxs")

vote := types.NewVote(1, voter.String(), govtypes.OptionYes, 1)
timestamp := time.Date(2020, 1, 1, 15, 00, 00, 000, time.UTC)

vote := types.NewVote(1, voter.String(), govtypes.OptionYes, timestamp, 1)
err := suite.database.SaveVote(vote)
suite.Require().NoError(err)

expected := dbtypes.NewVoteRow(int64(proposal.ProposalID), voter.String(), govtypes.OptionYes.String(), 1)
expected := dbtypes.NewVoteRow(int64(proposal.ProposalID), voter.String(), govtypes.OptionYes.String(), timestamp, 1)

var result []dbtypes.VoteRow
err = suite.database.Sqlx.Select(&result, `SELECT * FROM proposal_vote`)
Expand All @@ -423,7 +429,7 @@ func (suite *DbTestSuite) TestBigDipperDb_SaveVote() {
suite.Require().True(expected.Equals(result[0]))

// Update with lower height should not change option
vote = types.NewVote(1, voter.String(), govtypes.OptionNo, 0)
vote = types.NewVote(1, voter.String(), govtypes.OptionNo, timestamp, 0)
err = suite.database.SaveVote(vote)
suite.Require().NoError(err)

Expand All @@ -434,11 +440,11 @@ func (suite *DbTestSuite) TestBigDipperDb_SaveVote() {
suite.Require().True(expected.Equals(result[0]))

// Update with same height should change option
vote = types.NewVote(1, voter.String(), govtypes.OptionAbstain, 1)
vote = types.NewVote(1, voter.String(), govtypes.OptionAbstain, timestamp, 1)
err = suite.database.SaveVote(vote)
suite.Require().NoError(err)

expected = dbtypes.NewVoteRow(int64(proposal.ProposalID), voter.String(), govtypes.OptionAbstain.String(), 1)
expected = dbtypes.NewVoteRow(int64(proposal.ProposalID), voter.String(), govtypes.OptionAbstain.String(), timestamp, 1)

result = []dbtypes.VoteRow{}
err = suite.database.Sqlx.Select(&result, `SELECT * FROM proposal_vote`)
Expand All @@ -447,11 +453,11 @@ func (suite *DbTestSuite) TestBigDipperDb_SaveVote() {
suite.Require().True(expected.Equals(result[0]))

// Update with higher height should change option
vote = types.NewVote(1, voter.String(), govtypes.OptionNoWithVeto, 2)
vote = types.NewVote(1, voter.String(), govtypes.OptionNoWithVeto, timestamp, 2)
err = suite.database.SaveVote(vote)
suite.Require().NoError(err)

expected = dbtypes.NewVoteRow(int64(proposal.ProposalID), voter.String(), govtypes.OptionNoWithVeto.String(), 2)
expected = dbtypes.NewVoteRow(int64(proposal.ProposalID), voter.String(), govtypes.OptionNoWithVeto.String(), timestamp, 2)

result = []dbtypes.VoteRow{}
err = suite.database.Sqlx.Select(&result, `SELECT * FROM proposal_vote`)
Expand Down
6 changes: 4 additions & 2 deletions database/schema/08-gov.sql
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ CREATE TABLE proposal_deposit
proposal_id INTEGER NOT NULL REFERENCES proposal (id),
depositor_address TEXT REFERENCES account (address),
amount COIN[],
height BIGINT NOT NULL REFERENCES block (height),
timestamp TIMESTAMP,
height BIGINT NOT NULL,
CONSTRAINT unique_deposit UNIQUE (proposal_id, depositor_address)
);
CREATE INDEX proposal_deposit_proposal_id_index ON proposal_deposit (proposal_id);
Expand All @@ -42,7 +43,8 @@ CREATE TABLE proposal_vote
proposal_id INTEGER NOT NULL REFERENCES proposal (id),
voter_address TEXT NOT NULL REFERENCES account (address),
option TEXT NOT NULL,
height BIGINT NOT NULL REFERENCES block (height),
timestamp TIMESTAMP,
height BIGINT NOT NULL,
CONSTRAINT unique_vote UNIQUE (proposal_id, voter_address)
);
CREATE INDEX proposal_vote_proposal_id_index ON proposal_vote (proposal_id);
Expand Down
24 changes: 16 additions & 8 deletions database/types/gov.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,23 +118,26 @@ func (w TallyResultRow) Equals(v TallyResultRow) bool {

// VoteRow represents a single row inside the vote table
type VoteRow struct {
Voter string `db:"voter_address"`
Option string `db:"option"`
ProposalID int64 `db:"proposal_id"`
Height int64 `db:"height"`
ProposalID int64 `db:"proposal_id"`
Voter string `db:"voter_address"`
Option string `db:"option"`
Timestamp time.Time `db:"timestamp"`
Height int64 `db:"height"`
}

// NewVoteRow allows to easily create a new VoteRow
func NewVoteRow(
proposalID int64,
voter string,
option string,
timestamp time.Time,
height int64,
) VoteRow {
return VoteRow{
ProposalID: proposalID,
Voter: voter,
Option: option,
Timestamp: timestamp,
Height: height,
}
}
Expand All @@ -144,28 +147,32 @@ func (w VoteRow) Equals(v VoteRow) bool {
return w.ProposalID == v.ProposalID &&
w.Voter == v.Voter &&
w.Option == v.Option &&
w.Timestamp.Equal(v.Timestamp) &&
w.Height == v.Height
}

// DepositRow represents a single row inside the deposit table
type DepositRow struct {
Depositor string `db:"depositor_address"`
Amount DbCoins `db:"amount"`
ProposalID int64 `db:"proposal_id"`
Height int64 `db:"height"`
ProposalID int64 `db:"proposal_id"`
Depositor string `db:"depositor_address"`
Amount DbCoins `db:"amount"`
Timestamp time.Time `db:"timestamp"`
Height int64 `db:"height"`
}

// NewDepositRow allows to easily create a new NewDepositRow
func NewDepositRow(
proposalID int64,
depositor string,
amount DbCoins,
timestamp time.Time,
height int64,
) DepositRow {
return DepositRow{
ProposalID: proposalID,
Depositor: depositor,
Amount: amount,
Timestamp: timestamp,
Height: height,
}
}
Expand All @@ -175,6 +182,7 @@ func (w DepositRow) Equals(v DepositRow) bool {
return w.ProposalID == v.ProposalID &&
w.Depositor == v.Depositor &&
w.Amount.Equal(&v.Amount) &&
w.Timestamp.Equal(v.Timestamp) &&
w.Height == v.Height
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ select_permissions:
- proposal_id
- depositor_address
- amount
- timestamp
- height
filter: {}
limit: 100
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,13 @@ object_relationships:
foreign_key_constraint_on: voter_address
- name: block
using:
foreign_key_constraint_on: height
manual_configuration:
column_mapping:
height: height
insertion_order: null
remote_table:
name: block
schema: public
- name: proposal
using:
foreign_key_constraint_on: proposal_id
Expand All @@ -18,6 +24,7 @@ select_permissions:
- proposal_id
- voter_address
- option
- timestamp
- height
filter: {}
limit: 100
Expand Down
9 changes: 5 additions & 4 deletions modules/gov/handle_genesis.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ func (m *Module) HandleGenesis(doc *tmtypes.GenesisDoc, appState map[string]json
}

// Save the proposals
err = m.saveProposals(genState.Proposals, doc.InitialHeight)
err = m.saveProposals(genState.Proposals, doc)
if err != nil {
return fmt.Errorf("error while storing genesis governance proposals: %s", err)
}
Expand All @@ -44,7 +44,7 @@ func (m *Module) HandleGenesis(doc *tmtypes.GenesisDoc, appState map[string]json
}

// saveProposals save proposals from genesis file
func (m *Module) saveProposals(slice govtypes.Proposals, genHeight int64) error {
func (m *Module) saveProposals(slice govtypes.Proposals, genDoc *tmtypes.GenesisDoc) error {
proposals := make([]types.Proposal, len(slice))
tallyResults := make([]types.TallyResult, len(slice))
deposits := make([]types.Deposit, len(slice))
Expand All @@ -70,14 +70,15 @@ func (m *Module) saveProposals(slice govtypes.Proposals, genHeight int64) error
proposal.FinalTallyResult.Abstain.String(),
proposal.FinalTallyResult.No.String(),
proposal.FinalTallyResult.NoWithVeto.String(),
genHeight,
genDoc.InitialHeight,
)

deposits[index] = types.NewDeposit(
proposal.ProposalId,
"",
proposal.TotalDeposit,
genHeight,
genDoc.GenesisTime,
genDoc.InitialHeight,
)
}

Expand Down
24 changes: 21 additions & 3 deletions modules/gov/handle_msg.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package gov

import (
"fmt"
"time"

"strconv"

"github.com/forbole/bdjuno/v3/types"
Expand Down Expand Up @@ -73,8 +75,13 @@ func (m *Module) handleMsgSubmitProposal(tx *juno.Tx, index int, msg *govtypes.M
return err
}

txTimestamp, err := time.Parse(time.RFC3339, tx.Timestamp)
if err != nil {
return fmt.Errorf("error while parsing time: %s", err)
}

// Store the deposit
deposit := types.NewDeposit(proposal.ProposalId, msg.Proposer, msg.InitialDeposit, tx.Height)
deposit := types.NewDeposit(proposal.ProposalId, msg.Proposer, msg.InitialDeposit, txTimestamp, tx.Height)
return m.db.SaveDeposits([]types.Deposit{deposit})
}

Expand All @@ -85,13 +92,24 @@ func (m *Module) handleMsgDeposit(tx *juno.Tx, msg *govtypes.MsgDeposit) error {
return fmt.Errorf("error while getting proposal deposit: %s", err)
}

txTimestamp, err := time.Parse(time.RFC3339, tx.Timestamp)
if err != nil {
return fmt.Errorf("error while parsing time: %s", err)
}

return m.db.SaveDeposits([]types.Deposit{
types.NewDeposit(msg.ProposalId, msg.Depositor, deposit.Amount, tx.Height),
types.NewDeposit(msg.ProposalId, msg.Depositor, deposit.Amount, txTimestamp, tx.Height),
})
}

// handleMsgVote allows to properly handle a handleMsgVote
func (m *Module) handleMsgVote(tx *juno.Tx, msg *govtypes.MsgVote) error {
vote := types.NewVote(msg.ProposalId, msg.Voter, msg.Option, tx.Height)
txTimestamp, err := time.Parse(time.RFC3339, tx.Timestamp)
if err != nil {
return fmt.Errorf("error while parsing time: %s", err)
}

vote := types.NewVote(msg.ProposalId, msg.Voter, msg.Option, txTimestamp, tx.Height)

return m.db.SaveVote(vote)
}
Loading

0 comments on commit 00631f7

Please sign in to comment.