From f20672dcde2364131e1a9af70c3361122f4a0d15 Mon Sep 17 00:00:00 2001 From: mossid Date: Thu, 1 Nov 2018 17:19:26 +0100 Subject: [PATCH 1/6] add ProposalBase --- x/gov/keeper.go | 16 +++++---- x/gov/proposals.go | 89 +++++++++++++++++++++++++++++----------------- 2 files changed, 65 insertions(+), 40 deletions(-) diff --git a/x/gov/keeper.go b/x/gov/keeper.go index d061f1206232..f0fa3dc179a1 100644 --- a/x/gov/keeper.go +++ b/x/gov/keeper.go @@ -83,14 +83,16 @@ func (keeper Keeper) NewTextProposal(ctx sdk.Context, title string, description return nil } var proposal Proposal = &TextProposal{ - ProposalID: proposalID, - Title: title, - Description: description, + ProposalBase: &ProposalBase{ + ProposalID: proposalID, + Title: title, + Description: description, + Status: StatusDepositPeriod, + TallyResult: EmptyTallyResult(), + TotalDeposit: sdk.Coins{}, + SubmitTime: ctx.BlockHeader().Time, + }, ProposalType: proposalType, - Status: StatusDepositPeriod, - TallyResult: EmptyTallyResult(), - TotalDeposit: sdk.Coins{}, - SubmitTime: ctx.BlockHeader().Time, } keeper.SetProposal(ctx, proposal) keeper.InactiveProposalQueuePush(ctx, proposal) diff --git a/x/gov/proposals.go b/x/gov/proposals.go index 9d1ba860adae..26b41bd8abc9 100644 --- a/x/gov/proposals.go +++ b/x/gov/proposals.go @@ -13,8 +13,9 @@ import ( //----------------------------------------------------------- // Proposal interface type Proposal interface { + GetProposalType() ProposalKind + GetProposalID() int64 - SetProposalID(int64) GetTitle() string SetTitle(string) @@ -22,9 +23,6 @@ type Proposal interface { GetDescription() string SetDescription(string) - GetProposalType() ProposalKind - SetProposalType(ProposalKind) - GetStatus() ProposalStatus SetStatus(ProposalStatus) @@ -39,6 +37,8 @@ type Proposal interface { GetVotingStartTime() time.Time SetVotingStartTime(time.Time) + + Enact(ctx sdk.Context, k Keeper) error } // checks if two proposals are equal @@ -57,13 +57,13 @@ func ProposalEqual(proposalA Proposal, proposalB Proposal) bool { return false } -//----------------------------------------------------------- -// Text Proposals -type TextProposal struct { - ProposalID int64 `json:"proposal_id"` // ID of the proposal - Title string `json:"title"` // Title of the proposal - Description string `json:"description"` // Description of the proposal - ProposalType ProposalKind `json:"proposal_type"` // Type of proposal. Initial set {PlainTextProposal, SoftwareUpgradeProposal} +// -------------------------------------------------------- +// Base struct for proposal types +type ProposalBase struct { + Title string `json:"title"` // Title of the proposal + Description string `json:"description"` // Description of the proposal + + ProposalID int64 `json:"proposal_id"` // ID of the proposal Status ProposalStatus `json:"proposal_status"` // Status of the Proposal {Pending, Active, Passed, Rejected} TallyResult TallyResult `json:"tally_result"` // Result of Tallys @@ -74,31 +74,54 @@ type TextProposal struct { VotingStartTime time.Time `json:"voting_start_time"` // Height of the block where MinDeposit was reached. -1 if MinDeposit is not reached } -// Implements Proposal Interface -var _ Proposal = (*TextProposal)(nil) - // nolint -func (tp TextProposal) GetProposalID() int64 { return tp.ProposalID } -func (tp *TextProposal) SetProposalID(proposalID int64) { tp.ProposalID = proposalID } -func (tp TextProposal) GetTitle() string { return tp.Title } -func (tp *TextProposal) SetTitle(title string) { tp.Title = title } -func (tp TextProposal) GetDescription() string { return tp.Description } -func (tp *TextProposal) SetDescription(description string) { tp.Description = description } -func (tp TextProposal) GetProposalType() ProposalKind { return tp.ProposalType } -func (tp *TextProposal) SetProposalType(proposalType ProposalKind) { tp.ProposalType = proposalType } -func (tp TextProposal) GetStatus() ProposalStatus { return tp.Status } -func (tp *TextProposal) SetStatus(status ProposalStatus) { tp.Status = status } -func (tp TextProposal) GetTallyResult() TallyResult { return tp.TallyResult } -func (tp *TextProposal) SetTallyResult(tallyResult TallyResult) { tp.TallyResult = tallyResult } -func (tp TextProposal) GetSubmitTime() time.Time { return tp.SubmitTime } -func (tp *TextProposal) SetSubmitTime(submitTime time.Time) { tp.SubmitTime = submitTime } -func (tp TextProposal) GetTotalDeposit() sdk.Coins { return tp.TotalDeposit } -func (tp *TextProposal) SetTotalDeposit(totalDeposit sdk.Coins) { tp.TotalDeposit = totalDeposit } -func (tp TextProposal) GetVotingStartTime() time.Time { return tp.VotingStartTime } -func (tp *TextProposal) SetVotingStartTime(votingStartTime time.Time) { - tp.VotingStartTime = votingStartTime +func (pb ProposalBase) GetTitle() string { return pb.Title } +func (pb *ProposalBase) SetTitle(title string) { pb.Title = title } +func (pb ProposalBase) GetDescription() string { return pb.Description } +func (pb *ProposalBase) SetDescription(description string) { pb.Description = description } +func (pb ProposalBase) GetProposalID() int64 { return pb.ProposalID } +func (pb ProposalBase) GetStatus() ProposalStatus { return pb.Status } +func (pb *ProposalBase) SetStatus(status ProposalStatus) { pb.Status = status } +func (pb ProposalBase) GetTallyResult() TallyResult { return pb.TallyResult } +func (pb *ProposalBase) SetTallyResult(tallyResult TallyResult) { pb.TallyResult = tallyResult } +func (pb ProposalBase) GetSubmitTime() time.Time { return pb.SubmitTime } +func (pb *ProposalBase) SetSubmitTime(submitTime time.Time) { pb.SubmitTime = submitTime } +func (pb ProposalBase) GetTotalDeposit() sdk.Coins { return pb.TotalDeposit } +func (pb *ProposalBase) SetTotalDeposit(totalDeposit sdk.Coins) { pb.TotalDeposit = totalDeposit } +func (pb ProposalBase) GetVotingStartTime() time.Time { return pb.VotingStartTime } +func (pb *ProposalBase) SetVotingStartTime(votingStartTime time.Time) { + pb.VotingStartTime = votingStartTime +} + +//---------------------------------------------------------- +// Internal proposal information +type ProposalInfo struct { +} + +func (pi *ProposalInfo) Info() *ProposalInfo { + return pi +} + +//----------------------------------------------------------- +// Text Proposals +type TextProposal struct { + *ProposalBase + + ProposalType ProposalKind `json:"proposal_type"` // Type of proposal. Initial set {PlainTextProposal, SoftwareUpgradeProposal} } +func (tp TextProposal) GetProposalType() ProposalKind { + return tp.ProposalType +} + +func (tp TextProposal) Enact(ctx sdk.Context, k Keeper) error { + // TextProposal do nothing + return nil +} + +// Implements Proposal Interface +var _ Proposal = (*TextProposal)(nil) + //----------------------------------------------------------- // ProposalQueue type ProposalQueue []int64 From 83dc7d3e3634644c770e825178dc7066c37c7fb6 Mon Sep 17 00:00:00 2001 From: mossid Date: Thu, 1 Nov 2018 17:25:59 +0100 Subject: [PATCH 2/6] add enact --- x/gov/handler.go | 9 ++++++++- x/gov/tags/tags.go | 1 + 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/x/gov/handler.go b/x/gov/handler.go index dc1dbfb10e40..3b20702286ab 100644 --- a/x/gov/handler.go +++ b/x/gov/handler.go @@ -140,7 +140,14 @@ func EndBlocker(ctx sdk.Context, keeper Keeper) (resTags sdk.Tags) { if passes { keeper.RefundDeposits(ctx, activeProposal.GetProposalID()) activeProposal.SetStatus(StatusPassed) - action = tags.ActionProposalPassed + err := activeProposal.Enact(ctx, keeper) + if err != nil { + logger.Info(fmt.Sprintf("proposal %d (%s) enacted but error: %v", + activeProposal.GetProposalID(), activeProposal.GetTitle(), err)) + action = tags.ActionProposalError + } else { + action = tags.ActionProposalPassed + } } else { keeper.DeleteDeposits(ctx, activeProposal.GetProposalID()) activeProposal.SetStatus(StatusRejected) diff --git a/x/gov/tags/tags.go b/x/gov/tags/tags.go index 2eded1901a6c..1493743b667d 100644 --- a/x/gov/tags/tags.go +++ b/x/gov/tags/tags.go @@ -11,6 +11,7 @@ var ( ActionVote = []byte("vote") ActionProposalDropped = []byte("proposal-dropped") ActionProposalPassed = []byte("proposal-passed") + ActionProposalError = []byte("proposal-error") ActionProposalRejected = []byte("proposal-rejected") Action = sdk.TagAction From 1ce0ecf918dc91d3dc16e42dfc2284d552344ba8 Mon Sep 17 00:00:00 2001 From: mossid Date: Thu, 1 Nov 2018 17:30:48 +0100 Subject: [PATCH 3/6] fix lint --- x/gov/proposals.go | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/x/gov/proposals.go b/x/gov/proposals.go index 26b41bd8abc9..ec6e6624cb9b 100644 --- a/x/gov/proposals.go +++ b/x/gov/proposals.go @@ -93,15 +93,6 @@ func (pb *ProposalBase) SetVotingStartTime(votingStartTime time.Time) { pb.VotingStartTime = votingStartTime } -//---------------------------------------------------------- -// Internal proposal information -type ProposalInfo struct { -} - -func (pi *ProposalInfo) Info() *ProposalInfo { - return pi -} - //----------------------------------------------------------- // Text Proposals type TextProposal struct { @@ -110,10 +101,12 @@ type TextProposal struct { ProposalType ProposalKind `json:"proposal_type"` // Type of proposal. Initial set {PlainTextProposal, SoftwareUpgradeProposal} } +// Implements Proposal func (tp TextProposal) GetProposalType() ProposalKind { return tp.ProposalType } +// Implements Proposal func (tp TextProposal) Enact(ctx sdk.Context, k Keeper) error { // TextProposal do nothing return nil From e23514ca69335511b1753b8818a2fc8f39f8d66e Mon Sep 17 00:00:00 2001 From: Sunny Aggarwal Date: Tue, 30 Oct 2018 17:01:41 -0700 Subject: [PATCH 4/6] switched gov proposals queue to use iterators --- PENDING.md | 1 + client/lcd/lcd_test.go | 12 ++-- types/utils.go | 8 +++ x/gov/endblocker_test.go | 141 +++++++++++++++++++++------------------ x/gov/handler.go | 72 ++++++-------------- x/gov/keeper.go | 118 +++++++++++--------------------- x/gov/keeper_keys.go | 42 +++++++++++- x/gov/keeper_test.go | 65 ++++++------------ x/gov/proposals.go | 28 ++++++-- 9 files changed, 234 insertions(+), 253 deletions(-) diff --git a/PENDING.md b/PENDING.md index 84a08a5abe26..f1c15e5fd380 100644 --- a/PENDING.md +++ b/PENDING.md @@ -39,6 +39,7 @@ IMPROVEMENTS * Gaia CLI (`gaiacli`) * Gaia + - #2637 [x/gov] Switched inactive and active proposal queues to an iterator based queue * SDK - #2573 [x/distribution] add accum invariance diff --git a/client/lcd/lcd_test.go b/client/lcd/lcd_test.go index cb25721e891d..de8f7fcd8b1e 100644 --- a/client/lcd/lcd_test.go +++ b/client/lcd/lcd_test.go @@ -629,7 +629,7 @@ func TestSubmitProposal(t *testing.T) { require.Equal(t, uint32(0), resultTx.DeliverTx.Code) var proposalID int64 - cdc.UnmarshalBinaryBare(resultTx.DeliverTx.GetData(), &proposalID) + cdc.MustUnmarshalBinary(resultTx.DeliverTx.GetData(), &proposalID) // query proposal proposal := getProposal(t, port, proposalID) @@ -651,7 +651,7 @@ func TestDeposit(t *testing.T) { require.Equal(t, uint32(0), resultTx.DeliverTx.Code) var proposalID int64 - cdc.UnmarshalBinaryBare(resultTx.DeliverTx.GetData(), &proposalID) + cdc.MustUnmarshalBinary(resultTx.DeliverTx.GetData(), &proposalID) // query proposal proposal := getProposal(t, port, proposalID) @@ -685,7 +685,7 @@ func TestVote(t *testing.T) { require.Equal(t, uint32(0), resultTx.DeliverTx.Code) var proposalID int64 - cdc.UnmarshalBinaryBare(resultTx.DeliverTx.GetData(), &proposalID) + cdc.MustUnmarshalBinary(resultTx.DeliverTx.GetData(), &proposalID) // query proposal proposal := getProposal(t, port, proposalID) @@ -733,17 +733,17 @@ func TestProposalsQuery(t *testing.T) { // Addr1 proposes (and deposits) proposals #1 and #2 resultTx := doSubmitProposal(t, port, seeds[0], names[0], passwords[0], addrs[0], 5) var proposalID1 int64 - cdc.UnmarshalBinaryBare(resultTx.DeliverTx.GetData(), &proposalID1) + cdc.MustUnmarshalBinary(resultTx.DeliverTx.GetData(), &proposalID1) tests.WaitForHeight(resultTx.Height+1, port) resultTx = doSubmitProposal(t, port, seeds[0], names[0], passwords[0], addrs[0], 5) var proposalID2 int64 - cdc.UnmarshalBinaryBare(resultTx.DeliverTx.GetData(), &proposalID2) + cdc.MustUnmarshalBinary(resultTx.DeliverTx.GetData(), &proposalID2) tests.WaitForHeight(resultTx.Height+1, port) // Addr2 proposes (and deposits) proposals #3 resultTx = doSubmitProposal(t, port, seeds[1], names[1], passwords[1], addrs[1], 5) var proposalID3 int64 - cdc.UnmarshalBinaryBare(resultTx.DeliverTx.GetData(), &proposalID3) + cdc.MustUnmarshalBinary(resultTx.DeliverTx.GetData(), &proposalID3) tests.WaitForHeight(resultTx.Height+1, port) // Addr2 deposits on proposals #2 & #3 diff --git a/types/utils.go b/types/utils.go index 10ec854728ef..db6851f80d8f 100644 --- a/types/utils.go +++ b/types/utils.go @@ -1,6 +1,7 @@ package types import ( + "encoding/binary" "encoding/json" "time" @@ -36,6 +37,13 @@ func MustSortJSON(toSortJSON []byte) []byte { return js } +// marshals int64 to a bigendian byte slice so it can be sorted +func Int64ToSortableBytes(i int64) []byte { + b := make([]byte, 8) + binary.BigEndian.PutUint64(b, uint64(i)) + return b +} + // Slight modification of the RFC3339Nano but it right pads all zeros and drops the time zone info const SortableTimeFormat = "2006-01-02T15:04:05.000000000" diff --git a/x/gov/endblocker_test.go b/x/gov/endblocker_test.go index 27eff15f6443..9f7803264273 100644 --- a/x/gov/endblocker_test.go +++ b/x/gov/endblocker_test.go @@ -16,35 +16,40 @@ func TestTickExpiredDepositPeriod(t *testing.T) { ctx := mapp.BaseApp.NewContext(false, abci.Header{}) govHandler := NewHandler(keeper) - require.Nil(t, keeper.InactiveProposalQueuePeek(ctx)) - require.False(t, shouldPopInactiveProposalQueue(ctx, keeper)) + inactiveQueue := keeper.InactiveProposalQueueIterator(ctx, ctx.BlockHeader().Time) + require.False(t, inactiveQueue.Valid()) + inactiveQueue.Close() newProposalMsg := NewMsgSubmitProposal("Test", "test", ProposalTypeText, addrs[0], sdk.Coins{sdk.NewInt64Coin("steak", 5)}) res := govHandler(ctx, newProposalMsg) require.True(t, res.IsOK()) - EndBlocker(ctx, keeper) - require.NotNil(t, keeper.InactiveProposalQueuePeek(ctx)) - require.False(t, shouldPopInactiveProposalQueue(ctx, keeper)) + inactiveQueue = keeper.InactiveProposalQueueIterator(ctx, ctx.BlockHeader().Time) + require.False(t, inactiveQueue.Valid()) + inactiveQueue.Close() newHeader := ctx.BlockHeader() newHeader.Time = ctx.BlockHeader().Time.Add(time.Duration(1) * time.Second) ctx = ctx.WithBlockHeader(newHeader) - EndBlocker(ctx, keeper) - require.NotNil(t, keeper.InactiveProposalQueuePeek(ctx)) - require.False(t, shouldPopInactiveProposalQueue(ctx, keeper)) + inactiveQueue = keeper.InactiveProposalQueueIterator(ctx, ctx.BlockHeader().Time) + require.False(t, inactiveQueue.Valid()) + inactiveQueue.Close() newHeader = ctx.BlockHeader() newHeader.Time = ctx.BlockHeader().Time.Add(keeper.GetDepositProcedure(ctx).MaxDepositPeriod) ctx = ctx.WithBlockHeader(newHeader) - require.NotNil(t, keeper.InactiveProposalQueuePeek(ctx)) - require.True(t, shouldPopInactiveProposalQueue(ctx, keeper)) + inactiveQueue = keeper.InactiveProposalQueueIterator(ctx, ctx.BlockHeader().Time) + require.True(t, inactiveQueue.Valid()) + inactiveQueue.Close() + EndBlocker(ctx, keeper) - require.Nil(t, keeper.InactiveProposalQueuePeek(ctx)) - require.False(t, shouldPopInactiveProposalQueue(ctx, keeper)) + + inactiveQueue = keeper.InactiveProposalQueueIterator(ctx, ctx.BlockHeader().Time) + require.False(t, inactiveQueue.Valid()) + inactiveQueue.Close() } func TestTickMultipleExpiredDepositPeriod(t *testing.T) { @@ -53,25 +58,26 @@ func TestTickMultipleExpiredDepositPeriod(t *testing.T) { ctx := mapp.BaseApp.NewContext(false, abci.Header{}) govHandler := NewHandler(keeper) - require.Nil(t, keeper.InactiveProposalQueuePeek(ctx)) - require.False(t, shouldPopInactiveProposalQueue(ctx, keeper)) + inactiveQueue := keeper.InactiveProposalQueueIterator(ctx, ctx.BlockHeader().Time) + require.False(t, inactiveQueue.Valid()) + inactiveQueue.Close() newProposalMsg := NewMsgSubmitProposal("Test", "test", ProposalTypeText, addrs[0], sdk.Coins{sdk.NewInt64Coin("steak", 5)}) res := govHandler(ctx, newProposalMsg) require.True(t, res.IsOK()) - EndBlocker(ctx, keeper) - require.NotNil(t, keeper.InactiveProposalQueuePeek(ctx)) - require.False(t, shouldPopInactiveProposalQueue(ctx, keeper)) + inactiveQueue = keeper.InactiveProposalQueueIterator(ctx, ctx.BlockHeader().Time) + require.False(t, inactiveQueue.Valid()) + inactiveQueue.Close() newHeader := ctx.BlockHeader() newHeader.Time = ctx.BlockHeader().Time.Add(time.Duration(2) * time.Second) ctx = ctx.WithBlockHeader(newHeader) - EndBlocker(ctx, keeper) - require.NotNil(t, keeper.InactiveProposalQueuePeek(ctx)) - require.False(t, shouldPopInactiveProposalQueue(ctx, keeper)) + inactiveQueue = keeper.InactiveProposalQueueIterator(ctx, ctx.BlockHeader().Time) + require.False(t, inactiveQueue.Valid()) + inactiveQueue.Close() newProposalMsg2 := NewMsgSubmitProposal("Test2", "test2", ProposalTypeText, addrs[1], sdk.Coins{sdk.NewInt64Coin("steak", 5)}) res = govHandler(ctx, newProposalMsg2) @@ -81,21 +87,25 @@ func TestTickMultipleExpiredDepositPeriod(t *testing.T) { newHeader.Time = ctx.BlockHeader().Time.Add(keeper.GetDepositProcedure(ctx).MaxDepositPeriod).Add(time.Duration(-1) * time.Second) ctx = ctx.WithBlockHeader(newHeader) - require.NotNil(t, keeper.InactiveProposalQueuePeek(ctx)) - require.True(t, shouldPopInactiveProposalQueue(ctx, keeper)) + inactiveQueue = keeper.InactiveProposalQueueIterator(ctx, ctx.BlockHeader().Time) + require.True(t, inactiveQueue.Valid()) + inactiveQueue.Close() EndBlocker(ctx, keeper) - require.NotNil(t, keeper.InactiveProposalQueuePeek(ctx)) - require.False(t, shouldPopInactiveProposalQueue(ctx, keeper)) + inactiveQueue = keeper.InactiveProposalQueueIterator(ctx, ctx.BlockHeader().Time) + require.False(t, inactiveQueue.Valid()) + inactiveQueue.Close() newHeader = ctx.BlockHeader() newHeader.Time = ctx.BlockHeader().Time.Add(time.Duration(5) * time.Second) ctx = ctx.WithBlockHeader(newHeader) - require.NotNil(t, keeper.InactiveProposalQueuePeek(ctx)) - require.True(t, shouldPopInactiveProposalQueue(ctx, keeper)) + inactiveQueue = keeper.InactiveProposalQueueIterator(ctx, ctx.BlockHeader().Time) + require.True(t, inactiveQueue.Valid()) + inactiveQueue.Close() EndBlocker(ctx, keeper) - require.Nil(t, keeper.InactiveProposalQueuePeek(ctx)) - require.False(t, shouldPopInactiveProposalQueue(ctx, keeper)) + inactiveQueue = keeper.InactiveProposalQueueIterator(ctx, ctx.BlockHeader().Time) + require.False(t, inactiveQueue.Valid()) + inactiveQueue.Close() } func TestTickPassedDepositPeriod(t *testing.T) { @@ -104,45 +114,39 @@ func TestTickPassedDepositPeriod(t *testing.T) { ctx := mapp.BaseApp.NewContext(false, abci.Header{}) govHandler := NewHandler(keeper) - require.Nil(t, keeper.InactiveProposalQueuePeek(ctx)) - require.False(t, shouldPopInactiveProposalQueue(ctx, keeper)) - require.Nil(t, keeper.ActiveProposalQueuePeek(ctx)) - require.False(t, shouldPopActiveProposalQueue(ctx, keeper)) + inactiveQueue := keeper.InactiveProposalQueueIterator(ctx, ctx.BlockHeader().Time) + require.False(t, inactiveQueue.Valid()) + inactiveQueue.Close() + activeQueue := keeper.ActiveProposalQueueIterator(ctx, ctx.BlockHeader().Time) + require.False(t, activeQueue.Valid()) + activeQueue.Close() newProposalMsg := NewMsgSubmitProposal("Test", "test", ProposalTypeText, addrs[0], sdk.Coins{sdk.NewInt64Coin("steak", 5)}) res := govHandler(ctx, newProposalMsg) require.True(t, res.IsOK()) var proposalID int64 - keeper.cdc.UnmarshalBinaryBare(res.Data, &proposalID) + keeper.cdc.MustUnmarshalBinary(res.Data, &proposalID) - EndBlocker(ctx, keeper) - require.NotNil(t, keeper.InactiveProposalQueuePeek(ctx)) - require.False(t, shouldPopInactiveProposalQueue(ctx, keeper)) + inactiveQueue = keeper.InactiveProposalQueueIterator(ctx, ctx.BlockHeader().Time) + require.False(t, inactiveQueue.Valid()) + inactiveQueue.Close() newHeader := ctx.BlockHeader() newHeader.Time = ctx.BlockHeader().Time.Add(time.Duration(1) * time.Second) ctx = ctx.WithBlockHeader(newHeader) - EndBlocker(ctx, keeper) - require.NotNil(t, keeper.InactiveProposalQueuePeek(ctx)) - require.False(t, shouldPopInactiveProposalQueue(ctx, keeper)) + inactiveQueue = keeper.InactiveProposalQueueIterator(ctx, ctx.BlockHeader().Time) + require.False(t, inactiveQueue.Valid()) + inactiveQueue.Close() newDepositMsg := NewMsgDeposit(addrs[1], proposalID, sdk.Coins{sdk.NewInt64Coin("steak", 5)}) res = govHandler(ctx, newDepositMsg) require.True(t, res.IsOK()) - require.NotNil(t, keeper.InactiveProposalQueuePeek(ctx)) - require.True(t, shouldPopInactiveProposalQueue(ctx, keeper)) - require.NotNil(t, keeper.ActiveProposalQueuePeek(ctx)) - - EndBlocker(ctx, keeper) - - require.Nil(t, keeper.InactiveProposalQueuePeek(ctx)) - require.False(t, shouldPopInactiveProposalQueue(ctx, keeper)) - require.NotNil(t, keeper.ActiveProposalQueuePeek(ctx)) - require.False(t, shouldPopActiveProposalQueue(ctx, keeper)) - + activeQueue = keeper.ActiveProposalQueueIterator(ctx, ctx.BlockHeader().Time) + require.False(t, activeQueue.Valid()) + activeQueue.Close() } func TestTickPassedVotingPeriod(t *testing.T) { @@ -152,17 +156,19 @@ func TestTickPassedVotingPeriod(t *testing.T) { ctx := mapp.BaseApp.NewContext(false, abci.Header{}) govHandler := NewHandler(keeper) - require.Nil(t, keeper.InactiveProposalQueuePeek(ctx)) - require.False(t, shouldPopInactiveProposalQueue(ctx, keeper)) - require.Nil(t, keeper.ActiveProposalQueuePeek(ctx)) - require.False(t, shouldPopActiveProposalQueue(ctx, keeper)) + inactiveQueue := keeper.InactiveProposalQueueIterator(ctx, ctx.BlockHeader().Time) + require.False(t, inactiveQueue.Valid()) + inactiveQueue.Close() + activeQueue := keeper.ActiveProposalQueueIterator(ctx, ctx.BlockHeader().Time) + require.False(t, activeQueue.Valid()) + activeQueue.Close() newProposalMsg := NewMsgSubmitProposal("Test", "test", ProposalTypeText, addrs[0], sdk.Coins{sdk.NewInt64Coin("steak", 5)}) res := govHandler(ctx, newProposalMsg) require.True(t, res.IsOK()) var proposalID int64 - keeper.cdc.UnmarshalBinaryBare(res.Data, &proposalID) + keeper.cdc.MustUnmarshalBinary(res.Data, &proposalID) newHeader := ctx.BlockHeader() newHeader.Time = ctx.BlockHeader().Time.Add(time.Duration(1) * time.Second) @@ -172,24 +178,27 @@ func TestTickPassedVotingPeriod(t *testing.T) { res = govHandler(ctx, newDepositMsg) require.True(t, res.IsOK()) - EndBlocker(ctx, keeper) - newHeader = ctx.BlockHeader() - newHeader.Time = ctx.BlockHeader().Time.Add(keeper.GetDepositProcedure(ctx).MaxDepositPeriod).Add(keeper.GetDepositProcedure(ctx).MaxDepositPeriod) + newHeader.Time = ctx.BlockHeader().Time.Add(keeper.GetDepositProcedure(ctx).MaxDepositPeriod).Add(keeper.GetVotingProcedure(ctx).VotingPeriod) ctx = ctx.WithBlockHeader(newHeader) - require.True(t, shouldPopActiveProposalQueue(ctx, keeper)) + inactiveQueue = keeper.InactiveProposalQueueIterator(ctx, ctx.BlockHeader().Time) + require.False(t, inactiveQueue.Valid()) + inactiveQueue.Close() + + activeQueue = keeper.ActiveProposalQueueIterator(ctx, ctx.BlockHeader().Time) + require.True(t, activeQueue.Valid()) + var activeProposalID int64 + keeper.cdc.UnmarshalBinary(activeQueue.Value(), &activeProposalID) + require.Equal(t, StatusVotingPeriod, keeper.GetProposal(ctx, activeProposalID).GetStatus()) depositsIterator := keeper.GetDeposits(ctx, proposalID) require.True(t, depositsIterator.Valid()) depositsIterator.Close() - require.Equal(t, StatusVotingPeriod, keeper.GetProposal(ctx, proposalID).GetStatus()) + activeQueue.Close() EndBlocker(ctx, keeper) - require.Nil(t, keeper.ActiveProposalQueuePeek(ctx)) - depositsIterator = keeper.GetDeposits(ctx, proposalID) - require.False(t, depositsIterator.Valid()) - depositsIterator.Close() - require.Equal(t, StatusRejected, keeper.GetProposal(ctx, proposalID).GetStatus()) - require.True(t, keeper.GetProposal(ctx, proposalID).GetTallyResult().Equals(EmptyTallyResult())) + activeQueue = keeper.ActiveProposalQueueIterator(ctx, ctx.BlockHeader().Time) + require.False(t, activeQueue.Valid()) + activeQueue.Close() } diff --git a/x/gov/handler.go b/x/gov/handler.go index dc1dbfb10e40..7c3500b7c60c 100644 --- a/x/gov/handler.go +++ b/x/gov/handler.go @@ -33,7 +33,7 @@ func handleMsgSubmitProposal(ctx sdk.Context, keeper Keeper, msg MsgSubmitPropos return err.Result() } - proposalIDBytes := keeper.cdc.MustMarshalBinaryBare(proposal.GetProposalID()) + proposalIDBytes := keeper.cdc.MustMarshalBinary(proposal.GetProposalID()) resTags := sdk.NewTags( tags.Action, tags.ActionSubmitProposal, @@ -102,40 +102,35 @@ func EndBlocker(ctx sdk.Context, keeper Keeper) (resTags sdk.Tags) { resTags = sdk.NewTags() - // Delete proposals that haven't met minDeposit - for shouldPopInactiveProposalQueue(ctx, keeper) { - inactiveProposal := keeper.InactiveProposalQueuePop(ctx) - if inactiveProposal.GetStatus() != StatusDepositPeriod { - continue - } + inactiveIterator := keeper.InactiveProposalQueueIterator(ctx, ctx.BlockHeader().Time) + for ; inactiveIterator.Valid(); inactiveIterator.Next() { + var proposalID int64 + keeper.cdc.MustUnmarshalBinary(inactiveIterator.Value(), &proposalID) + inactiveProposal := keeper.GetProposal(ctx, proposalID) + keeper.RefundDeposits(ctx, proposalID) + keeper.DeleteProposal(ctx, proposalID) - proposalIDBytes := keeper.cdc.MustMarshalBinaryBare(inactiveProposal.GetProposalID()) - keeper.DeleteProposal(ctx, inactiveProposal) resTags = resTags.AppendTag(tags.Action, tags.ActionProposalDropped) - resTags = resTags.AppendTag(tags.ProposalID, proposalIDBytes) + resTags = resTags.AppendTag(tags.ProposalID, []byte(string(proposalID))) logger.Info( - fmt.Sprintf("proposal %d (%s) didn't meet minimum deposit of %v steak (had only %v steak); deleted", + fmt.Sprintf("proposal %d (%s) didn't meet minimum deposit of %s (had only %s); deleted", inactiveProposal.GetProposalID(), inactiveProposal.GetTitle(), - keeper.GetDepositProcedure(ctx).MinDeposit.AmountOf("steak"), - inactiveProposal.GetTotalDeposit().AmountOf("steak"), + keeper.GetDepositProcedure(ctx).MinDeposit, + inactiveProposal.GetTotalDeposit(), ), ) } + inactiveIterator.Close() - // Check if earliest Active Proposal ended voting period yet - for shouldPopActiveProposalQueue(ctx, keeper) { - activeProposal := keeper.ActiveProposalQueuePop(ctx) - - proposalStartTime := activeProposal.GetVotingStartTime() - votingPeriod := keeper.GetVotingProcedure(ctx).VotingPeriod - if ctx.BlockHeader().Time.Before(proposalStartTime.Add(votingPeriod)) { - continue - } - + activeIterator := keeper.ActiveProposalQueueIterator(ctx, ctx.BlockHeader().Time) + for ; activeIterator.Valid(); activeIterator.Next() { + var proposalID int64 + keeper.cdc.MustUnmarshalBinary(activeIterator.Value(), &proposalID) + activeProposal := keeper.GetProposal(ctx, proposalID) passes, tallyResults := tally(ctx, keeper, activeProposal) - proposalIDBytes := keeper.cdc.MustMarshalBinaryBare(activeProposal.GetProposalID()) + var action []byte if passes { keeper.RefundDeposits(ctx, activeProposal.GetProposalID()) @@ -149,37 +144,14 @@ func EndBlocker(ctx sdk.Context, keeper Keeper) (resTags sdk.Tags) { activeProposal.SetTallyResult(tallyResults) keeper.SetProposal(ctx, activeProposal) + keeper.RemoveFromActiveProposalQueue(ctx, activeProposal.GetVotingEndTime(), activeProposal.GetProposalID()) + logger.Info(fmt.Sprintf("proposal %d (%s) tallied; passed: %v", activeProposal.GetProposalID(), activeProposal.GetTitle(), passes)) resTags = resTags.AppendTag(tags.Action, action) - resTags = resTags.AppendTag(tags.ProposalID, proposalIDBytes) + resTags = resTags.AppendTag(tags.ProposalID, []byte(string(proposalID))) } return resTags } -func shouldPopInactiveProposalQueue(ctx sdk.Context, keeper Keeper) bool { - depositProcedure := keeper.GetDepositProcedure(ctx) - peekProposal := keeper.InactiveProposalQueuePeek(ctx) - - if peekProposal == nil { - return false - } else if peekProposal.GetStatus() != StatusDepositPeriod { - return true - } else if !ctx.BlockHeader().Time.Before(peekProposal.GetSubmitTime().Add(depositProcedure.MaxDepositPeriod)) { - return true - } - return false -} - -func shouldPopActiveProposalQueue(ctx sdk.Context, keeper Keeper) bool { - votingProcedure := keeper.GetVotingProcedure(ctx) - peekProposal := keeper.ActiveProposalQueuePeek(ctx) - - if peekProposal == nil { - return false - } else if !ctx.BlockHeader().Time.Before(peekProposal.GetVotingStartTime().Add(votingProcedure.VotingPeriod)) { - return true - } - return false -} diff --git a/x/gov/keeper.go b/x/gov/keeper.go index d061f1206232..e0e83f3dccb8 100644 --- a/x/gov/keeper.go +++ b/x/gov/keeper.go @@ -1,6 +1,8 @@ package gov import ( + "time" + codec "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/bank" @@ -92,8 +94,12 @@ func (keeper Keeper) NewTextProposal(ctx sdk.Context, title string, description TotalDeposit: sdk.Coins{}, SubmitTime: ctx.BlockHeader().Time, } + + depositPeriod := keeper.GetDepositProcedure(ctx).MaxDepositPeriod + proposal.SetDepositEndTime(proposal.GetSubmitTime().Add(depositPeriod)) + keeper.SetProposal(ctx, proposal) - keeper.InactiveProposalQueuePush(ctx, proposal) + keeper.InsertInactiveProposalQueue(ctx, proposal.GetDepositEndTime(), proposalID) return proposal } @@ -119,9 +125,12 @@ func (keeper Keeper) SetProposal(ctx sdk.Context, proposal Proposal) { } // Implements sdk.AccountKeeper. -func (keeper Keeper) DeleteProposal(ctx sdk.Context, proposal Proposal) { +func (keeper Keeper) DeleteProposal(ctx sdk.Context, proposalID int64) { store := ctx.KVStore(keeper.storeKey) - store.Delete(KeyProposal(proposal.GetProposalID())) + proposal := keeper.GetProposal(ctx, proposalID) + keeper.RemoveFromInactiveProposalQueue(ctx, proposal.GetDepositEndTime(), proposalID) + keeper.RemoveFromActiveProposalQueue(ctx, proposal.GetVotingEndTime(), proposalID) + store.Delete(KeyProposal(proposalID)) } // Get Proposal from store by ProposalID @@ -216,9 +225,13 @@ func (keeper Keeper) peekCurrentProposalID(ctx sdk.Context) (proposalID int64, e func (keeper Keeper) activateVotingPeriod(ctx sdk.Context, proposal Proposal) { proposal.SetVotingStartTime(ctx.BlockHeader().Time) + votingPeriod := keeper.GetVotingProcedure(ctx).VotingPeriod + proposal.SetVotingEndTime(proposal.GetVotingStartTime().Add(votingPeriod)) proposal.SetStatus(StatusVotingPeriod) keeper.SetProposal(ctx, proposal) - keeper.ActiveProposalQueuePush(ctx, proposal) + + keeper.RemoveFromInactiveProposalQueue(ctx, proposal.GetDepositEndTime(), proposal.GetProposalID()) + keeper.InsertActiveProposalQueue(ctx, proposal.GetVotingEndTime(), proposal.GetProposalID()) } // ===================================================== @@ -426,93 +439,40 @@ func (keeper Keeper) DeleteDeposits(ctx sdk.Context, proposalID int64) { // ===================================================== // ProposalQueues -func (keeper Keeper) getActiveProposalQueue(ctx sdk.Context) ProposalQueue { +// Returns an iterator for all the proposals in the Active Queue that expire by endTime +func (keeper Keeper) ActiveProposalQueueIterator(ctx sdk.Context, endTime time.Time) sdk.Iterator { store := ctx.KVStore(keeper.storeKey) - bz := store.Get(KeyActiveProposalQueue) - if bz == nil { - return nil - } - - var proposalQueue ProposalQueue - keeper.cdc.MustUnmarshalBinary(bz, &proposalQueue) - - return proposalQueue + return store.Iterator(PrefixActiveProposalQueue, sdk.PrefixEndBytes(ActiveProposalQueueTimePrefix(endTime))) } -func (keeper Keeper) setActiveProposalQueue(ctx sdk.Context, proposalQueue ProposalQueue) { +// Inserts a ProposalID into the active proposal queue at endTime +func (keeper Keeper) InsertActiveProposalQueue(ctx sdk.Context, endTime time.Time, proposalID int64) { store := ctx.KVStore(keeper.storeKey) - bz := keeper.cdc.MustMarshalBinary(proposalQueue) - store.Set(KeyActiveProposalQueue, bz) -} - -// Return the Proposal at the front of the ProposalQueue -func (keeper Keeper) ActiveProposalQueuePeek(ctx sdk.Context) Proposal { - proposalQueue := keeper.getActiveProposalQueue(ctx) - if len(proposalQueue) == 0 { - return nil - } - return keeper.GetProposal(ctx, proposalQueue[0]) -} - -// Remove and return a Proposal from the front of the ProposalQueue -func (keeper Keeper) ActiveProposalQueuePop(ctx sdk.Context) Proposal { - proposalQueue := keeper.getActiveProposalQueue(ctx) - if len(proposalQueue) == 0 { - return nil - } - frontElement, proposalQueue := proposalQueue[0], proposalQueue[1:] - keeper.setActiveProposalQueue(ctx, proposalQueue) - return keeper.GetProposal(ctx, frontElement) -} - -// Add a proposalID to the back of the ProposalQueue -func (keeper Keeper) ActiveProposalQueuePush(ctx sdk.Context, proposal Proposal) { - proposalQueue := append(keeper.getActiveProposalQueue(ctx), proposal.GetProposalID()) - keeper.setActiveProposalQueue(ctx, proposalQueue) + bz := keeper.cdc.MustMarshalBinary(proposalID) + store.Set(ActiveProposalQueueProposalKey(endTime, proposalID), bz) } -func (keeper Keeper) getInactiveProposalQueue(ctx sdk.Context) ProposalQueue { +// removes a proposalID from the Active Proposal Queue +func (keeper Keeper) RemoveFromActiveProposalQueue(ctx sdk.Context, endTime time.Time, proposalID int64) { store := ctx.KVStore(keeper.storeKey) - bz := store.Get(KeyInactiveProposalQueue) - if bz == nil { - return nil - } - - var proposalQueue ProposalQueue - - keeper.cdc.MustUnmarshalBinary(bz, &proposalQueue) - - return proposalQueue + store.Set(ActiveProposalQueueProposalKey(endTime, proposalID), nil) } -func (keeper Keeper) setInactiveProposalQueue(ctx sdk.Context, proposalQueue ProposalQueue) { +// Returns an iterator for all the proposals in the Inactive Queue that expire by endTime +func (keeper Keeper) InactiveProposalQueueIterator(ctx sdk.Context, endTime time.Time) sdk.Iterator { store := ctx.KVStore(keeper.storeKey) - bz := keeper.cdc.MustMarshalBinary(proposalQueue) - store.Set(KeyInactiveProposalQueue, bz) + return store.Iterator(PrefixInactiveProposalQueue, sdk.PrefixEndBytes(InactiveProposalQueueTimePrefix(endTime))) } -// Return the Proposal at the front of the ProposalQueue -func (keeper Keeper) InactiveProposalQueuePeek(ctx sdk.Context) Proposal { - proposalQueue := keeper.getInactiveProposalQueue(ctx) - if len(proposalQueue) == 0 { - return nil - } - return keeper.GetProposal(ctx, proposalQueue[0]) -} - -// Remove and return a Proposal from the front of the ProposalQueue -func (keeper Keeper) InactiveProposalQueuePop(ctx sdk.Context) Proposal { - proposalQueue := keeper.getInactiveProposalQueue(ctx) - if len(proposalQueue) == 0 { - return nil - } - frontElement, proposalQueue := proposalQueue[0], proposalQueue[1:] - keeper.setInactiveProposalQueue(ctx, proposalQueue) - return keeper.GetProposal(ctx, frontElement) +// Inserts a ProposalID into the inactive proposal queue at endTime +func (keeper Keeper) InsertInactiveProposalQueue(ctx sdk.Context, endTime time.Time, proposalID int64) { + store := ctx.KVStore(keeper.storeKey) + bz := keeper.cdc.MustMarshalBinary(proposalID) + store.Set(InactiveProposalQueueProposalKey(endTime, proposalID), bz) } -// Add a proposalID to the back of the ProposalQueue -func (keeper Keeper) InactiveProposalQueuePush(ctx sdk.Context, proposal Proposal) { - proposalQueue := append(keeper.getInactiveProposalQueue(ctx), proposal.GetProposalID()) - keeper.setInactiveProposalQueue(ctx, proposalQueue) +// removes a proposalID from the Inactive Proposal Queue +func (keeper Keeper) RemoveFromInactiveProposalQueue(ctx sdk.Context, endTime time.Time, proposalID int64) { + store := ctx.KVStore(keeper.storeKey) + store.Set(InactiveProposalQueueProposalKey(endTime, proposalID), nil) } diff --git a/x/gov/keeper_keys.go b/x/gov/keeper_keys.go index 7b1bf43f2f77..57f38fdd6b29 100644 --- a/x/gov/keeper_keys.go +++ b/x/gov/keeper_keys.go @@ -1,7 +1,9 @@ package gov import ( + "bytes" "fmt" + "time" sdk "github.com/cosmos/cosmos-sdk/types" ) @@ -10,9 +12,9 @@ import ( // Key for getting a the next available proposalID from the store var ( - KeyNextProposalID = []byte("newProposalID") - KeyActiveProposalQueue = []byte("activeProposalQueue") - KeyInactiveProposalQueue = []byte("inactiveProposalQueue") + KeyNextProposalID = []byte("newProposalID") + PrefixActiveProposalQueue = []byte("activeProposalQueue") + PrefixInactiveProposalQueue = []byte("inactiveProposalQueue") ) // Key for getting a specific proposal from the store @@ -39,3 +41,37 @@ func KeyDepositsSubspace(proposalID int64) []byte { func KeyVotesSubspace(proposalID int64) []byte { return []byte(fmt.Sprintf("votes:%d:", proposalID)) } + +// Returns the key for a proposalID in the activeProposalQueue +func ActiveProposalQueueTimePrefix(endTime time.Time) []byte { + return bytes.Join([][]byte{ + PrefixActiveProposalQueue, + sdk.FormatTimeBytes(endTime), + }, []byte("/")) +} + +// Returns the key for a proposalID in the activeProposalQueue +func ActiveProposalQueueProposalKey(endTime time.Time, proposalID int64) []byte { + return bytes.Join([][]byte{ + PrefixActiveProposalQueue, + sdk.FormatTimeBytes(endTime), + sdk.Int64ToSortableBytes(proposalID), + }, []byte("/")) +} + +// Returns the key for a proposalID in the activeProposalQueue +func InactiveProposalQueueTimePrefix(endTime time.Time) []byte { + return bytes.Join([][]byte{ + PrefixInactiveProposalQueue, + sdk.FormatTimeBytes(endTime), + }, []byte("/")) +} + +// Returns the key for a proposalID in the activeProposalQueue +func InactiveProposalQueueProposalKey(endTime time.Time, proposalID int64) []byte { + return bytes.Join([][]byte{ + PrefixInactiveProposalQueue, + sdk.FormatTimeBytes(endTime), + sdk.Int64ToSortableBytes(proposalID), + }, []byte("/")) +} diff --git a/x/gov/keeper_test.go b/x/gov/keeper_test.go index 91c41d7d7dde..656abc843a7b 100644 --- a/x/gov/keeper_test.go +++ b/x/gov/keeper_test.go @@ -47,12 +47,17 @@ func TestActivateVotingPeriod(t *testing.T) { proposal := keeper.NewTextProposal(ctx, "Test", "description", ProposalTypeText) require.True(t, proposal.GetVotingStartTime().Equal(time.Time{})) - require.Nil(t, keeper.ActiveProposalQueuePeek(ctx)) keeper.activateVotingPeriod(ctx, proposal) require.True(t, proposal.GetVotingStartTime().Equal(ctx.BlockHeader().Time)) - require.Equal(t, proposal.GetProposalID(), keeper.ActiveProposalQueuePeek(ctx).GetProposalID()) + + activeIterator := keeper.ActiveProposalQueueIterator(ctx, proposal.GetVotingEndTime()) + require.True(t, activeIterator.Valid()) + var proposalID int64 + keeper.cdc.UnmarshalBinary(activeIterator.Value(), &proposalID) + require.Equal(t, proposalID, proposal.GetProposalID()) + activeIterator.Close() } func TestDeposits(t *testing.T) { @@ -79,7 +84,6 @@ func TestDeposits(t *testing.T) { deposit, found := keeper.GetDeposit(ctx, proposalID, addrs[1]) require.False(t, found) require.True(t, keeper.GetProposal(ctx, proposalID).GetVotingStartTime().Equal(time.Time{})) - require.Nil(t, keeper.ActiveProposalQueuePeek(ctx)) // Check first deposit err, votingStarted := keeper.AddDeposit(ctx, proposalID, addrs[0], fourSteak) @@ -116,8 +120,6 @@ func TestDeposits(t *testing.T) { // Check that proposal moved to voting period require.True(t, keeper.GetProposal(ctx, proposalID).GetVotingStartTime().Equal(ctx.BlockHeader().Time)) - require.NotNil(t, keeper.ActiveProposalQueuePeek(ctx)) - require.Equal(t, proposalID, keeper.ActiveProposalQueuePeek(ctx).GetProposalID()) // Test deposit iterator depositsIterator := keeper.GetDeposits(ctx, proposalID) @@ -207,44 +209,21 @@ func TestProposalQueues(t *testing.T) { ctx := mapp.BaseApp.NewContext(false, abci.Header{}) mapp.InitChainer(ctx, abci.RequestInitChain{}) - require.Nil(t, keeper.InactiveProposalQueuePeek(ctx)) - require.Nil(t, keeper.ActiveProposalQueuePeek(ctx)) - // create test proposals proposal := keeper.NewTextProposal(ctx, "Test", "description", ProposalTypeText) - proposal2 := keeper.NewTextProposal(ctx, "Test2", "description", ProposalTypeText) - proposal3 := keeper.NewTextProposal(ctx, "Test3", "description", ProposalTypeText) - proposal4 := keeper.NewTextProposal(ctx, "Test4", "description", ProposalTypeText) - - // test pushing to inactive proposal queue - keeper.InactiveProposalQueuePush(ctx, proposal) - keeper.InactiveProposalQueuePush(ctx, proposal2) - keeper.InactiveProposalQueuePush(ctx, proposal3) - keeper.InactiveProposalQueuePush(ctx, proposal4) - - // test peeking and popping from inactive proposal queue - require.Equal(t, keeper.InactiveProposalQueuePeek(ctx).GetProposalID(), proposal.GetProposalID()) - require.Equal(t, keeper.InactiveProposalQueuePop(ctx).GetProposalID(), proposal.GetProposalID()) - require.Equal(t, keeper.InactiveProposalQueuePeek(ctx).GetProposalID(), proposal2.GetProposalID()) - require.Equal(t, keeper.InactiveProposalQueuePop(ctx).GetProposalID(), proposal2.GetProposalID()) - require.Equal(t, keeper.InactiveProposalQueuePeek(ctx).GetProposalID(), proposal3.GetProposalID()) - require.Equal(t, keeper.InactiveProposalQueuePop(ctx).GetProposalID(), proposal3.GetProposalID()) - require.Equal(t, keeper.InactiveProposalQueuePeek(ctx).GetProposalID(), proposal4.GetProposalID()) - require.Equal(t, keeper.InactiveProposalQueuePop(ctx).GetProposalID(), proposal4.GetProposalID()) - - // test pushing to active proposal queue - keeper.ActiveProposalQueuePush(ctx, proposal) - keeper.ActiveProposalQueuePush(ctx, proposal2) - keeper.ActiveProposalQueuePush(ctx, proposal3) - keeper.ActiveProposalQueuePush(ctx, proposal4) - - // test peeking and popping from active proposal queue - require.Equal(t, keeper.ActiveProposalQueuePeek(ctx).GetProposalID(), proposal.GetProposalID()) - require.Equal(t, keeper.ActiveProposalQueuePop(ctx).GetProposalID(), proposal.GetProposalID()) - require.Equal(t, keeper.ActiveProposalQueuePeek(ctx).GetProposalID(), proposal2.GetProposalID()) - require.Equal(t, keeper.ActiveProposalQueuePop(ctx).GetProposalID(), proposal2.GetProposalID()) - require.Equal(t, keeper.ActiveProposalQueuePeek(ctx).GetProposalID(), proposal3.GetProposalID()) - require.Equal(t, keeper.ActiveProposalQueuePop(ctx).GetProposalID(), proposal3.GetProposalID()) - require.Equal(t, keeper.ActiveProposalQueuePeek(ctx).GetProposalID(), proposal4.GetProposalID()) - require.Equal(t, keeper.ActiveProposalQueuePop(ctx).GetProposalID(), proposal4.GetProposalID()) + + inactiveIterator := keeper.InactiveProposalQueueIterator(ctx, proposal.GetDepositEndTime()) + require.True(t, inactiveIterator.Valid()) + var proposalID int64 + keeper.cdc.UnmarshalBinary(inactiveIterator.Value(), &proposalID) + require.Equal(t, proposalID, proposal.GetProposalID()) + inactiveIterator.Close() + + keeper.activateVotingPeriod(ctx, proposal) + + activeIterator := keeper.ActiveProposalQueueIterator(ctx, proposal.GetVotingEndTime()) + require.True(t, activeIterator.Valid()) + keeper.cdc.UnmarshalBinary(activeIterator.Value(), &proposalID) + require.Equal(t, proposalID, proposal.GetProposalID()) + activeIterator.Close() } diff --git a/x/gov/proposals.go b/x/gov/proposals.go index 9d1ba860adae..1d9d562a6b25 100644 --- a/x/gov/proposals.go +++ b/x/gov/proposals.go @@ -34,11 +34,17 @@ type Proposal interface { GetSubmitTime() time.Time SetSubmitTime(time.Time) + GetDepositEndTime() time.Time + SetDepositEndTime(time.Time) + GetTotalDeposit() sdk.Coins SetTotalDeposit(sdk.Coins) GetVotingStartTime() time.Time SetVotingStartTime(time.Time) + + GetVotingEndTime() time.Time + SetVotingEndTime(time.Time) } // checks if two proposals are equal @@ -68,10 +74,12 @@ type TextProposal struct { Status ProposalStatus `json:"proposal_status"` // Status of the Proposal {Pending, Active, Passed, Rejected} TallyResult TallyResult `json:"tally_result"` // Result of Tallys - SubmitTime time.Time `json:"submit_time"` // Height of the block where TxGovSubmitProposal was included - TotalDeposit sdk.Coins `json:"total_deposit"` // Current deposit on this proposal. Initial value is set at InitialDeposit + SubmitTime time.Time `json:"submit_time"` // Time of the block where TxGovSubmitProposal was included + DepositEndTime time.Time `json:"deposit_end_time"` // Time that the Proposal would expire if deposit amount isn't met + TotalDeposit sdk.Coins `json:"total_deposit"` // Current deposit on this proposal. Initial value is set at InitialDeposit - VotingStartTime time.Time `json:"voting_start_time"` // Height of the block where MinDeposit was reached. -1 if MinDeposit is not reached + VotingStartTime time.Time `json:"voting_start_time"` // Time of the block where MinDeposit was reached. -1 if MinDeposit is not reached + VotingEndTime time.Time `json:"voting_end_time"` // Time that the VotingPeriod for this proposal will end and votes will be tallied } // Implements Proposal Interface @@ -92,12 +100,20 @@ func (tp TextProposal) GetTallyResult() TallyResult { return tp.T func (tp *TextProposal) SetTallyResult(tallyResult TallyResult) { tp.TallyResult = tallyResult } func (tp TextProposal) GetSubmitTime() time.Time { return tp.SubmitTime } func (tp *TextProposal) SetSubmitTime(submitTime time.Time) { tp.SubmitTime = submitTime } -func (tp TextProposal) GetTotalDeposit() sdk.Coins { return tp.TotalDeposit } -func (tp *TextProposal) SetTotalDeposit(totalDeposit sdk.Coins) { tp.TotalDeposit = totalDeposit } -func (tp TextProposal) GetVotingStartTime() time.Time { return tp.VotingStartTime } +func (tp TextProposal) GetDepositEndTime() time.Time { return tp.DepositEndTime } +func (tp *TextProposal) SetDepositEndTime(depositEndTime time.Time) { + tp.DepositEndTime = depositEndTime +} +func (tp TextProposal) GetTotalDeposit() sdk.Coins { return tp.TotalDeposit } +func (tp *TextProposal) SetTotalDeposit(totalDeposit sdk.Coins) { tp.TotalDeposit = totalDeposit } +func (tp TextProposal) GetVotingStartTime() time.Time { return tp.VotingStartTime } func (tp *TextProposal) SetVotingStartTime(votingStartTime time.Time) { tp.VotingStartTime = votingStartTime } +func (tp TextProposal) GetVotingEndTime() time.Time { return tp.VotingEndTime } +func (tp *TextProposal) SetVotingEndTime(votingEndTime time.Time) { + tp.VotingEndTime = votingEndTime +} //----------------------------------------------------------- // ProposalQueue From 3664498e43c5f05bcbc5b8e8bf70cc831f8aab42 Mon Sep 17 00:00:00 2001 From: Sunny Aggarwal Date: Tue, 30 Oct 2018 17:17:42 -0700 Subject: [PATCH 5/6] update gov spec --- docs/spec/governance/state.md | 45 +++++++++++------------------------ 1 file changed, 14 insertions(+), 31 deletions(-) diff --git a/docs/spec/governance/state.md b/docs/spec/governance/state.md index 5b577cec3918..6335ddad68cc 100644 --- a/docs/spec/governance/state.md +++ b/docs/spec/governance/state.md @@ -96,11 +96,13 @@ type Proposal struct { Type ProposalType // Type of proposal. Initial set {PlainTextProposal, SoftwareUpgradeProposal} TotalDeposit sdk.Coins // Current deposit on this proposal. Initial value is set at InitialDeposit Deposits []Deposit // List of deposits on the proposal - SubmitTime time.Time // Time of the block where TxGovSubmitProposal was included - Submitter sdk.Address // Address of the submitter + SubmitTime time.Time // Time of the block where TxGovSubmitProposal was included + DepositEndTime time.Time // Time that the DepositPeriod of a proposal would expire + Submitter sdk.Address // Address of the submitter VotingStartTime time.Time // Time of the block where MinDeposit was reached. time.Time{} if MinDeposit is not reached - CurrentStatus ProposalStatus // Current status of the proposal + VotingEndTime time.Time // Time of the block that the VotingPeriod for a proposal will end. + CurrentStatus ProposalStatus // Current status of the proposal YesVotes sdk.Dec NoVotes sdk.Dec @@ -134,47 +136,29 @@ For pseudocode purposes, here are the two function we will use to read or write **Store:** * `ProposalProcessingQueue`: A queue `queue[proposalID]` containing all the - `ProposalIDs` of proposals that reached `MinDeposit`. Each round, the oldest - element of `ProposalProcessingQueue` is checked during `BeginBlock` to see if - `CurrentTime == VotingStartTime + activeProcedure.VotingPeriod`. If it is, - then the application tallies the votes, compute the votes of each validator and checks if every validator in the valdiator set have voted - and, if not, applies `GovernancePenalty`. If the proposal is accepted, deposits are refunded. - After that proposal is ejected from `ProposalProcessingQueue` and the next element of the queue is evaluated. + `ProposalIDs` of proposals that reached `MinDeposit`. Each `EndBlock`, all the proposals + that have reached the end of their voting period are processed. + To process a finished proposal, the application tallies the votes, compute the votes of + each validator and checks if every validator in the valdiator set have voted. + If the proposal is accepted, deposits are refunded. And the pseudocode for the `ProposalProcessingQueue`: ```go in EndBlock do - - checkProposal() // First call of the recursive function - - - // Recursive function. First call in BeginBlock - func checkProposal() - proposalID = ProposalProcessingQueue.Peek() - if (proposalID == nil) - return - proposal = load(Governance, ) // proposal is a const key votingProcedure = load(GlobalParams, 'VotingProcedure') + + for finishedProposalID in GetAllFinishedProposalIDs(block.Time) + proposal = load(Governance, ) // proposal is a const key - if (CurrentTime == proposal.VotingStartTime + votingProcedure.VotingPeriod && proposal.CurrentStatus == ProposalStatusActive) - - // End of voting period, tally - - ProposalProcessingQueue.pop() - validators = - - - Keeper.getAllValidators() + validators = Keeper.getAllValidators() tmpValMap := map(sdk.Address)ValidatorGovInfo // Initiate mapping at 0. Validators that remain at 0 at the end of tally will be punished for each validator in validators tmpValMap(validator).Minus = 0 - - // Tally voterIterator = rangeQuery(Governance, ) //return all the addresses that voted on the proposal for each (voterAddress, vote) in voterIterator @@ -212,5 +196,4 @@ And the pseudocode for the `ProposalProcessingQueue`: proposal.CurrentStatus = ProposalStatusRejected store(Governance, , proposal) - checkProposal() ``` From 94ba064a707ef09a9185edc2d751f5d08fbcf3e1 Mon Sep 17 00:00:00 2001 From: Sunny Aggarwal Date: Fri, 2 Nov 2018 18:58:11 -0700 Subject: [PATCH 6/6] address chris and bez comments --- types/utils.go | 4 ++-- x/gov/keeper_keys.go | 14 ++++++++------ 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/types/utils.go b/types/utils.go index db6851f80d8f..289aef472f43 100644 --- a/types/utils.go +++ b/types/utils.go @@ -37,8 +37,8 @@ func MustSortJSON(toSortJSON []byte) []byte { return js } -// marshals int64 to a bigendian byte slice so it can be sorted -func Int64ToSortableBytes(i int64) []byte { +// Int64ToBigEndian - marshals int64 to a bigendian byte slice so it can be sorted +func Int64ToBigEndian(i int64) []byte { b := make([]byte, 8) binary.BigEndian.PutUint64(b, uint64(i)) return b diff --git a/x/gov/keeper_keys.go b/x/gov/keeper_keys.go index 57f38fdd6b29..b171961f99c0 100644 --- a/x/gov/keeper_keys.go +++ b/x/gov/keeper_keys.go @@ -12,6 +12,8 @@ import ( // Key for getting a the next available proposalID from the store var ( + KeyDelimiter = []byte("/") + KeyNextProposalID = []byte("newProposalID") PrefixActiveProposalQueue = []byte("activeProposalQueue") PrefixInactiveProposalQueue = []byte("inactiveProposalQueue") @@ -47,7 +49,7 @@ func ActiveProposalQueueTimePrefix(endTime time.Time) []byte { return bytes.Join([][]byte{ PrefixActiveProposalQueue, sdk.FormatTimeBytes(endTime), - }, []byte("/")) + }, KeyDelimiter) } // Returns the key for a proposalID in the activeProposalQueue @@ -55,8 +57,8 @@ func ActiveProposalQueueProposalKey(endTime time.Time, proposalID int64) []byte return bytes.Join([][]byte{ PrefixActiveProposalQueue, sdk.FormatTimeBytes(endTime), - sdk.Int64ToSortableBytes(proposalID), - }, []byte("/")) + sdk.Int64ToBigEndian(proposalID), + }, KeyDelimiter) } // Returns the key for a proposalID in the activeProposalQueue @@ -64,7 +66,7 @@ func InactiveProposalQueueTimePrefix(endTime time.Time) []byte { return bytes.Join([][]byte{ PrefixInactiveProposalQueue, sdk.FormatTimeBytes(endTime), - }, []byte("/")) + }, KeyDelimiter) } // Returns the key for a proposalID in the activeProposalQueue @@ -72,6 +74,6 @@ func InactiveProposalQueueProposalKey(endTime time.Time, proposalID int64) []byt return bytes.Join([][]byte{ PrefixInactiveProposalQueue, sdk.FormatTimeBytes(endTime), - sdk.Int64ToSortableBytes(proposalID), - }, []byte("/")) + sdk.Int64ToBigEndian(proposalID), + }, KeyDelimiter) }