From 4f9372c46b330d8d8af6a486ef14e6dd984632e4 Mon Sep 17 00:00:00 2001 From: Gui Iribarren Date: Mon, 3 Jul 2023 23:07:32 +0200 Subject: [PATCH 1/9] test: refactor to allow benchmark Vote (WIP, half-baked) --- test/api_test.go | 74 ++++++++++++++++++++-------------- test/benchmark_vochain_test.go | 17 ++++++++ 2 files changed, 61 insertions(+), 30 deletions(-) create mode 100644 test/benchmark_vochain_test.go diff --git a/test/api_test.go b/test/api_test.go index 293e1f3c6..60621dfe4 100644 --- a/test/api_test.go +++ b/test/api_test.go @@ -22,9 +22,22 @@ import ( "google.golang.org/protobuf/proto" ) +type testElection struct { + server testcommon.APIserver + election api.ElectionCreate + censusData *api.Census + stx models.SignedTx + voterKey ethereum.SignKeys +} + func TestAPIcensusAndVote(t *testing.T) { - server := testcommon.APIserver{} - server.Start(t, + te := testElection{} + te.CreateCensusAndElection(t, 10) +} + +func (te *testElection) CreateCensusAndElection(t testing.TB, nvotes int) { + te.server = testcommon.APIserver{} + te.server.Start(t, api.ChainHandler, api.CensusHandler, api.VoteHandler, @@ -33,22 +46,22 @@ func TestAPIcensusAndVote(t *testing.T) { api.WalletHandler, ) // Block 1 - server.VochainAPP.AdvanceTestBlock() + te.server.VochainAPP.AdvanceTestBlock() token1 := uuid.New() - c := testutil.NewTestHTTPclient(t, server.ListenAddr, &token1) + c := testutil.NewTestHTTPclient(t, te.server.ListenAddr, &token1) // create a new census resp, code := c.Request("POST", nil, "censuses", "weighted") qt.Assert(t, code, qt.Equals, 200) - censusData := &api.Census{} - qt.Assert(t, json.Unmarshal(resp, censusData), qt.IsNil) - id1 := censusData.CensusID.String() + te.censusData = &api.Census{} + qt.Assert(t, json.Unmarshal(resp, te.censusData), qt.IsNil) + id1 := te.censusData.CensusID.String() // add a bunch of keys and values (weights) rnd := testutil.NewRandom(1) cparts := api.CensusParticipants{} - for i := 1; i < 10; i++ { + for i := 1; i < nvotes; i++ { cparts.Participants = append(cparts.Participants, api.CensusParticipant{ Key: rnd.RandomBytes(20), Weight: (*types.BigInt)(big.NewInt(int64(1))), @@ -58,25 +71,24 @@ func TestAPIcensusAndVote(t *testing.T) { qt.Assert(t, code, qt.Equals, 200) // add the key we'll use for cast votes - voterKey := ethereum.SignKeys{} - qt.Assert(t, voterKey.Generate(), qt.IsNil) + qt.Assert(t, te.voterKey.Generate(), qt.IsNil) _, code = c.Request("POST", &api.CensusParticipants{Participants: []api.CensusParticipant{{ - Key: voterKey.Address().Bytes(), + Key: te.voterKey.Address().Bytes(), Weight: (*types.BigInt)(big.NewInt(1)), }}}, "censuses", id1, "participants") qt.Assert(t, code, qt.Equals, 200) resp, code = c.Request("POST", nil, "censuses", id1, "publish") qt.Assert(t, code, qt.Equals, 200) - qt.Assert(t, json.Unmarshal(resp, censusData), qt.IsNil) - qt.Assert(t, censusData.CensusID, qt.IsNotNil) - root := censusData.CensusID + qt.Assert(t, json.Unmarshal(resp, te.censusData), qt.IsNil) + qt.Assert(t, te.censusData.CensusID, qt.IsNotNil) + root := te.censusData.CensusID - resp, code = c.Request("GET", nil, "censuses", root.String(), "proof", fmt.Sprintf("%x", voterKey.Address().Bytes())) + resp, code = c.Request("GET", nil, "censuses", root.String(), "proof", fmt.Sprintf("%x", te.voterKey.Address().Bytes())) qt.Assert(t, code, qt.Equals, 200) - qt.Assert(t, json.Unmarshal(resp, censusData), qt.IsNil) - qt.Assert(t, censusData.Weight.String(), qt.Equals, "1") + qt.Assert(t, json.Unmarshal(resp, te.censusData), qt.IsNil) + qt.Assert(t, te.censusData.Weight.String(), qt.Equals, "1") metadataBytes, err := json.Marshal( &api.ElectionMetadata{ @@ -110,10 +122,10 @@ func TestAPIcensusAndVote(t *testing.T) { } txb, err := proto.Marshal(&tx) qt.Assert(t, err, qt.IsNil) - signedTxb, err := server.Account.SignVocdoniTx(txb, server.VochainAPP.ChainID()) + signedTxb, err := te.server.Account.SignVocdoniTx(txb, te.server.VochainAPP.ChainID()) qt.Assert(t, err, qt.IsNil) - stx := models.SignedTx{Tx: txb, Signature: signedTxb} - stxb, err := proto.Marshal(&stx) + te.stx = models.SignedTx{Tx: txb, Signature: signedTxb} + stxb, err := proto.Marshal(&te.stx) qt.Assert(t, err, qt.IsNil) election := api.ElectionCreate{ @@ -126,9 +138,11 @@ func TestAPIcensusAndVote(t *testing.T) { qt.Assert(t, err, qt.IsNil) // Block 2 - server.VochainAPP.AdvanceTestBlock() + te.server.VochainAPP.AdvanceTestBlock() waitUntilHeight(t, c, 2) +} +func (te *testElection) Vote(t testing.TB, c *testutil.TestHTTPclient, nvotes int) { // Send a vote votePackage := &state.VotePackage{ Votes: []int{1}, @@ -138,37 +152,37 @@ func TestAPIcensusAndVote(t *testing.T) { vote := &models.VoteEnvelope{ Nonce: util.RandomBytes(16), - ProcessId: election.ElectionID, + ProcessId: te.election.ElectionID, VotePackage: votePackageBytes, } vote.Proof = &models.Proof{ Payload: &models.Proof_Arbo{ Arbo: &models.ProofArbo{ Type: models.ProofArbo_BLAKE2B, - Siblings: censusData.Proof, - LeafWeight: censusData.Value, + Siblings: te.censusData.Proof, + LeafWeight: te.censusData.Value, }, }, } - stx.Tx, err = proto.Marshal(&models.Tx{Payload: &models.Tx_Vote{Vote: vote}}) + te.stx.Tx, err = proto.Marshal(&models.Tx{Payload: &models.Tx_Vote{Vote: vote}}) qt.Assert(t, err, qt.IsNil) - stx.Signature, err = voterKey.SignVocdoniTx(stx.Tx, server.VochainAPP.ChainID()) + te.stx.Signature, err = te.voterKey.SignVocdoniTx(te.stx.Tx, te.server.VochainAPP.ChainID()) qt.Assert(t, err, qt.IsNil) - stxb, err = proto.Marshal(&stx) + stxb, err := proto.Marshal(&te.stx) qt.Assert(t, err, qt.IsNil) v := &api.Vote{TxPayload: stxb} - resp, code = c.Request("POST", v, "votes") + resp, code := c.Request("POST", v, "votes") qt.Assert(t, code, qt.Equals, 200) err = json.Unmarshal(resp, &v) qt.Assert(t, err, qt.IsNil) // Block 3 - server.VochainAPP.AdvanceTestBlock() + te.server.VochainAPP.AdvanceTestBlock() waitUntilHeight(t, c, 3) // Verify the vote - _, code = c.Request("GET", nil, "votes", "verify", election.ElectionID.String(), v.VoteID.String()) + _, code = c.Request("GET", nil, "votes", "verify", te.election.ElectionID.String(), v.VoteID.String()) qt.Assert(t, code, qt.Equals, 200) // Get the vote and check the data diff --git a/test/benchmark_vochain_test.go b/test/benchmark_vochain_test.go new file mode 100644 index 000000000..3f6a217c3 --- /dev/null +++ b/test/benchmark_vochain_test.go @@ -0,0 +1,17 @@ +package test + +import ( + "testing" +) + +func BenchmarkCreateElectionAndVote10(b *testing.B) { + for n := 0; n < b.N; n++ { + CreateCensusAndElection(b, 10) + } +} + +func BenchmarkCreateElectionAndVote100(b *testing.B) { + for n := 0; n < b.N; n++ { + CreateCensusAndElection(b, 100) + } +} From e365ed55c96998d8f7b14c46dcfa2896a71cc2b1 Mon Sep 17 00:00:00 2001 From: Gui Iribarren Date: Tue, 4 Jul 2023 13:59:19 +0200 Subject: [PATCH 2/9] mvp --- test/api_test.go | 159 ++++++++++++++++++++------------- test/benchmark_vochain_test.go | 27 ++++-- 2 files changed, 116 insertions(+), 70 deletions(-) diff --git a/test/api_test.go b/test/api_test.go index 60621dfe4..dc990247c 100644 --- a/test/api_test.go +++ b/test/api_test.go @@ -10,6 +10,7 @@ import ( qt "github.com/frankban/quicktest" "github.com/google/uuid" "go.vocdoni.io/dvote/api" + "go.vocdoni.io/dvote/apiclient" "go.vocdoni.io/dvote/crypto/ethereum" "go.vocdoni.io/dvote/data/ipfs" "go.vocdoni.io/dvote/test/testcommon" @@ -24,18 +25,38 @@ import ( type testElection struct { server testcommon.APIserver + c *testutil.TestHTTPclient election api.ElectionCreate censusData *api.Census - stx models.SignedTx - voterKey ethereum.SignKeys + + voters []voter +} + +type voter struct { + key *ethereum.SignKeys + proof *apiclient.CensusProof + vote *api.Vote } func TestAPIcensusAndVote(t *testing.T) { - te := testElection{} - te.CreateCensusAndElection(t, 10) + te := NewTestElection(t, 10) + te.CreateCensusAndElection(t) + // Block 2 + te.server.VochainAPP.AdvanceTestBlock() + + te.VoteAll(t) + + // Block 3 + te.server.VochainAPP.AdvanceTestBlock() + //waitUntilHeight(t, te.c, 3) + + te.VerifyVotes(t) } -func (te *testElection) CreateCensusAndElection(t testing.TB, nvotes int) { +func NewTestElection(t testing.TB, nvotes int) *testElection { + te := &testElection{} + + // Server te.server = testcommon.APIserver{} te.server.Start(t, api.ChainHandler, @@ -48,47 +69,57 @@ func (te *testElection) CreateCensusAndElection(t testing.TB, nvotes int) { // Block 1 te.server.VochainAPP.AdvanceTestBlock() + // Client token1 := uuid.New() - c := testutil.NewTestHTTPclient(t, te.server.ListenAddr, &token1) + te.c = testutil.NewTestHTTPclient(t, te.server.ListenAddr, &token1) + // Voters + for i := 0; i < nvotes; i++ { + k := ethereum.NewSignKeys() + qt.Assert(t, k.Generate(), qt.IsNil) + te.voters = append(te.voters, voter{key: k}) + } + + return te +} + +func (te *testElection) CreateCensusAndElection(t testing.TB) { // create a new census - resp, code := c.Request("POST", nil, "censuses", "weighted") + resp, code := te.c.Request("POST", nil, "censuses", "weighted") qt.Assert(t, code, qt.Equals, 200) te.censusData = &api.Census{} qt.Assert(t, json.Unmarshal(resp, te.censusData), qt.IsNil) id1 := te.censusData.CensusID.String() // add a bunch of keys and values (weights) - rnd := testutil.NewRandom(1) cparts := api.CensusParticipants{} - for i := 1; i < nvotes; i++ { + for _, voter := range te.voters { cparts.Participants = append(cparts.Participants, api.CensusParticipant{ - Key: rnd.RandomBytes(20), - Weight: (*types.BigInt)(big.NewInt(int64(1))), + Key: voter.key.Address().Bytes(), + Weight: (*types.BigInt)(big.NewInt(1)), }) } - _, code = c.Request("POST", &cparts, "censuses", id1, "participants") + _, code = te.c.Request("POST", &cparts, "censuses", id1, "participants") qt.Assert(t, code, qt.Equals, 200) - // add the key we'll use for cast votes - qt.Assert(t, te.voterKey.Generate(), qt.IsNil) - - _, code = c.Request("POST", &api.CensusParticipants{Participants: []api.CensusParticipant{{ - Key: te.voterKey.Address().Bytes(), - Weight: (*types.BigInt)(big.NewInt(1)), - }}}, "censuses", id1, "participants") - qt.Assert(t, code, qt.Equals, 200) - - resp, code = c.Request("POST", nil, "censuses", id1, "publish") + resp, code = te.c.Request("POST", nil, "censuses", id1, "publish") qt.Assert(t, code, qt.Equals, 200) qt.Assert(t, json.Unmarshal(resp, te.censusData), qt.IsNil) qt.Assert(t, te.censusData.CensusID, qt.IsNotNil) root := te.censusData.CensusID - resp, code = c.Request("GET", nil, "censuses", root.String(), "proof", fmt.Sprintf("%x", te.voterKey.Address().Bytes())) - qt.Assert(t, code, qt.Equals, 200) - qt.Assert(t, json.Unmarshal(resp, te.censusData), qt.IsNil) - qt.Assert(t, te.censusData.Weight.String(), qt.Equals, "1") + for i, voter := range te.voters { + censusData := &api.Census{} + resp, code = te.c.Request("GET", nil, "censuses", root.String(), + "proof", fmt.Sprintf("%x", voter.key.Address().Bytes())) + qt.Assert(t, code, qt.Equals, 200) + qt.Assert(t, json.Unmarshal(resp, censusData), qt.IsNil) + qt.Assert(t, censusData.Weight.String(), qt.Equals, "1") + te.voters[i].proof = &apiclient.CensusProof{ + Proof: censusData.Proof, + LeafValue: censusData.Value, + } + } metadataBytes, err := json.Marshal( &api.ElectionMetadata{ @@ -115,7 +146,7 @@ func (te *testElection) CreateCensusAndElection(t testing.TB, nvotes int) { VoteOptions: &models.ProcessVoteOptions{MaxCount: 1, MaxValue: 1}, EnvelopeType: &models.EnvelopeType{}, Metadata: &metadataURI, - MaxCensusSize: 1000, + MaxCensusSize: uint64(len(te.voters)), }, }, }, @@ -124,25 +155,28 @@ func (te *testElection) CreateCensusAndElection(t testing.TB, nvotes int) { qt.Assert(t, err, qt.IsNil) signedTxb, err := te.server.Account.SignVocdoniTx(txb, te.server.VochainAPP.ChainID()) qt.Assert(t, err, qt.IsNil) - te.stx = models.SignedTx{Tx: txb, Signature: signedTxb} - stxb, err := proto.Marshal(&te.stx) + stx := models.SignedTx{Tx: txb, Signature: signedTxb} + stxb, err := proto.Marshal(&stx) qt.Assert(t, err, qt.IsNil) - election := api.ElectionCreate{ + te.election = api.ElectionCreate{ TxPayload: stxb, Metadata: metadataBytes, } - resp, code = c.Request("POST", election, "elections") + resp, code = te.c.Request("POST", te.election, "elections") qt.Assert(t, code, qt.Equals, 200) - err = json.Unmarshal(resp, &election) + err = json.Unmarshal(resp, &te.election) qt.Assert(t, err, qt.IsNil) +} - // Block 2 - te.server.VochainAPP.AdvanceTestBlock() - waitUntilHeight(t, c, 2) +func (te *testElection) VoteAll(t testing.TB) { + for i, voter := range te.voters { + te.voters[i].vote = te.Vote(t, voter) + } } -func (te *testElection) Vote(t testing.TB, c *testutil.TestHTTPclient, nvotes int) { +// Vote sends a vote +func (te *testElection) Vote(t testing.TB, voter voter) *api.Vote { // Send a vote votePackage := &state.VotePackage{ Votes: []int{1}, @@ -159,44 +193,47 @@ func (te *testElection) Vote(t testing.TB, c *testutil.TestHTTPclient, nvotes in Payload: &models.Proof_Arbo{ Arbo: &models.ProofArbo{ Type: models.ProofArbo_BLAKE2B, - Siblings: te.censusData.Proof, - LeafWeight: te.censusData.Value, + Siblings: voter.proof.Proof, + LeafWeight: voter.proof.LeafValue, }, }, } - te.stx.Tx, err = proto.Marshal(&models.Tx{Payload: &models.Tx_Vote{Vote: vote}}) + stx := models.SignedTx{} + stx.Tx, err = proto.Marshal(&models.Tx{Payload: &models.Tx_Vote{Vote: vote}}) qt.Assert(t, err, qt.IsNil) - te.stx.Signature, err = te.voterKey.SignVocdoniTx(te.stx.Tx, te.server.VochainAPP.ChainID()) + stx.Signature, err = voter.key.SignVocdoniTx(stx.Tx, te.server.VochainAPP.ChainID()) qt.Assert(t, err, qt.IsNil) - stxb, err := proto.Marshal(&te.stx) + stxb, err := proto.Marshal(&stx) qt.Assert(t, err, qt.IsNil) v := &api.Vote{TxPayload: stxb} - resp, code := c.Request("POST", v, "votes") + resp, code := te.c.Request("POST", v, "votes") + if code == 500 { + t.Logf("%s", resp) + } qt.Assert(t, code, qt.Equals, 200) err = json.Unmarshal(resp, &v) qt.Assert(t, err, qt.IsNil) + return v +} - // Block 3 - te.server.VochainAPP.AdvanceTestBlock() - waitUntilHeight(t, c, 3) - - // Verify the vote - _, code = c.Request("GET", nil, "votes", "verify", te.election.ElectionID.String(), v.VoteID.String()) - qt.Assert(t, code, qt.Equals, 200) - - // Get the vote and check the data - resp, code = c.Request("GET", nil, "votes", v.VoteID.String()) - qt.Assert(t, code, qt.Equals, 200) - v2 := &api.Vote{} - err = json.Unmarshal(resp, v2) - qt.Assert(t, err, qt.IsNil) - qt.Assert(t, v2.VoteID.String(), qt.Equals, v.VoteID.String()) - qt.Assert(t, v2.BlockHeight, qt.Equals, uint32(2)) - qt.Assert(t, *v2.TransactionIndex, qt.Equals, int32(0)) +func (te *testElection) VerifyVotes(t testing.TB) { + for _, voter := range te.voters { + // Verify the vote + _, code := te.c.Request("GET", nil, "votes", "verify", + te.election.ElectionID.String(), voter.vote.VoteID.String()) + qt.Assert(t, code, qt.Equals, 200) - // TODO (painan): check why the voterID is not present on the reply - //qt.Assert(t, v2.VoterID.String(), qt.Equals, voterKey.AddressString()) + // Get the vote and check the data + resp, code := te.c.Request("GET", nil, "votes", voter.vote.VoteID.String()) + qt.Assert(t, code, qt.Equals, 200) + v2 := &api.Vote{} + err := json.Unmarshal(resp, v2) + qt.Assert(t, err, qt.IsNil) + qt.Assert(t, v2.VoteID.String(), qt.Equals, voter.vote.VoteID.String()) + qt.Assert(t, v2.BlockHeight, qt.Equals, uint32(2)) + qt.Assert(t, v2.VoterID.String(), qt.Equals, fmt.Sprintf("%x", voter.key.Address().Bytes())) + } } func TestAPIaccount(t *testing.T) { diff --git a/test/benchmark_vochain_test.go b/test/benchmark_vochain_test.go index 3f6a217c3..e5183d299 100644 --- a/test/benchmark_vochain_test.go +++ b/test/benchmark_vochain_test.go @@ -4,14 +4,23 @@ import ( "testing" ) -func BenchmarkCreateElectionAndVote10(b *testing.B) { - for n := 0; n < b.N; n++ { - CreateCensusAndElection(b, 10) - } -} +func BenchmarkVote(b *testing.B) { + te := NewTestElection(b, b.N) + te.CreateCensusAndElection(b) + + // Block 2 + te.server.VochainAPP.AdvanceTestBlock() + waitUntilHeight(b, te.c, 2) + + b.ResetTimer() + + te.VoteAll(b) + + b.StopTimer() + + // Block 3 + te.server.VochainAPP.AdvanceTestBlock() + waitUntilHeight(b, te.c, 3) -func BenchmarkCreateElectionAndVote100(b *testing.B) { - for n := 0; n < b.N; n++ { - CreateCensusAndElection(b, 100) - } + te.VerifyVotes(b) } From 4c06dfea0d22009250428ec29f370f4d35d06977 Mon Sep 17 00:00:00 2001 From: Gui Iribarren Date: Wed, 5 Jul 2023 10:05:30 +0200 Subject: [PATCH 3/9] ci: run benchmarks (job_go_bench) --- .github/workflows/main.yml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index d87a776ea..924c07c97 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -94,6 +94,18 @@ jobs: if: github.event_name == 'push' # this is limited to selected branches at the beginning of this file run: go test -coverprofile=unit.covdata.txt -vet=off -timeout=15m -race ./... # note that -race can easily make the crypto stuff 10x slower + job_go_bench: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v3 + - name: Set up Go environment + uses: actions/setup-go@v4 + with: + go-version: '1.20' + - name: Run Go bench + run: go test -run='^$' -bench=. ./... + job_compose_test: runs-on: [self-hosted, ci2-1] steps: From 858bff593e02dcc34d06a74668136f0f026a1ae3 Mon Sep 17 00:00:00 2001 From: Gui Iribarren Date: Tue, 11 Jul 2023 12:26:18 +0200 Subject: [PATCH 4/9] use b.Run() --- test/api_test.go | 13 ++++++++----- test/benchmark_vochain_test.go | 31 ++++++++++++++++++++++--------- 2 files changed, 30 insertions(+), 14 deletions(-) diff --git a/test/api_test.go b/test/api_test.go index dc990247c..70a1d4896 100644 --- a/test/api_test.go +++ b/test/api_test.go @@ -39,7 +39,8 @@ type voter struct { } func TestAPIcensusAndVote(t *testing.T) { - te := NewTestElection(t, 10) + te := NewTestElection(t) + te.GenerateVoters(t, 10) te.CreateCensusAndElection(t) // Block 2 te.server.VochainAPP.AdvanceTestBlock() @@ -53,7 +54,7 @@ func TestAPIcensusAndVote(t *testing.T) { te.VerifyVotes(t) } -func NewTestElection(t testing.TB, nvotes int) *testElection { +func NewTestElection(t testing.TB) *testElection { te := &testElection{} // Server @@ -72,15 +73,16 @@ func NewTestElection(t testing.TB, nvotes int) *testElection { // Client token1 := uuid.New() te.c = testutil.NewTestHTTPclient(t, te.server.ListenAddr, &token1) + return te +} +func (te *testElection) GenerateVoters(t testing.TB, nvotes int) { // Voters for i := 0; i < nvotes; i++ { k := ethereum.NewSignKeys() qt.Assert(t, k.Generate(), qt.IsNil) te.voters = append(te.voters, voter{key: k}) } - - return te } func (te *testElection) CreateCensusAndElection(t testing.TB) { @@ -99,7 +101,8 @@ func (te *testElection) CreateCensusAndElection(t testing.TB) { Weight: (*types.BigInt)(big.NewInt(1)), }) } - _, code = te.c.Request("POST", &cparts, "censuses", id1, "participants") + resp, code = te.c.Request("POST", &cparts, "censuses", id1, "participants") + t.Logf("%s", resp) qt.Assert(t, code, qt.Equals, 200) resp, code = te.c.Request("POST", nil, "censuses", id1, "publish") diff --git a/test/benchmark_vochain_test.go b/test/benchmark_vochain_test.go index e5183d299..a26e81235 100644 --- a/test/benchmark_vochain_test.go +++ b/test/benchmark_vochain_test.go @@ -5,12 +5,23 @@ import ( ) func BenchmarkVote(b *testing.B) { - te := NewTestElection(b, b.N) - te.CreateCensusAndElection(b) - - // Block 2 - te.server.VochainAPP.AdvanceTestBlock() - waitUntilHeight(b, te.c, 2) + te := &testElection{} + b.Run("NewTestElection", func(b *testing.B) { + te = NewTestElection(b) + }) + b.Run("GenerateVoters", func(b *testing.B) { + te.GenerateVoters(b, b.N) + }) + + b.Run("CreateCensusAndElection", func(b *testing.B) { + te.CreateCensusAndElection(b) + }) + + b.Run("AdvanceTestBlock2", func(b *testing.B) { + // Block 2 + te.server.VochainAPP.AdvanceTestBlock() + waitUntilHeight(b, te.c, 2) + }) b.ResetTimer() @@ -18,9 +29,11 @@ func BenchmarkVote(b *testing.B) { b.StopTimer() - // Block 3 - te.server.VochainAPP.AdvanceTestBlock() - waitUntilHeight(b, te.c, 3) + b.Run("AdvanceTestBlock3", func(b *testing.B) { + // Block 3 + te.server.VochainAPP.AdvanceTestBlock() + waitUntilHeight(b, te.c, 3) + }) te.VerifyVotes(b) } From 2d4ca83262944a2cc4a1711ab71733f3ea479a98 Mon Sep 17 00:00:00 2001 From: Gui Iribarren Date: Thu, 13 Jul 2023 11:12:42 +0200 Subject: [PATCH 5/9] api_test wip --- test/api_test.go | 53 ++++++++++++++++++++++++++++++++---------------- 1 file changed, 35 insertions(+), 18 deletions(-) diff --git a/test/api_test.go b/test/api_test.go index 70a1d4896..a3e5e9217 100644 --- a/test/api_test.go +++ b/test/api_test.go @@ -85,32 +85,49 @@ func (te *testElection) GenerateVoters(t testing.TB, nvotes int) { } } -func (te *testElection) CreateCensusAndElection(t testing.TB) { - // create a new census - resp, code := te.c.Request("POST", nil, "censuses", "weighted") - qt.Assert(t, code, qt.Equals, 200) - te.censusData = &api.Census{} - qt.Assert(t, json.Unmarshal(resp, te.censusData), qt.IsNil) - id1 := te.censusData.CensusID.String() +func (te *testElection) AddCensusParticipants(t testing.TB, id string) types.HexBytes { + // add a bunch of keys and values (weights) in batches of api.MaxCensusAddBatchSize + for i := 0; i < len(te.voters); i += api.MaxCensusAddBatchSize { + // Get the next chunk of voters + end := i + api.MaxCensusAddBatchSize + if end > len(te.voters) { + end = len(te.voters) + } - // add a bunch of keys and values (weights) - cparts := api.CensusParticipants{} - for _, voter := range te.voters { - cparts.Participants = append(cparts.Participants, api.CensusParticipant{ - Key: voter.key.Address().Bytes(), - Weight: (*types.BigInt)(big.NewInt(1)), - }) + // Add the voters in the chunk to the cparts struct + cparts := api.CensusParticipants{} + for _, voter := range te.voters[i:end] { + cparts.Participants = append(cparts.Participants, api.CensusParticipant{ + Key: voter.key.Address().Bytes(), + Weight: (*types.BigInt)(big.NewInt(1)), + }) + } + + t.Log("wtf") + // POST this chunk of voters + resp, code := te.c.Request("POST", &cparts, "censuses", id, "participants") + qt.Assert(t, code, qt.Equals, 200) + qt.Assert(t, resp, qt.IsNotNil) } - resp, code = te.c.Request("POST", &cparts, "censuses", id1, "participants") - t.Logf("%s", resp) - qt.Assert(t, code, qt.Equals, 200) - resp, code = te.c.Request("POST", nil, "censuses", id1, "publish") + resp, code := te.c.Request("POST", nil, "censuses", id, "publish") qt.Assert(t, code, qt.Equals, 200) qt.Assert(t, json.Unmarshal(resp, te.censusData), qt.IsNil) qt.Assert(t, te.censusData.CensusID, qt.IsNotNil) root := te.censusData.CensusID + return root +} + +func (te *testElection) CreateCensusAndElection(t testing.TB) { + // create a new census + resp, code := te.c.Request("POST", nil, "censuses", "weighted") + qt.Assert(t, code, qt.Equals, 200) + te.censusData = &api.Census{} + qt.Assert(t, json.Unmarshal(resp, te.censusData), qt.IsNil) + id1 := te.censusData.CensusID.String() + root := te.AddCensusParticipants(t, id1) + t.Log(root, id1) for i, voter := range te.voters { censusData := &api.Census{} resp, code = te.c.Request("GET", nil, "censuses", root.String(), From bb0b5e057727767b1e239c44a8a1f6de6f65a938 Mon Sep 17 00:00:00 2001 From: Gui Iribarren Date: Thu, 13 Jul 2023 13:22:21 +0200 Subject: [PATCH 6/9] mvp using b.Timer in different Benchmarks --- test/api_test.go | 13 +++-- test/apierror_test.go | 2 +- test/benchmark_vochain_test.go | 94 ++++++++++++++++++++++++++-------- test/testcommon/api.go | 43 ++++++++++------ 4 files changed, 105 insertions(+), 47 deletions(-) diff --git a/test/api_test.go b/test/api_test.go index a3e5e9217..b3a23147b 100644 --- a/test/api_test.go +++ b/test/api_test.go @@ -39,7 +39,7 @@ type voter struct { } func TestAPIcensusAndVote(t *testing.T) { - te := NewTestElection(t) + te := NewTestElection(t, t.TempDir()) te.GenerateVoters(t, 10) te.CreateCensusAndElection(t) // Block 2 @@ -54,12 +54,12 @@ func TestAPIcensusAndVote(t *testing.T) { te.VerifyVotes(t) } -func NewTestElection(t testing.TB) *testElection { +func NewTestElection(t testing.TB, datadir string) *testElection { te := &testElection{} // Server te.server = testcommon.APIserver{} - te.server.Start(t, + te.server.Start(t, datadir, api.ChainHandler, api.CensusHandler, api.VoteHandler, @@ -78,6 +78,7 @@ func NewTestElection(t testing.TB) *testElection { func (te *testElection) GenerateVoters(t testing.TB, nvotes int) { // Voters + te.voters = nil for i := 0; i < nvotes; i++ { k := ethereum.NewSignKeys() qt.Assert(t, k.Generate(), qt.IsNil) @@ -103,7 +104,6 @@ func (te *testElection) AddCensusParticipants(t testing.TB, id string) types.Hex }) } - t.Log("wtf") // POST this chunk of voters resp, code := te.c.Request("POST", &cparts, "censuses", id, "participants") qt.Assert(t, code, qt.Equals, 200) @@ -127,7 +127,6 @@ func (te *testElection) CreateCensusAndElection(t testing.TB) { id1 := te.censusData.CensusID.String() root := te.AddCensusParticipants(t, id1) - t.Log(root, id1) for i, voter := range te.voters { censusData := &api.Census{} resp, code = te.c.Request("GET", nil, "censuses", root.String(), @@ -183,6 +182,7 @@ func (te *testElection) CreateCensusAndElection(t testing.TB) { TxPayload: stxb, Metadata: metadataBytes, } + te.server.AccountInit(t) resp, code = te.c.Request("POST", te.election, "elections") qt.Assert(t, code, qt.Equals, 200) err = json.Unmarshal(resp, &te.election) @@ -251,14 +251,13 @@ func (te *testElection) VerifyVotes(t testing.TB) { err := json.Unmarshal(resp, v2) qt.Assert(t, err, qt.IsNil) qt.Assert(t, v2.VoteID.String(), qt.Equals, voter.vote.VoteID.String()) - qt.Assert(t, v2.BlockHeight, qt.Equals, uint32(2)) qt.Assert(t, v2.VoterID.String(), qt.Equals, fmt.Sprintf("%x", voter.key.Address().Bytes())) } } func TestAPIaccount(t *testing.T) { server := testcommon.APIserver{} - server.Start(t, + server.Start(t, t.TempDir(), api.ChainHandler, api.CensusHandler, api.VoteHandler, diff --git a/test/apierror_test.go b/test/apierror_test.go index f18c0e45a..bbc430920 100644 --- a/test/apierror_test.go +++ b/test/apierror_test.go @@ -15,7 +15,7 @@ import ( func TestAPIerror(t *testing.T) { server := testcommon.APIserver{} - server.Start(t, + server.Start(t, t.TempDir(), api.ChainHandler, api.CensusHandler, api.VoteHandler, diff --git a/test/benchmark_vochain_test.go b/test/benchmark_vochain_test.go index a26e81235..94f4c754c 100644 --- a/test/benchmark_vochain_test.go +++ b/test/benchmark_vochain_test.go @@ -4,36 +4,86 @@ import ( "testing" ) +func BenchmarkCreateCensus(b *testing.B) { + te := NewTestElection(b, b.TempDir()) + te.GenerateVoters(b, b.N) + + b.ResetTimer() + te.CreateCensusAndElection(b) + b.StopTimer() + + // Block 2 + te.server.VochainAPP.AdvanceTestBlock() + waitUntilHeight(b, te.c, 2) + + te.VoteAll(b) + + // Block 3 + te.server.VochainAPP.AdvanceTestBlock() + waitUntilHeight(b, te.c, 3) + + te.VerifyVotes(b) +} + func BenchmarkVote(b *testing.B) { - te := &testElection{} - b.Run("NewTestElection", func(b *testing.B) { - te = NewTestElection(b) - }) - b.Run("GenerateVoters", func(b *testing.B) { - te.GenerateVoters(b, b.N) - }) - - b.Run("CreateCensusAndElection", func(b *testing.B) { - te.CreateCensusAndElection(b) - }) - - b.Run("AdvanceTestBlock2", func(b *testing.B) { - // Block 2 - te.server.VochainAPP.AdvanceTestBlock() - waitUntilHeight(b, te.c, 2) - }) + te := NewTestElection(b, b.TempDir()) + te.GenerateVoters(b, b.N) + + te.CreateCensusAndElection(b) + + // Block 2 + te.server.VochainAPP.AdvanceTestBlock() + waitUntilHeight(b, te.c, 2) b.ResetTimer() + te.VoteAll(b) + b.StopTimer() + + // Block 3 + te.server.VochainAPP.AdvanceTestBlock() + waitUntilHeight(b, te.c, 3) + + te.VerifyVotes(b) +} + +func BenchmarkVerifyVotes(b *testing.B) { + te := NewTestElection(b, b.TempDir()) + te.GenerateVoters(b, b.N) + + te.CreateCensusAndElection(b) + + // Block 2 + te.server.VochainAPP.AdvanceTestBlock() + waitUntilHeight(b, te.c, 2) te.VoteAll(b) + // Block 3 + te.server.VochainAPP.AdvanceTestBlock() + waitUntilHeight(b, te.c, 3) + + b.ResetTimer() + te.VerifyVotes(b) b.StopTimer() +} + +func BenchmarkVoteAndVerifyN(b *testing.B) { + te := NewTestElection(b, b.TempDir()) + te.GenerateVoters(b, b.N) + + te.CreateCensusAndElection(b) + + // Block 2 + te.server.VochainAPP.AdvanceTestBlock() + waitUntilHeight(b, te.c, 2) - b.Run("AdvanceTestBlock3", func(b *testing.B) { - // Block 3 - te.server.VochainAPP.AdvanceTestBlock() - waitUntilHeight(b, te.c, 3) - }) + b.ResetTimer() + te.VoteAll(b) + + // Block 3 + te.server.VochainAPP.AdvanceTestBlock() + waitUntilHeight(b, te.c, 3) te.VerifyVotes(b) + b.StopTimer() } diff --git a/test/testcommon/api.go b/test/testcommon/api.go index ec2d81a43..677a125f9 100644 --- a/test/testcommon/api.go +++ b/test/testcommon/api.go @@ -32,35 +32,28 @@ type APIserver struct { } // Start starts a basic URL API server for testing -func (d *APIserver) Start(t testing.TB, apis ...string) { - // create the account signer - d.Account = ethereum.NewSignKeys() - if err := d.Account.Generate(); err != nil { - t.Fatal(err) - } - +func (d *APIserver) Start(t testing.TB, datadir string, apis ...string) { // create the IPFS storage d.Storage = &data.DataMockTest{} - d.Storage.Init(&types.DataStore{Datadir: t.TempDir()}) - + err := d.Storage.Init(&types.DataStore{Datadir: datadir}) + qt.Assert(t, err, qt.IsNil) + t.Log(datadir) // creeate the API router router := httprouter.HTTProuter{} - router.Init("127.0.0.1", 0) + err = router.Init("127.0.0.1", 0) + qt.Assert(t, err, qt.IsNil) + addr, err := url.Parse("http://" + router.Address().String() + "/") qt.Assert(t, err, qt.IsNil) d.ListenAddr = addr t.Logf("address: %s", addr.String()) - api, err := api.NewAPI(&router, "/", t.TempDir()) + + api, err := api.NewAPI(&router, "/", datadir) qt.Assert(t, err, qt.IsNil) // create vochain application d.VochainAPP = vochain.TestBaseApplication(t) - // create and add balance for the pre-created Account - err = d.VochainAPP.State.CreateAccount(d.Account.Address(), "", nil, 100000) - qt.Assert(t, err, qt.IsNil) - d.VochainAPP.Commit() - // create vochain info (we do not start since it is not required) d.VochainInfo = vochaininfo.NewVochainInfo(d.VochainAPP) @@ -68,7 +61,7 @@ func (d *APIserver) Start(t testing.TB, apis ...string) { d.Indexer = NewMockIndexer(t, d.VochainAPP) // create census database - db, err := metadb.New(db.TypePebble, t.TempDir()) + db, err := metadb.New(db.TypePebble, datadir) qt.Assert(t, err, qt.IsNil) censusDB := censusdb.NewCensusDB(db) @@ -78,4 +71,20 @@ func (d *APIserver) Start(t testing.TB, apis ...string) { // enable the required handlers err = api.EnableHandlers(apis...) qt.Assert(t, err, qt.IsNil) + + d.AccountInit(t) +} + +func (d *APIserver) AccountInit(t testing.TB) { + // create the account signer + d.Account = ethereum.NewSignKeys() + if err := d.Account.Generate(); err != nil { + t.Fatal(err) + } + + // create and add balance for the pre-created Account + err := d.VochainAPP.State.CreateAccount(d.Account.Address(), "", nil, 100000) + qt.Assert(t, err, qt.IsNil) + d.VochainAPP.Commit() + } From 59998e7fab4b8583e82a348015d274b55f216d12 Mon Sep 17 00:00:00 2001 From: Gui Iribarren Date: Thu, 13 Jul 2023 13:26:25 +0200 Subject: [PATCH 7/9] arbo: remove failing BenchmarkAdd --- tree/arbo/tree_test.go | 33 --------------------------------- 1 file changed, 33 deletions(-) diff --git a/tree/arbo/tree_test.go b/tree/arbo/tree_test.go index e2ed69205..ea69d896a 100644 --- a/tree/arbo/tree_test.go +++ b/tree/arbo/tree_test.go @@ -890,36 +890,3 @@ func TestKeyLenBiggerThan32(t *testing.T) { randomBytes(bLen)) c.Assert(err, qt.IsNil) } - -func BenchmarkAdd(b *testing.B) { - bLen := 32 // for both Poseidon & Sha256 - // prepare inputs - var ks, vs [][]byte - for i := 0; i < 1000; i++ { - k := BigIntToBytes(bLen, big.NewInt(int64(i))) - v := BigIntToBytes(bLen, big.NewInt(int64(i))) - ks = append(ks, k) - vs = append(vs, v) - } - - b.Run("Poseidon", func(b *testing.B) { - benchmarkAdd(b, HashFunctionPoseidon, ks, vs) - }) - b.Run("Sha256", func(b *testing.B) { - benchmarkAdd(b, HashFunctionSha256, ks, vs) - }) -} - -func benchmarkAdd(b *testing.B, hashFunc HashFunction, ks, vs [][]byte) { - c := qt.New(b) - database := metadb.NewTest(c) - tree, err := NewTree(Config{Database: database, MaxLevels: 140, - HashFunction: hashFunc}) - c.Assert(err, qt.IsNil) - - for i := 0; i < len(ks); i++ { - if err := tree.Add(ks[i], vs[i]); err != nil { - b.Fatal(err) - } - } -} From 83f2f52332285b057fa0942c9bbcd038b6a9a87f Mon Sep 17 00:00:00 2001 From: Gui Iribarren Date: Thu, 13 Jul 2023 13:27:50 +0200 Subject: [PATCH 8/9] vochain: remove failing BenchmarkCheckTx it's failing with: ERR vochain/app.go:287 > checkTx error="voteTx: maxCensusSize reached 0/0" app_benchmark_test.go:131: checkTX failed: checkTx voteTx: maxCensusSize reached 0/0 --- vochain/app_benchmark_test.go | 145 ---------------------------------- 1 file changed, 145 deletions(-) delete mode 100644 vochain/app_benchmark_test.go diff --git a/vochain/app_benchmark_test.go b/vochain/app_benchmark_test.go deleted file mode 100644 index ab668695c..000000000 --- a/vochain/app_benchmark_test.go +++ /dev/null @@ -1,145 +0,0 @@ -package vochain - -// go test -benchmem -run=^$ -bench=. -cpu=10 - -import ( - "fmt" - "sync/atomic" - "testing" - - abcitypes "github.com/cometbft/cometbft/abci/types" - "go.vocdoni.io/dvote/censustree" - "go.vocdoni.io/dvote/crypto/ethereum" - "go.vocdoni.io/dvote/db/metadb" - "go.vocdoni.io/dvote/types" - "go.vocdoni.io/dvote/util" - "go.vocdoni.io/dvote/vochain/state" - "go.vocdoni.io/proto/build/go/models" - "google.golang.org/protobuf/proto" -) - -const benchmarkVoters = 2000 - -func BenchmarkCheckTx(b *testing.B) { - b.ReportAllocs() - app := TestBaseApplication(b) - var voters [][]*models.SignedTx - for i := 0; i < b.N+1; i++ { - voters = append(voters, prepareBenchCheckTx(b, app, benchmarkVoters, b.TempDir())) - } - var i int32 - b.ResetTimer() - b.RunParallel(func(pb *testing.PB) { - for pb.Next() { - b.Logf("Running vote %d", i) - benchCheckTx(b, app, voters[atomic.AddInt32(&i, 1)]) - } - }) -} - -func prepareBenchCheckTx(b *testing.B, app *BaseApplication, - nvoters int, tmpDir string) (voters []*models.SignedTx) { - db := metadb.NewTest(b) - tr, err := censustree.New(censustree.Options{Name: util.RandomHex(12), ParentDB: db, - MaxLevels: 256, CensusType: models.Census_ARBO_BLAKE2B}) - if err != nil { - b.Fatal(err) - } - - keys := ethereum.NewSignKeysBatch(nvoters) - if keys == nil { - b.Fatal("cannot create keys batch") - } - claims := []string{} - for _, k := range keys { - c := k.Address().Bytes() - if err := tr.Add(c, nil); err != nil { - b.Error(err) - } - claims = append(claims, string(c)) - } - censusURI := ipfsUrlTest - pid := util.RandomBytes(types.ProcessIDsize) - root, err := tr.Root() - if err != nil { - b.Fatal(err) - } - - if err := app.State.AddProcess(&models.Process{ - ProcessId: pid, - StartBlock: 0, - EnvelopeType: &models.EnvelopeType{EncryptedVotes: false}, - Mode: &models.ProcessMode{Interruptible: true, AutoStart: true}, - Status: models.ProcessStatus_READY, - EntityId: util.RandomBytes(types.EthereumAddressSize), - CensusRoot: root, - CensusURI: &censusURI, - CensusOrigin: models.CensusOrigin_OFF_CHAIN_TREE, - BlockCount: 1024, - }); err != nil { - b.Error(err) - } - - vp, err := state.NewVotePackage([]int{1, 2, 3}).Encode() - if err != nil { - b.Error(err) - } - - var proof []byte - for i, s := range keys { - _, proof, err = tr.GenProof([]byte(claims[i])) - if err != nil { - b.Fatal(err) - } - tx := &models.VoteEnvelope{ - Nonce: util.RandomBytes(16), - ProcessId: pid, - Proof: &models.Proof{Payload: &models.Proof_Graviton{ - Graviton: &models.ProofGraviton{Siblings: proof}}}, - VotePackage: vp, - } - - stx := models.SignedTx{} - if stx.Tx, err = proto.Marshal(&models.Tx{Payload: &models.Tx_Vote{Vote: tx}}); err != nil { - b.Fatal(err) - } - if stx.Signature, err = s.SignVocdoniTx(stx.Tx, app.chainID); err != nil { - b.Fatal(err) - } - voters = append(voters, &stx) - } - return voters -} - -func benchCheckTx(b *testing.B, app *BaseApplication, voters []*models.SignedTx) { - var cktx abcitypes.RequestCheckTx - var detx abcitypes.RequestDeliverTx - - var cktxresp abcitypes.ResponseCheckTx - var detxresp abcitypes.ResponseDeliverTx - - var err error - var txBytes []byte - i := 0 - for _, tx := range voters { - if txBytes, err = proto.Marshal(tx); err != nil { - b.Fatal(err) - } - cktx.Tx = txBytes - cktxresp = app.CheckTx(cktx) - if cktxresp.Code != 0 { - b.Fatalf(fmt.Sprintf("checkTX failed: %s", cktxresp.Data)) - } else { - detx.Tx = txBytes - detxresp = app.DeliverTx(detx) - if detxresp.Code != 0 { - b.Fatalf(fmt.Sprintf("deliverTX failed: %s", detxresp.Data)) - } - } - i++ - if i%100 == 0 { - app.Commit() - } - } - app.Commit() -} From c17ff68743941a2b9a89e5deeb379f9b5906deba Mon Sep 17 00:00:00 2001 From: Gui Iribarren Date: Thu, 13 Jul 2023 13:29:46 +0200 Subject: [PATCH 9/9] indexer: remove failing BenchmarkFetchTx it's failing with: --- FAIL: BenchmarkFetchTx bench_test.go:157: error: got non-nil error got: e"transaction not found" stack: /home/runner/work/vocdoni-node/vocdoni-node/vochain/indexer/bench_test.go:157 qt.Assert(b, err, qt.IsNil) --- vochain/indexer/bench_test.go | 35 ----------------------------------- 1 file changed, 35 deletions(-) diff --git a/vochain/indexer/bench_test.go b/vochain/indexer/bench_test.go index 4303b5594..8f6837881 100644 --- a/vochain/indexer/bench_test.go +++ b/vochain/indexer/bench_test.go @@ -1,7 +1,6 @@ package indexer import ( - "fmt" "math/big" "sync" "testing" @@ -127,40 +126,6 @@ func BenchmarkIndexer(b *testing.B) { } } -func BenchmarkFetchTx(b *testing.B) { - numTxs := 1000 - app := vochain.TestBaseApplication(b) - - idx := newTestIndexer(b, app, true) - - b.ReportAllocs() - b.ResetTimer() - for i := 0; i < b.N; i++ { - for j := 0; j < numTxs; j++ { - idx.OnNewTx(&vochaintx.Tx{TxID: util.Random32()}, uint32(i), int32(j)) - } - err := idx.Commit(uint32(i)) - qt.Assert(b, err, qt.IsNil) - - time.Sleep(time.Second * 2) - - startTime := time.Now() - for j := 0; j < numTxs; j++ { - _, err = idx.GetTransaction(uint64((i * numTxs) + j + 1)) - qt.Assert(b, err, qt.IsNil) - } - log.Infof("fetched %d transactions (out of %d total) by index, took %s", - numTxs, (i+1)*numTxs, time.Since(startTime)) - startTime = time.Now() - for j := 0; j < numTxs; j++ { - _, err = idx.GetTxHashReference([]byte(fmt.Sprintf("hash%d%d", i, j))) - qt.Assert(b, err, qt.IsNil) - } - log.Infof("fetched %d transactions (out of %d total) by hash, took %s", - numTxs, (i+1)*numTxs, time.Since(startTime)) - } -} - func BenchmarkNewProcess(b *testing.B) { app := vochain.TestBaseApplication(b)