Skip to content

Commit

Permalink
end2end: update ballot test to use different voting types: ranked, qu…
Browse files Browse the repository at this point in the history
…adratic, range and approval, update helpers for ballotVotes and add newTestProcess to use as standard models.Process on tests.
  • Loading branch information
mariajdab committed Sep 4, 2023
1 parent 0c4a1a9 commit 73d0d46
Show file tree
Hide file tree
Showing 4 changed files with 313 additions and 118 deletions.
244 changes: 194 additions & 50 deletions cmd/end2endtest/ballot.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,104 +7,248 @@ import (

"go.vocdoni.io/dvote/apiclient"
"go.vocdoni.io/dvote/log"
"go.vocdoni.io/dvote/types"
"go.vocdoni.io/proto/build/go/models"
)

const (
rankedVote = "ranked"
quadraticVote = "quadratic"
rangeVote = "range"
approvalVote = "approval"
)

func init() {
ops["ballotelection"] = operation{
test: &E2EBallotElection{},
description: "ballot election with unique values, maxCount, maxValue, maxTotalCost to test different ballotProtocol configurations",
example: os.Args[0] + " --operation=ballotelection --votes=1000",
ops["ballotRanked"] = operation{
test: &E2EBallotRanked{},
description: "ballot election to test ranked voting",
example: os.Args[0] + " --operation=ballotRanked --votes=1000",
}
ops["ballotQuadratic"] = operation{
test: &E2EBallotQuadratic{},
description: "ballot election to test quadratic voting",
example: os.Args[0] + " --operation=ballotQuadratic --votes=1000",
}
ops["ballotRange"] = operation{
test: &E2EBallotRange{},
description: "ballot election to test range voting",
example: os.Args[0] + " --operation=ballotRange --votes=1000",
}
ops["ballotApproval"] = operation{
test: &E2EBallotApproval{},
description: "ballot election to test approval voting",
example: os.Args[0] + " --operation=ballotApproval --votes=1000",
}
}

var _ VochainTest = (*E2EBallotRanked)(nil)
var _ VochainTest = (*E2EBallotQuadratic)(nil)
var _ VochainTest = (*E2EBallotRange)(nil)
var _ VochainTest = (*E2EBallotApproval)(nil)

type E2EBallotRanked struct{ e2eElection }
type E2EBallotQuadratic struct{ e2eElection }
type E2EBallotRange struct{ e2eElection }
type E2EBallotApproval struct{ e2eElection }

func (t *E2EBallotRanked) Setup(api *apiclient.HTTPclient, c *config) error {
t.api = api
t.config = c

//setup for ranked voting
p := newTestProcess()
p.VoteOptions = &models.ProcessVoteOptions{
MaxCount: 4,
MaxValue: 3,
MaxTotalCost: 6,
CostExponent: 1,
}

if err := t.setupElectionRaw(p); err != nil {
return fmt.Errorf("error in setupElectionRaw for ranked voting: %s", err)
}

log.Debugf("election details: %+v", *t.election)
return nil
}

func (*E2EBallotRanked) Teardown() error {
// nothing to do here
return nil
}

func (t *E2EBallotRanked) Run() error {
nvotes := t.config.nvotes

choices, expectedResults := ballotVotes(
t.election.TallyMode, nvotes,
rankedVote, t.election.VoteMode.UniqueValues)

if err := sendAndValidateVotes(t.e2eElection, choices, expectedResults); err != nil {
return err
}

return nil
}

func (t *E2EBallotQuadratic) Setup(api *apiclient.HTTPclient, c *config) error {
t.api = api
t.config = c

// setup for quadratic voting
p := newTestProcess()
// update uniqueValues to false
p.EnvelopeType.UniqueValues = false
p.VoteOptions = &models.ProcessVoteOptions{
MaxCount: 4,
MaxValue: 3,
MaxTotalCost: 12,
CostExponent: 2,
}

if err := t.setupElectionRaw(p); err != nil {
return fmt.Errorf("error in setupElectionRaw for quadratic voting: %s", err)
}

log.Debugf("election details: %+v", *t.election)
return nil
}

func (*E2EBallotQuadratic) Teardown() error {
// nothing to do here
return nil
}

var _ VochainTest = (*E2EBallotElection)(nil)
func (t *E2EBallotQuadratic) Run() error {
nvotes := t.config.nvotes

choices, expectedResults := ballotVotes(
t.election.TallyMode, nvotes,
quadraticVote, t.election.VoteMode.UniqueValues)

type E2EBallotElection struct {
e2eElection
if err := sendAndValidateVotes(t.e2eElection, choices, expectedResults); err != nil {
return err
}

return nil
}

func (t *E2EBallotElection) Setup(api *apiclient.HTTPclient, c *config) error {
func (t *E2EBallotRange) Setup(api *apiclient.HTTPclient, c *config) error {
t.api = api
t.config = c

p := &models.Process{
StartBlock: 0,
BlockCount: 100,
Status: models.ProcessStatus_READY,
EnvelopeType: &models.EnvelopeType{
EncryptedVotes: false,
UniqueValues: true},
CensusOrigin: models.CensusOrigin_OFF_CHAIN_TREE_WEIGHTED,
VoteOptions: &models.ProcessVoteOptions{
MaxCount: 2,
MaxValue: 6,
MaxTotalCost: 10,
CostExponent: 1,
},
Mode: &models.ProcessMode{
AutoStart: true,
Interruptible: true,
},
MaxCensusSize: uint64(t.config.nvotes),
// setup for range voting
p := newTestProcess()
p.VoteOptions = &models.ProcessVoteOptions{
MaxCount: 4,
MaxValue: 3,
MaxTotalCost: 10,
CostExponent: 1,
}

if err := t.setupElectionRaw(p); err != nil {
return fmt.Errorf("error in setupElectionRaw for range voting: %s", err)
}

log.Debugf("election details: %+v", *t.election)
return nil
}

func (*E2EBallotRange) Teardown() error {
// nothing to do here
return nil
}

func (t *E2EBallotRange) Run() error {
nvotes := t.config.nvotes

choices, expectedResults := ballotVotes(
t.election.TallyMode, nvotes,
rangeVote, t.election.VoteMode.UniqueValues)

if err := sendAndValidateVotes(t.e2eElection, choices, expectedResults); err != nil {
return err
}

return nil
}

func (t *E2EBallotApproval) Setup(api *apiclient.HTTPclient, c *config) error {
t.api = api
t.config = c

//setup for approval voting
p := newTestProcess()
// update uniqueValues to false
p.EnvelopeType.UniqueValues = false
p.VoteOptions = &models.ProcessVoteOptions{
MaxCount: 4,
MaxValue: 1,
MaxTotalCost: 4,
CostExponent: 1,
}

if err := t.setupElectionRaw(p); err != nil {
return fmt.Errorf("error in setupElectionRaw for approval voting: %s", err)
}

log.Debugf("election details: %+v", *t.election)
return nil
}

func (*E2EBallotElection) Teardown() error {
func (*E2EBallotApproval) Teardown() error {
// nothing to do here
return nil
}

func (t *E2EBallotElection) Run() error {
bdata := ballotData{
maxValue: t.election.TallyMode.MaxValue,
maxCount: t.election.TallyMode.MaxCount,
maxTotalCost: t.election.TallyMode.MaxTotalCost,
costExponent: t.election.TallyMode.CostExponent,
func (t *E2EBallotApproval) Run() error {
nvotes := t.config.nvotes

choices, expectedResults := ballotVotes(
t.election.TallyMode, nvotes,
approvalVote, t.election.VoteMode.UniqueValues)

if err := sendAndValidateVotes(t.e2eElection, choices, expectedResults); err != nil {
return err
}

choices, expectedResults := ballotVotes(bdata, len(t.voterAccounts))
return nil
}

// Send the votes (parallelized)
func sendAndValidateVotes(e e2eElection, choices [][]int, expectedResults [][]*types.BigInt) error {
nvotes := e.config.nvotes
startTime := time.Now()

log.Infow("enqueuing votes", "n", len(t.voterAccounts), "election", t.election.ElectionID)
log.Infow("enqueuing votes", "n", len(e.voterAccounts), "election", e.election.ElectionID)
votes := []*apiclient.VoteData{}
for i, acct := range t.voterAccounts {
for i, acct := range e.voterAccounts {
votes = append(votes, &apiclient.VoteData{
ElectionID: t.election.ElectionID,
ProofMkTree: t.proofs[acct.Address().Hex()],
ElectionID: e.election.ElectionID,
ProofMkTree: e.proofs[acct.Address().Hex()],
Choices: choices[i],
VoterAccount: acct,
})
}
t.sendVotes(votes)
e.sendVotes(votes)

log.Infow("votes submitted successfully",
"n", len(t.voterAccounts), "time", time.Since(startTime),
"vps", int(float64(len(t.voterAccounts))/time.Since(startTime).Seconds()))
"n", len(e.voterAccounts), "time", time.Since(startTime),
"vps", int(float64(len(e.voterAccounts))/time.Since(startTime).Seconds()))

if err := t.verifyVoteCount(t.config.nvotes); err != nil {
return err
if err := e.verifyVoteCount(nvotes); err != nil {
return fmt.Errorf("error in verifyVoteCount: %s", err)
}

elres, err := t.endElectionAndFetchResults()
elres, err := e.endElectionAndFetchResults()
if err != nil {
return err
return fmt.Errorf("error in electionAndFetchResults: %s", err)
}

if !matchResults(elres.Results, expectedResults) {
return fmt.Errorf("election result must match, expected Results: %v but got Results: %v", expectedResults, elres.Results)
return fmt.Errorf("election result must match, expected Results: %s but got Results: %v", expectedResults, elres.Results)
}

log.Infof("election %s status is RESULTS", t.election.ElectionID.String())
log.Infof("election %s status is RESULTS", e.election.ElectionID.String())
log.Infof("election results: %v", elres.Results)

return nil
}
22 changes: 4 additions & 18 deletions cmd/end2endtest/csp.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,24 +27,10 @@ func (t *E2ECSPElection) Setup(api *apiclient.HTTPclient, c *config) error {
t.api = api
t.config = c

p := &models.Process{
StartBlock: 0,
BlockCount: 100,
Status: models.ProcessStatus_READY,
EnvelopeType: &models.EnvelopeType{
EncryptedVotes: false,
UniqueValues: true},
CensusOrigin: models.CensusOrigin_OFF_CHAIN_CA,
VoteOptions: &models.ProcessVoteOptions{
MaxCount: 1,
MaxValue: 1,
},
Mode: &models.ProcessMode{
AutoStart: true,
Interruptible: true,
},
MaxCensusSize: uint64(t.config.nvotes),
}
//setup for ranked voting
p := newTestProcess()
// update to use csp origin
p.CensusOrigin = models.CensusOrigin_OFF_CHAIN_CA

if err := t.setupElectionRaw(p); err != nil {
return err
Expand Down
Loading

0 comments on commit 73d0d46

Please sign in to comment.