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

MX-15031: Add cancel method before voting start #6496

Open
wants to merge 5 commits into
base: feat/governance-fixes
Choose a base branch
from
Open
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
25 changes: 21 additions & 4 deletions vm/systemSmartContracts/governance.go
Original file line number Diff line number Diff line change
Expand Up @@ -683,8 +683,8 @@ func (g *governanceContract) closeProposal(args *vmcommon.ContractCallInput) vmc
return vmcommon.UserError
}

currentEpoch := g.eei.BlockChainHook().CurrentEpoch()
if uint64(currentEpoch) <= generalProposal.EndVoteEpoch {
currentEpoch := uint64(g.eei.BlockChainHook().CurrentEpoch())
if g.cannotClose(currentEpoch, generalProposal) {
g.eei.AddReturnMessage(fmt.Sprintf("proposal can be closed only after epoch %d", generalProposal.EndVoteEpoch))
return vmcommon.UserError
}
Expand All @@ -696,7 +696,7 @@ func (g *governanceContract) closeProposal(args *vmcommon.ContractCallInput) vmc
return vmcommon.UserError
}

generalProposal.Passed = g.computeEndResults(generalProposal, baseConfig)
generalProposal.Passed = g.computeEndResults(currentEpoch, generalProposal, baseConfig)
if err != nil {
g.eei.AddReturnMessage("computeEndResults error " + err.Error())
return vmcommon.UserError
Expand Down Expand Up @@ -731,6 +731,17 @@ func (g *governanceContract) closeProposal(args *vmcommon.ContractCallInput) vmc
return vmcommon.Ok
}

func (g *governanceContract) cannotClose(currentEpoch uint64, proposal *GeneralProposal) bool {
if !g.enableEpochsHandler.IsFlagEnabled(common.GovernanceFixesFlag) {
return currentEpoch <= proposal.EndVoteEpoch
}

voteStarted := currentEpoch >= proposal.StartVoteEpoch
voteEnded := currentEpoch > proposal.EndVoteEpoch

return voteStarted && !voteEnded
}

func (g *governanceContract) clearEndedProposals(args *vmcommon.ContractCallInput) vmcommon.ReturnCode {
if args.CallValue.Cmp(zero) != 0 {
g.eei.AddReturnMessage("clearEndedProposals callValue expected to be 0")
Expand Down Expand Up @@ -1018,7 +1029,13 @@ func (g *governanceContract) getTotalStakeInSystem() *big.Int {
}

// computeEndResults computes if a proposal has passed or not based on votes accumulated
func (g *governanceContract) computeEndResults(proposal *GeneralProposal, baseConfig *GovernanceConfigV2) bool {
func (g *governanceContract) computeEndResults(currentEpoch uint64, proposal *GeneralProposal, baseConfig *GovernanceConfigV2) bool {
voteStarted := currentEpoch >= proposal.StartVoteEpoch
if g.enableEpochsHandler.IsFlagEnabled(common.GovernanceFixesFlag) && !voteStarted {
g.eei.Finish([]byte("Proposal closed before voting started"))
return true
}

totalVotes := big.NewInt(0).Add(proposal.Yes, proposal.No)
totalVotes.Add(totalVotes, proposal.Veto)
totalVotes.Add(totalVotes, proposal.Abstain)
Expand Down
111 changes: 106 additions & 5 deletions vm/systemSmartContracts/governance_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1698,6 +1698,92 @@ func TestGovernanceContract_CloseProposalAfterGovernanceFixesShouldSetLastEndedN
require.Equal(t, uint64(6), lastEndedNonce)
}

func TestGovernanceContract_CannotClose(t *testing.T) {
t.Parallel()

args := createMockGovernanceArgs()
gsc, _ := NewGovernanceContract(args)

currentEpoch := uint64(10)
startVoteEpoch := currentEpoch + 1
endVoteEpoch := currentEpoch + 10
closedBeforeStart := &GeneralProposal{
Yes: big.NewInt(20),
No: big.NewInt(0),
Veto: big.NewInt(0),
Abstain: big.NewInt(10),
StartVoteEpoch: startVoteEpoch,
EndVoteEpoch: endVoteEpoch,
}

t.Run("fixes flag disabled, should return false only after end vote epoch", func(t *testing.T) {
t.Run("current epoch is less than start epoch", func(t *testing.T) {
t.Parallel()

cannotClose := gsc.cannotClose(currentEpoch, closedBeforeStart)
require.True(t, cannotClose)
})
t.Run("current epoch is equal with start epoch", func(t *testing.T) {
t.Parallel()

cannotClose := gsc.cannotClose(startVoteEpoch, closedBeforeStart)
require.True(t, cannotClose)
})
t.Run("current epoch is between start and end epochs", func(t *testing.T) {
t.Parallel()

cannotClose := gsc.cannotClose(currentEpoch+5, closedBeforeStart)
require.True(t, cannotClose)
})
t.Run("current epoch is equal with end epoch", func(t *testing.T) {
t.Parallel()

cannotClose := gsc.cannotClose(endVoteEpoch, closedBeforeStart)
require.True(t, cannotClose)
})
t.Run("current epoch is larger than end epoch", func(t *testing.T) {
t.Parallel()

cannotClose := gsc.cannotClose(endVoteEpoch+1, closedBeforeStart)
require.False(t, cannotClose)
})
})
t.Run("fixes flag enabled, should return false only between start and end epoch", func(t *testing.T) {
gsc.enableEpochsHandler = enableEpochsHandlerMock.NewEnableEpochsHandlerStub(common.GovernanceFixesFlag)

t.Run("current epoch is less than start epoch", func(t *testing.T) {
t.Parallel()

cannotClose := gsc.cannotClose(currentEpoch, closedBeforeStart)
require.False(t, cannotClose)
})
t.Run("current epoch is equal with start epoch", func(t *testing.T) {
t.Parallel()

cannotClose := gsc.cannotClose(startVoteEpoch, closedBeforeStart)
require.True(t, cannotClose)
})
t.Run("current epoch is between start and end epochs", func(t *testing.T) {
t.Parallel()

cannotClose := gsc.cannotClose(currentEpoch+5, closedBeforeStart)
require.True(t, cannotClose)
})
t.Run("current epoch is equal with end epoch", func(t *testing.T) {
t.Parallel()

cannotClose := gsc.cannotClose(endVoteEpoch, closedBeforeStart)
require.True(t, cannotClose)
})
t.Run("current epoch is larger than end epoch", func(t *testing.T) {
t.Parallel()

cannotClose := gsc.cannotClose(endVoteEpoch+1, closedBeforeStart)
require.False(t, cannotClose)
})
})
}

func TestGovernanceContract_ClearEndedProposalsCallValue(t *testing.T) {
t.Parallel()

Expand Down Expand Up @@ -2214,13 +2300,28 @@ func TestComputeEndResults(t *testing.T) {
}
gsc, _ := NewGovernanceContract(args)

startVoteEpoch := uint64(10)
gsc.enableEpochsHandler = enableEpochsHandlerMock.NewEnableEpochsHandlerStub(common.GovernanceFixesFlag)
closedBeforeStart := &GeneralProposal{
Yes: big.NewInt(20),
No: big.NewInt(0),
Veto: big.NewInt(0),
Abstain: big.NewInt(10),
StartVoteEpoch: startVoteEpoch,
}
passed := gsc.computeEndResults(startVoteEpoch-1, closedBeforeStart, baseConfig)
require.True(t, passed)
require.Equal(t, "Proposal closed before voting started", retMessage)
require.False(t, closedBeforeStart.Passed)

gsc.enableEpochsHandler = enableEpochsHandlerMock.NewEnableEpochsHandlerStub()
didNotPassQuorum := &GeneralProposal{
Yes: big.NewInt(20),
No: big.NewInt(0),
Veto: big.NewInt(0),
Abstain: big.NewInt(10),
}
passed := gsc.computeEndResults(didNotPassQuorum, baseConfig)
passed = gsc.computeEndResults(startVoteEpoch, didNotPassQuorum, baseConfig)
require.False(t, passed)
require.Equal(t, "Proposal did not reach minQuorum", retMessage)
require.False(t, didNotPassQuorum.Passed)
Expand All @@ -2231,7 +2332,7 @@ func TestComputeEndResults(t *testing.T) {
Veto: big.NewInt(0),
Abstain: big.NewInt(10),
}
passed = gsc.computeEndResults(didNotPassVotes, baseConfig)
passed = gsc.computeEndResults(startVoteEpoch, didNotPassVotes, baseConfig)
require.False(t, passed)
require.Equal(t, "Proposal rejected", retMessage)
require.False(t, didNotPassVotes.Passed)
Expand All @@ -2242,7 +2343,7 @@ func TestComputeEndResults(t *testing.T) {
Veto: big.NewInt(0),
Abstain: big.NewInt(10),
}
passed = gsc.computeEndResults(didNotPassVotes2, baseConfig)
passed = gsc.computeEndResults(startVoteEpoch, didNotPassVotes2, baseConfig)
require.False(t, passed)
require.Equal(t, "Proposal rejected", retMessage)
require.False(t, didNotPassVotes2.Passed)
Expand All @@ -2253,7 +2354,7 @@ func TestComputeEndResults(t *testing.T) {
Veto: big.NewInt(70),
Abstain: big.NewInt(10),
}
passed = gsc.computeEndResults(didNotPassVeto, baseConfig)
passed = gsc.computeEndResults(startVoteEpoch, didNotPassVeto, baseConfig)
require.False(t, passed)
require.Equal(t, "Proposal vetoed", retMessage)
require.False(t, didNotPassVeto.Passed)
Expand All @@ -2264,7 +2365,7 @@ func TestComputeEndResults(t *testing.T) {
Veto: big.NewInt(10),
Abstain: big.NewInt(10),
}
passed = gsc.computeEndResults(pass, baseConfig)
passed = gsc.computeEndResults(startVoteEpoch, pass, baseConfig)
require.True(t, passed)
require.Equal(t, "Proposal passed", retMessage)
}
Expand Down
Loading