Skip to content

Commit

Permalink
Merge pull request #6496 from multiversx/MX-15031
Browse files Browse the repository at this point in the history
MX-15031: Add cancel method before voting start
  • Loading branch information
dragos-rebegea authored Oct 2, 2024
2 parents 51bcc3e + 7791a43 commit 233c48d
Show file tree
Hide file tree
Showing 2 changed files with 127 additions and 9 deletions.
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

0 comments on commit 233c48d

Please sign in to comment.